<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>my.log</title>
    <link>https://yerang2.tistory.com/</link>
    <description>성장중...</description>
    <language>ko</language>
    <pubDate>Mon, 13 Apr 2026 17:03:34 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>머랑</managingEditor>
    <image>
      <title>my.log</title>
      <url>https://tistory1.daumcdn.net/tistory/3934614/attach/40e20fba6823421fbf28633cd9086592</url>
      <link>https://yerang2.tistory.com</link>
    </image>
    <item>
      <title>[C++] 1525: 퍼즐</title>
      <link>https://yerang2.tistory.com/88</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;방문처리를 맵 전체를 담기에는 메모리를 많이 잡아먹을 것 같고..&lt;/li&gt;
&lt;li&gt;하지만 0의 위치만으로는 방문처리를 정확히 할 수 없음..&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전체 상태를 하나의 키로 저장하기 위해서 set을 이용함.&lt;/li&gt;
&lt;li&gt;1 2 3&lt;br /&gt;4 5 6&lt;br /&gt;7 0 8 이면 -&amp;gt; &lt;b&gt;&quot;123456708&quot;&lt;/b&gt;을 set에 등록해서 방문 처리처럼 사용함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};

int bfs(string start) {
    unordered_set&amp;lt;string&amp;gt; visited;
    queue&amp;lt;pair&amp;lt;string, int&amp;gt;&amp;gt; q;

    visited.insert(start);
    q.push({start, 0});
    while (!q.empty()) {
        auto [mapStr, cnt] = q.front();
        q.pop();

        if (mapStr == &quot;123456780&quot;) return cnt;

        int zeroIdx = mapStr.find('0');
        int x = zeroIdx / 3;
        int y = zeroIdx % 3;

        for (int dir = 0; dir &amp;lt; 4; dir++) {
            int mx = x + dx[dir];
            int my = y + dy[dir];

            if (mx &amp;lt; 0 || mx &amp;gt;= 3 || my &amp;lt; 0 || my &amp;gt;= 3) continue;

            int nextZeroIdx = mx * 3 + my;

            string next = mapStr;
            swap(next[zeroIdx], next[nextZeroIdx]);

            if (visited.find(next) == visited.end()) {
                visited.insert(next);
                q.push({next, cnt+1});
            }
        }
    }

    return -1;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int x, y;
    string start = &quot;&quot;;
    for (int i = 0; i &amp;lt; 9; i++) {
        int num;
        cin &amp;gt;&amp;gt; num;
        start += to_string(num);
    }

    cout &amp;lt;&amp;lt; bfs(start) &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/88</guid>
      <comments>https://yerang2.tistory.com/88#entry88comment</comments>
      <pubDate>Tue, 24 Jun 2025 17:19:51 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 다익스트라와 플로이드 와샬 알고리즘</title>
      <link>https://yerang2.tistory.com/87</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;차이&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;다익스트라&lt;/b&gt; : &lt;b&gt;하나의 시작점&lt;/b&gt;에서 &lt;b&gt;다른 모든 정점까지&lt;/b&gt;의 최소 거리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매번 가장 적은 비용을 가진 노드를 하나씩 꺼내, 가장 적은 비용을 하나씩 선택함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;플로이드 와샬&lt;/b&gt; : &lt;b&gt;모든 시작점&lt;/b&gt;에서 에서 &lt;b&gt;다른 모든 정점까지&lt;/b&gt;의 최단 거리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애초에 거쳐가는 정점을 하나씩 다 설정해서 직접 확인함. 거쳐가는 정점을 기준으로 최단 거리를 구성함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;우선순위 큐를 이용한 다익스트라&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우선순위큐 + BFS의 형태를 가진다.&lt;/li&gt;
&lt;li&gt;각 정점까지의 최단거리를 저장하는 배열 dp[]를 유지하고, 정점을 방문할 때마다 인접한 정점을 모두 검사한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1750228946741&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;

const int INF = 1e9;
int V, E, start;

void dijkstra(int start, vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt;&amp;amp; graph) {
	// 노드별 거리 저장용
	vector&amp;lt;int&amp;gt; dist(V+1, INF);
    // 우선순위 큐로 cost가 낮은 순으로 정렬됨
    priority_queue&amp;lt;pair&amp;lt;int, int&amp;gt;, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;, greater&amp;lt;&amp;gt;&amp;gt; pq;
    
	dist[start] = 0;
    pq.push({0, start});
    
    while (!pq.empty()) {
    	auto [cost, now] = pq.top();
        pq.pop();
        
        if (dist[now] &amp;lt; cost) continue;
        
        // 현재 노드에서 이어진 다음 노드 탐색
        for (const auto&amp;amp; [next, weight] : graph[now]) {
        	// 여태 기록해놨던 거리보다 더 적은 비용으로 갈 수 있으면 큐에 push
        	if (dist[next] &amp;gt; cost + weight) {
            	dist[next] = cost + weight;
                pq.push({dist[next], next);
            }
        }
    }
    
    // 결과 출력
    for (int i=1; i&amp;lt;=V; i++) {
    	if (dist[i] == INF) cout &amp;lt;&amp;lt; &quot;INF\n&quot;;
        else cout &amp;lt;&amp;lt; dist[i] &amp;lt;&amp;lt; '\n';
    }
}

int main() {
	cin &amp;gt;&amp;gt; V &amp;gt;&amp;gt; E;
    vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; graph(V+1);
    
    for (int i=0; i&amp;lt;E; i++) {
    	int a, b, cost;
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; cost;
        graph[a].push_back({cost, b});
        // 양방향일 경우 b-&amp;gt;a도 고려해주어야 함.
    }
    
    cin &amp;gt;&amp;gt; start;
    dijkstra(start, graph);
    
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모든 쌍 간의 최단거리 구하는 플로이드 와샬&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경유점이라는 개념을 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 정점 u, v를 잇는 경로가 있다고 할 때, 이 경로는 시작점 u, 끝점 v를 가진다. 이 외에 이 경로는 다른 정점들을 지나쳐서 갈 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[u, n, m, p, v]도 u와 v를 잇는 경로이다. 여기서 [n, m p] 가 경유점이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 경유점을 이용해서 더 짧은 거리를 찾는 방법이 플로이드 와샬이다.&lt;/p&gt;
&lt;pre id=&quot;code_1750237104303&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;

const int INF = 1e9;
int V, E;

int main() {
	cin &amp;gt;&amp;gt; V &amp;gt;&amp;gt; E;
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dist(V+1, INF);
    
    // 자기 자신까지의 거리는 0
    for (int i=1; i&amp;lt;=V; i++) dist[i][i] = 0;
    
    for (int i=0; i&amp;lt;E; i++) {
    	int a, b, cost;
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; cost;
        dist[a][b] = c;
    }
    
    // 플로이드 와샬 알고리즘
    for (int k=1; k&amp;lt;=V; k++) {
    	for (int i=1; i&amp;lt;=V; i++) {
        	for(int j=1; j&amp;lt;=V; j++) {
            	// i -&amp;gt; k에 갈 수 있고, k -&amp;gt; j로 갈 수 있으면
            	if (dist[i][k] != INF &amp;amp;&amp;amp; dist[k][j] != INF) {
                	dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
                }
            }
        }
    }
    
    // 결과 출력
    for (int i=1; i&amp;lt;=V; i++) {
    	for (int j=1; j&amp;lt;=V; j++) {
        	if (dist[i][j] == INF) cout &amp;lt;&amp;lt; &quot;INF &quot;;
            else cout &amp;lt;&amp;lt; dist[i][j] &amp;lt;&amp;lt; ' ';
        }
        cout &amp;lt;&amp;lt; '\n';
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/87</guid>
      <comments>https://yerang2.tistory.com/87#entry87comment</comments>
      <pubDate>Wed, 18 Jun 2025 19:13:25 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 10830: 행렬 제곱</title>
      <link>https://yerang2.tistory.com/86</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;5 * 5 * 100,000,000,000 = &lt;b&gt;2조5천억번&lt;/b&gt;의 제곱연산...&lt;/li&gt;
&lt;li&gt;1억번이 1~2초니까 1조번의 연산이면 10000~20000초임(시간초과가 안나는게 이상)&lt;/li&gt;
&lt;/ul&gt;
&lt;p 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-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지금은 계속 원본 행렬만 곱하는데 a2 * a = a3.. 이런 식으로 &lt;b&gt;원본 행렬의 제곱끼리 곱하는 방식&lt;/b&gt;을 이용하는건 어떨까?&lt;/li&gt;
&lt;li&gt;단, 2의 제곱수가 아닌 &lt;b&gt;5와 같은 수&lt;/b&gt;면 a2 * a2 * a라는 걸 어떻게 계산해서 구하지? log연산을 해야하는건가?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10이면 a4 * a4 * a2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2조 5천억은 2의 42제곱이라고 함. 그러니까 연산 횟수를 줄일 수 있을 것 같음.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1750134475153&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;

int N;
using Matrix = vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;;

Matrix multiply(Matrix&amp;amp; A, Matrix&amp;amp; B) {
    Matrix result(N, vector&amp;lt;int&amp;gt;(N, 0));
    for (int i=0; i&amp;lt;N; i++) {
        for (int j=0; j&amp;lt;N; j++) {
            for (int k=0; k&amp;lt;N; k++) {
                result[i][j] = (result[i][j] + A[i][k] * B[k][j]) % 1000;
            }
        }
    }
    return result;
}

Matrix power(Matrix A, long long exp) {
    if (exp == 1) {
        // 모든 원소를 1000으로 나눠줌
        for (int i=0; i&amp;lt;N; i++) {
            for (int j=0; j&amp;lt;N; j++) {
                A[i][j] %= 1000;
            }
        }
        return A;
    }

    Matrix half = power(A, exp / 2);
    Matrix result = multiply(half, half);
    if (exp % 2 == 1) {
        result = multiply(result, A);
    }
    return result;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    long long B;
    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; B;

    Matrix A(N, vector&amp;lt;int&amp;gt;(N));
    for (int i=0; i&amp;lt;N; i++) {
        for (int j=0; j&amp;lt;N; j++) {
            cin &amp;gt;&amp;gt; A[i][j];
        }
    }

    Matrix answer = power(A, B);
    for (const auto&amp;amp; row : answer) {
        for (int val : row) {
            cout &amp;lt;&amp;lt; val % 1000 &amp;lt;&amp;lt; ' ';
        }
        cout &amp;lt;&amp;lt; '\n';
    }
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  시간 초과되었던 기존 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int N, B;
    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; B;
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; v(N, vector&amp;lt;int&amp;gt;(N, 0));
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; accuV(N, vector&amp;lt;int&amp;gt;(N, 0));
    for (int i=0; i&amp;lt;N; i++) {
        for (int j=0; j&amp;lt;N; j++) {
            cin &amp;gt;&amp;gt; v[i][j];
            accuV[i][j] = v[i][j];
        }
    }

    for (int t=0; t&amp;lt;B - 1; t++) {
        vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; newV(N, vector&amp;lt;int&amp;gt;(N, 0));
        for (int i=0; i&amp;lt;N; i++) {
            for (int j=0; j&amp;lt;N; j++) {
                // i행과 j열 각각의 원소의 곱 더한거
                int sum = 0;
                for (int k=0; k&amp;lt;N; k++) {
                    sum += (accuV[i][k] * v[k][j]) % 1000;
                    // cout &amp;lt;&amp;lt; v[i][k] &amp;lt;&amp;lt; &quot; * &quot; &amp;lt;&amp;lt; v[k][j] &amp;lt;&amp;lt; &quot; = &quot; &amp;lt;&amp;lt; v[i][k] * v[k][j] &amp;lt;&amp;lt; '\n';
                }
                newV[i][j] = sum % 1000;
            }
        }
        accuV = newV;
    }

    for (int i=0; i&amp;lt;N; i++) {
        for (int j=0; j&amp;lt;N; j++) {
            cout &amp;lt;&amp;lt; accuV[i][j] &amp;lt;&amp;lt; ' ';
        }
        cout &amp;lt;&amp;lt; '\n';
    }
    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/86</guid>
      <comments>https://yerang2.tistory.com/86#entry86comment</comments>
      <pubDate>Tue, 17 Jun 2025 13:28:54 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 함수 타입</title>
      <link>https://yerang2.tistory.com/85</link>
      <description>&lt;p&gt;함수 타입은 어떤 &amp;quot;타입의&amp;quot; 매개변수를 받고, 어떤 &amp;quot;타입의&amp;quot; 결과값을 반환하는지 표현한다.&lt;br&gt;반환값 타입은 생략해도 알아서 추론하기 때문에 생략해도 괜찮다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function func(a: number, b: number) {
    return a + b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;화살표 함수의 타입&lt;/h2&gt;
&lt;p&gt;일반 함수에서와 동일하게 반환값 타입은 생략해도 알아서 추론하기 때문에 생략해도 괜찮다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const add = (a: number, b: number): number =&amp;gt; a + b;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;필수 매개변수와 선택적 매개변수&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function intruduce(name = &amp;quot;머랑&amp;quot;, tall: number) {
    console.log(`name : ${name}`);
      if (typeof tall === &amp;quot;number&amp;quot;) {
        console.log(`tall : ${tall}`);
    }
}
introduce(&amp;quot;머랑이&amp;quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;매개변수명 뒤에 &lt;code&gt;?&lt;/code&gt;를 붙여서 선택적 매개변수로 설정할 수 있다.&lt;br&gt;단, 선택적 매개변수를 이용할 때는 타입가드를 붙여주어 에러를 방지하자.&lt;br&gt;그리고 선택적 매개변수는 항상 필수 매개변수보다 뒷쪽에 위치해야 한다.&lt;/p&gt;
&lt;h2&gt;가변 길이 매개변수&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;functino getSum(...rest: number[]) {
    let sum = 0;
    rest.forEach((it) =&amp;gt; (sum += it));
    return sum;
}
getSum(1, 2, 3); // 6
getSum(1, 2, 3, 4, 5); // 15&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;만약, 매개변수 길이를 3개로 고정시키고 싶으면 튜플 타입을 이용하면 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;functino getSum(...rest: [number, number, number]) {
    let sum = 0;
    rest.forEach((it) =&amp;gt; (sum += it));
    return sum;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Language/TypeScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/85</guid>
      <comments>https://yerang2.tistory.com/85#entry85comment</comments>
      <pubDate>Tue, 27 May 2025 14:11:14 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 9935: 문자열 폭발</title>
      <link>https://yerang2.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간 초과&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;substr로 폭발 이전꺼 + 폭발 이후꺼 를 잘라서 더했더니 시간 초과가 발생했다.&lt;/li&gt;
&lt;li&gt;result배열에 하나씩 추가하며 폭발 시 폭발 문자열 길이만큼만 지우도록 하여 시간 초과를 해결했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    string str, boom;
    cin &amp;gt;&amp;gt; str &amp;gt;&amp;gt; boom;

    string result = &quot;&quot;;
    int boomLen = boom.length();

    for (char ch : str) {
        result += ch;
        if (result.length() &amp;gt;= boomLen) {
            if (result.substr(result.length() - boomLen) == boom) {
                result.erase(result.end() - boomLen, result.end());
            }
        }
    }

    cout &amp;lt;&amp;lt; (result.empty() ? &quot;FRULA&quot; : result) &amp;lt;&amp;lt; '\n';
    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/84</guid>
      <comments>https://yerang2.tistory.com/84#entry84comment</comments>
      <pubDate>Tue, 13 May 2025 15:49:07 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 5639: 이진 검색 트리</title>
      <link>https://yerang2.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;풀이 방법을 생각해내지 못해서 결국 검색해서 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로, 입력 종료를 위해 EOF를 입력하려면 맥(리눅스)기준으로 &lt;b&gt;Cmd + D&lt;/b&gt;를 입력하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전위 순회로 &lt;b&gt;30 24 5 28 45&lt;/b&gt; 다음과 같은 트리가 있을 때,&lt;/li&gt;
&lt;li&gt;30(루트)을 기준으로 더 큰 값이 나오기 전까지는 모두 왼쪽 자식 노드에 해당한다. &lt;b&gt;&lt;u&gt;30&lt;/u&gt; (24 5 28) 45&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;그리고 나머지는 오른쪽 자식 트리에 해당하게 된다.&lt;/li&gt;
&lt;li&gt;이 탐색을 재귀적으로 반복해서, 탐색하는 범위가 1개가 되는 경우나 맨 마지막 노드일 경우에 출력해주면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

vector&amp;lt;int&amp;gt; v;

void printPost(int start, int end) {
    if (start &amp;gt;= end) return;
    if (start == end-1) {
        cout &amp;lt;&amp;lt; v[start] &amp;lt;&amp;lt; '\n';
        return;
    }

    // 현재 노드보다 더 큰 값이 등장하면 그 전까지 왼쪽 자식
    // 그 노드는 오른쪽 자식임 -&amp;gt; 현재보다 더 커지는 인덱스 찾음
    int idx = start + 1;
    while (idx &amp;lt; end) {
        if (v[start] &amp;lt; v[idx]) break;
        idx++;
    }

    // 왼 -&amp;gt; 오 -&amp;gt; 본인 순서로 출력해야 하므로 순서대로 호출
    printPost(start + 1, idx);
    printPost(idx, end);
    cout &amp;lt;&amp;lt; v[start] &amp;lt;&amp;lt; '\n';
    return;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    while (cin &amp;gt;&amp;gt; n) {
        v.push_back(n);
    }

    printPost(0, v.size());

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/83</guid>
      <comments>https://yerang2.tistory.com/83#entry83comment</comments>
      <pubDate>Wed, 30 Apr 2025 15:33:58 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 서로소 유니온 타입 : 묶어서 선언할 때 유의할 점</title>
      <link>https://yerang2.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서&lt;b&gt; 1번 방법&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;Failed의 경우에 Error가 포함된다는 점&lt;/li&gt;
&lt;li&gt;Success에서 response가 포함된다는 점&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 processResult 함수에서도 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;task.error?&lt;/span&gt; 나 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;task.error!&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;2번 방법&lt;/b&gt;과 같이, 각 타입을 정의하고 해당 타입을 |으로 엮어서 서로소 유니온 타입을 표현하는 것이 가독성과 관리 면에서 더 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1745828580909&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 비동기 작업의 결과를 처리하는 객체

// 1️⃣번 방법
type AsyncTask = {
  state: &quot;SUCCESS&quot; | &quot;FAILED&quot; | &quot;LOADING&quot;;
  error?: { ... };
  response?: { ... };
};

// 2️⃣번 방법
type LoadingTask = {
  state: &quot;LOADING&quot;;
};
type FailedTask = {
  state: &quot;FAILED&quot;;
  error: {
    message: string;
  };
};
type SuccessTask = {
  state: &quot;SUCCESS&quot;;
  response: {
    data: string;
  };
};
type AsyncTask = LoadingTask | FailedTask | SuccessTask; //서로소 유니언 타입이다!

// 로딩중 -&amp;gt; 콘솔에 로딩중 출력
// 실패 -&amp;gt; 실패 : 에러메세지 출력
// 성공 -&amp;gt; 성공 : 데이터를 출력
function processResult(task: AsyncTask) {
  switch (task.state) {
    case &quot;LOADING&quot;: {
      console.log(`로딩 중`);
      break;
    }
    case &quot;FAILED&quot;: {
      console.log(`에러발생 : ${task.error.message}`);
      break;
    }
    case &quot;SUCCESS&quot;: {
      console.log(`성공 : ${task.response.data}`);
      break;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/TypeScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/82</guid>
      <comments>https://yerang2.tistory.com/82#entry82comment</comments>
      <pubDate>Mon, 28 Apr 2025 17:26:51 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] instanceof와 in 타입 가드</title>
      <link>https://yerang2.tistory.com/81</link>
      <description>&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;type Person = {
  name: string;
  age: number;
};

function func(value: number | string | Date | null | Person) {
  if (typeof value === &quot;number&quot;) {
    console.log(value.toFixed());
  } else if (typeof value === &quot;string&quot;) {
    console.log(value.toUpperCase());
  } else if (value instanceof Date) {
    console.log(value.getTime());
  } else if (value &amp;amp;&amp;amp; &quot;age&quot; in value) {
    console.log(`${value.name}은 ${value.age}살 입니다`)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;instanceof 타입가드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;instanceof를 이용하면 &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;code&gt;Instanceof&lt;/code&gt;는 &lt;b&gt;내장 클래스 또는 직접 만든 클래스에만 사용이 가능&lt;/b&gt;한 연산이다. 따라서 직접 만든 타입(예시에서는 &lt;code&gt;Person&lt;/code&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;in 타입 가드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 직접 만든 타입과 함께 사용하려면 다음과 같이 in 연산자를 이용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 프로퍼티가 포함되어있는지 확인할 수 있지만 해당 객체 자체의 존재 여부는 확인하지 않기 때문에 &lt;code&gt;value &amp;amp;&amp;amp;&lt;/code&gt; 를 붙여서 확인해주어야 한다.&lt;/p&gt;</description>
      <category>Language/TypeScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/81</guid>
      <comments>https://yerang2.tistory.com/81#entry81comment</comments>
      <pubDate>Mon, 28 Apr 2025 17:16:09 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 1753: 최단경로 (priority_queue 선언법)</title>
      <link>https://yerang2.tistory.com/80</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다익스트라로 풀었는데 시간 초과가 발생했다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;priority_queue의 기본값&lt;/b&gt;은&lt;b&gt; 내림차순 정렬&lt;/b&gt;인 것을 망각했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  priority_queue 선언 방법&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1745376786529&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;
    class T,
    class Container = std::vector&amp;lt;T&amp;gt;,
    class Compare = std::less&amp;lt;typename Container::value_type&amp;gt;
&amp;gt; class priority_queue;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인자 설명
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;T&lt;/b&gt; : 정렬할 요소의 타입, 큐에 저장할 타입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Container&lt;/b&gt; :내부적으로 데이터를 어떻게 저장할지를 나타내는 컨테이너 타입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Compare&lt;/b&gt; :요소를 정렬할 기준이 되는 함수 객체(함수 포인터 또는 functor)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;less&amp;lt;T&amp;gt;&lt;/b&gt;&lt;/span&gt;: 큰 값을 먼저 꺼냄 &amp;rarr; Max Heap &lt;span style=&quot;background-color: #f5f5a6;&quot;&gt;(기본값)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;greater&amp;lt;T&amp;gt;&lt;/b&gt;&lt;/span&gt;: 작은 값을 먼저 꺼냄 &amp;rarr; Min Heap&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그래서 다음과 같이 선언하면 내림차순이 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;priority_queue&amp;lt;pair&amp;lt;int, int&amp;gt;, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;, greater&amp;lt;&amp;gt;&amp;gt; pq;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;

using namespace std;

const int INF = 1e7;
int V, E, K; // 정점 수 (1~), 간선 개수, 시작 정점 번호

void dajikstra(vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; &amp;amp;graph, vector&amp;lt;int&amp;gt; &amp;amp;dist) {
    priority_queue&amp;lt;pair&amp;lt;int, int&amp;gt;, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;, greater&amp;lt;&amp;gt;&amp;gt; pq; // 정점, 누적 거리
    pq.push({0, K});
    dist[K] = 0;
    while (!pq.empty()) {
        auto [sumDir, node] = pq.top();
        pq.pop();

        for (const auto &amp;amp;[next, dir] : graph[node]) {
            if (dist[next] == INF || dist[next] &amp;gt; sumDir + dir) {
                dist[next] = sumDir + dir;
                pq.push({sumDir + dir, next});
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    cin &amp;gt;&amp;gt; V &amp;gt;&amp;gt; E &amp;gt;&amp;gt; K;
    vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; graph(V+1);
    vector&amp;lt;int&amp;gt; dist(V+1, INF);

    // vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; graph[E];
    for (int i=0; i&amp;lt;E; i++) {
        int u, v, w;
        // u -&amp;gt; v, 가중치 w
        cin &amp;gt;&amp;gt; u &amp;gt;&amp;gt; v &amp;gt;&amp;gt; w;
        graph[u].push_back({v, w});
    }

    // 각 정점까지 최단경로 저장 시작점은 0
    dajikstra(graph, dist);

    for (int i=1; i&amp;lt;=V; i++) {
        if (dist[i] == INF) {
            cout &amp;lt;&amp;lt; &quot;INF&quot; &amp;lt;&amp;lt; '\n';
        }
        else {
            cout &amp;lt;&amp;lt; dist[i] &amp;lt;&amp;lt; '\n';
        }
    }

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/80</guid>
      <comments>https://yerang2.tistory.com/80#entry80comment</comments>
      <pubDate>Wed, 23 Apr 2025 12:01:15 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 타입 단언</title>
      <link>https://yerang2.tistory.com/79</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;타입 단언이 뭘까&lt;/h2&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;type Person = {
  name: string;
  age: number;
};

let person: Person = {}; // ❌ 에러 발생
person.name = &quot;&quot;;
person.age = 23;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 코드가 있다고 해보자. Person 타입으로 선언했지만, 빈 객체로 초기화했기 때문에 에러가 발생한다.&lt;br /&gt;이럴 때 이용하는 것이 &lt;b&gt;타입 단언&lt;/b&gt;이다. 타입 단언을 이용하면 빈 객체도 Person 타입으로 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;type Person = {
  name: string;
  age: number;
};

let person = {} as Person;
person.name = &quot;&quot;;
person.age = 23; &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;초과 프로퍼티에서도 유용!&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 어쩔 수 없이 기존 타입에 특정 프로퍼티를 추가해야 할 때도 이용 가능하다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;type Dog = {
  name: string;
  color: string;
};

let dog: Dog = {
  name: &quot;돌돌이&quot;,
  color: &quot;brown&quot;,
  breed: &quot;진도&quot;,
} as Dog&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;타입 단언의 규칙&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A가 B의 슈퍼타입이거나&lt;/li&gt;
&lt;li&gt;A가 B의 서브타입이어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;let num1 = 10 as never;   // ✅ 10은 never의 슈퍼타입 (업 캐스팅)
let num2 = 10 as unknown; // ✅ 10은 unknown의 슈퍼타입 (업 캐스팅)

let num3 = 10 as string;  // ❌ number와 string 타입은 슈퍼-서브 관계가 아니다. 겹치지 않는다.
let num3 = 10 as unknown as string; // ✅ 다중 단언을 이용하면 통과한다. 근데 쓰지말자.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;const 단언&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 변수를 const로 선언한 것 처럼 만들어주는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 객체 타입에서 사용된다. 모든 프로퍼티가 읽기 전용이 된다. 매번 &lt;code&gt;readonly&lt;/code&gt;를 붙여줄 필요가 없다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;let num4 = 10 as const;
// 10 Number Literal 타입으로 단언됨

let cat = {
  name: &quot;야옹이&quot;,
  color: &quot;yellow&quot;,
} as const;
// 모든 프로퍼티가 readonly를 갖도록 단언됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Non null 단언&lt;/h2&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;Non Null 단언은 지금까지 살펴본 값 as 타입 형태를 따르지 않는 단언이다. 값 뒤에 느낌표(!) 를 붙여주면 &quot;이 값은 &lt;code&gt;undefined&lt;/code&gt;이거나 &lt;code&gt;null&lt;/code&gt;이 아니야!&quot; 하고 단언할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;type Post = {
  title: string;
  author?: string;
};

let post: Post = {
  title: &quot;게시글1&quot;,
};

const len: number = post.author!.length;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Language/TypeScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/79</guid>
      <comments>https://yerang2.tistory.com/79#entry79comment</comments>
      <pubDate>Tue, 22 Apr 2025 18:05:21 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 객체 타입 간 호환성</title>
      <link>https://yerang2.tistory.com/78</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;객체에도 슈퍼타입이 있다.&lt;/b&gt;&lt;/h2&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;Animal에 Dog 타입을 대입하는 것은 되지만&lt;/li&gt;
&lt;li&gt;Dog에 Animal 타입을 대입하는 것은 불가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 &lt;b&gt;Animal 타입이 슈퍼타입이기 때문&lt;/b&gt;이다. 모든 객체 타입은 각각 다른 객체 타입들과 슈퍼-서브 타입 관계를 갖는다. 객체 타입도 마찬가지로 업캐스팅은 되고, 다운캐스팅은 안되기 때문에 서브타입인 Dog에 슈퍼타입인 Animal을 대입할 수 없다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;type Animal = {
  name: string;
  color: string;
};

type Dog = {
  name: string;
  color: string;
  breed: string;
};

let animal: Animal = {
  name: &quot;기린&quot;,
  color: &quot;yellow&quot;,
};

let dog: Dog = {
  name: &quot;돌돌이&quot;,
  color: &quot;brown&quot;,
  breed: &quot;진도&quot;,
};

animal = dog; // ✅ OK
dog = animal; // ❌ NO&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;초과 프로퍼티 검사&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 코드가 있을 때, Book은 슈퍼타입이므로, book2 변수 선언 시 서브타입에 해당하는 값으로 초기화하려고 하면 오류가 발생한다.&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;lsquo;&lt;b&gt;초과 프로퍼티 검사&lt;/b&gt;&amp;rsquo;가 발생했기 때문이다.&amp;nbsp;초과 프로퍼티 검사란 &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;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;type Book = {
  name: string;
  price: number;
};

type ProgrammingBook = {
  name: string;
  price: number;
  skill: string;
};

(...)

let book2: Book = { // 오류 발생
  name: &quot;한 입 크기로 잘라먹는 리액트&quot;,
  price: 33000,
  skill: &quot;reactjs&quot;,
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대입을 가능하게 하려면 &lt;b&gt;서브타입에 값을 대입한 후에, 해당 서브타입 객체를 슈퍼타입에 대입하는 방법&lt;/b&gt;을 이용해야 한다.&lt;/p&gt;</description>
      <category>Language/TypeScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/78</guid>
      <comments>https://yerang2.tistory.com/78#entry78comment</comments>
      <pubDate>Tue, 22 Apr 2025 16:41:00 +0900</pubDate>
    </item>
    <item>
      <title>[모던 자바스크립트] 27장 배열 요점 정리</title>
      <link>https://yerang2.tistory.com/77</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;배열이란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열이란 여러개의 값을 순차적으로 나열한 자료구조.&lt;/li&gt;
&lt;li&gt;배열의 값은 요소(element)라고 부름.&lt;/li&gt;
&lt;li&gt;배열은 length 프로퍼티를 가짐&lt;/li&gt;
&lt;li&gt;배열은 배열 리터럴, Array 생성자 함수, Array.of, Array.from 메서드로 생성할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자바스크립트 배열은 배열이 아니다&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자료구조에서 말하는 배열은 동일한 크기의 메모리 공간이 빈틈없이 연속적으로 나열된 자료구조를 말함.&lt;/li&gt;
&lt;li&gt;이러한 배열을 &lt;b&gt;밀집 배열&lt;/b&gt;이라고 함.&lt;/li&gt;
&lt;li&gt;하지만 자바스크립트의 배열은
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열의 요소를 위한 각각의 메모리 공간은 동일한 크기를 갖지 않아도 되며&lt;/li&gt;
&lt;li&gt;연속적으로 이어져있지 않을 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이렇게 연속적이지 않은 배열을 &lt;b&gt;희소 배열&lt;/b&gt;이라고 한다.&lt;/li&gt;
&lt;li&gt;자바스크립트의 배열은 일반적인 배열의 동작을 흉내낸 &lt;b&gt;특수한 객체&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;배열의 요소는 사실 프로퍼티 값이다.&lt;/li&gt;
&lt;li&gt;어떤 타입의 값이라도 배열의 요소가 될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일반적인 배열과 자바스크립트 배열의 장단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적인 배열은 인덱스로 요소에 빠르게 접근하지만, 삽입과 삭제 시에는 비효율적이다.&lt;/li&gt;
&lt;li&gt;자바스크립트 배열은 해시테이블로 구현된 객체이므로, 인덱스로 요소에 접근하는 경우 느리지만, &lt;b&gt;요소를 삽입 삭제하는 경우 일반 배열보다 빠르다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;인덱스로 배열에 요소에 접근할 때 일반적인 배열보다 느릴 수 밖에 없는 단점을 보완하려고&lt;/li&gt;
&lt;li&gt;일반 객체보다는 더 배열처럼 동작하도록 최적화하긴 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;length 프로퍼티와 희소 배열&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;length 프로퍼티에 임의의 숫자 값을 명시적으로 할당할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 length값보다 작은 값을 할당하면 배열의 길이가 줄어든다.&lt;/li&gt;
&lt;li&gt;큰 값을 할당하면 배열의 길이가 늘어나진 않고, 희소 배열이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;큰 값을 할당한 뒤&lt;/b&gt; console.log로 찍어보면 &lt;code&gt;[1, empty * 2]&lt;/code&gt;와 같이 있는 듯 보이지만, &lt;b&gt;실제 배열에는 아무런 변함이 없다&lt;/b&gt;. 값 없이 비어있는 요소를 위해 메모리 공간을 확보하지 않고, 빈 요소를 생성하지도 않는다.&lt;/li&gt;
&lt;li&gt;희소 배열은 요소가 연속적이지 않고, 일부가 비어있는 배열이다.&lt;/li&gt;
&lt;li&gt;희소 배열은 length와 배열 요소의 개수가 일치하지 않는다. length가 개수보다 항상 ㅋ다.&lt;/li&gt;
&lt;li&gt;되도록 사용하지 않는 것이 좋다.&lt;/li&gt;
&lt;li&gt;배열에는 같은 타입의 요소를 연속적으로 위치시키는 것이 최선이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배열 생성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;new Array()&lt;/code&gt; : 전달된 인수가 2개 이상이거나, 숫자가 아닌 경우 인수를 요소로 갖는 배열 생성&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Array.of()&lt;/code&gt; : ES6 도입, 전달된 인수가 1개이고, 숫자이더라도 배열 생성 가능&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Array.from()&lt;/code&gt; : ES6 도입, 유사 배열 객체 또는 이터러블 객체를 인수로 전달받아 배열로 변환하여 반환.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두번째 인수로 전달한 콜백함수를 통해 &lt;b&gt;값을 만들면서 요소를 채울 수 있음&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(_, i) =&amp;gt; i&lt;/code&gt; 를 콜백으로 전달하면 요소 값으로 인덱스에 해당하는 값이 할당됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배열 요소의 삭제(delete와 splice)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;delete 연산자로 삭제하면 배열은 희소 배열이 된다. length값도 변하지 않는다.&lt;/li&gt;
&lt;li&gt;따라서, Array.prototype.splice를 사용하자.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;arr.splice(1, 1);&lt;/code&gt; arr[1]부터 1개의 요소를 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배열 메서드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결과물을 반환하는 패턴이 두 가지이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원본 배열을 직접 변경하는 메서드 : mutator method&lt;/li&gt;
&lt;li&gt;원본 배열 직접 변경 안하고, 새로운 배열 생성하여 반환하는 메서드 : accessor method&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.indexOf&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원본 배열에 인수로 전달된 요소를 검색하여 인덱스를 반환한다.&lt;/li&gt;
&lt;li&gt;인수로 전달한 요소가 여러 개 있다면 첫 번째로 검색된 요소의 인덱스를 반환한다.&lt;/li&gt;
&lt;li&gt;원본 배열에 인수로 전달한 요소가 없으면 -1을 반환한다.
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;if (foods.indexOf('Orange') === -1) {
  foods.push('Orange');
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;ES7에서 가독성 더 좋게 쓸 수 있는 Array.prototype.includes 메서드가 도입됐다.
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;if (!foods.includes('Orange')) {
  foods.push('Orange');
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.push ⭐️&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;push메서드는 성능 면에서 좋지 않다.&lt;/li&gt;
&lt;li&gt;마지막 요소로 추가할 요소가 하나뿐이면 push보다 length를 이용하자. &lt;b&gt;훨씬 빠르다.&lt;/b&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const arr = [1, 2];
arr[arr.length] = 3;
console.log(arr); // [1, 2, 3]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;ES6의 스프레드 문법을 사용하는 것도 좋다. (원본 배열을 직접 변경하지 않으므로)
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const arr = [1, 2];
const newArr = [...arr, 3];&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;맨 앞에 요소를 추가하는 unshift의 경우에도 스프레드 문법을 사용하는 것이 좋다.
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const arr = [2, 3];
const newArr = [1, ...arr];&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.concat&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인수로 전달된 값들을 원본 배열의 마지막 요소로 추가한 새로운 배열을 반환&lt;/li&gt;
&lt;li&gt;원본 배열은 변경되지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.splice&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원본 &lt;b&gt;배열의 중간에 요소를 추가&lt;/b&gt;하거나, 중간에 있는 요소를 &lt;b&gt;제거&lt;/b&gt;하는 경우 사용&lt;/li&gt;
&lt;li&gt;매개변수
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;제거 시작 인덱스&lt;/li&gt;
&lt;li&gt;제거할 요소의 개수&lt;/li&gt;
&lt;li&gt;삽입할 요소들의 목록&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;두 번째 인수를 0으로 지정하면 아무런 요소도 제거하지 않고 세 번째 요소를 삽입함.&lt;/li&gt;
&lt;li&gt;두 번째 인수를 생략하면 첫 번째 인수로 전달된 시작 인덱스부터 모든 요소를 제거함.&lt;/li&gt;
&lt;li&gt;세 번째 요소를 전달하지 않으면 제거만 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Arrray.prototype.slice&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인수로 전달된 범위의 요소들을 &lt;b&gt;복사하여 배열로 반환&lt;/b&gt;함.&lt;/li&gt;
&lt;li&gt;원본 배열은 변경되지 않음&lt;/li&gt;
&lt;li&gt;첫 번째 인수로 전달받은 인덱스부터 모든 요소를 복사하여 배열로 반환함.&lt;/li&gt;
&lt;li&gt;인수를 모두 생략하면 원본 배열의 복사본을 생성하여 반환함. (얕은 복사)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.join&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원본 배열의 모든 요소를 문자열로 변환한 후, 구분자로 연결한 문자열을 반환함.&lt;/li&gt;
&lt;li&gt;기본 구분자는 &lt;code&gt;,&lt;/code&gt; 콤마다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.reverse&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;reverse 메서드는 순서를 반대로 뒤집는다.&lt;/li&gt;
&lt;li&gt;원본 배열이 변경된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.fill&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인수로 전달받은 값을 배열의 처음부터 끝까지 요소로 채운다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.includes&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 요소가 포함되어 있는지 확인하여 true 또는 false를 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.flat&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ES10(2019)에서 도입되었다.&lt;/li&gt;
&lt;li&gt;인수로 전달한 깊이만큼 재귀적으로 배열을 평탄화한다.&lt;/li&gt;
&lt;li&gt;평탄화할 깊이를 인수로 전달할 수 있는데, 생략하면 기본값은 1이다. &lt;code&gt;Infinity&lt;/code&gt;를 주면 모두 평탄화한다.
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;[1, [2, [3, [4]]]].flat(); // -&amp;gt; [1, 2,[3, [4]]]
[1, [2, [3, [4]]]].flat(2); // -&amp;gt; [1, 2, 3, [4]]
[1, [2, [3, [4]]]].flat().flat(); // -&amp;gt; [1, 2, 3, [4]]
[1, [2, [3, [4]]]].flat(Infinity); // -&amp;gt; [1, 2, 3, 4]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배열 고차 함수&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고차함수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수를 인수로 전달받거나 함수를 반환하는 함수&lt;/li&gt;
&lt;li&gt;JS의 함수는 일급 객체이므로 함수를 갚처럼 인수로 전달, 반환이 가능함.&lt;/li&gt;
&lt;li&gt;외부 상태의 변경이나 가변 데이터를 피하고 &lt;b&gt;불변성을 지향&lt;/b&gt;하는 함수형 프로그래밍에 기반을 둠.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;함수형 프로그래밍
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순수 함수와 보조 함수의 조합을 통해 로직 내에 존재하는 &lt;b&gt;조건문과 반복문을 제거&lt;/b&gt;하여 복잡성을 해결하고 &lt;b&gt;변수의 사용을 억제&lt;/b&gt;하여 상태 변경을 피하려는 프로그래밍 패러다임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;✏️ 특히 배열에서는 매우 유용한 고차함수들을 제공하기 때문에 알아두면 좋다!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.sort&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원본 배열을 직접 변경하여 정렬된 배열을 반환한다.&lt;/li&gt;
&lt;li&gt;기본적으로 오름차순이다.&lt;/li&gt;
&lt;li&gt;내림차순으로 정렬하려면 &lt;code&gt;reverse&lt;/code&gt;메서드를 사용해서 뒤집어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  주의할 점!&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자 요소로 이루어진 배열을 정렬할 때는 주의가 필요하다.
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;const points = [40, 100, 1, 5, 2, 25, 10];
points.sort();
console.log(points); // [1, 10, 100, 2, 25, 40, 5]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;의도한대로 정렬되지 않는다... &lt;/li&gt;
&lt;li&gt;sort 메서드는 &lt;b&gt;배열의 요소를 일시적으로 문자열로 변환한 후 정렬&lt;/b&gt;한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'1'은 유니코드로 &lt;code&gt;U+0031&lt;/code&gt;이다&lt;/li&gt;
&lt;li&gt;'2'는 &lt;code&gt;U+0032&lt;/code&gt;다.&lt;/li&gt;
&lt;li&gt;'10'은 &lt;code&gt;U+0031U+0030&lt;/code&gt;이다.&lt;/li&gt;
&lt;li&gt;-&amp;gt; 그래서 &lt;code&gt;['2', '10']&lt;/code&gt;를 정렬하면 &lt;code&gt;['10', '2']&lt;/code&gt;가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;따라서, 숫자 요소를 정렬할 때에는 &lt;b&gt;sort 메서드에 정렬 순서를 정의하는 비교 함수를 인수로 전달&lt;/b&gt;해야 한다.&lt;/li&gt;
&lt;li&gt;비교 함수의 반환값이 0보다 작으면 첫 번째 인수가 앞으로 온다.
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const points = [40, 100, 1, 5, 2, 25, 10];
points.sort((a, b) =&amp;gt; a - b); // 오름차순
points.sort((a, b) =&amp;gt; b - a); // 내림차순
points.sort((a, b) =&amp;gt; a &amp;lt; b); // ❌ -&amp;gt; 이렇게 쓰면 안된다.&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;비교 함수는 양수나 음수 또는 0을 반환해야 한다. 그래서 위 예제의 마지막줄이 안된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;객체의 정렬&lt;/h4&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;const todos = [
    {id : 3, name : &quot;JS&quot;},
    {id : 1, name : &quot;HTML&quot;},
    {id : 2, name : &quot;CSS&quot;}
]

function compare(key) {
    return (a, b) =&amp;gt; (a[key] &amp;gt; b[key] ? 1 : (a[key] &amp;lt; b[key] ? -1 : 0));
}
todos.sort(compare('id')); // id를 기준으로 오름차순 정렬&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.forEach&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자신을 호출한 배열을 순회하면서 수행해야 할 처리를 콜백 함수로 전달받아 반복 호출한다.&lt;/li&gt;
&lt;li&gt;콜백함수는 일만 함수로 호출되므로, 내부의 this는 undefined를 가리킨다. (클래스 내부의 모든 코드에서는 암묵적으로 strict mode가 적용되기 때문이다.)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;this를 사용하고 싶다면 직접 전달해주거나, 화살표 함수를 사용하면 된다. 화살표 함수를 쓰자.&lt;/li&gt;
&lt;li&gt;화살표 함수를 사용하면 상위 스코프의 this를 그대로 참조한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;break, continue문을 사용할 수 없다.&lt;/li&gt;
&lt;li&gt;희소 배열의 경우 존재하지 않는 요소는 순회 대상에서 제외된다. (map, filter, reduce도 마찬가지다.)&lt;/li&gt;
&lt;li&gt;for문에 비해 성능은 떨어지지만, 가독성은 더 좋다. 높은 성능이 필요한 경우가 아니면 forEach 메서드를 쓰는 것을 권장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.map&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열의 보든 요소를 순회하며 콜백 함수를 실행하고, 그 반환값들로 구성된 새로운 배열을 반환한다.&lt;/li&gt;
&lt;li&gt;요소값을 다른 값으로 매핑한 새로운 배열을 생성하기 위한 고차함수다.&lt;/li&gt;
&lt;li&gt;forEach와 동일하게 this 접근은 명시해주어야 하니 화살표 함수를 쓰자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.filter&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;콜백 함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환한다.&lt;/li&gt;
&lt;li&gt;forEach, map과 동일하게 this 접근은 명시해주어야 하니 화살표 함수를 쓰자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array.prototype.reduce&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;얘는 좀 더 어렵다. 집중해서 보자.&lt;/li&gt;
&lt;li&gt;자신을 호출한 배열의 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출한다.&lt;/li&gt;
&lt;li&gt;그리고 콜백 함수의 반환값을 다음 순회 시에 첫 번째 인수로 전달하면서 콜백 함수를 호출하고&lt;/li&gt;
&lt;li&gt;&lt;b&gt;하나의 결과값을 만들어 반환&lt;/b&gt;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;// 1 ~ 4까지 누적합
const sum = [1, 2, 3, 4];
sum.reduce((accumulator, currentValue, index, array) =&amp;gt; accumulator + currentValue, 0);

console.log(sum); // 10&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;accumulator&lt;/code&gt; : 초기값 또는 콜백 함수의 이전 반환값&lt;/li&gt;
&lt;li&gt;&lt;code&gt;currentValue&lt;/code&gt; : reduce 메서드를 호출한 배열의 요소값&lt;/li&gt;
&lt;li&gt;&lt;code&gt;index&lt;/code&gt; : reduce 메서드를 호출한 배열의 인덱스&lt;/li&gt;
&lt;li&gt;&lt;code&gt;array&lt;/code&gt; : reduce 메서드를 호출한 배열 자체 (this)&lt;/li&gt;
&lt;li&gt;, 초기값 : 생략이 가능하지만 전달하는 것이 안전하므로, 전달하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 방법으로 활용이 가능한데, 몇 가지만 살펴보자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;평균 구하기&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const sum = [1, 2, 3, 4, 5, 6];
const average = values.reduce((acc, cur, i, { length }) =&amp;gt; {
    return i === length - 1 ? (acc + cur) / length : acc + cur;
}, 0);

console.log(average); // 3.5
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;중복 횟수 구하기&lt;/h4&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const fruits = ['banana', 'apple', 'orange', 'orange', 'apple'];

const count = fruits.reduce((acc, cur) =&amp;gt; {
    acc[cur] = (acc[cur] || 0) + 1;
    return acc;
}, {});

console.log(count); // {banana: 1, apple: 2, orange: 2}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;중복 요소 제거하기&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const values = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];

const result = values.reduce(
    (unique, val, i, _values) =&amp;gt;
        _values.indexOf(val) === i ? [...unique, val] : unique,
    []
);
console.log(result); // [1, 2, 3, 5, 4];&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사실 이건 filter를 쓰는게 더 직관적이다.&lt;/li&gt;
&lt;li&gt;저자는 set을 사용하는 방법을 추천한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에도 reduce로 다음과 같은 작업을 할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최대값, 최소값&lt;/li&gt;
&lt;li&gt;중첩 배열 평탄화&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Language/JavaScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/77</guid>
      <comments>https://yerang2.tistory.com/77#entry77comment</comments>
      <pubDate>Wed, 16 Apr 2025 21:47:13 +0900</pubDate>
    </item>
    <item>
      <title>[React] useRef는 뭐고, 언제 쓸까</title>
      <link>https://yerang2.tistory.com/76</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;검색창에 검색어를 입력했을 때 자동완성을 띄워주는 다음과 같은 UI를 개발중이었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1Vcpw/btsNn2bxwyy/KR8yqad7uYL30QMtPxN5KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1Vcpw/btsNn2bxwyy/KR8yqad7uYL30QMtPxN5KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1Vcpw/btsNn2bxwyy/KR8yqad7uYL30QMtPxN5KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1Vcpw%2FbtsNn2bxwyy%2FKR8yqad7uYL30QMtPxN5KK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;434&quot; height=&quot;138&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색창에서 포커스가 아웃되면 드롭다운된 자동완성 리스트를 안보이게 만들어줘야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 검색창 컴포넌트와 자동완성 리스트 컴포넌트는 각기 다른 컴포넌트로 분리되어있다.&lt;/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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;부모 컴포넌트에 isFocused라는 상태값을 하나 만들어두고&lt;/li&gt;
&lt;li&gt;검색창 컴포넌트에서 onFocus시에 isFocused를 true로,&lt;/li&gt;
&lt;li&gt;onBlur시에 isFocused를 false로 바꿔준다.&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;하지만 LLM에게 물어봤을 때, useRef를 사용하는 방법으로 안내를 해주었다. 공부를 위해 useRef가 뭔지 부터, 내가 떠올린 방법과 useRef를 사용하는 방법은 어떻게 다른지 탐구해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;useRef란&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useRef는 react의 훅 중 하나로, DOM 요소에 직접 접근할 수 있게 해준다.&lt;/li&gt;
&lt;li&gt;값이 변경되어도 리렌더링을 하지 않는다. &lt;b&gt;(useState와 다른 점)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;내가 생각한 방법과 어떻게 다른가?&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. useRef + addEventListener&lt;/h3&gt;
&lt;pre id=&quot;code_1744792775863&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const searchContainerRef = useRef(null);
const [isSearchFocused, setIsSearchFocused] = useState(false);

useEffect(() =&amp;gt; {
  const handleClickOutside = (event: MouseEvent) =&amp;gt; {
    if (ref.current &amp;amp;&amp;amp; !ref.current.contains(event.target as Node)) {
      // 참조 중인 DOM 요소가 있고, 클릭한 요소가 ref에 포함되는 요소이면
      setIsFocused(false);
    }
  };
  document.addEventListener(&quot;mousedown&quot;, handleClickOutside);
  return () =&amp;gt; document.removeEventListener(&quot;mousedown&quot;, handleClickOutside);
}, []);

// 중략

{/* 검색창 JSX */}
&amp;lt;div ref={searchContainerRef} className=&quot;w-full md:w-[400px] z-10&quot;&amp;gt;
  &amp;lt;SearchSmall onFocus={() =&amp;gt; setIsSearchFocused(true)} /&amp;gt;
  {isSearchFocused &amp;amp;&amp;amp; (
    &amp;lt;UserSearchResultList isLoading={isLoading} isError={isError} data={data} /&amp;gt;
  )}
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;jsx요소에 ref를 등록하여 사용한다.&lt;/li&gt;
&lt;li&gt;클릭 이벤트가 발생했는지 document.addEventListener로 감지하고, 발생 시 handleClickOutside가 실행된다.&lt;/li&gt;
&lt;li&gt;해당 함수 안에서는 ref 요소가 있으면, 클릭한 요소가 ref에 포함되는 요소이면 setIsFocused를 false로 만들어 자동완성 컴포넌트를 disable한다.&lt;/li&gt;
&lt;li&gt;그래서 &lt;b&gt;&quot;특정 영역 바깥을 클릭했을 때&quot;라는 조건 검사가 가능&lt;/b&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;2. onFocus, onBlur&lt;span&gt;&amp;nbsp;&lt;/span&gt;props 전달&lt;/h3&gt;
&lt;pre id=&quot;code_1744793673067&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;input onFocus={() =&amp;gt; setIsFocused(true)} onBlur={() =&amp;gt; setIsFocused(false)} /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 Input 요소에 대해서만 focus와 blur로만 판단하기 때문에, &lt;b&gt;자동완성 영역을 클릭해도 자동완성 리스트가 사라진다.&lt;/b&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;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;추가로 useRef를 활용 가능한 사례&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;버튼 클릭했을 때 input에 포커싱하기&lt;/li&gt;
&lt;li&gt;렌더링에 영향을 받지 않고 값을 저장하기 때문에, 렌더링 시점마다 &lt;span&gt;state&lt;/span&gt;는 새로운 값으로 바뀌므로 이전 값을 비교하기 어려울 때 렌더링 이전 값을 저장해둘 수 있음&lt;/li&gt;
&lt;li&gt;드래그 도중 마우스 위치를 추적해야 할 때 (state로 하면 너무 자주 렌더링되기 때문에!)&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>FE/React</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/76</guid>
      <comments>https://yerang2.tistory.com/76#entry76comment</comments>
      <pubDate>Wed, 16 Apr 2025 18:04:23 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 17070: 파이프 옮기기 1</title>
      <link>https://yerang2.tistory.com/75</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 번 지나간 길을 다시 안가는 문제가 있었다. visited[방향][x][y] 배열 때문이었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최단거리가 아니므로, visited 배열이 필요가 없다. bfs로 모든 루트를 돌면서 도달하는 cnt만 재면 되었던 문제다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;

int N, cnt = 0;
bool board[16][16];

enum DIR {
    HOR,
    VER,
    DIAG
};

enum MAP {
    EMPTY,
    WALL
};

struct Pipe {
    int x;
    int y;
    int dir;
};

bool checkEnd(Pipe p) {
    if (p.dir == HOR &amp;amp;&amp;amp; p.x == N-1 &amp;amp;&amp;amp; p.y == N-2) return true;
    else if (p.dir == VER &amp;amp;&amp;amp; p.x == N-2 &amp;amp;&amp;amp; p.y == N-1) return true;
    else if (p.dir == DIAG &amp;amp;&amp;amp; p.x == N-2 &amp;amp;&amp;amp; p.y == N-2) return true;
    return false;
}


void bfs() {
    queue&amp;lt;Pipe&amp;gt; q;
    q.push({0, 0, HOR});

    while (!q.empty()) {
        Pipe now = q.front();
        q.pop();
        if (checkEnd(now)) {
            cnt++;
            continue;
        }

        if (now.dir == HOR) { // 가로
            // 가로 이동
            if (now.y+2 &amp;lt; N &amp;amp;&amp;amp; board[now.x][now.y+2] == EMPTY) {
                q.push({now.x, now.y+1, HOR});
            }
            // 대각 이동
            if (now.y+2 &amp;lt; N &amp;amp;&amp;amp; now.x+1 &amp;lt; N &amp;amp;&amp;amp;
                board[now.x][now.y+2] == EMPTY &amp;amp;&amp;amp; board[now.x+1][now.y+1] == EMPTY &amp;amp;&amp;amp;
                board[now.x+1][now.y+2] == EMPTY) {
                q.push({now.x, now.y+1, DIAG});
            }
        }
        else if (now.dir == VER) { // 세로
            // 세로 이동
            if (now.x+2 &amp;lt; N &amp;amp;&amp;amp; board[now.x+2][now.y] == EMPTY) {
                q.push({now.x+1, now.y, VER});
            }
            // 대각 이동
            if (now.x+2 &amp;lt; N &amp;amp;&amp;amp; now.y+1 &amp;lt; N &amp;amp;&amp;amp;
                board[now.x+2][now.y] == EMPTY &amp;amp;&amp;amp; board[now.x+1][now.y+1] == EMPTY &amp;amp;&amp;amp;
                board[now.x+2][now.y+1] == EMPTY) {
                q.push({now.x+1, now.y, DIAG});
            }
        }
        else if (now.dir == DIAG){ // 대각선
            // 가로 이동
            if (now.x+1 &amp;lt; N &amp;amp;&amp;amp; now.y+2 &amp;lt; N &amp;amp;&amp;amp;
                board[now.x+1][now.y+2] == EMPTY) {
                q.push({now.x+1, now.y+1, HOR});
            }
            // 세로 이동
            if (now.x+2 &amp;lt; N &amp;amp;&amp;amp; now.y+1 &amp;lt; N &amp;amp;&amp;amp;
                board[now.x+2][now.y+1] == EMPTY) {
                q.push({now.x+1, now.y+1, VER});
            }
            // 대각 이동
            if (now.x+2 &amp;lt; N &amp;amp;&amp;amp; now.y+2 &amp;lt; N &amp;amp;&amp;amp;
                board[now.x+1][now.y+2] == EMPTY &amp;amp;&amp;amp; board[now.x+2][now.y+1] == EMPTY &amp;amp;&amp;amp;
                board[now.x+2][now.y+2] == EMPTY) {
                q.push({now.x+1, now.y+1, DIAG});
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    cin &amp;gt;&amp;gt; N;
    for (int i=0; i&amp;lt;N; i++) {
        for (int j=0; j&amp;lt;N; j++) {
            cin &amp;gt;&amp;gt; board[i][j];
        }
    }

    bfs();

    cout &amp;lt;&amp;lt; cnt &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lBNMq/btsNm2b6vhf/PAWoekedy4z3S0KA4LHro0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lBNMq/btsNm2b6vhf/PAWoekedy4z3S0KA4LHro0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lBNMq/btsNm2b6vhf/PAWoekedy4z3S0KA4LHro0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlBNMq%2FbtsNm2b6vhf%2FPAWoekedy4z3S0KA4LHro0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;231&quot; height=&quot;231&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/75</guid>
      <comments>https://yerang2.tistory.com/75#entry75comment</comments>
      <pubDate>Wed, 16 Apr 2025 14:05:02 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 객체 타입의 값을 변경할 때 주의사항 : 불변성</title>
      <link>https://yerang2.tistory.com/74</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;불변성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불변성이란 &lt;b&gt;데이터가 최초 생성된 후 그 상태를 변경할 수 없는 성질&lt;/b&gt;이다. 불변성을 지키는 것은 예측이 가능하고, 안정적인 코드를 만드는데 중요하다. 그런데, 객체와 배열같은 참조 타입은 &lt;b&gt;가변적&lt;/b&gt;이다. 예를 들어, 객체의 프로퍼티 값을 변경할 수 있다. 다음과 같이 말이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;person.name = &quot;yerang&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 불변성을 유지하고 싶다면, 객체의 프로퍼티를 직접 변경하지 않고 &lt;b&gt;새로운 객체를 생성하는 방식&lt;/b&gt;을 사용해야 한다. 예를 들면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const person = { name: &quot;yerang&quot;, age: 27 };

// 불변성 유지
const updatedPerson = { ...person, age: 28 };&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;code&gt;Object.assign()&lt;/code&gt;, &lt;code&gt;Object.freeze()&lt;/code&gt;와 같은 내장 기능을 활용하거나, Immutable js와 같은 도구를 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;불변성을 유지하면 성능 측면에서 안좋지 않나?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업데이트마다 새로운 객체를 생성해야 하므로 메모리 비용이 약간 증가할 수 있다. 하지만 이 비용은 일반적으로 무시할만한 수준이고, 불변성 유지는 &lt;b&gt;장기적으로 유지보수성과 안정성을 크게 향상시킨다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, &lt;b&gt;불변성은 데이터의 변경 흐름을 추적하기 쉽게 만들어준다.&lt;/b&gt; 가변 데이터를 이곳저곳에서 수정해가며 사용하면 데이터가 언제, 어디서 변경되었는지 파악하기 어렵다. 반면에, 불변성을 지키면 데이터 변경이 항상 새로운 객체 생성을 통해 이루어지므로 변경 지점을 명확하게 추적할 수 있다.&lt;/p&gt;</description>
      <category>Language/TypeScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/74</guid>
      <comments>https://yerang2.tistory.com/74#entry74comment</comments>
      <pubDate>Wed, 16 Apr 2025 10:14:29 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 12865: 평범한 배낭</title>
      <link>https://yerang2.tistory.com/73</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDCq0P/btsNmA6EiRl/9XRb1Dxm5JW7ikAnysU2oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDCq0P/btsNmA6EiRl/9XRb1Dxm5JW7ikAnysU2oK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDCq0P/btsNmA6EiRl/9XRb1Dxm5JW7ikAnysU2oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDCq0P%2FbtsNmA6EiRl%2F9XRb1Dxm5JW7ikAnysU2oK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;428&quot; height=&quot;285&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제 해결 아이디어가 안떠올랐다. 완전 탐색으로 풀면 시간 초과였다. 역시나 DP였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DP최소 단위를 설계한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;dp[n]을 n무게일 때 최대 가치&lt;/b&gt;라고 하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;점화식을 세운다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;dp[i] = max(dp[i], dp[i - weight[i]] + value[i]);&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;포인트는 순회를 &lt;b&gt;거꾸로&lt;/b&gt; K에서부터 1씩 줄여나가며 한다는 것이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1에서 부터 돌면 점화식에서 더 작은 값을 이용하기 때문에, 이미 계산한 물건을 또 담을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int N, K;
    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; K;

    vector&amp;lt;int&amp;gt; weight(N), value(N);
    for (int i = 0; i &amp;lt; N; i++) {
        cin &amp;gt;&amp;gt; weight[i] &amp;gt;&amp;gt; value[i];
    }

    vector&amp;lt;int&amp;gt; dp(K + 1, 0);
    for (int i = 0; i &amp;lt; N; i++) {
        for (int w = K; w &amp;gt;= weight[i]; w--) {
            dp[w] = max(dp[w], dp[w - weight[i]] + value[i]);
        }
    }
    cout &amp;lt;&amp;lt; dp[K] &amp;lt;&amp;lt; '\n';
    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <category>needreview</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/73</guid>
      <comments>https://yerang2.tistory.com/73#entry73comment</comments>
      <pubDate>Tue, 15 Apr 2025 15:49:50 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 9251: LCS</title>
      <link>https://yerang2.tistory.com/72</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1wEBp/btsNlyhNWZn/dOdQkTdfTCfuc2kOMKEZs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1wEBp/btsNlyhNWZn/dOdQkTdfTCfuc2kOMKEZs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1wEBp/btsNlyhNWZn/dOdQkTdfTCfuc2kOMKEZs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1wEBp%2FbtsNlyhNWZn%2FdOdQkTdfTCfuc2kOMKEZs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;284&quot; height=&quot;284&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 글자씩 이동시켜가면서.. 누적해서 탐색해야하나? 생각을 했는데 풀이 방법이 떠오르질 않았다.&lt;/li&gt;
&lt;li&gt;보통 이러면 DP더라.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A문자열에 대한 인덱스 i, B문자열에 대한 인덱스 j를 1씩 증가시켜가면서 LCS값을 누적하며 계산해서 최종적으로 [A문자열 길이][B문자열 길이]를 알아낸다. -&amp;gt; &lt;b&gt;DP&lt;/b&gt;로 풀이해야 한다.&lt;/li&gt;
&lt;li&gt;DP설계하기 : &lt;b&gt;dp[i][j]&lt;/b&gt;를 A문자열의 i번째 문자까지 부분 문자열과, B문자열의 j까지 부분 문자열 간의 LCS 길이라고 하자.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;점화식&lt;/b&gt; 세우기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dp[i][j]가 같으면 현재 비교하는 문자 이전 까지 비교한 거에 1더한거 =&amp;gt; &lt;b&gt;dp[i][j] = dp[i-1][j-1] + 1&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;같지 않으면 A에서 하나 빼고 &amp;amp; B 그대로, A 그대로 &amp;amp; B에서 하나 뺀거 중에 더 큰 LCS값 가져오기.&amp;nbsp; =&amp;gt; &lt;b&gt;max(dp[i-1][j], dp[i][j-1]) &lt;/b&gt;(현재 비교하는 문자열을 포함해도 괜찮으므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    string A, B;
    cin &amp;gt;&amp;gt; A &amp;gt;&amp;gt; B;
    int n = A.size(), m = B.size();

    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dp(n+1, vector&amp;lt;int&amp;gt;(m+1, 0));

    for (int i=1; i&amp;lt;=n; i++) {
        for (int j=1; j&amp;lt;=m; j++) {
            if (A[i-1] == B[j-1])
                dp[i][j] = dp[i-1][j-1] + 1;
            else
                dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
        }
    }

    cout &amp;lt;&amp;lt; dp[n][m] &amp;lt;&amp;lt; '\n';
    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <category>needreview</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/72</guid>
      <comments>https://yerang2.tistory.com/72#entry72comment</comments>
      <pubDate>Tue, 15 Apr 2025 13:57:33 +0900</pubDate>
    </item>
    <item>
      <title>MIME과 MIME 타입, 뭐가 다른 걸까?</title>
      <link>https://yerang2.tistory.com/71</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lDpWG/btsNlisjFQ0/A4Uq4rIGtv7E4SpC23k8Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lDpWG/btsNlisjFQ0/A4Uq4rIGtv7E4SpC23k8Rk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lDpWG/btsNlisjFQ0/A4Uq4rIGtv7E4SpC23k8Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlDpWG%2FbtsNlisjFQ0%2FA4Uq4rIGtv7E4SpC23k8Rk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;249&quot; height=&quot;249&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 개발을 하다 보면 &lt;code&gt;Content-Type: application/json&lt;/code&gt; 같은 헤더를 자주 보게 되는데, 여기서 말하는 &lt;b&gt;MIME 타입&lt;/b&gt;은 뭘 의미하고, &lt;b&gt;MIME&lt;/b&gt;와는 어떤 관계일까 궁금해졌다. &lt;b&gt;MIME와 MIME 타입의 개념 차이와 역할&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;h2 data-ke-size=&quot;size26&quot;&gt;MIME란?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MIME (Multipurpose Internet Mail Extensions)&lt;br /&gt;원래는 이메일에서 텍스트 외에 이미지, 오디오, 파일 등을 첨부할 수 있게 만든 표준 규약이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 이메일 시스템은 &lt;b&gt;텍스트(ASCII 문자)&lt;/b&gt;만 전송 가능했음&lt;/li&gt;
&lt;li&gt;MIME을 통해 이메일 본문에 &lt;b&gt;파일, 이미지, 비디오 등을 첨부&lt;/b&gt;할 수 있게 됨&lt;/li&gt;
&lt;li&gt;이후 이 구조가 유용해서, &lt;b&gt;HTTP 같은 웹 프로토콜에서도 널리 사용&lt;/b&gt;됨&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MIME 타입이란?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MIME의 표준 형식을 따라 콘텐츠의 종류를 표현하는 문자열&lt;/p&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;MIME 타입은 &lt;b&gt;서버가 클라이언트에게 &amp;ldquo;이건 어떤 종류의 데이터야~&amp;rdquo;라고 알려주는 역할&lt;/b&gt;을 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  MIME 타입 형식&lt;/h3&gt;
&lt;pre class=&quot;fsharp&quot;&gt;&lt;code&gt;&amp;lt;type&amp;gt;/&amp;lt;subtype&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  예시&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MIME 타입&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text/plain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;일반 텍스트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text/html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTML 문서&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;application/json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON 데이터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image/png&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PNG 이미지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video/mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MP4 동영상&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;h2 data-ke-size=&quot;size26&quot;&gt;MIME과 MIME 타입의 관계&lt;/h2&gt;
&lt;table style=&quot;height: 77px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;항목&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;MIME&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;텍스트 외 다양한 콘텐츠 전송을 위한 &lt;b&gt;전체 프로토콜/표준&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;MIME 타입&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;MIME 표준 내에서 정의된 &lt;b&gt;개별 콘텐츠의 형식&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;관계&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;MIME 타입은 MIME의 &lt;b&gt;핵심 요소 중 하나&lt;/b&gt;로, 콘텐츠의 종류를 명시&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;h2 data-ke-size=&quot;size26&quot;&gt;실제 사용 예 (HTTP)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 브라우저에게 JSON 데이터를 보낼 때:&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: application/json

{
&amp;ldquo;message&amp;rdquo;: &amp;ldquo;Hello, world!&amp;rdquo;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt; 헤더가 바로 &lt;b&gt;MIME 타입&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;브라우저나 클라이언트는 이 값을 보고 &lt;b&gt;어떻게 파싱할지 결정&lt;/b&gt;함&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>FE</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/71</guid>
      <comments>https://yerang2.tistory.com/71#entry71comment</comments>
      <pubDate>Tue, 15 Apr 2025 10:58:27 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 타입 호환성 : 업캐스팅과 다운캐스팅</title>
      <link>https://yerang2.tistory.com/70</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;예를 들어 number 타입의 경우 10, 1.5, -1, Infinity와 같은 숫자를 모두 포함한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 만약 타입에 10이라고 적는다면? 10이라는 값만 가질 수 있는 타입이 된다. 이를 &lt;b&gt;number literal type&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;number literal 타입은 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;number타입에&lt;/span&gt;&amp;nbsp;포함되는 구조라서 number타입은 &lt;b&gt;슈퍼타입&lt;/b&gt;, number literal타입은 &lt;b&gt;서브타입&lt;/b&gt;이라고 부를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;타입 호환성&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5at8B/btsNgyaWlkp/vZh4B2ZIy1vwFyWp1ZJfV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5at8B/btsNgyaWlkp/vZh4B2ZIy1vwFyWp1ZJfV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5at8B/btsNgyaWlkp/vZh4B2ZIy1vwFyWp1ZJfV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5at8B%2FbtsNgyaWlkp%2FvZh4B2ZIy1vwFyWp1ZJfV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;741&quot; height=&quot;417&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 호환 시에 주의할 점은 슈퍼타입에 서브타입을 대입할 순 있지만, 서브타입에 슈퍼타입을 대입할 수 없다는 점이다. 마치 정사각형은 사각형이지만, 사각형 모두가 정사각형은 아닌 것처럼 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/TypeScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/70</guid>
      <comments>https://yerang2.tistory.com/70#entry70comment</comments>
      <pubDate>Fri, 11 Apr 2025 14:44:29 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 2096: 내려가기</title>
      <link>https://yerang2.tistory.com/69</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최댓값과 최솟값 계산 결과를 담기 위한 배열을 [100000][3] 사이즈로 만들었다.&lt;/li&gt;
&lt;li&gt;총 byte수로는 360만 = 3600KB = 3.6MB라서 4MB 이내니까 괜찮겠지 했는데 &lt;b&gt;메모리 초과가 발생&lt;/b&gt;했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어짜피 이전행과 현재행만 필요한 거기 때문에 dp배열을 [2][3]으로 선언해서 i%2로 줄만 번갈아서 사용해서 해결했다.&lt;/li&gt;
&lt;li&gt;GPT가 이 방식을 &lt;b&gt;슬라이딩 윈도우&lt;/b&gt;라고 알려줬다.&lt;/li&gt;
&lt;li&gt;마치 미닫이문이 스르륵 옮겨가듯 조회하는 행이 스르륵...............&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;589&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pAXOu/btsNiIDFCsL/H7m9TSv9jNeek5fz7gJcZ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pAXOu/btsNiIDFCsL/H7m9TSv9jNeek5fz7gJcZ0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pAXOu/btsNiIDFCsL/H7m9TSv9jNeek5fz7gJcZ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpAXOu%2FbtsNiIDFCsL%2FH7m9TSv9jNeek5fz7gJcZ0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;262&quot; height=&quot;210&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;589&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int N;
int board[100000][3];
// N 최댓값만큼 만들 필요가 없구나...
// 2줄만 만들고 최근 누적값까지만 기록하고 i % 2 로 줄만 번갈아 사용하면 됨... 충격 
int dpMax[2][3];
int dpMin[2][3];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    cin &amp;gt;&amp;gt; N;
    for (int i=0; i&amp;lt;N; i++) {
        cin &amp;gt;&amp;gt; board[i][0] &amp;gt;&amp;gt; board[i][1] &amp;gt;&amp;gt; board[i][2];
    }

    dpMax[0][0] = board[0][0];
    dpMax[0][1] = board[0][1];
    dpMax[0][2] = board[0][2];

    dpMin[0][0] = board[0][0];
    dpMin[0][1] = board[0][1];
    dpMin[0][2] = board[0][2];

    for (int i=1; i&amp;lt;N; i++) {
        int before = (i - 1) % 2;
        dpMax[i % 2][0] = board[i][0] + max(dpMax[before][0], dpMax[before][1]);
        dpMax[i % 2][1] = board[i][1] + max({dpMax[before][0], dpMax[before][1], dpMax[before][2]});
        dpMax[i % 2][2] = board[i][2] + max(dpMax[before][1], dpMax[before][2]);

        dpMin[i % 2][0] = board[i][0] + min(dpMin[before][0], dpMin[before][1]);
        dpMin[i % 2][1] = board[i][1] + min({dpMin[before][0], dpMin[before][1], dpMin[before][2]});
        dpMin[i % 2][2] = board[i][2] + min(dpMin[before][1], dpMin[before][2]);
    }

    int last = (N+1)%2;
    cout &amp;lt;&amp;lt; max({dpMax[last][0], dpMax[last][1], dpMax[last][2]}) &amp;lt;&amp;lt; ' ' &amp;lt;&amp;lt;
    min({dpMin[last][0], dpMin[last][1], dpMin[last][2]}) &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/69</guid>
      <comments>https://yerang2.tistory.com/69#entry69comment</comments>
      <pubDate>Fri, 11 Apr 2025 14:14:31 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 1916: 최소비용 구하기</title>
      <link>https://yerang2.tistory.com/68</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  다익스트라 해결 포인트&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그래프 자체를 &lt;code&gt;&lt;b&gt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; buses[N];&lt;/b&gt;&lt;/code&gt; 으로 선언해서 a-&amp;gt;b로 이어지는 간선을 &lt;b&gt;&lt;code&gt;buses[a].push_back({b, 비용});&lt;/code&gt;&lt;/b&gt; 이렇게 표현한다.&lt;/li&gt;
&lt;li&gt;그래프를 탐색할 때는 &lt;b&gt;Priority Queue&lt;/b&gt;를 사용해서 비용이 작은 순서로 순회하도록 한다.&lt;/li&gt;
&lt;li&gt;선언은 오름차순이면 &lt;code&gt;priority_queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; pq;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;내림차순이면 &lt;code&gt;priority_queue&amp;lt;pair&amp;lt;int, int&amp;gt;, vector&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt;, less&amp;lt;&amp;gt;&amp;gt; pq;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;시작 노드부터 출발해서 누적된 거리를 담기 위한 dist 배열은 최댓값으로 초기화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    // N개의 도시
    // M개의 버스
    // A-&amp;gt;B 버스 비용 최소화

    int N, M, A, B;
    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; M;
    vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; buses[N+1];
    vector&amp;lt;int&amp;gt; dist(N+1, 100000001);
    for (int i=0; i&amp;lt;M; i++) {
        int de, ar, p;
        cin &amp;gt;&amp;gt; de &amp;gt;&amp;gt; ar &amp;gt;&amp;gt; p;
        buses[de].push_back(make_pair(ar, p));
    }
    cin &amp;gt;&amp;gt; A &amp;gt;&amp;gt; B;

    // 다익스트라
    // 시작점부터 B노드까지 가면서 최솟값 찾기
    priority_queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; pq;
    pq.push({0, A}); // 비용, 시작 노드 (앞에 오는 애 기준으로 정렬하니까 무조건 비용이 앞으로 와야 함)
    dist[A] = 0;
    while(!pq.empty()) {
        int cost = pq.top().first;
        int cur = pq.top().second;
        pq.pop();

        if (dist[cur] &amp;lt; cost) continue;

        for (int i=0; i&amp;lt;buses[cur].size(); i++) {
            int next = buses[cur][i].first;
            int nextCost = cost + buses[cur][i].second;
            if (dist[next] &amp;gt; nextCost) {
                dist[next] = nextCost;
                pq.push({nextCost, next});
            }
        }
    }
    cout &amp;lt;&amp;lt; dist[B] &amp;lt;&amp;lt; '\n';


    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <category>needreview</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/68</guid>
      <comments>https://yerang2.tistory.com/68#entry68comment</comments>
      <pubDate>Fri, 11 Apr 2025 11:32:38 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] Any, Unknown 타입</title>
      <link>https://yerang2.tistory.com/67</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;any 타입&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바스크립트처럼 어떤 타입이든지 변수에 담을 수 있게 하려면 'any' 타입을 사용하면 된다.&lt;/li&gt;
&lt;li&gt;number값이 들어있어도, string 관련 메서드도 자유롭게 사용할 수 있게 된다.&lt;/li&gt;
&lt;li&gt;반대로, &lt;b&gt;모든 타입의 값에 any 타입의 값을 대입할 수 있다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;치트키 같은 타입이다. 근데 문제가 안발생하냐? -&amp;gt; NO! 문제가  &lt;span style=&quot;color: #5733b1;&quot;&gt;&lt;b&gt;런타임에 발생&lt;/b&gt;&lt;/span&gt; 한다. (최악의 상황)&lt;/li&gt;
&lt;li&gt;쓰지 말자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;unknown 타입&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;any와 마찬가지로 어떤 타입이든 담을 수 있다.&lt;/li&gt;
&lt;li&gt;하지만, any와는 다르게 반대로 &lt;b&gt;다른 타입에 unknown 타입의 값을 대입할 수 없다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;덧셈, 뺄셈, 나눗셈, 곱셈 등의 연산과 문자열 관련 메서드도 사용이 불가하다.&lt;/li&gt;
&lt;li&gt;이걸 활용하려면 조건문으로 검사한 뒤 사용해야 한다.&lt;/li&gt;
&lt;li&gt;그래서, any보단 &lt;b&gt;차라리 unknown&lt;/b&gt;을 써야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1743745863849&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let unknownVar: unknown;

// 어떤 타입이든 담을 순 있다.
unknownVar = &quot;&quot;;
unknownVar = 1;
unknownVar = () =&amp;gt; {};

// 타입 체크를 한 뒤 이용해야 한다.
if (typeof unknownVar === &quot;number&quot;) {
  num = unknownVar;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/TypeScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/67</guid>
      <comments>https://yerang2.tistory.com/67#entry67comment</comments>
      <pubDate>Fri, 4 Apr 2025 14:51:26 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] enum 열거형 사용하기</title>
      <link>https://yerang2.tistory.com/66</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 회사에서 enum을 굉장히 많이 서서 그런지 enum에 대한 갈구가 있었는데 TS에 enum타입이 있었구나!&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;숫자형 enum&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 작성하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1743744976611&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum Role {
  ADMIN, // 0
  USER,
  GUEST = 10, // 이 다음부터는 11, 12, ... 
}

const user1 = {
  name: &quot;머랑&quot;,
  role: Role.ADMIN,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++에서와 동일하게 시작하는 숫자를 지정할 수 있다.&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문자형 enum&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1743745159105&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum Language {
  Korean = 'ko',
  English = 'en',
}

const user1 = {
  name: &quot;머랑&quot;,
  language: Language.Korean,
}&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;TS를 빌드해서 JS가 되면서 enum이 제거되면 버그가 발생하는거 아니야?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니다. TS를 빌드해도 enum은 JS에 객체 형태로 남아있게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kJjez/btsM8Mt7xS5/BFBNyGv5Oof9TCf5T3kRv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kJjez/btsM8Mt7xS5/BFBNyGv5Oof9TCf5T3kRv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kJjez/btsM8Mt7xS5/BFBNyGv5Oof9TCf5T3kRv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkJjez%2FbtsM8Mt7xS5%2FBFBNyGv5Oof9TCf5T3kRv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Language/TypeScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/66</guid>
      <comments>https://yerang2.tistory.com/66#entry66comment</comments>
      <pubDate>Fri, 4 Apr 2025 14:36:36 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] key-value 데이터에 타입 붙이기 - 인덱스 시그니처</title>
      <link>https://yerang2.tistory.com/65</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4x6nY/btsM7YgDJK2/VGkJijLrLnTXpLch7ZFTRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4x6nY/btsM7YgDJK2/VGkJijLrLnTXpLch7ZFTRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4x6nY/btsM7YgDJK2/VGkJijLrLnTXpLch7ZFTRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4x6nY%2FbtsM7YgDJK2%2FVGkJijLrLnTXpLch7ZFTRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;126&quot; height=&quot;126&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&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;국가 코드를 담는 key-value 형태의 데이터가 있다고 해보자.&lt;/li&gt;
&lt;li&gt;국가 코드는 지금은 3개 뿐이지만, 앞으로 200개 가량의 국가가 추가될 거라고 하면&lt;/li&gt;
&lt;li&gt;200개를 type에 추가해줘야 하는 번거로움이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1743683028478&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type CountryCodes = {
  Korea: string,
  UnitedStates: string,
  UnitedKingdom: string,
  // 2. 여기서 매번 타입을 수정해줘야 한다.
}

const countryCodes: CountryCodes = {
  Korea: 'ko',
  UnitedStates: 'us',
  UnitedKingdom: 'uk',
  // 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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;인덱스 시그니처&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1743683287730&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type CountryCodes = {
  [key: string]: string;
};

const countryCodes: CountryCodes = {
  Korea: 'ko',
  UnitedStates: 'us',
  UnitedKingdom: 'uk',
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주의할 점 1&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;그래서 아래와 같이 &lt;b&gt;텅 비어도&lt;/b&gt; 규칙은 위반하지 않았으므로, 에러를 표시하지 않게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1743683424108&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const countryCodes: CountryCodes = {
  // 텅..
};&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주의할 점 2&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 한국 코드를 필수로 저장하되, string 뿐만 아니라 number 코드까지 저장하고 싶어서 아래와 같이 작성하면 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적인 프로퍼티의 value 타입은 인덱스 시그니처의 value의 타입과 &lt;b&gt;일치하거나 호환&lt;/b&gt;해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1743683596255&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type CountryCodes = {
  [key: string]: string;
  Korea: number;
};

const countryCodes: CountryCodes = {
  Korea: 410,
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/TypeScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/65</guid>
      <comments>https://yerang2.tistory.com/65#entry65comment</comments>
      <pubDate>Thu, 3 Apr 2025 21:43:00 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 1149: RGB거리</title>
      <link>https://yerang2.tistory.com/64</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;658&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdIYWl/btsM8loAIoV/5G7TpFmLUsKlhmbtJB8kok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdIYWl/btsM8loAIoV/5G7TpFmLUsKlhmbtJB8kok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdIYWl/btsM8loAIoV/5G7TpFmLUsKlhmbtJB8kok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdIYWl%2FbtsM8loAIoV%2F5G7TpFmLUsKlhmbtJB8kok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;203&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;658&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;알고리즘 보기를 눌러보고 DP로 풀어야 한다는 점을 알게됐다. 그래서 DP로 풀이했는데 너무 오랜만에 DP문제 혼자 성공해서 뿌듯해서 로그를 남긴다  &lt;/li&gt;
&lt;li&gt;&lt;b&gt;근데, DP로 풀어야하는 문제인지를 어떻게 알 수 있을까?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 집마다 3가지 색상을 선택할 수 있음. 최악의 경우에 &lt;b&gt;3^1000&lt;/b&gt;이 됨.&lt;/li&gt;
&lt;li&gt;int의 범위가 -2^31 ~ 2^31 - 1 이므로, 엄~~~~청 큰 수다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int dx[] = {1, 0, -1, 0};
int dy[] = {0, -1, 0, 1};

struct Price {
    int r;
    int g;
    int b;
};

enum COLOR {
    RED,
    GREEN,
    BLUE,
    COLOR_MAX
};

int N;
vector&amp;lt;Price&amp;gt; prices;
int dp[1001][COLOR_MAX];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    cin &amp;gt;&amp;gt; N;
    for (int i=0; i&amp;lt;N; i++) {
        int r, g, b;
        cin &amp;gt;&amp;gt; r &amp;gt;&amp;gt; g &amp;gt;&amp;gt; b;
        prices.push_back({r, g, b});
    }

    dp[0][RED] = prices[0].r;
    dp[0][GREEN] = prices[0].g;
    dp[0][BLUE] = prices[0].b;

    for (int i=1; i&amp;lt;N; i++) {
        dp[i][RED] = min(dp[i-1][GREEN] + prices[i].r,
            dp[i-1][BLUE] + prices[i].r);
        dp[i][GREEN] = min(dp[i-1][RED] + prices[i].g,
            dp[i-1][BLUE] + prices[i].g);
        dp[i][BLUE] = min(dp[i-1][RED] + prices[i].b,
            dp[i-1][GREEN] + prices[i].b);
    }

    int minPrice = min({dp[N-1][RED], dp[N-1][GREEN], dp[N-1][BLUE]});

    cout &amp;lt;&amp;lt; minPrice &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/64</guid>
      <comments>https://yerang2.tistory.com/64#entry64comment</comments>
      <pubDate>Thu, 3 Apr 2025 18:03:27 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 15685: 드래곤 커브</title>
      <link>https://yerang2.tistory.com/63</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vbj0u/btsM6G1Mz15/po7xHBeYkPUZli81xd2iNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vbj0u/btsM6G1Mz15/po7xHBeYkPUZli81xd2iNK/img.png&quot; data-alt=&quot;from GPT (문제와 연관은 없음)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vbj0u/btsM6G1Mz15/po7xHBeYkPUZli81xd2iNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvbj0u%2FbtsM6G1Mz15%2Fpo7xHBeYkPUZli81xd2iNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;468&quot; height=&quot;468&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;from GPT (문제와 연관은 없음)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 실패한 원인&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;드래곤 커브 구현 방법을 생각하는 과정에서 방향 변화에 규칙이 있을 것 같다고 유추는 하였으나,&lt;/li&gt;
&lt;li&gt;규칙을 발견을 못했고, 공식으로 풀어내지 못함 ㅜㅜ (결국 답안을 보고 풀이했다...)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;드래곤 커브 n세대는 드래곤 커브 n-1세대의 &lt;b&gt;방향을 역순&lt;/b&gt;으로 해서, &lt;b&gt;+1한 뒤(반시계 90도 회전) %4&lt;/b&gt;를 하면 된다.&lt;/li&gt;
&lt;li&gt;(x+1)%4까지는 계산해봤는데 역순일거라고 생각을 전혀 못했다.&lt;/li&gt;
&lt;li&gt;회전을 하게되면 방향의 순서가 바뀌게 되어서 역순이 되는데 그걸 캐치를 못했다... 다음에 비슷한 문제가 나오면 생각해서 풀 수 있도록 잘 익혀두자!!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

const int SIZE = 101;
bool board[SIZE][SIZE] = {false,};

int dx[] = {1, 0, -1, 0};
int dy[] = {0, -1, 0, 1};

void drawDragonCurve(int x, int y, int d, int g) {
    vector&amp;lt;int&amp;gt; dirList = {d};
    for (int gen=0; gen&amp;lt;g; gen++) {
        vector&amp;lt;int&amp;gt; reverse;
        for (int i=dirList.size()-1; i&amp;gt;=0; i--) {
            reverse.push_back((dirList[i]+1)%4);
        }
        dirList.insert(dirList.end(), reverse.begin(), reverse.end());
    }
    board[y][x] = true;
    for (int dir : dirList) {
        y += dy[dir];
        x += dx[dir];
        board[y][x] = 1;
    }
}

int countSquares() {
    int cnt = 0;
    for (int i=0; i&amp;lt;SIZE-1; i++) {
        for (int j=0; j&amp;lt;SIZE-1; j++) {
            if (board[i][j] &amp;amp;&amp;amp; board[i][j + 1] &amp;amp;&amp;amp;
                board[i + 1][j] &amp;amp;&amp;amp; board[i + 1][j + 1]) {
                ++cnt;
            }
        }
    }
    return cnt;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int n;

    cin &amp;gt;&amp;gt; n;
    for (int i=0; i&amp;lt;n; i++) {
        int x, y, d, g;
        cin &amp;gt;&amp;gt; x &amp;gt;&amp;gt; y &amp;gt;&amp;gt; d &amp;gt;&amp;gt; g;
        drawDragonCurve(x, y, d, g);
    }

    cout &amp;lt;&amp;lt; countSquares() &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <category>needreview</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/63</guid>
      <comments>https://yerang2.tistory.com/63#entry63comment</comments>
      <pubDate>Thu, 3 Apr 2025 13:58:21 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 15684: 사다리 조작</title>
      <link>https://yerang2.tistory.com/62</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3FiG6/btsM5ufMo5n/trJKKKX2cqWw9id6nZl6q1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3FiG6/btsM5ufMo5n/trJKKKX2cqWw9id6nZl6q1/img.jpg&quot; data-alt=&quot;흐으음...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3FiG6/btsM5ufMo5n/trJKKKX2cqWw9id6nZl6q1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3FiG6%2FbtsM5ufMo5n%2FtrJKKKX2cqWw9id6nZl6q1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;229&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;526&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;흐으음...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백트래킹으로 푸는 방법을 아예 몰라서 1~3중까지 for문으로 구현해 풀려고 하였으나, 코드가 길어지다보니 로직에 오류가 생겼음.&lt;/li&gt;
&lt;li&gt;그래서 문제를 풀지 못했다 ㅜㅜ 복습을 할 때 참고하려고 가장 마음에 드는 풀이를 보고 글을 적는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;/h3&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;check() 함수는 열에 대해 순회하며
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 위치에 true가 있으면 다리를 타고 우측으로 이동하므로 pos를 ++,&amp;nbsp;&lt;/li&gt;
&lt;li&gt;현재 위치 좌측에 true가 있으면 다리를 타고 좌측으로 이동하므로 pos를 --해서&lt;/li&gt;
&lt;li&gt;최종 pos가 시작한 열과 동일하면 조건을 만족한다고 판단함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int N, M, H;
int minCnt = 4;
vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; ladder(31, vector&amp;lt;bool&amp;gt;(11, false));

bool check() {
    for (int i=1; i&amp;lt;=N; i++) {
        int pos = i;

        for (int j=1; j&amp;lt;=H; j++) {
            if (ladder[j][pos]) pos++;
            else if (ladder[j][pos-1]) pos--;
        }

        if (pos != i) return false;
    }

    return true;
}

void DFS(int h, int cnt) {
    if (cnt &amp;gt;= minCnt) return;
    if (check()) minCnt = cnt;

    for (int i=h; i&amp;lt;=H; i++) {
        for (int j=1; j&amp;lt;N; j++) {
            if (ladder[i][j] || ladder[i][j-1] || ladder[i][j+1]) continue;
            ladder[i][j] = true;
            DFS(i, cnt+1);
            ladder[i][j] = false;
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; M &amp;gt;&amp;gt; H;

    for (int i=0; i&amp;lt;M; i++) {
        int a, b;
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b; // 1-base
        ladder[a][b] = true;
    }

    DFS(1,0);

    if (minCnt &amp;gt; 3) cout &amp;lt;&amp;lt; -1;
    else cout &amp;lt;&amp;lt; minCnt;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <category> </category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/62</guid>
      <comments>https://yerang2.tistory.com/62#entry62comment</comments>
      <pubDate>Wed, 2 Apr 2025 16:45:29 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 14890: 경사로 (설명 및 주석 추가)</title>
      <link>https://yerang2.tistory.com/61</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;589&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mUo3p/btsM3RPTnmv/k2qCrOZ7To2McD7aDYbNx0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mUo3p/btsM3RPTnmv/k2qCrOZ7To2McD7aDYbNx0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mUo3p/btsM3RPTnmv/k2qCrOZ7To2McD7aDYbNx0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmUo3p%2FbtsM3RPTnmv%2Fk2qCrOZ7To2McD7aDYbNx0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;292&quot; height=&quot;234&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;589&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;행 열에 대해 검사하는 bfsRow()와 bfsCol()을 만듦.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재귀로 구현할 수도 있었을텐데 queue를 쓰는게 더 편할 것 같아서 이렇게 짬.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그리고 이미 경사로가 만들어진 것을 구분하기 위해 already라는 bool형 벡터를 만들어놓고 탐색 시작.&lt;/li&gt;
&lt;li&gt;경우의 수는 다음과 같음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앞으로 이동할 곳의 높이가 더 높은 경우&lt;/li&gt;
&lt;li&gt;더 낮은 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;각 경우에 맞게 경사로를 설치할 수 있는지 여부를 판단해서 불가하면 false를 리턴하도록 함수를 설계함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드 (주석 포함)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

struct Axis {
    int x;
    int y;
};

int n, l;
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; v;

bool bfsRow(int idx) {
    // ROW 기준
    vector&amp;lt;int&amp;gt; already(n, false);
    queue&amp;lt;Axis&amp;gt; q;
    q.push({idx, 0});

    while(!q.empty()) {
        Axis now = q.front();
        q.pop();

        int mx = now.x;
        int my = now.y + 1;
        if (my &amp;gt;= n) break;

        // 경사 차이가 2 이상 난다면 불가
        if (abs(v[now.x][now.y] - v[mx][my]) &amp;gt; 1) return false;

        // 경사 차이가 1인데, 경사로를 놓을 수 없으면 불가
        // 1. 앞으로 이동할 높이가 더 높을 때
        if (v[now.x][now.y] &amp;lt; v[mx][my]) {
            // 낮은쪽 길이가 l이상인지, 이미 놓여진 경사로가 없는지 체크
            int startIdx = now.y - l + 1;
            if (startIdx &amp;lt; 0) return false;
            for (int i=now.y; i&amp;gt;=startIdx; i--) {
                if (v[now.x][now.y] != v[now.x][i] || already[i]) return false;
            }

            // 경사로 추가
            for (int i=now.y; i&amp;gt;=startIdx; i--) {
                already[i] = true;
            }
        }

        // 2. 앞으로 이동할 높이가 더 낮을 때
        else if (v[mx][my] &amp;lt; v[now.x][now.y]) {
            // 이동할 곳에 경사로 놓고, l만큼 길이가 유지되는지 확인
            int endIdx = my + l - 1;
            if (endIdx &amp;gt;= n) return false;
            for (int i=my; i&amp;lt;= endIdx; i++) {
                if (v[mx][my] != v[mx][i] || already[i]) return false;
            }

            for (int i=my; i&amp;lt;= endIdx; i++) {
                already[i] = true;
            }
            // l만큼 유지된다면 경사로가 끝나는 위치를 push
            my = endIdx;
        }

        // 위 케이스 모두 통과 시 패스
        q.push({mx, my});
    }
    return true;
}

bool bfsCol(int idx) {
    // COL 기준
    vector&amp;lt;int&amp;gt; already(n, false);
    queue&amp;lt;Axis&amp;gt; q;
    q.push({0, idx});

    while(!q.empty()) {
        Axis now = q.front();
        q.pop();

        int mx = now.x + 1;
        int my = now.y;

        if (mx &amp;gt;= n) break;

        // 경사 차이가 2 이상 난다면 불가
        if (abs(v[now.x][now.y] - v[mx][my]) &amp;gt; 1) return false;

        // 경사 차이가 1인데, 경사로를 놓을 수 없으면 불가
        // 1. 앞으로 이동할 높이가 더 높을 때
        if (v[now.x][now.y] &amp;lt; v[mx][my]) {
            // 낮은쪽 길이가 l이상인지, 이미 놓여진 경사로가 없는지 체크
            int startIdx = now.x - l + 1;

            if (startIdx &amp;lt; 0) return false;
            for (int i=now.x; i&amp;gt;=startIdx; i--) {
                if (v[now.x][now.y] != v[i][now.y] || already[i]) return false;
            }

            // 경사로 추가
            for (int i=now.x; i&amp;gt;=startIdx; i--) {
                already[i] = true;
            }
        }

        // 2. 앞으로 이동할 높이가 더 낮을 때
        else if (v[mx][my] &amp;lt; v[now.x][now.y]) {
            // 이동할 곳에 경사로 놓고, l만큼 길이가 유지되는지 확인
            int endIdx = mx + l - 1;
            if (endIdx &amp;gt;= n) return false;
            for (int i=mx; i&amp;lt;= endIdx; i++) {
                if (v[mx][my] != v[i][my] || already[i]) return false;
            }

            for (int i=mx; i&amp;lt;= endIdx; i++) {
                already[i] = true;
            }
            // l만큼 유지된다면 경사로가 끝나는 위치를 push
            mx = endIdx;
        }

        // 위 케이스 모두 통과 시 패스
        q.push({mx, my});
    }
    return true;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    // 하나의 행, 열에 대해서 높이 차이가 날 때
    // 낮은 높이 쪽 길이가 L만큼 이어진다면
    // 경사로를 놓을 수 있음.
    int cnt = 0;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; l;
    v = vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; (n, vector&amp;lt;int&amp;gt;(n));
    for (int i=0; i&amp;lt;n; i++) {
        for (int j=0; j&amp;lt;n; j++) {
            cin &amp;gt;&amp;gt; v[i][j];
        }
    }

    for (int i=0; i&amp;lt;n; i++) {
        // 행 기준 검사
        if (bfsRow(i)) {
            cnt++;
        }

        // 열 기준 검사
        if (bfsCol(i)) {
            cnt++;
        }
    }

    cout &amp;lt;&amp;lt; cnt &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/61</guid>
      <comments>https://yerang2.tistory.com/61#entry61comment</comments>
      <pubDate>Wed, 2 Apr 2025 11:53:25 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 14502: 연구소</title>
      <link>https://yerang2.tistory.com/60</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 낚시왕 문제에서 &lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;&lt;b&gt;1억이면 1~2초정도 걸린다&lt;/b&gt;는 걸 배웠다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;그래서 최악을 생각해보니 1억 미만이길래 아 그럼 완전탐색으로 풀어도 되겠구나 싶어서 무려 6중 for 문을 만들었다 ㅋㅋ&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

struct Axis {
    int x;
    int y;
};

int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
const int DIR_MAX = 4;

int n, m;
const int WALL_MAX = 3;
const int WALL = 1;
const int VIRUS = 2;
const int EMPTY = 0;
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; v;

int bfs(queue&amp;lt;Axis&amp;gt; viruses) {
    vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; visited(n, vector&amp;lt;bool&amp;gt;(m, false));
    int cnt = 0;

    while(!viruses.empty()) {
        Axis now = viruses.front();
        viruses.pop();

        if (v[now.x][now.y] == EMPTY) {
            cnt++;
        }

        for (int i=0; i&amp;lt;DIR_MAX; i++) {
            int mx = now.x + dx[i];
            int my = now.y + dy[i];
            if (mx &amp;lt; 0 || mx &amp;gt;= n || my &amp;lt; 0 || my &amp;gt;= m ||
                visited[mx][my] || v[mx][my] != EMPTY) continue;
            visited[mx][my] = true;
            viruses.push({mx, my});
        }
    }

    return cnt;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int minVirus = 65;
    int emptyCnt = 0;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;

    v = vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; (n, vector&amp;lt;int&amp;gt;(m));
    queue&amp;lt;Axis&amp;gt; viruses;
    for (int i=0; i&amp;lt;n; i++) {
        for (int j=0; j&amp;lt;m; j++) {
            cin &amp;gt;&amp;gt; v[i][j];
            if (v[i][j] == VIRUS) {
                viruses.push({i, j});
            }
            else if (v[i][j] == EMPTY) {
                emptyCnt++;
            }
        }
    }

    for (int i=0; i&amp;lt;n; i++) {
        for (int j=0; j&amp;lt;m; j++) {
            if (v[i][j] != EMPTY) continue;
            v[i][j] = WALL;

            for (int x=0; x&amp;lt;n; x++) {
                for (int y=0; y&amp;lt;m; y++) {
                    if ((i == x &amp;amp;&amp;amp; j == y) || v[x][y] != EMPTY) continue;
                    v[x][y] = WALL;

                    for (int a=0; a&amp;lt;n; a++) {
                        for (int b=0; b&amp;lt;m; b++) {
                            if ((i == a &amp;amp;&amp;amp; j == b) ||
                                (x == a &amp;amp;&amp;amp; y == b) ||
                                v[a][b] != EMPTY) continue;
                            v[a][b] = WALL;

                            int bfsResult = bfs(viruses);
                            minVirus = min(minVirus, bfsResult);

                            v[a][b] = EMPTY;
                        }
                    }
                    v[x][y] = EMPTY;
                }
            }
            v[i][j] = EMPTY;
        }
    }
    int safe = emptyCnt - minVirus - WALL_MAX;
    cout &amp;lt;&amp;lt; safe &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/60</guid>
      <comments>https://yerang2.tistory.com/60#entry60comment</comments>
      <pubDate>Wed, 2 Apr 2025 10:22:36 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 16235: 나무 재테크</title>
      <link>https://yerang2.tistory.com/59</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문제 안에 봄, 여름, 가을 겨울이 있는 마치 폭싹 속았수다 같은 문제였다..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A9wYH/btsM5xbCcnx/7g0DDpmuWALSaWJHUOYux0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A9wYH/btsM5xbCcnx/7g0DDpmuWALSaWJHUOYux0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A9wYH/btsM5xbCcnx/7g0DDpmuWALSaWJHUOYux0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA9wYH%2FbtsM5xbCcnx%2F7g0DDpmuWALSaWJHUOYux0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;275&quot; height=&quot;275&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간 초과..
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로직은 잘못된 게 없는 것 같은데 시간 초과가 났다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  문제 파악&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 코드에서 vector에 새로 추가할 나무를 다 담아놓고, 매년마다 맨 처음에 age 오름차순 기준으로 정렬했었다.&lt;/li&gt;
&lt;li&gt;이를 해결하기 위해서 연도에 해당하는 for문 들어가기 전에 정렬을 1번만 하고,&lt;/li&gt;
&lt;li&gt;나무를 저장하는 자료구조를 deque로 변경하고, 새로 추가될 나무들은 맨 앞에 삽입하도록 수정했다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이후에는 babyTrees라는 deque를 따로 분리해서 매년 겨울 종료 후 앞에 붙여주도록 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;deque&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

struct Tree {
    int x;
    int y;
    int age;
};

vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; field;
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; fertilizer;
// 상하좌우 좌상 좌하 우상 우하
int dx[8] = {-1, 1, 0, 0, -1, 1, -1, 1};
int dy[8] = {0, 0, -1, 1, -1, -1, 1, 1};
const int DIR_MAX = 8;

bool sortTrees(Tree a, Tree b) {
    return a.age &amp;lt; b.age;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int n, m, k;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; k;

    deque&amp;lt;Tree&amp;gt; aliveTrees;
    fertilizer = vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;(n, vector&amp;lt;int&amp;gt;(n));
    field = vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;(n, vector&amp;lt;int&amp;gt;(n, 5));
    for (int i=0; i&amp;lt;n; i++) {
        for (int j=0; j&amp;lt;n; j++) {
            cin &amp;gt;&amp;gt; fertilizer[i][j];
        }
    }

    for (int i=0; i&amp;lt;m; i++) {
        int x, y, z;
        cin &amp;gt;&amp;gt; x &amp;gt;&amp;gt; y &amp;gt;&amp;gt; z;
        aliveTrees.push_back({x-1, y-1, z});
    }

    sort(aliveTrees.begin(), aliveTrees.end(), sortTrees);
    for (int i=0; i&amp;lt;k; i++) {
        deque&amp;lt;Tree&amp;gt; newAliveTrees;
        deque&amp;lt;Tree&amp;gt; babyTrees;
        vector&amp;lt;Tree&amp;gt; dieTrees;
        // 봄 : 나이만큼 양분 먹고 나이 증가
        // 어린 순으로 먹고 못/덜먹은 애들은 사망
        for (int j=0; j&amp;lt;aliveTrees.size(); j++) {
            Tree now = aliveTrees[j];
            if (field[now.x][now.y] &amp;gt;= now.age) {
                field[now.x][now.y] -= now.age;
                newAliveTrees.push_back({now.x, now.y, now.age+1});
            }
            else {
                dieTrees.push_back(now);
            }
        }

        // 여름 - 죽은 나무 양분 전환
        for (int j=0; j&amp;lt;dieTrees.size(); j++) {
            Tree now = dieTrees[j];
            field[now.x][now.y] += now.age / 2;
        }

        // 가을 - 나무 번식
        for (int j=0; j&amp;lt;newAliveTrees.size(); j++) {
            Tree now = newAliveTrees[j];
            if (now.age % 5 == 0) {
                // 인접 8칸에 나이 1인 나무 생성
                for (int s=0; s&amp;lt;DIR_MAX; s++) {
                    int mx = now.x + dx[s];
                    int my = now.y + dy[s];
                    if (mx &amp;lt; 0 || mx &amp;gt;= n || my &amp;lt; 0 || my &amp;gt;= n) continue;
                    babyTrees.push_front({mx, my, 1});
                }
            }
        }

        // 겨울
        for (int j=0; j&amp;lt;n; j++) {
            for (int s=0; s&amp;lt;n; s++) {
                field[j][s] += fertilizer[j][s];
            }
        }
        while (!babyTrees.empty()) {
            newAliveTrees.push_front(babyTrees.back());
            babyTrees.pop_back();
        }
        aliveTrees = newAliveTrees;
    }
    cout &amp;lt;&amp;lt; aliveTrees.size() &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/59</guid>
      <comments>https://yerang2.tistory.com/59#entry59comment</comments>
      <pubDate>Tue, 1 Apr 2025 18:54:37 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 16236: 아기 상어</title>
      <link>https://yerang2.tistory.com/58</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우리 조카가 보는 아기상어는 귀여운데.. 문제 속 아기 상어는 그렇게 귀엽진 않다.. 약육강식 야생의 상어다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSDFYn/btsM5zG1iFs/kynlwnzOjeatIrvbHlKuo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSDFYn/btsM5zG1iFs/kynlwnzOjeatIrvbHlKuo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSDFYn/btsM5zG1iFs/kynlwnzOjeatIrvbHlKuo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSDFYn%2FbtsM5zG1iFs%2FkynlwnzOjeatIrvbHlKuo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;225&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제 이해의 미흡이 가장 크다....&lt;/li&gt;
&lt;li&gt;끝까지 다 안읽고 int n.. 하고 코드를 쓰고있다..(^.ㅜ) 습관을 고치자!!!!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  문제 파악&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상어는 현재 크기만큼의 물고기를 먹어야 사이즈업을 한다!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

struct sharkMove {
    int x;
    int y;
    int time;
};
int n, sx, sy, sSize = 2, t = 0, eatCnt = 0;
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; v;
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
const int DIR_MAX = 4;
bool sortQ(sharkMove a, sharkMove b) {
    if (a.x &amp;lt; b.x) return true;
    else if (a.x &amp;gt; b.x) return false;
    else {
        if (a.y &amp;lt; b.y) return true;
        else return false;
    }
}
bool bfsFind() {
    queue&amp;lt;sharkMove&amp;gt; q;
    vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; visited(n, vector&amp;lt;bool&amp;gt;(n, false));
    vector&amp;lt;sharkMove&amp;gt; ready;
    q.push({sx, sy, 0});
    visited[sx][sy] = true;

    while (!q.empty()) {
        sharkMove now = q.front();
        q.pop();
        if (v[now.x][now.y] &amp;gt; 0 &amp;amp;&amp;amp; v[now.x][now.y] &amp;lt;= 6 &amp;amp;&amp;amp;
            v[now.x][now.y] &amp;lt; sSize) {
            if (ready.empty())
                ready.push_back(now);
            else if (ready[0].time == now.time) {
                ready.push_back(now);
            }
            continue;
        }

        for (int i=0; i&amp;lt;DIR_MAX; i++) {
            int mx = now.x + dx[i];
            int my = now.y + dy[i];

            if (mx &amp;lt; 0 || mx &amp;gt;= n || my &amp;lt; 0 || my &amp;gt;= n) continue;
            if (visited[mx][my] || v[mx][my] &amp;gt; sSize) continue;

            visited[mx][my] = true;
            q.push({mx, my, now.time+1});
        }
    }
    if (!ready.empty()) {
        // readyQ 중에서 가장 위에 있으면서, 가장 왼쪽에 있는 물고기 먹음
        sort(ready.begin(), ready.end(), sortQ);
        sharkMove now = ready[0];
        v[now.x][now.y] = 0;
        sx = now.x;
        sy = now.y;
        t += now.time;
        eatCnt++;
        return true;
    }
    return false;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    cin &amp;gt;&amp;gt; n;
    v = vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;(n, vector&amp;lt;int&amp;gt;(n));
    for (int i=0; i&amp;lt;n; i++) {
        for (int j=0; j&amp;lt;n; j++) {
            cin &amp;gt;&amp;gt; v[i][j];
            if (v[i][j] == 9) {
                sx = i;
                sy = j;
            }
        }
    }
    v[sx][sy] = 0;

    while (true) {
        // 상어 주변에 있는 상어보다 크기가 작은 물고기 찾기
        if (bfsFind()) {
            if (sSize == eatCnt) {
                sSize++;
                eatCnt = 0;
            }
        }
        else {
            break;
        }
        // 찾으면 해당 물고기 먹고, 상어 크기 증가, 시간 증가
        // 다 돌았는데 못찾으면 종료
    }

    cout &amp;lt;&amp;lt; t &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/58</guid>
      <comments>https://yerang2.tistory.com/58#entry58comment</comments>
      <pubDate>Tue, 1 Apr 2025 16:29:39 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 14891: 톱니 바퀴 (설명 및 주석 포함)</title>
      <link>https://yerang2.tistory.com/57</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왼쪽(9시 방향)과 오른쪽(3시 방향)의 인덱스를 기억해서&lt;/li&gt;
&lt;li&gt;시계 방향 회전 시 인덱스가 하나씩 줄어들고&lt;/li&gt;
&lt;li&gt;반시계 방향 회전 시 인덱스가 하나씩 늘어나는 원리를 이용함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회전을 하기 전에 양 옆의 노드랑 같은지 체크하고,&amp;nbsp;회전한 뒤에 다른 값을 가지던 톱니는 반대로 회전해줘야하는데&lt;/li&gt;
&lt;li&gt;회전을 한 뒤에 양 옆의 노드를 비교하니까 결과가 다르게 나옴 ㅜㅜ&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  문제 해결&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회전하는 로직을 재귀함수의 맨 뒤로 이동시켜서 회전을 나중에 수행하도록 변경!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;
struct Topni {
    int leftIdx; // 9시
    int rightIdx; // 3시
};
const int TOPNI_LENGTH = 8;
const int TOPNI_COUNT = 4;
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; topni(TOPNI_COUNT, vector&amp;lt;int&amp;gt;(TOPNI_LENGTH));
vector&amp;lt;Topni&amp;gt; status(TOPNI_LENGTH, {6, 2});

void rotateTopni(int idx, int rotateDir) {
    status[idx].leftIdx -= rotateDir;
    status[idx].rightIdx -= rotateDir;
    status[idx].leftIdx = (status[idx].leftIdx + TOPNI_LENGTH) % TOPNI_LENGTH;
    status[idx].rightIdx = (status[idx].rightIdx + TOPNI_LENGTH) % TOPNI_LENGTH;
}

void moveTopni(int beforeValue, int beforeRotate, int idx, int dir) {
    // 진행 방향이 우측이면 9시랑 비교해야되고
    int checkValue;
    if (dir == 1) {
        if (beforeValue == topni[idx][status[idx].leftIdx]) {
            return;
        }
    }
    // 왼쪽이면 3시랑 비교
    else if (dir == -1) {
        if (beforeValue == topni[idx][status[idx].rightIdx]) {
            return;
        }
    }
    // 회전
    int newRotate = (beforeRotate == -1) ? 1 : -1;
    const int nextIdx = idx + dir;
    if (nextIdx &amp;gt;= 0 &amp;amp;&amp;amp; nextIdx &amp;lt; TOPNI_COUNT) {
        if (dir == 1) {
            moveTopni(topni[idx][status[idx].rightIdx], newRotate, idx + dir, dir);
        }
        else if (dir == -1) {
            moveTopni(topni[idx][status[idx].leftIdx], newRotate, idx + dir, dir);
        }
    }
    rotateTopni(idx, newRotate);
    return;
}

int calculateAnswer() {
    int ans = 0;
    for (int i=0; i&amp;lt;4; i++) {
        int idx12 = status[i].rightIdx - 2;
        idx12 = (idx12 + TOPNI_LENGTH) % TOPNI_LENGTH;
        if (topni[i][idx12] == 1) ans += pow(2, i);
    }
    return ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int k;

    for (int i=0; i&amp;lt;TOPNI_COUNT; i++) {
        string str;
        cin &amp;gt;&amp;gt; str;
        for(int j=0; j&amp;lt;TOPNI_LENGTH; j++) {
            topni[i][j] = str[j] - '0';
        }
    }

    cin &amp;gt;&amp;gt; k;
    for (int i=0; i&amp;lt;k; i++) {
        int n, rotateDir;
        cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; rotateDir;
        n--; // for 0-base

        if (n + 1 &amp;lt; TOPNI_COUNT) {
            // 오른쪽 전파();
            moveTopni(topni[n][status[n].rightIdx], rotateDir, n+1, 1);
        }
        if (n - 1 &amp;gt;= 0) {
            // 왼쪽 전파(인덱스, 진행 방향(+1/-1), 회전 방향(+1/-1));
            moveTopni(topni[n][status[n].leftIdx], rotateDir, n-1, -1);
        }
        rotateTopni(n, rotateDir);
    }
    cout &amp;lt;&amp;lt; calculateAnswer() &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/57</guid>
      <comments>https://yerang2.tistory.com/57#entry57comment</comments>
      <pubDate>Tue, 1 Apr 2025 14:17:43 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 17143: 낚시왕 (설명 및 주석 포함)</title>
      <link>https://yerang2.tistory.com/56</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;상어를 크기순으로 소팅하고 시작해서, 해당 칸에 이미 상어가 있으면 나중에 온 상어는 추가하지 않도록 했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 겪은 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상어를 이동시킬 때&lt;b&gt; shark.speed만큼 반복&amp;nbsp;&lt;/b&gt;하다보니 &lt;b&gt;시간 초과가 발생&lt;/b&gt;했다.&lt;/li&gt;
&lt;li&gt;100&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; (턴) &amp;times; &lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;1000&lt;/span&gt; (속력) &amp;times; &lt;span&gt;10&lt;/span&gt;,&lt;span&gt;000&lt;/span&gt; (상어 수) = &lt;b&gt;1&lt;span&gt;,&lt;/span&gt;000&lt;span&gt;,&lt;/span&gt;000&lt;span&gt;,&lt;/span&gt;000&lt;span&gt; (&lt;/span&gt;10&lt;span&gt;억 번 연산)&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;1억이면 1~2초정도 걸린다고 하는데, 이 경우면 10초가 넘게된다..&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  문제 해결&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 위치에서 한 주기를 돌면 다시 현재 위치로 오니까&lt;/li&gt;
&lt;li&gt;주기를 계산해서 모듈러 연산으로 주기만큼 제거하고 나머지 이동량만큼만 이동시켰다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140656764&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;

struct Shark {
    int x;
    int y;
    int speed;
    int dir;
    int size;
};
// 상 하 우 좌
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, 1, -1};
const int DIR_MAX = 4;

bool cmp(Shark a, Shark b) {
    return a.size &amp;gt;= b.size;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int r, c, m, sum = 0;
    vector&amp;lt;Shark&amp;gt; sharks;
    cin &amp;gt;&amp;gt; r &amp;gt;&amp;gt; c &amp;gt;&amp;gt; m;
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; sea(r, vector&amp;lt;int&amp;gt;(c, 0));
    for (int i=0; i&amp;lt;m; i++) {
        int rr, cc, s, d, z;
        cin &amp;gt;&amp;gt; rr &amp;gt;&amp;gt; cc &amp;gt;&amp;gt; s &amp;gt;&amp;gt; d &amp;gt;&amp;gt; z;
        sharks.push_back({rr-1,cc-1,s,d-1,z});
        sea[rr-1][cc-1] = z;
    }

    sort(sharks.begin(), sharks.end(), cmp);

    for (int i=0; i&amp;lt;c; i++) {
        // 낚시왕이 오른쪽으로 한 칸 이동한다.
        // 낚시왕이 있는 열에 있는 상어 중에서 땅과 제일 가까운 상어를 잡는다. 상어를 잡으면 격자판에서 잡은 상어가 사라진다.
        for (int j=0; j&amp;lt;r; j++) {
            if (sea[j][i] &amp;gt; 0) {
                sum += sea[j][i];
                sea[j][i] = -1;
                break;
            }
        }

        vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; newSea(r, vector&amp;lt;int&amp;gt;(c, 0));
        vector&amp;lt;Shark&amp;gt; newSharks;

        // 상어가 이동한다.
        for (int j=0; j&amp;lt;sharks.size(); j++) {
            Shark now = sharks[j];
            if (sea[now.x][now.y] == -1) continue;
            // 이동
            int jugi = (now.dir == 0 || now.dir == 1) ? 2 * (r - 1) : 2 * (c - 1);
            int move = now.speed % jugi;
            int x = now.x, y = now.y, d = now.dir;
            for (int s = 0; s &amp;lt; move; ++s) {
                int nx = x + dx[d];
                int ny = y + dy[d];

                // 벽에 닿으면 방향 반대로
                if (nx &amp;lt; 0 || nx &amp;gt;= r || ny &amp;lt; 0 || ny &amp;gt;= c) {
                    if (d == 0) d = 1;
                    else if (d == 1) d = 0;
                    else if (d == 2) d = 3;
                    else if (d == 3) d = 2;

                    // 방향 바꾸고 다시 계산
                    nx = x + dx[d];
                    ny = y + dy[d];
                }

                x = nx;
                y = ny;
            }

            if (newSea[x][y] == 0) {
                newSharks.push_back({x, y, now.speed, d, now.size});
                newSea[x][y] = now.size;
            }
        }
        sharks = newSharks;
        sea = newSea;
    }

    cout &amp;lt;&amp;lt; sum &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/56</guid>
      <comments>https://yerang2.tistory.com/56#entry56comment</comments>
      <pubDate>Tue, 1 Apr 2025 10:38:41 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 14503: 로봇 청소기 (설명 및 주석 포함)</title>
      <link>https://yerang2.tistory.com/54</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;겪은 문제 (멍청)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1743140026277&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;7 7
4 2 1
1 1 1 1 1 1 1
1 0 0 0 1 0 1
1 0 1 1 0 0 1
1 0 0 0 0 1 1
1 0 0 1 0 0 1
1 0 0 0 0 0 1
1 1 1 1 1 1 1
&amp;gt;&amp;gt; 답 11&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 케이스에서 답이 11이 나와야 했는데 9가 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  문제의 원인&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시판을 돌다보니 나처럼 해당 케이스에서 어려움을 겪는 사람이 있었고, 해결사가 댓글에 루트를 달아주었는데, 이런!!! 나랑 영 다른 길을 간다...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s3Z73/btsM0fvOByZ/18vwr0B51xatRngMZWAhC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s3Z73/btsM0fvOByZ/18vwr0B51xatRngMZWAhC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s3Z73/btsM0fvOByZ/18vwr0B51xatRngMZWAhC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs3Z73%2FbtsM0fvOByZ%2F18vwr0B51xatRngMZWAhC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;383&quot; height=&quot;342&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고보니 멍청한 실수를 했다. 방향이 1~4가 아니라 0~3 인데, 나는 1 베이스를 제로베이스로 만든다고 dir을 -1한 뒤 시작했다... &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이해하기 쉬우라고 일부러 주석을 안지웠다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1743140163497&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;

enum floorStatus {
    DIRTY,
    WALL,
    CLEAN
};

vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; floorMap;
int n, m, r, c, d, maxCnt = 0;
// 북 동 남 서 -&amp;gt; +2 % 4 하면 반대 방향이 됨.
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
const int DIR_MAX = 4;

struct Robot {
    int x;
    int y;
    int dir;
    int cnt;
};

void bfs() {
    queue&amp;lt;Robot&amp;gt; q;
    q.push({r, c, d, 0}); // dir은 0-base

    while (!q.empty()) {
        Robot now = q.front();
        q.pop();

        // 현재 칸이 아직 청소되지 않은 경우, 현재 칸을 청소한다.
        if (floorMap[now.x][now.y] == DIRTY) {
            floorMap[now.x][now.y] = CLEAN;
            now.cnt++;
            maxCnt = max(maxCnt, now.cnt);
        }

        // 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 없는 경우,
        bool isAnyDirty = false;
        for (int i=0; i&amp;lt;DIR_MAX; i++) {
            int mx = now.x + dx[i];
            int my = now.y + dy[i];
            if (mx &amp;lt; 0 || my &amp;lt; 0 || mx &amp;gt;= n || my &amp;gt;= m) continue;
            if (floorMap[mx][my] == DIRTY) isAnyDirty = true;
        }

        // 바라보는 방향을 유지한 채로 한 칸 후진할 수 있다면 한 칸 후진하고 1번으로 돌아간다.
        // 바라보는 방향의 뒤쪽 칸이 벽이라 후진할 수 없다면 작동을 멈춘다.
        if (!isAnyDirty) {
            int mx = now.x + dx[(now.dir + 2) % 4];
            int my = now.y + dy[(now.dir + 2) % 4];
            if (mx &amp;lt; 0 || my &amp;lt; 0 || mx &amp;gt;= n || my &amp;gt;= m || floorMap[mx][my] == WALL) continue;
            q.push({mx, my, now.dir, now.cnt});
            continue;
        }

        // 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 있는 경우,
        // 반시계 방향으로 90도 회전한다.
        int newDir = (now.dir - 1 + 4) % 4;

        // 바라보는 방향을 기준으로 앞쪽 칸이 청소되지 않은 빈 칸인 경우 한 칸 전진한다.
        int mx = now.x + dx[newDir];
        int my = now.y + dy[newDir];
        if (mx &amp;gt;= 0 &amp;amp;&amp;amp; my &amp;gt;= 0 &amp;amp;&amp;amp; mx &amp;lt; n &amp;amp;&amp;amp; my &amp;lt; m) {
            if (floorMap[mx][my] == DIRTY) {
                q.push({mx, my, newDir, now.cnt});
                continue;
            }
        }
        // 앞 칸이 청소 된 칸이면 계속 회전해서 청소 안된 칸을 찾아야 함.
        q.push({now.x, now.y, newDir, now.cnt});

    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    cin &amp;gt;&amp;gt; r &amp;gt;&amp;gt; c &amp;gt;&amp;gt; d;
    floorMap = vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;(n, vector&amp;lt;int&amp;gt;(m));

    // 1 : 벽, 0 : 청소 안된 곳
    for (int i=0; i&amp;lt;n; i++) {
        for (int j=0; j&amp;lt;m; j++) {
            cin &amp;gt;&amp;gt; floorMap[i][j];
        }
    }

    bfs();
    cout &amp;lt;&amp;lt; maxCnt &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/54</guid>
      <comments>https://yerang2.tistory.com/54#entry54comment</comments>
      <pubDate>Fri, 28 Mar 2025 14:39:45 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 퍼즐 조각 채우기 - 프로그래머스 (설명 및 주석 포함)</title>
      <link>https://yerang2.tistory.com/53</link>
      <description>&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;테이블 블록을 bfs로 찾고 (A)&lt;/li&gt;
&lt;li&gt;게임 보드의 빈공간을 bfs로 찾아서 (B)&lt;/li&gt;
&lt;li&gt;A와 B가 같은 모양일 때 블록 칸 수를 answer에 더해주면 되겠는데?&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;&lt;b&gt;회전&lt;/b&gt;을 시킬 수 있기 때문이다...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.gif&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xs0AW/btsMZbz5wjI/2IlRGLOwKwC8YAYH2vjbo1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xs0AW/btsMZbz5wjI/2IlRGLOwKwC8YAYH2vjbo1/img.gif&quot; data-alt=&quot;나.. 회전 안시켜봤어&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xs0AW/btsMZbz5wjI/2IlRGLOwKwC8YAYH2vjbo1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/xs0AW/btsMZbz5wjI/2IlRGLOwKwC8YAYH2vjbo1/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;391&quot; height=&quot;221&quot; data-filename=&quot;img.gif&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;나.. 회전 안시켜봤어&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.&lt;/b&gt; 같은 모양이더라도 누군 (2,4)부터 그려지고, 누군 (3,8)부터 그려지고.. 하면 비교가 어려우므로 &lt;b&gt;각 블록들의 좌표를 0-base로&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 좌표를 x, y 라고 하고, 90도 시계방향 회전한 좌표를 x', y'라고 하면 &lt;b&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;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;x' = y&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;y' = (돌리기 전 height - 1) - x&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2044&quot; data-origin-height=&quot;952&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFzZ6r/btsMZWWxJFR/aYyLwd1dD36mhrGaoy8nm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFzZ6r/btsMZWWxJFR/aYyLwd1dD36mhrGaoy8nm1/img.png&quot; data-alt=&quot;그림으로 설명해보자면 이렇다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFzZ6r/btsMZWWxJFR/aYyLwd1dD36mhrGaoy8nm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFzZ6r%2FbtsMZWWxJFR%2FaYyLwd1dD36mhrGaoy8nm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;265&quot; data-origin-width=&quot;2044&quot; data-origin-height=&quot;952&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그림으로 설명해보자면 이렇다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2.&lt;/b&gt; 그래서 &lt;b&gt;height, width&lt;/b&gt;를 블록 구조체에 담았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.&lt;/b&gt; 더 빨리 걸러지라고 &lt;b&gt;블록 개수 cnt&lt;/b&gt;도 블록 구조체에 담았다 ^...^히히&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;소스코드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로직을 미리 주석으로 적어놓고 구현한거라 주석이 좀 많다.&lt;/p&gt;
&lt;pre id=&quot;code_1743066322267&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

struct Axis {
    int x;
    int y;
};

struct Block {
    vector&amp;lt;Axis&amp;gt; points;
    int width;
    int height;
    int cnt;
};

// 상 하 좌 우
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
#define MAX_DIR 4

int SIZE;
vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; visited;
vector&amp;lt;bool&amp;gt; visited1D;
vector&amp;lt;Block&amp;gt; tableBlocks;
vector&amp;lt;Block&amp;gt; blankBlocks;

bool sortAxises(const Axis&amp;amp; a, const Axis&amp;amp; b) {
    return a.x == b.x ? (a.y &amp;lt; b.y) : (a.x &amp;lt; b.x);
}
vector&amp;lt;Axis&amp;gt; bfs(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;amp; board, int x, int y, int validValue) {
    vector&amp;lt;Axis&amp;gt; axises;
    queue&amp;lt;Axis&amp;gt; q;
    
    q.push({x, y});
    axises.push_back({x, y});
    
    while (!q.empty()) {
        Axis now = q.front();
        q.pop();
        for (int i=0; i&amp;lt;MAX_DIR; i++) {
            Axis next = {now.x + dx[i], now.y + dy[i]};
            
            // 유효성 체크
            if (next.x &amp;lt; 0 || next.x &amp;gt;= SIZE || next.y &amp;lt; 0 || next.y &amp;gt;= SIZE ||
                visited[next.x][next.y] || board[next.x][next.y] != validValue)
                continue;
            
            visited[next.x][next.y] = true;
            q.push(next);
            axises.push_back(next);
        }
    }

    return axises;
}

Block convertAxisToBlock(vector&amp;lt;Axis&amp;gt; axises) {
    Block block;
    block.cnt = axises.size();
    Axis min = {51, 51};
    Axis max = {-1, -1};
    
    // 0-base 블록 리스트 만들기 위해 min-max 체크
    for (const Axis&amp;amp; axis : axises) {
        if (min.x &amp;gt; axis.x) min.x = axis.x;
        if (min.y &amp;gt; axis.y) min.y = axis.y;
        if (max.x &amp;lt; axis.x) max.x = axis.x;
        if (max.y &amp;lt; axis.y) max.y = axis.y;
    }
    
    // 0-base 블록 넣기
    for (const Axis&amp;amp; axis : axises) {
        block.points.push_back({axis.x - min.x, axis.y - min.y});
    }
    
    // width, height 계산
    block.width = max.y - min.y + 1;
    block.height = max.x - min.x + 1;
    
    
    return block;
}

bool checkValidSize(Block&amp;amp; A, Block&amp;amp; B) {
    // 3-1. 칸 수 동일해야됨
    if (A.cnt != B.cnt) return false;
    // 3-2. 가로:세로 비율이 동일해야됨
    if (!(A.width == B.width &amp;amp;&amp;amp; A.height == B.height) &amp;amp;&amp;amp;
        !(A.width == B.height &amp;amp;&amp;amp; A.height == B.width)) return false;
    return true;
}

bool checkSameAxises(vector&amp;lt;Axis&amp;gt; A, vector&amp;lt;Axis&amp;gt; B) {
    for (int i=0; i&amp;lt;A.size(); i++) {
        if (A[i].x != B[i].x || A[i].y != B[i].y) return false;
    }
    return true;
}

void rotateAxisesClockWise(Block&amp;amp; block) {
    for (int i=0; i&amp;lt;block.points.size(); i++) {
        int tmpX = block.points[i].x;
        block.points[i].x = block.points[i].y;
        block.points[i].y = block.height - 1 - tmpX;
    }
    int tmpW = block.width;
    block.width = block.height;
    block.height = tmpW;
    return;
}

bool checkSameShapeBlock(Block A, Block B) {
    // 회전 공식 1. x'축은 y축 인덱스가 그대로 오고
    // y'축은 거꾸로 x축이 되네!!!!!!!!!!! 유레카다
    // ✅ x' = y, y' = (size - 1) - x;
    //   단, 이 방법은 width, height가 동일해야 함. 정사각형 안에 있어야 함.

    // 회전 공식 2. 만약 width, height가 다르더라도 size대신
    // 각 축에 width, height를 사용하면 괜찮지 않을까?
    // ✅ x' = y, y' = (기존 height(바뀐 width) - 1) - x;
    sort(A.points.begin(), A.points.end(), sortAxises);
    for (int i=0; i&amp;lt;MAX_DIR; i++) {
        // 같은 좌표 체크
        sort(B.points.begin(), B.points.end(), sortAxises);
        if (checkSameAxises(A.points, B.points)) {
            return true;
        }
        // B 블록 회전
        rotateAxisesClockWise(B);
    }
    return false;
}

int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; game_board, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; table) {
    int answer = 0;
    SIZE = table.size();
    
    // 1. 테이블 위에 있는 블록을 다음과 같이 저장
        // 좌표 리스트 (정렬된 상태, 0-베이스)
        // width, height, 칸 수
    visited = vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt;(SIZE + 1, vector&amp;lt;bool&amp;gt;(SIZE + 1, false));
    for (int i=0; i&amp;lt;SIZE; i++) {
        for (int j=0; j&amp;lt;SIZE; j++) {
            if (!visited[i][j] &amp;amp;&amp;amp; table[i][j] == 1) {
                visited[i][j] = true;
                vector&amp;lt;Axis&amp;gt; axises = bfs(table, i, j, 1);
                // 리턴받은 블록 리스트를 Block으로 형변환 후 푸시
                tableBlocks.push_back(convertAxisToBlock(axises));
            }
        }
    }
    
    // 2. 게임 보드의 빈 칸을 bfs로 찾기
        // 얘도 테이블 블록과 같이 0-base로 (x, y) 저장
            // 제로베이스는 minX, minY를 각각 빼서.
        // width, height, 칸 수
    visited = vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt;(SIZE + 1, vector&amp;lt;bool&amp;gt;(SIZE + 1, false));
    for (int i=0; i&amp;lt;SIZE; i++) {
        for (int j=0; j&amp;lt;SIZE; j++) {
            if (!visited[i][j] &amp;amp;&amp;amp; game_board[i][j] == 0) {
                visited[i][j] = true;
                vector&amp;lt;Axis&amp;gt; axises = bfs(game_board, i, j, 0);
                // 리턴받은 블록 리스트를 Block으로 형변환 후 푸시
                blankBlocks.push_back(convertAxisToBlock(axises));
            }
        }
    }
    
    visited1D = vector&amp;lt;bool&amp;gt;(tableBlocks.size() + 1, false);
    // 3. 게임 보드의 빈 칸에 넣을 수 있는 테이블 블록 찾기
    for (int i=0; i&amp;lt;blankBlocks.size(); i++) {
        for (int j=0; j&amp;lt;tableBlocks.size(); j++) {
            // 같은 사이즈면서 사용한 블록 아니고
            if (!checkValidSize(blankBlocks[i], tableBlocks[j]) || visited1D[j]) continue;
            // 회전시켜서 같은 모양이 나오는지
            if (!checkSameShapeBlock(blankBlocks[i], tableBlocks[j])) continue;
            // 해당 블록 사용 완료
            visited1D[j] = true;
            // 블록 사이즈만큼 answer에 추가
            answer += tableBlocks[j].cnt;
            break;
    }
            
        }
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;후기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머리로 푸는 방법 생각하는데만 거의 1시간이 걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 작성하고 오류까지 해결하니까 거진 2시간이 걸렸다. 오늘 해봤으니 다음엔 더 잘풀어야지&lt;/p&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/53</guid>
      <comments>https://yerang2.tistory.com/53#entry53comment</comments>
      <pubDate>Thu, 27 Mar 2025 18:27:49 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 소수찾기</title>
      <link>https://yerang2.tistory.com/52</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;풀이 (비효율적 주의)&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1742981211547&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;iostream&amp;gt;

using namespace std;

int solution(string numbers) {
    map&amp;lt;int, int&amp;gt; m;
    set&amp;lt;int&amp;gt; s;
    
    const int limit = pow(10, numbers.length()) - 1;
    
    // 넘버 미리 맵에 넣어두기
    for (int i=0; i&amp;lt;numbers.length(); i++) {
        int number = numbers[i] - '0';
        if (m.find(number) == m.end()) {
            m.insert({number, 1});
        }
        else {
            m[number]++;
        }
    }

    // 에라토스테네스의 체 구현
    vector&amp;lt;int&amp;gt; v(limit+1);
    for (int i=2; i&amp;lt;=limit; i++) {
        for (int j=2; i*j&amp;lt;=limit; j++) {
            v[i*j] = -1;
        }
    }
    
    // 본격 만들 수 있는지 체크
    for (int i=2; i&amp;lt;=limit; i++) {
        map&amp;lt;int, int&amp;gt; copyM;
        copyM.insert(m.begin(), m.end());
        
        if (v[i] != -1) { // 소수면서
            int num = i;
            bool isValid = true;
            while (num &amp;gt; 0) { // 한자리씩 체크
                int digit = num%10;
                if (copyM.find(digit) == copyM.end() || copyM[digit] == 0) {
                    // 맵에 해당 숫자 없거나 개수가 0개면 만들 수 없는 숫자
                    isValid = false;
                    break;
                }
                copyM[digit]--;
                num /= 10;
            }
            if (isValid) {
                s.insert(i);
            }
        }
    }
    
    return s.size();
}&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&gt;가능한 숫자 조합을 숫자가 3개면 3중 for문, 4개면 4중 for문... 이렇게 돌려야 한다고 생각해서 이걸 구현 못하겠다고 생각함&lt;/li&gt;
&lt;li&gt;그래서 숫자가 3개면 1~999, 4개면 1~9999로 범위를 잡아서 에라토스테네스 체로 그 안에 소수만 남기고&lt;/li&gt;
&lt;li&gt;남은 소수에 대해서 주어진 숫자의 조합으로 만들 수 있는지 map을 막 카피하고.. 별 짓을 다했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;더 나은 해결책&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1742981363684&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

set&amp;lt;int&amp;gt; candidates;

// 숫자 조합 생성
void generate(string current, string rest) {
    if (!current.empty()) {
        candidates.insert(stoi(current));
    }
    for (int i = 0; i &amp;lt; rest.size(); i++) {
        string next = rest;
        next.erase(i, 1);
        generate(current + rest[i], next);
    }
}

// 소수 판별 함수
bool isPrime(int num) {
    if (num &amp;lt;= 1) return false;
    if (num == 2) return true;
    if (num % 2 == 0) return false;
    for (int i = 3; i &amp;lt;= sqrt(num); i += 2) {
        if (num % i == 0) return false;
    }
    return true;
}

int solution(string numbers) {
    generate(&quot;&quot;, numbers);

    int count = 0;
    for (int num : candidates) {
        if (isPrime(num)) {
            count++;
        }
    }
    return count;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;후보자 파악은 재귀함수로 할 수 있다. 인자로 (선택된 넘버, 남은 넘버들) 이렇게 해서 재귀로 돌면서 set에 담아주면 더 효율적으로 돌릴 수 있다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm/PS</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/52</guid>
      <comments>https://yerang2.tistory.com/52#entry52comment</comments>
      <pubDate>Wed, 26 Mar 2025 18:31:30 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 코테에서 자주 쓰는 문법 정리</title>
      <link>https://yerang2.tistory.com/51</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;웹개발로 지원하다보면.. JavaScript 언어로 강제하는데가 있는가 하면 어디는 Python/C++/Java 인 곳도 있고..결국 다 할 줄 알아야된다.. ㅜㅜ 또륵&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dWsPkl/btsMYd5krLN/QPiaNThth9I0WvsQKPnvwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dWsPkl/btsMYd5krLN/QPiaNThth9I0WvsQKPnvwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dWsPkl/btsMYd5krLN/QPiaNThth9I0WvsQKPnvwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdWsPkl%2FbtsMYd5krLN%2FQPiaNThth9I0WvsQKPnvwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;153&quot; height=&quot;153&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자.. 어쨌든 시작해보자면 크게 &lt;b&gt;STL&lt;/b&gt; 관련된 거랑, &lt;b&gt;문자열&lt;/b&gt; 관련된 걸로 분리했습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;STL 관련&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;code&gt;find()&lt;/code&gt; &amp;ndash; 특정 키 존재 여부 확인&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;if (mp.find(x) != mp.end()) {
    // 키 x가 존재함
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;적용 가능한 STL : &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt;, &lt;code&gt;unordered_map&lt;/code&gt;, &lt;code&gt;unordered_set&lt;/code&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;h3 data-ke-size=&quot;size23&quot;&gt;2. &lt;code&gt;pair&lt;/code&gt;, &lt;code&gt;make_pair&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;pair&amp;lt;int, int&amp;gt; p = make_pair(3, 4);
int x = p.first;
int y = p.second;&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;h3 data-ke-size=&quot;size23&quot;&gt;3. &lt;code&gt;sort()&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; v = {3, 1, 4};
sort(v.begin(), v.end());    // 오름차순
sort(v.rbegin(), v.rend());  // 내림차순&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. &lt;code&gt;priority_queue&lt;/code&gt; - 우선순위 큐, Max-Heap&lt;/h3&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;priority_queue&amp;lt;int&amp;gt; pq;  // Max-Heap
pq.push(3);
pq.push(1);
int top = pq.top();  // 3
pq.pop();&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최댓값이나 최솟값에 접근하는 경우&lt;/li&gt;
&lt;li&gt;값의 추가나 제거가 찾은 경우&lt;/li&gt;
&lt;li&gt;반대로 정렬된 데이터에서 특정 값을 찾는 경우에는 &lt;b&gt;이분 탐색&lt;/b&gt;을 이용!&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Min-Heap 으로 만들기&lt;/h4&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; minPq;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. &lt;code&gt;lower_bound&lt;/code&gt;, &lt;code&gt;upper_bound&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; v = {1, 2, 2, 3, 5};
int idx = lower_bound(v.begin(), v.end(), 2) - v.begin();  // 2의 첫 위치&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;lower_bound&lt;/code&gt; : value &lt;b&gt;이상&lt;/b&gt;이 처음 나오는 위치&lt;/li&gt;
&lt;li&gt;&lt;code&gt;upper_bound&lt;/code&gt; : value &lt;b&gt;초과&lt;/b&gt;가 처음 나오는 위치&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬된 컨테이너에서 이분탐색 활용할 때 사용&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. &lt;code&gt;memset&lt;/code&gt; &amp;ndash; 배열 초기화&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int arr[100] = {0};
memset(arr, -1, sizeof(arr));  // 모든 요소를 -1로 초기화
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의: memset은 0, -1 등 단일 바이트 값만 안전하게 초기화할 수 있음&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 벡터 초기화&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;// 1차원 벡터
int N = 10;
std::vector&amp;lt;int&amp;gt; v(N, 0); // 길이 N, 모든 원소 0으로 초기화

// 2차원 벡터
int row = 5, col = 4;
std::vector&amp;lt;std::vector&amp;lt;int&amp;gt;&amp;gt; grid(row, std::vector&amp;lt;int&amp;gt;(col, 0));


std::vector&amp;lt;std::vector&amp;lt;int&amp;gt;&amp;gt; grid2; // 초기값 없는 버전&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. &lt;code&gt;unordered_map&lt;/code&gt;, &lt;code&gt;unordered_set&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;map&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt;보다 &lt;b&gt;빠른 해시 기반 컨테이너&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;단, &lt;b&gt;정렬이 필요&lt;/b&gt;하다면 &lt;code&gt;map&lt;/code&gt;/&lt;code&gt;set&lt;/code&gt; 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;unordered_map&amp;lt;int, string&amp;gt; um;
um[1] = &quot;hi&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. &lt;code&gt;tie()&lt;/code&gt; - 구조 분해 할당&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;std::pair&amp;lt;int, int&amp;gt; p = {1, 2};
int a, b;
std::tie(a, b) = p; // 구조 분해&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;js에만 있는 줄 알았는데 C++에도 있네&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10. 벡터 뒤집기&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;std::reverse(v.begin(), v.end());&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;11. &lt;code&gt;bitset&lt;/code&gt; - 비트마스킹&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;bitset&amp;gt;
std::bitset&amp;lt;4&amp;gt; bs(&quot;1010&quot;); // 4비트
bs.set(1); // 1번 비트 ON
bs.reset(1); // 1번 비트 OFF
bs.test(2); // 2번 비트가 켜졌는지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;String 관련&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 문자열 순회&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;for (char c : s) {
    std::cout &amp;lt;&amp;lt; c;
}

for (int i = 0; i &amp;lt; s.size(); i++) {
    std::cout &amp;lt;&amp;lt; s[i];
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;for (a:b)&lt;/code&gt;는 range-based for loop로, C++ 11에 도입된 문법임&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;범위 기반 for문 - 참조 순회로 성능이득 보기&lt;/h4&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;for (auto x : vec) {
    // x는 vec의 요소를 '복사'한 값
}

for (auto&amp;amp; elem : vec) {
    // x는 vec의 요소에 대한 '참조'
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;&lt;/code&gt; 키워드를 붙여 참조해서 접근하면 메모리 절약 및 속도를 줄일 수 있음&lt;/li&gt;
&lt;li&gt;읽기 전용이면 &lt;code&gt;const auto&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;수정할 거면 &lt;code&gt;auto&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;작은 타입(&lt;code&gt;int&lt;/code&gt;, &lt;code&gt;char&lt;/code&gt; 등)은 그냥 auto도 괜찮음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 문자열 찾기&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;s.find(&quot;bc&quot;);     // 처음 등장하는 위치
s.rfind(&quot;bc&quot;);    // 마지막 등장 위치
s.find(&quot;xyz&quot;) == std::string::npos // 없는 경우&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. &lt;code&gt;&amp;lt;cctype&amp;gt;&lt;/code&gt; - 문자 관련 함수들&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;cctype&amp;gt;

isalpha(c); // 알파벳인지
isdigit(c); // 숫자인지
islower(c); // 소문자인지

toupper(c); // 대문자로
tolower(c); // 소문자로&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 문자 반복해서 초기화하기&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;std::string s(n, 'x'); // x를 n번 반복한 문자열&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. &lt;code&gt;substr()&lt;/code&gt; - 문자열 자르기&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;std::string s = &quot;abcdef&quot;;
s.substr(1, 3); // &quot;bcd&quot; (1번 인덱스부터 3개)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. &lt;code&gt;find()&lt;/code&gt; - 문자열 찾기&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;s.find(&quot;bc&quot;);     // 처음 등장하는 위치
s.rfind(&quot;bc&quot;);    // 마지막 등장 위치
s.find(&quot;xyz&quot;) == std::string::npos // 없는 경우&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. &lt;code&gt;reverse()&lt;/code&gt; - 문자열 반전&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;std::reverse(s.begin(), s.end());&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. &lt;code&gt;set&lt;/code&gt;이용해서 문자열에서 문자 중복 제거&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;std::string s = &quot;banana&quot;;
std::set&amp;lt;char&amp;gt; unique(s.begin(), s.end());&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. &lt;code&gt;getline()&lt;/code&gt; + &lt;code&gt;stringstream()&lt;/code&gt; - 토큰으로 문자열 쪼개기&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;sstream&amp;gt;
std::string s = &quot;a,b,c&quot;;
std::stringstream ss(s);
std::string token;
while (std::getline(ss, token, ',')) {
    std::cout &amp;lt;&amp;lt; token &amp;lt;&amp;lt; &quot;\n&quot;;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Language/C++</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/51</guid>
      <comments>https://yerang2.tistory.com/51#entry51comment</comments>
      <pubDate>Wed, 26 Mar 2025 18:09:40 +0900</pubDate>
    </item>
    <item>
      <title>[비동기] async, await과 구 방식 비교</title>
      <link>https://yerang2.tistory.com/50</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;async와 await은 비동기 코드를 작성하고 처리하는 방식을 간단하고 직관적으로 만들어주는 문법이다. ES8(ES2017)에서 도입되었으며, Promise를 사용한 비동기 처리보다 가독성과 사용성을 크게 개선했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;구 방식&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;XHttpRequest 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 주요 브라우저에서 지원되므로 호환성 문제가 없다.&lt;/li&gt;
&lt;li&gt;하지만 콜백 지옥에 갇히거나, 코드 복잡도가 올라갈 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1742792371119&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function fetchData(callback) {
  const xhr = new XMLHttpRequest();
  xhr.open(&quot;GET&quot;, &quot;https://api.example.com/data&quot;, true);
  // POST : xhr.setRequestHeader(&quot;Content-Type&quot;, &quot;application/json;charset=UTF-8&quot;);

  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) { // 요청이 완료되면
      if (xhr.status === 200) { // 성공적인 응답
        callback(null, xhr.responseText);
      } else {
        callback(new Error(`HTTP error: ${xhr.status}`), null);
      }
    }
  };

  xhr.send();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Promise 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;then과 catch 메서드를 사용하여 비동기 작업을 체인으로 연결한다.&lt;/li&gt;
&lt;li&gt;에러 처리를 한 번에 할 수 있다.&lt;/li&gt;
&lt;li&gt;복잡한 체인으로 인해 가독성이 떨어질 수도 있다.
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function fetchData() {
  return new Promise((resolve, reject) =&amp;gt; {
    setTimeout(() =&amp;gt; {
      resolve(&quot;Data fetched&quot;);
    }, 1000);
  });
}

fetchData()
  .then((data) =&amp;gt; {
    console.log(data); // &quot;Data fetched&quot;
  })
  .catch((error) =&amp;gt; {
    console.error('Error:', error);
  });&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개선 버전&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;async&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;항상 Promise를 반환한다.&lt;/li&gt;
&lt;li&gt;함수 내에서 await 키워드를 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;함수 내에서 return 키워드를 사용하면 Promise.resolve와 동일하게 작동한다.&lt;/li&gt;
&lt;li&gt;함수 내에서 예외가 발생하면 Promise.reject와 동일하게 작동한다.&lt;/li&gt;
&lt;li&gt;단, 구 브라우저에서는&amp;nbsp;호환성이&amp;nbsp;떨어질&amp;nbsp;수&amp;nbsp;있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;async function fetchData() {
  return &quot;Data fetched&quot;;
}

fetchData().then((data) =&amp;gt; console.log(data)); // &quot;Data fetched&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;await&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;async 함수 내에서만 사용 가능함.&lt;/li&gt;
&lt;li&gt;Promise가 처리될 때까지 비동기 함수를 일시 중지함.&lt;/li&gt;
&lt;li&gt;Promise가 해결되면 await은 그 결과 값을 반환하고, Promise가 거부되면 예외를 발생시킴&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1742792285234&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function fetchData() {
  let promise = new Promise((resolve, reject) =&amp;gt; {
    setTimeout(() =&amp;gt; resolve(&quot;Data fetched&quot;), 1000);
  });

  let result = await promise; // `promise`가 해결될 때까지 기다림
  console.log(result); // &quot;Data fetched&quot;
}

fetchData();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;조합&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 두개를 조합해서 비동기 코드를 아래와 같이 짤 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1742792312397&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function fetchData() {
  try {
    let response = await fetch('https://api.example.com/data');
    let data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error fetching data:', error);
  }
}

fetchData();&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;이점&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;/li&gt;
&lt;li&gt;try...catch문을 통해 에러 처리가 쉬움&lt;/li&gt;
&lt;li&gt;비동직 작업을 순차적으로 실행할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p 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-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;병렬 처리가 어려움. 비동기 작업을 병렬로 처리하기 위해서는 &lt;b&gt;Promise.all&lt;/b&gt;과 같은 방법을 사용해야 함.&lt;/li&gt;
&lt;li&gt;오래된 브라우저 지원이 안됨. (트랜스파일러(Babel 등)을 사용하면 가능하긴 함)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Language/JavaScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/50</guid>
      <comments>https://yerang2.tistory.com/50#entry50comment</comments>
      <pubDate>Mon, 24 Mar 2025 14:00:18 +0900</pubDate>
    </item>
    <item>
      <title>[JS] 초기화된 배열 만들기</title>
      <link>https://yerang2.tistory.com/49</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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. &amp;nbsp;&lt;code&gt;Array.from()&lt;/code&gt; 방식&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const arr = Array.from({ length: 10 }, () =&amp;gt; 0);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;유연한 초기화가 가능하다&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Array.from({ length: 10 }, (_, i) =&amp;gt; i); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. &amp;nbsp;&lt;code&gt;Array()&lt;/code&gt; 방식&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const arr = Array(10).fill(0);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;code&gt;&lt;/code&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;code&gt;Array()&lt;/code&gt;의 주의할 점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체나 배열과 같이 참조가 가능한 타입으로 fill을 하게 되면, &lt;b&gt;참조값 하나가 전체 요소에 fill&lt;/b&gt;된다.&lt;br /&gt;그래서 &lt;b&gt;한 요소의 값만 바꿔도 전체가 바뀐다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const arr = Array(3).fill({});
arr[0].a = 1;
console.log(arr); // [ { a: 1 }, { a: 1 }, { a: 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;code&gt;Array.from()&lt;/code&gt;을 사용하면 각 요소가 독립적이라서 참조값도 대입해서 사용할 수 있다.&lt;/p&gt;</description>
      <category>Language/JavaScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/49</guid>
      <comments>https://yerang2.tistory.com/49#entry49comment</comments>
      <pubDate>Fri, 21 Mar 2025 12:03:18 +0900</pubDate>
    </item>
    <item>
      <title>forEach()에서 break가 안된다면 some()과 every()</title>
      <link>https://yerang2.tistory.com/48</link>
      <description>&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;PS중&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;forEach()&lt;/span&gt;&lt;span&gt;에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;break&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;를 사용할 수 없다는 사실&lt;/b&gt;&lt;/span&gt;&lt;span&gt;을 깨달았다.&lt;/span&gt;&lt;br /&gt;&lt;span&gt;이를 해결하기 위해 &lt;/span&gt;&lt;b&gt;&lt;span&gt;some()&lt;/span&gt;&lt;span&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;every()&lt;/span&gt;&lt;/b&gt;&lt;span&gt;를 알아봤다&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;some()&lt;/span&gt;&lt;span&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;every()&lt;/span&gt;&lt;span&gt;란?&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;자바스크립트 배열 메서드 중 하나로, 배열의 각 요소를 순회하면서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;특정 조건을 만족하는지 검사&lt;/b&gt;&lt;/span&gt;&lt;span&gt;하는 역할을 한다.&lt;/span&gt;&lt;br /&gt;&lt;span&gt;둘 다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;콜백 함수를 실행하며, Boolean 값을 반환&lt;/b&gt;&lt;/span&gt;&lt;span&gt;한다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 85.9302%; height: 57px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; width: 37.9843%;&quot;&gt;&lt;b&gt;&lt;span&gt;메서드&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; width: 62.1957%;&quot;&gt;&lt;b&gt;&lt;span&gt;설명&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; width: 37.9843%;&quot;&gt;&lt;span&gt;some(callback)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; width: 62.1957%;&quot;&gt;&lt;span&gt;배열 요소 중&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;하나라도&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;조건을 만족하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;반환&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; width: 37.9843%;&quot;&gt;&lt;span&gt;every(callback)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; width: 62.1957%;&quot;&gt;&lt;span&gt;배열 요소가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;모두&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;조건을 만족해야&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;반환&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;&lt;/div&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;some()&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용법&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;some()&lt;/span&gt;&lt;span&gt;은 배열 요소 중&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;하나라도&lt;/b&gt;&lt;/span&gt;&lt;span&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;/b&gt;&lt;/span&gt;&lt;span&gt;즉시&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;true&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;를 반환&lt;/b&gt;하고 순회를 멈춘다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;const numbers = [1, 3, 7, 10, 12];

// 요소 중 10보다 큰 값이 있는지 확인
const hasLargeNumber = numbers.some(num =&amp;gt; num &amp;gt; 10);

console.log(hasLargeNumber); // true (12가 조건을 만족)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;some()&lt;/span&gt;&lt;span&gt;의 특징&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;를 만나면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;즉시 실행 종료&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;빈 배열&lt;/b&gt;&lt;/span&gt;&lt;span&gt;에서는 무조건&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;반환&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;jboss-cli&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;[].some(el =&amp;gt; el &amp;gt; 10); // 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;&lt;/div&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;every()&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용법&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;every()&lt;/span&gt;&lt;span&gt;는 배열 요소가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;모두&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;조건을 만족해야&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;b&gt;를 반환&lt;/b&gt;한다.&lt;/span&gt;&lt;br /&gt;&lt;span&gt;하나라도 조건을 만족하지 않으면 즉시&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;를 반환하고 순회를 멈춘다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;const numbers = [2, 4, 6, 8, 10];

// 모든 요소가 짝수인지 확인
const allEven = numbers.every(num =&amp;gt; num % 2 === 0);

console.log(allEven); // true (모든 숫자가 짝수)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;every()&lt;/span&gt;&lt;span&gt;의 특징&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;false&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;를 만나면 즉시 실행 종료&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;빈 배열에서는 무조건&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;true&lt;/b&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;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;jboss-cli&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;[].every(el =&amp;gt; el &amp;gt; 10); // 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;&lt;/div&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;결론&lt;/span&gt;&lt;/h2&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 100%; height: 57px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;&lt;span&gt;메서드&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;&lt;span&gt;역할&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;&lt;span&gt;실행 종료 조건&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;some()&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;하나라도 조건을 만족하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;반환&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;조건을 만족하는 요소를 찾으면 종료&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;every()&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;모든 요소가 조건을 만족해야&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;반환&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;하나라도 조건을 만족하지 않으면 종료&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/JavaScript</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/48</guid>
      <comments>https://yerang2.tistory.com/48#entry48comment</comments>
      <pubDate>Fri, 21 Mar 2025 11:03:50 +0900</pubDate>
    </item>
    <item>
      <title>[백준허브 커스텀] 커밋되는 디렉토리/파일명 변경하기</title>
      <link>https://yerang2.tistory.com/47</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;백준허브를 내가 기존에 사용하던 Repository에 연결하려니까 &lt;b&gt;폴더 명이 달라 백준 폴더가 2개&lt;/b&gt;가 되는 것이 아닌가!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;눈뜨고 볼 수 없어서 릴리즈 파일을 가지고 크롬 확장프로그램인 백준허브를&lt;b&gt; 커스터마이징&lt;/b&gt;했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 백준허브 깃허브에서 릴리즈 파일을 다운받는다.&lt;/b&gt;&lt;/h2&gt;
&lt;figure id=&quot;og_1742468506246&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - BaekjoonHub/BaekjoonHub: 백준 자동 푸시 익스텐션(Auto Git Push for BOJ)&quot; data-og-description=&quot;백준 자동 푸시 익스텐션(Auto Git Push for BOJ). Contribute to BaekjoonHub/BaekjoonHub development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/BaekjoonHub/BaekjoonHub&quot; data-og-url=&quot;https://github.com/BaekjoonHub/BaekjoonHub&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eDGeLN/hyYvmicqid/vFYBmwsKfRpRupYas6hu60/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bvrnVj/hyYukkQuRl/VsLsdtMsT66Cj0QV0sABfK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/BaekjoonHub/BaekjoonHub&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/BaekjoonHub/BaekjoonHub&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eDGeLN/hyYvmicqid/vFYBmwsKfRpRupYas6hu60/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bvrnVj/hyYukkQuRl/VsLsdtMsT66Cj0QV0sABfK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - BaekjoonHub/BaekjoonHub: 백준 자동 푸시 익스텐션(Auto Git Push for BOJ)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;백준 자동 푸시 익스텐션(Auto Git Push for BOJ). Contribute to BaekjoonHub/BaekjoonHub development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2470&quot; data-origin-height=&quot;1062&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyQ4P3/btsMRRajhZD/iAnuf2BKIrkyikNkB1tAX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyQ4P3/btsMRRajhZD/iAnuf2BKIrkyikNkB1tAX1/img.png&quot; data-alt=&quot;깃허브 레포 정보 우측에서 최신 릴리즈 버전을 클릭하면 이런 화면이 나온다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyQ4P3/btsMRRajhZD/iAnuf2BKIrkyikNkB1tAX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyQ4P3%2FbtsMRRajhZD%2FiAnuf2BKIrkyikNkB1tAX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;804&quot; height=&quot;346&quot; data-origin-width=&quot;2470&quot; data-origin-height=&quot;1062&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;깃허브 레포 정보 우측에서 최신 릴리즈 버전을 클릭하면 이런 화면이 나온다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Source code를 다운받고, 압축 해제한다.&lt;/b&gt;&lt;/h2&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. IDE로 열어서 원하는 부분을 수정한다.&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예시 1. 프로그래머스에 커밋되는 파일 명 수정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;script/programmers/parse.js&lt;/code&gt;의 &lt;code&gt;fileName&lt;/code&gt;을 수정하면 된다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;  const fileName = `${problemId}_${convertSingleCharToDoubleChar(title)}.${language_extension}`;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2338&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLHBxj/btsMSlIIRrZ/yFxsWGDDK7K0PB68t4Lrvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLHBxj/btsMSlIIRrZ/yFxsWGDDK7K0PB68t4Lrvk/img.png&quot; data-alt=&quot;그럼 이렇게 커밋된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLHBxj/btsMSlIIRrZ/yFxsWGDDK7K0PB68t4Lrvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLHBxj%2FbtsMSlIIRrZ%2FyFxsWGDDK7K0PB68t4Lrvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;689&quot; height=&quot;146&quot; data-origin-width=&quot;2338&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그럼 이렇게 커밋된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예시 2. 백준에 커밋되는 디렉토리 위치 수정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;script/baekjoon/parse.js&lt;/code&gt;를 수정하자. 패턴을 보면 알겠지만 script하위에 플랫폼별 디렉토리가 있고, 그 안에서 설정을 건드릴 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const directory = await getDirNameByOrgOption(
  `BOJ`,
  langVersionRemove(language, null)
);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1836&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJ6Jwo/btsMRiTMzeo/oD9YdYZzNw7WOMAuHBESbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJ6Jwo/btsMRiTMzeo/oD9YdYZzNw7WOMAuHBESbk/img.png&quot; data-alt=&quot;BOJ 바로 밑에 생성된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJ6Jwo/btsMRiTMzeo/oD9YdYZzNw7WOMAuHBESbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJ6Jwo%2FbtsMRiTMzeo%2FoD9YdYZzNw7WOMAuHBESbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;188&quot; data-origin-width=&quot;1836&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BOJ 바로 밑에 생성된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예시3. 리드미 파일 커밋 안되게 하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;script/{플랫폼}/uploadfunctions.js&lt;/code&gt;의 아래 부분을 다음과 같이 주석처리 하면 된다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// const readme = await git.createBlob(readmeText, `${directory}/README.md`); // readme 파일
const treeSHA = await git.createTree(refSHA, [source/*, readme*/]);&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;code&gt;manifest.json&lt;/code&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 우측 상단 퍼즐 버튼을 눌러 확장 프로그램 관리에 들어간다.&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;684&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2Wj9c/btsMQbVAGaS/h51spnR7kjjUGFqfxsktLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2Wj9c/btsMQbVAGaS/h51spnR7kjjUGFqfxsktLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2Wj9c/btsMQbVAGaS/h51spnR7kjjUGFqfxsktLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2Wj9c%2FbtsMQbVAGaS%2Fh51spnR7kjjUGFqfxsktLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;383&quot; height=&quot;83&quot; data-origin-width=&quot;684&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 우측 상단에 개발자 모드를 enable 한다.&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmY67I/btsMRHTbZz6/oQvf78kEwRpkdcfyKVrVUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmY67I/btsMRHTbZz6/oQvf78kEwRpkdcfyKVrVUK/img.png&quot; data-alt=&quot;그럼 파란 버튼들이 뜬다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmY67I/btsMRHTbZz6/oQvf78kEwRpkdcfyKVrVUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmY67I%2FbtsMRHTbZz6%2FoQvf78kEwRpkdcfyKVrVUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;814&quot; height=&quot;89&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그럼 파란 버튼들이 뜬다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. '압축해제된 확장 프로그램을 로드합니다' 버튼을 눌러 수정한 디렉토리를 선택한다.&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NQ5PZ/btsMRh1BHbf/k6dxYDCBklslCypJUWtIy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NQ5PZ/btsMRh1BHbf/k6dxYDCBklslCypJUWtIy1/img.png&quot; data-alt=&quot;그럼 이렇게 등록이 완료된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NQ5PZ/btsMRh1BHbf/k6dxYDCBklslCypJUWtIy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNQ5PZ%2FbtsMRh1BHbf%2Fk6dxYDCBklslCypJUWtIy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;396&quot; height=&quot;213&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그럼 이렇게 등록이 완료된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 사용법은 기존 백준 허브 설정법과 동일하다. 깃허브의 레포지토리에 연동하고, 플랫폼에서 정답을 맞추면 자동으로 push된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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 style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;저작권은 &lt;a href=&quot;https://github.com/BaekjoonHub&quot;&gt;https://github.com/BaekjoonHub&lt;/a&gt;에 있습니다.&lt;/p&gt;</description>
      <category>FE</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/47</guid>
      <comments>https://yerang2.tistory.com/47#entry47comment</comments>
      <pubDate>Thu, 20 Mar 2025 20:03:02 +0900</pubDate>
    </item>
    <item>
      <title>Next.js 버전 12 &amp;lt;&amp;rarr; 13 차이</title>
      <link>https://yerang2.tistory.com/46</link>
      <description>&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&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;책을 따라하다보니 막히는 부분이 있었다. 바로 Next.js 버전 차이로 인해서 app디렉토리 구조로 변경되었기 때문에 &lt;code&gt;app&lt;/code&gt; 폴더 사용 시 &lt;code&gt;getStaticProps&lt;/code&gt;, &lt;code&gt;getInitialProps&lt;/code&gt;, &lt;code&gt;getServerSideProps&lt;/code&gt; 등의 이전 방법은 app 디렉토리 구조에선 지원이 안되는 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서칭해보니, &lt;code&gt;async/await&lt;/code&gt;을 사용한 &lt;code&gt;fetch()&lt;/code&gt; API를 사용하는 방식으로 변경해야 했다. 이 참에 Next.js 13버전의 차이를 조금 기록해보고자 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;주요 변화&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 13버전에서는 src 디렉토리에서 관리하는 것이 아니라, 애플리케이션 디렉토리 구조로 변경되었다. 그래서 app디렉토리 하단에 페이지들이 위치하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js13버전의 &lt;b&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;b&gt;app 디렉토리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 기반 라우팅 지원&lt;/li&gt;
&lt;li&gt;컴포넌트와 페이지의 구조화된 관리, 서버 구성 요소, 레이아웃 관리 등을 용이하게 함.&lt;/li&gt;
&lt;li&gt;각 디렉토리에는 페이지를 정의하는 page.js파일이 있어야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버 컴포넌트&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 측에서 렌더링되는 컴포넌트로, 클라이언트 측 JavaScript 로드를 최소화하고, 성능을 최적화함&lt;/li&gt;
&lt;li&gt;서버 컴포넌트를 사용하여 데이터를 서버에서 페칭하고 렌더링함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;새로운 데이터 fetch 방식&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;getStaticProps&lt;/code&gt;, &lt;code&gt;getServerSideProps&lt;/code&gt; 대신 &lt;code&gt;fetch&lt;/code&gt;를 사용하여 서버 컴포넌트 내에서 데이터를 페칭한다.&lt;/li&gt;
&lt;li&gt;데이터 페칭 로직을 페이지와 더 가깝게 배치하여 코드의 가독성과 유지보수성을 높임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책 공부하면서 추가로 알게된 부분&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;책에서는 사용했던 NextPage 타입을 사용할 필요가 없다. 타입스크립트에서 Next.js의 페이지 컴포넌트를 안전하게 정의하기 위해서 사용했지만, 이제는 app 디렉토리 하나에서 다 해결이 되니까 필요가 없어졌다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;새로운 Fetching 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/data-fetching&quot;&gt;https://nextjs.org/docs/app/building-your-application/data-fetching&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기존 SSG 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/api-reference/data-fetching/get-static-props&quot;&gt;getStaticProps&lt;/a&gt;는 아래와 같은 경우에 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 데이터(file system, API, DB..)를 미리 가져와야 하는 경우 사용&lt;/li&gt;
&lt;li&gt;페이지가 아니거나 &lt;code&gt;_app&lt;/code&gt;, &lt;code&gt;_document&lt;/code&gt;, &lt;code&gt;_error&lt;/code&gt; 파일에서 사용불가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 코드는 아래와 같았다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export const getStaticProps: GetStaticProps&amp;lt;SSG2Props&amp;gt; = async (context) =&amp;gt; {
  const timestamp = new Date().toLocaleString()
  const message = `${timestamp}에 getStaticProps가 실행됐습니다.`
  console.log(message);
  return {
    props: {
      message,
    },
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 수정했다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;async function getMessage(): Promise&amp;lt;SSG2Props&amp;gt; {
  const timestamp = new Date().toLocaleString()
  const message = `${timestamp}에 SSG로 페이지가 실행됐습니다.`
  console.log(message);

  return { message }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>FE/Next.js</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/46</guid>
      <comments>https://yerang2.tistory.com/46#entry46comment</comments>
      <pubDate>Tue, 18 Mar 2025 21:07:24 +0900</pubDate>
    </item>
    <item>
      <title>[JS] 구간 합 시간 초과 해결하기</title>
      <link>https://yerang2.tistory.com/44</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;BOJ 11659 : 배열이 주어지고, 여러 번의 구간합을 구하는 문제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;구간합을 매번 직접 계산하면 &lt;code&gt;O(n*m)&lt;/code&gt;시간 복잡도가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누적합을 이용하면 O(1)로 구간합을 구할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열의 각 위치까지 누적합을 미리 계산해두고, 구간 합을 빠르게 구하는 기법&lt;/li&gt;
&lt;li&gt;예를 들어 3부터 5까지의 합이면, 5까지의 누적합 - 2까지의 누적합 하면 되니까 빠르게 계산 가능!&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1740544604852&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().trim().split('\n');
const [n, m] = input[0].split(' ').map(Number);

input.shift();
let list = input[0].split(' ').map(Number);

// ✅ 누적 합 배열 생성 (Prefix Sum)
let prefixSum = Array(n + 1).fill(0);
for (let i = 1; i &amp;lt;= n; i++) {
    prefixSum[i] = prefixSum[i - 1] + list[i - 1];
}

input.shift();

// ✅ 쿼리 처리 (O(1) 연산)
let result = [];
for (let i = 0; i &amp;lt; m; i++) {
    const [a, b] = input[i].split(' ').map(Number);
    result.push(prefixSum[b] - prefixSum[a - 1]);  // O(1)로 구간 합 계산
}

console.log(result.join('\n'));  // 한번에 출력 (출력 시간 최적화)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/44</guid>
      <comments>https://yerang2.tistory.com/44#entry44comment</comments>
      <pubDate>Wed, 26 Feb 2025 13:48:36 +0900</pubDate>
    </item>
    <item>
      <title>[PS] 최대공약수와 최소공배수 구하기</title>
      <link>https://yerang2.tistory.com/43</link>
      <description>&lt;h1&gt;최대 공약수&lt;/h1&gt;
&lt;p 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-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;큰 수를 작은 수로 나눠 나머지를 구한다.&lt;/li&gt;
&lt;li&gt;그 작은 수와 나머지로 다시 나눈다&lt;/li&gt;
&lt;li&gt;나머지가 0이 될 때 작은 수가 최대공약수다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시 &lt;code&gt;gcd(48, 18)&lt;/code&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;48 / &lt;b&gt;18&lt;/b&gt; = 몫이 2, 나머지가 &lt;b&gt;12&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;gcd(18, 12)&lt;/li&gt;
&lt;li&gt;18 / &lt;b&gt;12&lt;/b&gt; = 몫이 1, 나머지가 &lt;b&gt;6&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;gcd(12, 6)&lt;/li&gt;
&lt;li&gt;12 / &lt;b&gt;6&lt;/b&gt; = 몫이 2, 나머지가 &lt;b&gt;0&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;나머지가 0이 나왔으니, 작은 수인 6이 최대공약수!&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;function gcd(a,b) {
  if (b == 0) return a;
  return a &amp;gt; b ? gcd(b, a % b) : gcd(a, b % a);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;최소공배수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소 공배수와 최대 공약수의 관계를 이용한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;151&quot; data-origin-height=&quot;45&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqSqV1/btsMvMhqoJP/KvVYXBlKkvWsnbNw0mbBQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqSqV1/btsMvMhqoJP/KvVYXBlKkvWsnbNw0mbBQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqSqV1/btsMvMhqoJP/KvVYXBlKkvWsnbNw0mbBQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqSqV1%2FbtsMvMhqoJP%2FKvVYXBlKkvWsnbNw0mbBQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;151&quot; height=&quot;45&quot; data-origin-width=&quot;151&quot; data-origin-height=&quot;45&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 숫자를 곱한 값을 최대공약수로 나누면 최소공배수가 나온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;function lcm(a, b) {
  return (a * b) / gcd(a, b);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>유클리드 호제법</category>
      <category>최대공약수</category>
      <category>최소공배수</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/43</guid>
      <comments>https://yerang2.tistory.com/43#entry43comment</comments>
      <pubDate>Wed, 26 Feb 2025 13:35:23 +0900</pubDate>
    </item>
    <item>
      <title>[GitHub Action] 워크플로우 파일 작성법 (코드 분해해서 보기)</title>
      <link>https://yerang2.tistory.com/42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;1. 태그&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;워크플로우는 여러 개의 작업(Job)으로 구성될 수 있으며, 각 작업은 여러 단계(Steps)로 이루어진다. 이 때 해당 부분이 어느 항목에 해당하는지 보여주기 위해서 &lt;b&gt;태그&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;태그 설명&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;name&lt;/td&gt;
&lt;td&gt;워크플로우의 이름. 명시하지 않으면 파일의 이름을 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;on&lt;/td&gt;
&lt;td&gt;워크플로우를 트리거하는 이벤트를 정의하는 부분.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jobs&lt;/td&gt;
&lt;td&gt;워크플로우에서 실행될 작업을 정의. 각 작업 안에는 여러 단계(steps)가 포함됨. 병렬로 실행하거나, 순차적으로 실행할 수 있음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build&lt;/td&gt;
&lt;td&gt;개별 작업(job)의 이름을 정의함. jobs 섹션 안에서만 유효함.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;runs-on&lt;/td&gt;
&lt;td&gt;작업(job)을 실행할 가상 환경을 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;steps&lt;/td&gt;
&lt;td&gt;작업(job) 안에서 실행되는 개별 단계를 정의. 명령어 실행 혹은 특정 액션 호출. 단계는 &lt;b&gt;순차적&lt;/b&gt;으로 실행됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;uses, run&lt;/td&gt;
&lt;td&gt;외부 액션을 사용하여 특정 작업을 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;2. 작성한 내용 부분별로 뜯어보기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. name과 on&lt;/h2&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;name: CI/CD Pipeline with Docker
on:
  push:
    branches:
      - main
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;워크플로우의 이름을 정의&lt;/li&gt;
&lt;li&gt;main 브랜치에 푸시될 때만 워크플로우가 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. build&lt;/h2&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;jobs:
  build:
    runs-on: ubuntu-latest
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드 작업을 수행할 때 가상 환경은 ubuntu-latest&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. steps&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-1. checkout&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;    steps:
    - name: Checkout code
      uses: actions/checkout@v4
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Github 저장소의 코드를 가져옴.&lt;/li&gt;
&lt;li&gt;빌드와 테스트를 위해서 가장 먼저 필요한 단계.&lt;/li&gt;
&lt;li&gt;코드를 체크아웃하여 작업이 최신 상태에서 수행되게 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-2. setup-node&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;    - name: Set up Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '20'
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node.js 환경을 설정함.&lt;/li&gt;
&lt;li&gt;버전을 설정하여 빌드 및 의존성 설치가 가능하도록 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-3. npm install/build&lt;/h3&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;    - name: Install dependencies
      run: npm install

    - name: Build project
      run: npm run build
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트에 필요한 Node.js 의존성 설치.&lt;/li&gt;
&lt;li&gt;프로젝트를 빌드하여 최종 실행 파일을 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-4. Docker Buildx&lt;/h3&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;    # Docker Buildx 설정: 멀티 플랫폼 빌드를 위해서 사용.
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;나의 배포 환경이 라즈베리파이 데비안 aarch64 기반이어서, 멀티 플랫폼 빌드가 요구되었다.&lt;/li&gt;
&lt;li&gt;멀티 플랫폼 도커 이미지 빌드를 위해서 buildx라는 도구를 사용.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-5. DockerHub 로그인&lt;/h3&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;    - name: Log in to DockerHub
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도커 허브에 로그인하 빌드한 이미지를 푸시하기 위한 준비를 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-6. Build and Push Docker Image&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;    - name: Build and Push Docker image
      uses: docker/build-push-action@v3
      with:
        context: .
        push: true
        tags: ${{ secrets.DOCKERHUB_USERNAME }}/folio-backend:latest
        platforms: linux/amd64,linux/arm64
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도커 이미지를 빌드 및 푸시함.&lt;/li&gt;
&lt;li&gt;단, 플랫폼에 linux/amd64와 linux/arm64을 명기하여 멀티 플랫폼용 이미지를 빌드하도록 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. deploy&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;- name: SSH to Deploy Server
  uses: appleboy/ssh-action@v0.1.7
  with:
    host: ${{ secrets.SSH_HOST }}
    username: ${{ secrets.SSH_USERNAME }}
    key: ${{ secrets.SSH_PRIVATE_KEY }}
    port: ${{ secrets.SSH_PORT }}
    script: |
      echo &quot;SSH 연결 테스트 완료.&quot;
      docker --version
      echo &quot;${{ secrets.DOCKERHUB_ACCESS_TOKEN }}&quot; | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
      docker pull ${{ secrets.DOCKERHUB_USERNAME }}/folio-backend:latest
      docker stop folio-backend || true
      docker rm folio-backend || true
      docker run -d --name folio-backend -p 8080:3000 ${{ secrets.DOCKERHUB_USERNAME }}/folio-backend:latest
      pm2 restart all || pm2 start folio-backend:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배포 서버에 SSH를 통해 접속&lt;/li&gt;
&lt;li&gt;docker pull 명령어를 실행하여 DockerHub에서 최신 이미지를 가져옴&lt;/li&gt;
&lt;li&gt;기존 컨테이터 중지 및 제거 후 새로운 컨테이너를 실행함&lt;/li&gt;
&lt;li&gt;pm2를 사용하여 프로세스 관리를 함.&lt;/li&gt;
&lt;li&gt;8080:3000은 외부 접속은 8080으로 하되, 컨테이너 내부에서는 3000번 포트를 이용하겠다는 말.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Git</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/42</guid>
      <comments>https://yerang2.tistory.com/42#entry42comment</comments>
      <pubDate>Tue, 27 Aug 2024 11:23:11 +0900</pubDate>
    </item>
    <item>
      <title>[C++] Map sort</title>
      <link>https://yerang2.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Map을 이용하기 위한 기본적인 문법은 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// 선언
map&amp;lt;int, int&amp;gt; m;

// key 1에 value는 10 대입
m[1] = 10;

// key값이 1인 요소를 찾음
if (m.find(1) != m.end()) {
    cout &amp;lt;&amp;lt; &quot;맵에 1이라는 키가 있다.\n&quot;;
}
else {
    cout &amp;lt;&amp;lt; &quot;맵에 1이라는 키가 없다.\n&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;key값을 기준으로 오름차순 정렬&lt;/b&gt;되어 있다.&lt;br /&gt;내가 풀고자 했던 문제에서는 value값을 기준으로 오름차순 정렬을 해야 했다. 이 경우에는 vector에 담아서 정렬해 쓸 수 있다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;vector&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; vec(m.begin(), m.end());
sort(vec.begin(), vec.end(), cmp);&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;value 기준으로 오름차순으로 정렬되도록 cmp함수는 다음과 같이 작성했다.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;bool cmp(const pair&amp;lt;int,int&amp;gt;&amp;amp; a, const pair&amp;lt;int,int&amp;gt;&amp;amp; b) {
    if (a.second == b.second) return a.first &amp;lt; b.first;
    return a.second &amp;lt; b.second;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 구문으로 찍어서 테스트해보자.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;for (auto v : vec) {
   cout &amp;lt;&amp;lt; &quot;key: &quot;&amp;lt;&amp;lt; v.first &amp;lt;&amp;lt; &quot;\n&quot;;
   cout &amp;lt;&amp;lt; &quot;value: &quot; &amp;lt;&amp;lt; v.second &amp;lt;&amp;lt; &quot;\n&quot;;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Language/C++</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/41</guid>
      <comments>https://yerang2.tistory.com/41#entry41comment</comments>
      <pubDate>Thu, 8 Aug 2024 15:34:49 +0900</pubDate>
    </item>
    <item>
      <title>Next.js 13버전 이상에서는 getStaticProps, getServerSideProps 대신에 fetch() 써야...</title>
      <link>https://yerang2.tistory.com/40</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;fetch()&lt;/code&gt;를 사용하도록 변경됐다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app폴더 사용 시 기존에 사용하던 렌더링 시 데이터 받아오는 함수들의 사용이 불가하다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;export default funciton Home(){
  const a = use(fetchData())
  return &amp;lt;&amp;gt;{/* ... */} &amp;lt;/&amp;gt;
}
export async function fetchData() {
  const res = await fetch(`https://.../data`)
  const data = await res.json()
  return data
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;fetch('https://url', option)&lt;/code&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;option 예시&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;code&gt;{ cache: 'force-cache' }&lt;/code&gt;: 기본값. 생략가능(&lt;code&gt;getStaticProps&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{ cache: 'no-store' }&lt;/code&gt;: 모든 요청에 최신 데이터 받아오기 (&lt;code&gt;getServerSideProps&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{ next: { revalidate: n } }&lt;/code&gt;: n초 후 요청오면 페이지 새로 생성 (revalidate옵션이 있는 &lt;code&gt;getStaticProps&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export default async function Page() {
  // SSG: Static Site Generation
  const staticData = await fetch(`https://...`, { cache: 'force-cache' });

  // SSR: Server Side Rendering
  const dynamicData = await fetch(`https://...`, { cache: 'no-store' });

  // ISR: Incremental Static Regeneration
  const revalidatedData = await fetch(`https://...`, {
    next: { revalidate: 10 },
  });

  return &amp;lt;div&amp;gt;...&amp;lt;/div&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>FE/Next.js</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/40</guid>
      <comments>https://yerang2.tistory.com/40#entry40comment</comments>
      <pubDate>Tue, 6 Aug 2024 22:02:51 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js] 렌더링 방식 (12버전 이하 기준)</title>
      <link>https://yerang2.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 기술할 개념은 버전에 상관없이 적용되지만, 예제의 경우에는 버전에 따른 차이가 있을 수 있다. 13버전 미만의 경우를 기준으로 작성되었고, 13버전 이상부터 사용되는 &lt;code&gt;fetch&lt;/code&gt;를 활용한 렌더링 예제는 추후 별도의 글로 게시할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js에서는 페이지별로 렌더링 방법을 전환할 수 있다. 4가지 방법을 사용할 수 있다.&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;정적 사이트 생성(SSG: Static Site Generation)&lt;/b&gt;. 내가 읽는 책에서는 정적 사이트도 SSG에 포함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라이언트 사이드 렌더링(CSR: Client Side Rendering)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버 사이드 렌더링(SSR: Server Side Rendering)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;점진적 정적 재생성(ISR: Incremental Static Regeneration)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 페이지에서 사전 렌더링이 가능한 부분은 사전 렌더링을 수행한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;정적 사이트 생성(SSG)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;빌드 시 API 등으로 부터 데이터를 얻어, 페이지를 그려 정적 파일로 생성&lt;/b&gt;하는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;절차&lt;/b&gt;는 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드 시 &lt;code&gt;getStaticProps&lt;/code&gt;라는 함수가 호출되며, 그 함수 안에서 API 호출 등을 수행하고, 페이지를 그리는데 필요한 &lt;code&gt;props&lt;/code&gt;를 반환한다.&lt;/li&gt;
&lt;li&gt;이 &lt;code&gt;props&lt;/code&gt;를 페이지 컴포넌트에 전달해서 화면을 그린다.&lt;/li&gt;
&lt;li&gt;화면을 그린 결과는 정적 파일의 형태로 빌드 결과로 저장한다.&lt;/li&gt;
&lt;li&gt;페이지 접근이 발생하면, 미리 생성한 정적 파일을 클라이언트에 보내고, 브라우저는 그것을 기반으로 화면을 그린다.&lt;/li&gt;
&lt;li&gt;브라우저에서 초기 화면을 그린 후에는 일반적인 리액트 애플리케이션과 마찬가지로, API 등으로부터 데이터를 얻어 동적으로 화면을 그린다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p 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-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적파일을 클라에 전달만 하므로, 초기 화면 그리기가 빠름.&lt;i&gt;&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p 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-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드 시에만 데이터를 얻으므로, 초기 화면을 그릴 때 오래된 데이터가 표시될 수 있음.&lt;/li&gt;
&lt;li&gt;그러므로, 실시간성 콘텐츠에는 비적합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 후에 표시 내용이 업데이트되지 않는 페이지나 초기 화면 그리기 이후에 데이터를 표시할 수 있는 페이지에 SSG가 적합하다.&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;예제 1 (&lt;code&gt;getStaticProps&lt;/code&gt;)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;getStaticProps&lt;/code&gt;함수를 정의하고, export하면 해당 함수는 &lt;b&gt;빌드 시 실행&lt;/b&gt;된다. &lt;code&gt;getStaticProps&lt;/code&gt;는 반환값으로 &lt;b&gt;props를 반환&lt;/b&gt;할 수 있고, &lt;b&gt;그 값이 페이지 컴포넌트에 전달&lt;/b&gt;되어 그려진다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;import { GetStaticProps, NextPage, NextPageContext } from &quot;next&quot;;
import Head from &quot;next/head&quot;;

// 페이지 컴포넌트인 props의 타입 정의
type SSGProps = {
  message: string;
};

// SSG는 getStaticProps가 반환한 props를 받을 수 있다
// NextPage&amp;lt;SSGProps&amp;gt;는 Message: string 만을 받아 생성된 페이지 타입
// Next.js의 페이지 컴포넌트나 함수 타입은 https://nextjs.org/learn/excel/typescript/nextjs-types도 참고한다
const SSG: NextPage&amp;lt;SSGProps&amp;gt; = (props) =&amp;gt; {
  const { message } = props;

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Static Site Generation&amp;lt;/title&amp;gt;
        &amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;main&amp;gt;
        &amp;lt;p&amp;gt;이 페이지는 정적 사이트 생성을 통해 빌드 시 생성된 페이지입니다.&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;{message}&amp;lt;/p&amp;gt;
      &amp;lt;/main&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

// getStaticProps는 빌드에 실행된다
// GetStaticProps&amp;lt;SSGProps&amp;gt;는 SSGProps인수로 받는 getSTaticProps 타입
export const getStaticProps: GetStaticProps&amp;lt;SSGProps&amp;gt; = async (context) =&amp;gt; {
  const timestamp = new Date().toLocaleString();
  const message = `${timestamp}에 getStaticProps가 실행되었습니다`;
  console.log(message);
  return {
    // 여기에서 반환한 props를 기반으로 페이지 컴포넌트를 그린다
    props: {
      message,
    },
  };
};

export default SSG;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;npm run build&lt;/code&gt; 를 실행하면 &lt;code&gt;getStaticProps&lt;/code&gt; 안에 있는 &lt;code&gt;console.log&lt;/code&gt;가 빌드 도중 실행된다. &lt;code&gt;npm run start&lt;/code&gt;를 실행해서 페이지를 표시하면 &lt;code&gt;getStaticProps에서&lt;/code&gt; 반환한 props를 사용해 페이지를 표시하고 있는 것을 알 수 있다.&lt;/li&gt;
&lt;li&gt;주의할 점은, &lt;code&gt;npm run dev&lt;/code&gt;를 사용해서 개발 서버 실행할 때는 최신 코드를 사용해 페이지를 표시하기 때문에, 요청이 있을 때마다 &lt;code&gt;getStaticProps가&lt;/code&gt; 실행되고, 서버에서 페이지를 생성한다.(SSG에 따라 예상한 결과가 안나올 수 있다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예제 2 (&lt;code&gt;getStaticPaths&lt;/code&gt;)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시판 글 상세보기 페이지와 같이 표시하는 데이터만 다른 페이지의 경우에는 SSG만으로는 대응이 어렵다. 글 하나 하나마다 페이지를 따로 팔 수는 없으니까. 이 경우에는 &lt;b&gt;동적 라우팅&lt;/b&gt;을 이용한다. 경로 파라미터를 사용해 여러 페이지를 1개의 파일로 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 라우팅으로 구성하려면 아래 요소가 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;[파라미터].tsx&lt;/code&gt;와 같이 &lt;code&gt;[]&lt;/code&gt;로 감싼 특별한 파일명&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getStaticProps&lt;/code&gt;에 맞춰 &lt;code&gt;getStaticPaths&lt;/code&gt;를 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;getStaticPaths&lt;/code&gt;는 &lt;code&gt;getStaticProps&lt;/code&gt;실행 전에 호출된다. 생성할 피이지의 경로 파라미터의 조합(paths)과 fallback을 반환한다. paths는 경로 파라미터의 조합을 나타내며, 배열의 각 요소가 1개의 페이지에 대응한다. fallback은 &lt;code&gt;getStaticPaths&lt;/code&gt;가 생성하는 페이지가 존재하지 않는 경우의 처리를 기술한다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } }
    ],
    fallback: false // true 도는 'blocking' 지정 가능
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pages/post 디렉터리를 새롭게 만들고, 해당 경로 내에 &lt;code&gt;[id].tsx&lt;/code&gt;파일을 만든다. 대괄호로 감싼 부분이 경로 파라미터를 나타낸다. &lt;code&gt;/posts/1&lt;/code&gt;, &lt;code&gt;/posts/2&lt;/code&gt;, &lt;code&gt;/posts/3&lt;/code&gt; 과 같이 접근하면 된다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 타입을 사용하기 위한 임포트
import { GetStaticPaths, GetStaticProps, NextPage } from 'next'
import Head from 'next/head'
import { useRouter } from 'next/router' // next/router에서 useRouter라는 훅을  삽입한다
import { ParsedUrlQuery } from 'querystring'

type PostProps = {
  id: string
}

const Post: NextPage&amp;lt;PostProps&amp;gt; = (props) =&amp;gt; {
  const { id } = props
  const router = useRouter()

  if (router.isFallback) {
    // 폴백 페이지용 표시를 반환한다
    return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Create Next App&amp;lt;/title&amp;gt;
        &amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;main&amp;gt; &amp;lt;p&amp;gt; 이 페이지는 정적 사이트 생성을 통해 빌드 시 생성된 페이지입니다.
        &amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;{`/posts/${id}에 대응하는 페이지입니다`}&amp;lt;/p&amp;gt;
      &amp;lt;/main&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;// 1번 순서.
// getStaticPaths는 생성한 페이지의 경로 파라미터의 조합을 반환한다
// 이 파일은 pages/posts/[id].tsx이므로, 경로 파라미터로서 id의 값을 반환해야 한다
export const getStaticPaths: GetStaticPaths = async () =&amp;gt; {
  // 각 페이지의 경로 파라미터를 모은 것
  const paths = [
    {
      params: {
        id: '1',
      },
    },
    {
      params: {
        id: '2',
      },
    },
    {
      params: {
        id: '3',
      },
    },
  ]

  // fallback을 false로 설정하면, paths에 정의된 페이지 아래는 404를 표시한다
  return { paths, fallback: false }
}

// 파라미터 타입을 정의
interface PostParams extends ParsedUrlQuery {
  id: string
}

// 2번 순서.
// getStaticPaths 실행 후에 각 경로에 대해 getStaticProps가 실행된다
// 반환하는 props를 페이지 컴포넌트에 전달. 여기서는 Post.
export const getStaticProps: GetStaticProps&amp;lt;PostProps, PostParams&amp;gt; = async (context) =&amp;gt; {
  return {
    props: {
      id: context.params!['id'], // ! : Non-Null Assertion Operator. TS가 undefined라고 컴파일 오류 낼까봐
    },
  }
}

export default Post&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;클라이언트 사이드 렌더링(CSR)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드 시에 데이터를 얻지 않고 페이지를 화면에 그려 저장한다. 그리고 브라우저에서 &lt;b&gt;초기 화면 그리기를 한 뒤, 비동기로 데이터를 얻어서 추가 데이터를 표시&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;본래 리액트 애플리케이션 흐름에 가까운 화면 그리기 방법이다. 페이지를 그리기 위해 필요한 데이터는 나중에 얻어서 반영하기 때문에, SEO(Search Engine Optimization)에는 그다지 유효하지 않다.&lt;/li&gt;
&lt;li&gt;CSR은 단독으로 사용되기보다는 SSG, SSR, ISR과 조합해서 사용된다.&lt;/li&gt;
&lt;li&gt;초기 화면을 그리는 것이 중요하지 않고, 실시간성이 중요한 페이지에 적합하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서버 사이드 렌더링(SSR)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR에서는 페이지 접근이 발생할 때마다 서버에서 &lt;code&gt;getServerSideProps&lt;/code&gt;를 호출하고, 그 결과 props를 &lt;b&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;b&gt;항상 최신 데이터를 기반&lt;/b&gt;으로 페이지 초기 화면을 그릴 수 있어, SEO에 대한 유효성을 기대할 수 있다.&lt;/li&gt;
&lt;li&gt;서버에서 처리를 하다보니, 다른 방법에 비해 지연이 높아질 가능성이 있다.&lt;/li&gt;
&lt;li&gt;최신 가격이 표시되는 제품 페이지 등, 항상 최신 데이터를 표시하고자 하는 경우에 적합하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예제 (&lt;code&gt;getServerSideProps&lt;/code&gt;)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;getServerSideProps&lt;/code&gt;는 페이지를 그리기 전에 호출된다. 빌드 후 실행해보면, 접근할 때마다 표시되는 내용이 변하는 것을 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;import { GetServerSideProps, NextPage } from 'next'
import Head from 'next/head'

type SSRProps = {
  message: string
}

const SSR: NextPage&amp;lt;SSRProps&amp;gt; = (props) =&amp;gt; {
  const { message } = props

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Create Next App&amp;lt;/title&amp;gt;
        &amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;main&amp;gt;
        &amp;lt;p&amp;gt;
          이 페이지는 서버 사이드 렌더링을 통해 접근 시에 서버에서 그려진 페이지입니다.
        &amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;{message}&amp;lt;/p&amp;gt;
      &amp;lt;/main&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

// getServerSideProps는 페이지로의 요청이 있을 때마다 실행된다
export const getServerSideProps: GetServerSideProps&amp;lt;SSRProps&amp;gt; = async (
  context
) =&amp;gt; {
  const timestamp = new Date().toLocaleString()
  const message = `${timestamp}에 이 페이지의 getServerSideProps가 실행되었습니다`
  console.log(message)

  return {
    props: {
      message,
    },
  }
}

export default SSR&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;점진적 정적 재생성(ISR)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ISR은 SSG의 응용이라고 할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사전에 페이지를 생성해서 전송하면서, 동시에 접근이 발생함에 따라 페이지를 다시 생성해서 새로운 페이지를 전송한다.&lt;/li&gt;
&lt;li&gt;페이지 접근이 발생하면 &lt;b&gt;사전에 렌더링해서 서버에 저장한 페이지의 데이터를 클라이언트에 전달&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;이 페이지 데이터에 &lt;b&gt;유효기간&lt;/b&gt;을 설정할 수 있고, 유효기간이 지난 상태에서 접근하면 백그라운드에서 다시 &lt;code&gt;getStaticProps&lt;/code&gt;를 실행해서 화면을 그리고, &lt;b&gt;서버에 저장된 페이지 데이터를 업데이트&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;서버 측에서 처리를 하지 않으므로, SSG와 마찬가지로 지연을 짧게 유지할 수 있다.&lt;/li&gt;
&lt;li&gt;어느 정도 최신 데이터를 기반으로 하는 페이지를 초기에 그릴 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예제&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSG에 수명덧붙인 응용이다보니 revalidate를&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;import { GetStaticPaths, NextPage, GetStaticProps } from 'next'
import Head from 'next/head'
import { useRouter } from 'next/router'

type ISRProps = {
  message: string
}

// ISRProps를 받는 NextPage(페이지) 타입
const ISR: NextPage&amp;lt;ISRProps&amp;gt; = (props) =&amp;gt; {
  const { message } = props

  const router = useRouter()

  if (router.isFallback) {
    // 폴백용 페이지를 반환한다
    return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Create Next App&amp;lt;/title&amp;gt;
        &amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;main&amp;gt;
        &amp;lt;p&amp;gt;이 페이지는 ISR을 통해 빌드 시 생성된 페이지입니다.&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;{message}&amp;lt;/p&amp;gt;
      &amp;lt;/main&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export const getStaticProps: GetStaticProps&amp;lt;ISRProps&amp;gt; = async (context) =&amp;gt; {
  const timestamp = new Date().toLocaleString()
  const message = `${timestamp}에 이 페이지의 getStaticProps가 실행되었습니다`

  return {
    props: {
      message,
    },
    // 페이지의 유효 기간을 초단위로 지정한다
    revalidate: 60,
  }
}

export default ISR&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;빌드할 때 렌더링 방식을 확인할 수 있다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;npm run build&lt;/code&gt;로 빌드하면 하단에 렌더링 방식을 볼 수 있는데, Server가 SSR, Static과 SSG가 SSG, ISR은 ISR을 나타낸다. CSR은 모든 페이지타입과 함께 사용할 수 있으며 기본적으로는 이 타입들에 포함돼있으므로 빌드 결과에서는 표시되지 않는다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;width: 69px;&quot; align=&quot;center&quot;&gt;종류&lt;/th&gt;
&lt;th style=&quot;width: 255px;&quot; align=&quot;left&quot;&gt;데이터 취득에 사용하는 주요 함수&lt;/th&gt;
&lt;th style=&quot;width: 180px;&quot;&gt;데이터 취득 시점&lt;/th&gt;
&lt;th style=&quot;width: 346px;&quot;&gt;보충 설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 69px;&quot; align=&quot;center&quot;&gt;SSG&lt;/td&gt;
&lt;td style=&quot;width: 255px;&quot; align=&quot;left&quot;&gt;&lt;code&gt;getStaticProps&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 180px;&quot;&gt;빌드 시&lt;/td&gt;
&lt;td style=&quot;width: 346px;&quot;&gt;데이터 취득을 전혀 수행하지 않는 경우도 SSG에 해당함.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 69px;&quot; align=&quot;center&quot;&gt;SSR&lt;/td&gt;
&lt;td style=&quot;width: 255px;&quot; align=&quot;left&quot;&gt;&lt;code&gt;getServerSideProps&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 180px;&quot;&gt;사용자 요청 시(서버 사이드)&lt;/td&gt;
&lt;td style=&quot;width: 346px;&quot;&gt;&lt;code&gt;getInitialProps&lt;/code&gt;를 사용해도 SSR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 69px;&quot; align=&quot;center&quot;&gt;ISR&lt;/td&gt;
&lt;td style=&quot;width: 255px;&quot; align=&quot;left&quot;&gt;revalidate를 반환하는 &lt;code&gt;getStaticProps&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 180px;&quot;&gt;빌드 시&lt;/td&gt;
&lt;td style=&quot;width: 346px;&quot;&gt;ISR은 배포 후에도 백그라운드 빌드가 실행됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 69px;&quot; align=&quot;center&quot;&gt;CSR&lt;/td&gt;
&lt;td style=&quot;width: 255px;&quot; align=&quot;left&quot;&gt;그 밖의 임의의 함수(&lt;code&gt;useSWR&lt;/code&gt; 등)&lt;/td&gt;
&lt;td style=&quot;width: 180px;&quot;&gt;사용자 요청 시(브라우저)&lt;/td&gt;
&lt;td style=&quot;width: 346px;&quot;&gt;CSR은 SSG/SSR/ISR과 동시에 사용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>FE/Next.js</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/39</guid>
      <comments>https://yerang2.tistory.com/39#entry39comment</comments>
      <pubDate>Tue, 6 Aug 2024 21:58:37 +0900</pubDate>
    </item>
    <item>
      <title>[React] 훅 정리</title>
      <link>https://yerang2.tistory.com/37</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;h2 data-ke-size=&quot;size26&quot;&gt;상태 훅&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;useState&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 새로운 상태를 작성한다. 업데이트 함수를 호출하면 상태가 바뀌고, 훅이 있는 컴포넌트는 다시 그려진다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;const [상태, 업데이트_함수] = useState(초기 상태)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업데이트 함수를 호출할 때 인수로 값 뿐만 아니라 함수를 호출할 수 있다. 함수를 전달하면 함수의 반환값이 다음 상태가 된다. 또한 해당 함수에는 현재 상태가 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 이용하면 현재 상태에서 1을 증가시키는 코드를 짤 수 있다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;업데이트_함수((현재_상태) =&amp;gt; 현재_상태 + 1)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;useReducer&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 상태 전이를 간단하게 기술할 수 있다. useState보다 복잡한 용도에 적합하다.&lt;br /&gt;useState에서는 업데이트 함수에 다음 상태를 직접 전달했지만, useReducer에서는 업데이트 함수(dispatch)에 action이라 불리는 데이터를 전달한다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;reducer(현재_상태, action) {
    return 다음_상태
}
const [현재_상태, dispatch] = useReducer(reducer, reducer_초기_상태)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;간단한 예제&lt;/h4&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;type Action = 'PLUS' | 'MINUS'
const reducer = (currentCount: number, action: Action) =&amp;gt; {
    switch (action) {
        case 'PLUS' : return currentCount + 1
        case 'MINUS': return currentCount - 1
        default     : return currentCount
    }
}

type CounterProps = {
    initialValue: number
}
const Counter = (props:CounterProps) =&amp;gt; {
    const { initialValue } = props
    const [count, dispatch] = useReducer(reducer, initialValue)
    return (
        &amp;lt;div&amp;gt;
            &amp;lt;p&amp;gt;`Count : ${count}`&amp;lt;/p&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; dispatch('PLUS')}&amp;gt;+&amp;lt;/button&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; dispatch('MINUS')}&amp;gt;-&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모이제이션 훅&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;useMemo&lt;/code&gt;&lt;/h3&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;props나 내부 상태가 업데이트 됐을 때&lt;/li&gt;
&lt;li&gt;컴포넌트 안에서 참조하는 Context 값이 업데이트 됐을 때&lt;/li&gt;
&lt;li&gt;부모 컴포넌트가 다시 그려졌을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면 그리기가 자식에게 전파되는 것을 막기 위해 메모이제이션 컴포넌트를 사용한다. 이를 사용하면 props나 context값이 바뀌지 않은 경우에는 부모 컴포넌트에 의한 화면 다시 그리기가 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모이제이션 컴포넌트는 함수 컴포넌트를 memo 함수로 감싸서 작성한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예제&lt;/h4&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;type switcherProps = {
    isOn: boolean
}
const Switcher = memo&amp;lt;switcherProps&amp;gt;((props) =&amp;gt; {
    const { isOn } = props
    console.log(`스위치가 다시 그려졌습니다. 상태: ${isOn}`)
    return &amp;lt;span&amp;gt;{isOn ? 'ON' : 'OFF'}&amp;lt;/span&amp;gt;
})

const Parent = () =&amp;gt; {
    const [status, setStatus] = useState(true);
    console.log(`Parent가 다시 그려졌습니다. 상태: ${status}`)
    return (
        &amp;lt;div&amp;gt;
            &amp;lt;p&amp;gt;{`현재 상태 : ${status}`}&amp;lt;/p&amp;gt;
            &amp;lt;Switcher isOn={status} /&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 위와 같은 코드에서 Switcher에 특정 목적을 위해 onClick과 같은 콜백이 전달되는 경우, 다시 그리기가 발생된다. 이를 방지하려면 하단의 &lt;code&gt;useCallback&lt;/code&gt; 훅을 사용하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;useCallback&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useCallback&lt;/code&gt;은 함수를 메모이제이션 하기 위한 훅이다.&lt;br /&gt;useCallback의 첫 번째 인수는 함수이고, 두 번째 인수는 의존 배열이다. 함수가 화면을 다시 그릴 때, useCallback의 의존 배열의 값을 비교해서 &lt;b&gt;같으면&lt;/b&gt; 메모이제이션된 함수를 반환하고, &lt;b&gt;다르면&lt;/b&gt; 현재의 첫 번째 인수의 함수를 메모에 저장한다. 그러므로 의존 배열 안의 값에 다른 것이 있을 때는 새로운 함수를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 의존 배열이 비어 있으므로, 첫 번째 그려질 때 생성된 함수를 항상 반환한다. 그 결과 Switcher 컴포넌트에 전달된 함수도 언제나 같으므로, 부모가 다시 그려지더라도 Switcher 컴포넌트는 다시 그려지지 않는다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;type switcherProps = {
    isOn: boolean
    onClick: () =&amp;gt; void
}
const Switcher = memo&amp;lt;switcherProps&amp;gt;((props) =&amp;gt; {
    const { isOn } = props
    console.log(`스위치가 다시 그려졌습니다. 상태: ${isOn}`)
    return &amp;lt;span onClick={onClick}&amp;gt;{isOn ? 'ON' : 'OFF'}&amp;lt;/span&amp;gt;
})

const Parent = () =&amp;gt; {
    const [status, setStatus] = useState(true);
    console.log(`Parent가 다시 그려졌습니다. 상태: ${status}`)
    const  onSwitchClick = useCallback(() =&amp;gt; {
        console.log(`Switch가 클릭되었습니다.`)
    }, [])
    return (
        &amp;lt;div&amp;gt;
            &amp;lt;p&amp;gt;{`현재 상태 : ${status}`}&amp;lt;/p&amp;gt;
            &amp;lt;Switcher isOn={status} onClick={onSwitchClick} /&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;useMemo&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useMemo&lt;/code&gt;는 값을 메모이제이션한다. 값이 필요할 때만 계산하여 성능을 최적화하는 데 도움을 준다. 예를 들어, 무거운 계산이 필요한 작업을 수행할 때 사용하면 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 인수는 값을 생성하는 함수, 두 번째 인수에는 의존 배열을 전달한다. useCallback과 마찬가지로 컴포넌트를 그릴 때 의존 배열을 비교한다. 의존 배열의 값이 이전에 그릴 때와 다른 경우에는 함수를 실행하고, 그 결과를 새로운 값으로 메모에 저장한다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;// num이 변경될 때만 factorial을 계산함
const factorial = useMemo(() =&amp;gt;  factorialCalc(num), [num]);&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;부가 작용 훅(그리기와는 직접적인 관계가 없는 처리)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;useEffect&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useEffect()&lt;/code&gt;를 사용하면 props나 state가 업데이트되고, 다시 그리기가 완료된 후 처리가 실행된다. 의존 배열을 지정해서, 특정 데이터가 변화할 때만 처리하도록 설정할 수 있다. 주로 데이터 가져오기, DOM 조작의 용도로 쓰인다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;useLayoutEffect&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useLayoutEffect&lt;/code&gt;는 DOM이 업데이트 된 후, 화면에 실제로 그려지기 전에 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useEffect&lt;/code&gt;로 값을 읽어 화면에 표시해주는 경우, 리로드 할 때마다 잠깐동안 US표기로 표시되어 살짝 이상하게 보일 수 있는데, &lt;code&gt;useLayoutEffect&lt;/code&gt;를 사용하면 초기화면 반영 이전에 데이터를 읽어 뿌려주므로 해결이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, &lt;code&gt;useLayoutEffect&lt;/code&gt;로 실행하는 처리는 동기적으로 실행되어, 무거운 처리 실행 시 화면 그리기가 지연되므로 주의해야한다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Context를 위한 훅&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;useContext&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useContext 훅은 리액트에서 컨텍스트(Context)를 쉽게 사용하기 위해 제공되는 훅이다. 컨텍스트는 리액트에서 컴포넌트 트리 전체에 걸쳐 전역적인 데이터를 공유할 수 있는 기능을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트리 구조 중 하위 컴포넌트들이 중첩된 props를 사용하지 않고도 데이터를 공유할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 전역 상태 관리가 필요한 경우(예: 사용자 인증 상태, 테마, 언어 설정 등)나, 여러 컴포넌트에서 공통적으로 사용되는 데이터를 관리할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 너무 많은 데이터를 컨텍스트에 넣지 않도록 주의해야 한다. 이는 성능 문제를 일으킬 수 있다. 컨텍스트 값이 변경되면 해당 컨텍스트를 사용하는 모든 컴포넌트가 다시 렌더링된다. 단순한 전역 상태 관리에는 적합하지만, 복잡한 상태 관리에는 리덕스(Redux)와 같은 상태 관리 라이브러리를 고려할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useContext, useState } from 'react';

// 컨텍스트 생성
const ThemeContext = React.createContext();

// ThemeContext.Provider를 사용하여 하위 컴포넌트들에게 theme와 toggleTheme 함수를 전달
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () =&amp;gt; {
    setTheme((prevTheme) =&amp;gt; (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    &amp;lt;ThemeContext.Provider value={{ theme, toggleTheme }}&amp;gt;
      {children}
    &amp;lt;/ThemeContext.Provider&amp;gt;
  );
}

function ThemedButton() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    &amp;lt;button
      onClick={toggleTheme}
      style={{
        backgroundColor: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#000' : '#fff',
      }}
    &amp;gt;
      현재 테마: {theme}
    &amp;lt;/button&amp;gt;
  );
}

function App() {
  return (
    &amp;lt;ThemeProvider&amp;gt;
      &amp;lt;ThemedButton /&amp;gt;
    &amp;lt;/ThemeProvider&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ref 훅&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;useRef&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useRef&lt;/code&gt;는 DOM 요소에 직접 접근하거나, 컴포넌트의 인스턴스 변수를 관리할 때 사용한다. 렌더링 사이에 값이 유지되지만, 값이 변경되어도 컴포넌트가 다시 렌더링되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 특정 DOM 요소에 포커스를 맞추거나, 스크롤 위치를 설정할 때나 이전 값 저장 및 참조를 위해 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 리렌더링이 필요 없는 변수의 경우에만 사용해야 하고, &lt;code&gt;useRef&lt;/code&gt;로 관리하는 값이 변경되어도 리렌더링이 일어나지 않음을 유념해야 한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useRef } from 'react';

function TextInputWithFocusButton() {
  const inputRef = useRef(null);

  const handleClick = () =&amp;gt; {
    inputRef.current.focus();
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input ref={inputRef} type=&quot;text&quot; /&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;Focus the input&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;useImperativeHandle&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useImperativeHandle&lt;/code&gt;는 부모 컴포넌트가 자식 컴포넌트의 인스턴스 메서드나 속성에 접근할 수 있도록 한다. &lt;code&gt;forwardRef&lt;/code&gt;와 함께 사용하여 부모 컴포넌트가 자식 컴포넌트의 특정 메서드를 호출할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 부모 컴포넌트가 자식 컴포넌트의 내부 기능에 접근할 필요가 있을 때 사용한다. 자주 사용되지는 않으며, 주로 복잡한 경우에만 사용한다. 그리고 컴포넌트의 캡슐화를 깨뜨릴 수 있으므로 신중하게 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제&lt;/p&gt;
&lt;pre class=&quot;moonscript&quot;&gt;&lt;code&gt;import React, { useImperativeHandle, forwardRef, useRef } from 'react';

const CustomInput = forwardRef((props, ref) =&amp;gt; {
  const inputRef = useRef();

  useImperativeHandle(ref, () =&amp;gt; ({
    focus: () =&amp;gt; {
      inputRef.current.focus();
    },
    clear: () =&amp;gt; {
      inputRef.current.value = '';
    }
  }));

  return &amp;lt;input ref={inputRef} type=&quot;text&quot; /&amp;gt;;
});

function ParentComponent() {
  const customInputRef = useRef();

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;CustomInput ref={customInputRef} /&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; customInputRef.current.focus()}&amp;gt;Focus&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; customInputRef.current.clear()}&amp;gt;Clear&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그 외 훅&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커스텀훅&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 개발자가 자신의 로직을 재사용할 수 있도록 만든 훅이다. 리액트의 내장 훅을 조합하여 새로운 훅을 만들어 여러 컴포넌트에서 동일한 로직을 재사용할 수 있게 해준다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;useDebugValue&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커스텀 훅의 디버깅 정보를 표시할 때 사용된다. React 개발자 도구에서 커스텀 훅의 상태를 더 잘 이해할 수 있도록 한다. 개발자 도구에서만 표시되므로, 실제 애플리케이션의 동작에는 영향을 주지 않는다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useDebugValue, useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useDebugValue(isOnline ? 'Online' : 'Offline');

  useEffect(() =&amp;gt; {
    // 가상의 구독
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    // 구독 설정
    // ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);

    // 구독 해제 함수 반환
    return () =&amp;gt; {
      // ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  }, [friendID]);

  return isOnline;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>FE/React</category>
      <category>react</category>
      <category>훅</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/37</guid>
      <comments>https://yerang2.tistory.com/37#entry37comment</comments>
      <pubDate>Thu, 1 Aug 2024 22:11:48 +0900</pubDate>
    </item>
    <item>
      <title>[C++] string 순회 관련 반복문</title>
      <link>https://yerang2.tistory.com/36</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;string 조작할 때 일반적인 for문으로 전체 순회를 했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 알게된 범위 기반 반복문에서 js에서 for in 과 같은 반복문을 사용하는데, 정리하고 되새기려고 기록한다.&lt;/p&gt;
&lt;pre id=&quot;code_1721960236205&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

int main() {
    string s = &quot;example&quot;;
    
    // 1. 일반 for 루프
    for (int i = 0; i &amp;lt; s.length(); i++) {
        cout &amp;lt;&amp;lt; s[i] &amp;lt;&amp;lt; &quot; &quot;;
    }
    cout &amp;lt;&amp;lt; endl;
    
    // 2. 범위 기반 for 루프
    for (char c : s) {
        cout &amp;lt;&amp;lt; c &amp;lt;&amp;lt; &quot; &quot;;
    }
    cout &amp;lt;&amp;lt; endl;
    
    // 3. Iterator 사용
    for (string::iterator it = s.begin(); it != s.end(); ++it) {
        cout &amp;lt;&amp;lt; *it &amp;lt;&amp;lt; &quot; &quot;;
    }
    cout &amp;lt;&amp;lt; endl;
    
    // 4. 참조를 사용한 범위 기반 for 루프 (각 문자를 수정해야 할 때 용이)
    for (char&amp;amp; c : s) {
        c = toupper(c);
    }
    cout &amp;lt;&amp;lt; s &amp;lt;&amp;lt; endl; // EXAMPLE

    return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm</category>
      <author>머랑</author>
      <guid isPermaLink="true">https://yerang2.tistory.com/36</guid>
      <comments>https://yerang2.tistory.com/36#entry36comment</comments>
      <pubDate>Fri, 26 Jul 2024 11:18:45 +0900</pubDate>
    </item>
  </channel>
</rss>