<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>cherry_.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sat, 11 Nov 2023 08:26:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>cherry_.log</title>
            <url>https://velog.velcdn.com/images/cherry_/profile/16d7914e-9a22-499f-bb52-fe20307f9012/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. cherry_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/cherry_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Three.js] Show Rigging GLTF]]></title>
            <link>https://velog.io/@cherry_/Three.js-Show-Rigging-GLTF</link>
            <guid>https://velog.io/@cherry_/Three.js-Show-Rigging-GLTF</guid>
            <pubDate>Sat, 11 Nov 2023 08:26:25 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/cherry_/post/350f5619-ee70-435e-adcb-4fc077c5bf29/image.gif" alt=""></p>
<pre><code>// 아바타 모델 로드
  loader.load(&quot;/gltf/avatar/animation_100.glb&quot;, (gltf) =&gt; {
    avatarMesh = gltf.scene;
    avatarRef.current.add(avatarMesh);
    console.log(&quot;avatar position: &quot; + avatarMesh.position.x, avatarMesh.position.y, avatarMesh.position.z);

    avatarMesh.traverse((object) =&gt; {
      if (object.isBone) {
        const helper = new THREE.SkeletonHelper(object);
        boneRef.current.add(helper);
      }
    });

    // 애니메이션 Mixer를 생성하고 애니메이션 클립을 추가합니다.
    mixer.current = new AnimationMixer(avatarMesh);
    clips = gltf.animations;
    if (clips &amp;&amp; clips.length &gt; 0) {
      clips.forEach((clip) =&gt; {
        mixer.current.clipAction(clip).play(); // 모든 애니메이션 클립을 재생합니다.
      });
    }

    loader.load(&quot;/gltf/avatar/top.glb&quot;, (gltf) =&gt; {
      const boneMesh = gltf.scene;
      //boneRef.current.add(boneMesh);
      console.log(&quot;top position: &quot; + boneMesh.position.x, boneMesh.position.y, boneMesh.position.z);

      printBone(boneMesh);

      boneMesh.traverse((object) =&gt; {
        if (object.isBone) {
          const helper = new THREE.SkeletonHelper(object);
          boneRef.current.add(helper);
        }
      });

      // 뼈 구조 매핑 및 조정 (뼈 구조가 완전히 일치할 때 필요)
      // bones.forEach((bone) =&gt; {
      //   const matchingBone = avatarMesh.getObjectByName(bone.name);
      //   if (matchingBone) {
      //     bone.bind(matchingBone);
      //   }
      // });
    });
  });

  // Three.js 렌더링 루프에서 위치 업데이트 함수와 애니메이션 업데이트 함수 호출
  const animate = () =&gt; {
    if (mixer.current) {
      mixer.current.update(0.01); // AnimationMixer 업데이트
    }
    requestAnimationFrame(animate);
  };

  animate();</code></pre><p>미완성.</p>
<ul>
<li>리깅된 의상을 토끼에게 입혀야 한다.<ul>
<li>리깅된 의상의 bone을 아바타 bone과 엮는 방법 테스트 중</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 15651 N과 M (3)]]></title>
            <link>https://velog.io/@cherry_/C-15651-N%EA%B3%BC-M-3</link>
            <guid>https://velog.io/@cherry_/C-15651-N%EA%B3%BC-M-3</guid>
            <pubDate>Thu, 26 Oct 2023 12:11:06 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/15651">https://www.acmicpc.net/problem/15651</a></p>
<h2 id="정답">정답</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;algorithm&gt;
using namespace std;

int ans[9];

void back(int n, int m, int x){
    if(x == m+1){
        //종료 조건
        for(int i=1; i&lt;=m; i++){
            cout &lt;&lt; ans[i] &lt;&lt; &quot; &quot;;
        }
        cout &lt;&lt; &quot;\n&quot;;
    }
    else{
        for(int i=1; i&lt;=n; i++){
            ans[x] = i;
            back(n, m, x+1);
       }
    }
}

int main()
{
    int n, m;
    cin &gt;&gt; n &gt;&gt; m;

    back(n, m, 1);

    return 0;
}</code></pre><p>난 정말... 백트래킹이 어렵다.
재귀도 어려움 ㅠㅠ 틀 짜는 것도 익숙하지 않고 한 번 들어갔다가 돌아나오면 머리가 꼬인다.
연습 부족이겠지 흑
좀 더 생각하는 연습을 해보자! 이번 문제는 정말 백트래킹의 기본적인 문제였다.</p>
<h3 id="참고">참고</h3>
<p><a href="https://cocoon1787.tistory.com/162">https://cocoon1787.tistory.com/162</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 11279 최대 힙]]></title>
            <link>https://velog.io/@cherry_/C-11279-%EC%B5%9C%EB%8C%80-%ED%9E%99</link>
            <guid>https://velog.io/@cherry_/C-11279-%EC%B5%9C%EB%8C%80-%ED%9E%99</guid>
            <pubDate>Fri, 20 Oct 2023 14:27:21 GMT</pubDate>
            <description><![CDATA[<h3 id="우선순위-큐">우선순위 큐</h3>
<blockquote>
<p>큐에 있는 모든 원소 중에서 가장 큰 값이 Top을 유지하도록, 우선순위가 가장 크도록 설계되어 있다 또한 우선순위 큐는  내부적으로 Heap이라는 자료구조를 사용하고 있다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/cherry_/post/6207e423-fc93-4b27-bbb1-82110effb836/image.png" alt="">
큐랑 사용법이 비슷하다.
자꾸 front랑 top이랑 헷갈리는데 얘는 덱이 아니니까 back이 없음! <code>top</code>이다.</p>
<h3 id="문제">문제</h3>
<p>널리 잘 알려진 자료구조 중 최대 힙이 있다. 최대 힙을 이용하여 다음과 같은 연산을 지원하는 프로그램을 작성하시오.</p>
<p>배열에 자연수 x를 넣는다.
배열에서 가장 큰 값을 출력하고, 그 값을 배열에서 제거한다.
프로그램은 처음에 비어있는 배열에서 시작하게 된다.</p>
<p>-&gt; 우선순위 큐를 사용하면 자연히 제일 큰 숫자가 top이 되도록 정렬되므로 우선순위 큐를 출력하면 된다.</p>
<h3 id="정답">정답</h3>
<pre><code>#include &lt;iostream&gt;
#include &lt;queue&gt;

using namespace std;

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

    int n, x;
    priority_queue&lt;int&gt; q;
    cin &gt;&gt; n;

    while(n--){
        cin &gt;&gt; x;
        if(x == 0){
            if(!q.empty()){
                cout &lt;&lt; q.top() &lt;&lt; &quot;\n&quot;;
                q.pop();
            }
            else{
                cout &lt;&lt; 0 &lt;&lt; &quot;\n&quot;;
            }
            continue;
        }
        //else
        q.push(x);
    }

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 4673 셀프 넘버 ]]></title>
            <link>https://velog.io/@cherry_/C-4673-%EC%85%80%ED%94%84-%EB%84%98%EB%B2%84</link>
            <guid>https://velog.io/@cherry_/C-4673-%EC%85%80%ED%94%84-%EB%84%98%EB%B2%84</guid>
            <pubDate>Mon, 16 Oct 2023 08:54:37 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><a href="https://www.acmicpc.net/problem/4673">4673 셀프 넘버</a>
<img src="https://velog.velcdn.com/images/cherry_/post/4c170f6e-49e2-477c-99c8-5c0f55042f9f/image.png" alt=""></p>
<h2 id="정답">정답</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;

using namespace std;

const int MAX = 10001;

int plusK(int k){
    int result = 0;
    while(k &gt; 0){
        result += k%10;
        k /= 10;
    }
    return result;
}

int main()
{
    vector&lt;bool&gt; v(MAX, true);

    for(int i=1; i&lt;MAX; i++){
        if(v[i] == false)   continue;   //셀.넘 아니면 검사 x
        //else : i는 셀.넘     
        int j = i;
        while(j &lt; MAX){
            j = j+plusK(j);
            v[j] = false;   
        }
    }

    for(int i=1; i&lt;MAX; i++){
        if(v[i] == true)
            cout &lt;&lt; i &lt;&lt; &quot;\n&quot;;
    }
    return 0;
}</code></pre><h2 id="생각의-흐름">생각의 흐름</h2>
<blockquote>
<p>10,000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 증가하는 순서로 출력한다.
100보다 작은 셀프 넘버는 총 13개가 있다. 1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, 97</p>
</blockquote>
<p>입력이 없는 문제는 첨 봤다 ㄷㄷ
셀프 넘버. 소수 탐색할 때처럼 지워주면 되려나? 주어진 13개를 생성자로 해서 지워주면 될 것도 같다.
그럼 일단 d(n)에 대한 함수를 코드로 정의해볼까?</p>
<p>dn = n + n%10 + n/10%10... (자릿수 더하는 코드)</p>
<ul>
<li>중간에 셀프넘버마저 false를 해버리는 바람에 좀 고민했다.</li>
<li>셀프 넘버가 아니면 pass 하는 부분을 추가하길 잘한 것 같다!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 2309 일곱 난쟁이]]></title>
            <link>https://velog.io/@cherry_/C-2309-%EC%9D%BC%EA%B3%B1-%EB%82%9C%EC%9F%81%EC%9D%B4</link>
            <guid>https://velog.io/@cherry_/C-2309-%EC%9D%BC%EA%B3%B1-%EB%82%9C%EC%9F%81%EC%9D%B4</guid>
            <pubDate>Mon, 16 Oct 2023 08:10:01 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><a href="https://www.acmicpc.net/problem/2309">2309 일곱 난쟁이</a>
<img src="https://velog.velcdn.com/images/cherry_/post/a172c902-86ab-4319-b307-daa13b3c1a03/image.png" alt=""></p>
<h2 id="정답">정답</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;

int main()
{
    vector&lt;int&gt; height(9);
    int sum = 0;

    for(int i=0; i&lt;9; i++){
        cin &gt;&gt; height[i];
        sum += height[i];
    }


    for(int i=0; i&lt;9; i++){
        for(int j=i+1; j&lt;9; j++){
            if(sum - height[i] - height[j] == 100){
                height.erase(height.begin()+i);
                height.erase(height.begin()+j-1);

                sort(height.begin(), height.end());
                for(auto i : height)
                    cout &lt;&lt; i &lt;&lt; &quot;\n&quot;;

                return 0;
            }   
        }    
    }

}</code></pre><h2 id="생각의-흐름">생각의 흐름</h2>
<p>완전 탐색!
9개의 수가 주어지고 합이 100이 되는 7개의 수를 찾으면 된다.
100 - i1, i2, .... i7 = 0이 되는 걸 찾으면 됨.</p>
<ul>
<li>9개 중 7개를 골라서 합이 100이 되는지 검사하기.
근데 잘 생각해보면 7개를 고르는 것보다 2개를 제하는 게 더 빠를 것 같다.
9개 전체를 다 더함 = sum
sum - for문으로 고른 2개 = 100?</li>
</ul>
<h2 id="기억할-것">기억할 것</h2>
<p>나는 출력할 때 erase를 사용했는데 </p>
<pre><code>for (int k = 0; k &lt; 9; k++) {
    if (k != i &amp;&amp; k != j) {
        cout &lt;&lt; v[k] &lt;&lt; &#39;\n&#39;;
    }
}</code></pre><p>이게 더 간단 ㅠㅠ!
글구</p>
<ul>
<li>v.erase(v.begin()+i) 사용법 또 까먹었다..ㅎㅎ 기억하기!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 23971 - ZOAC 4]]></title>
            <link>https://velog.io/@cherry_/C-23971-ZOAC-4</link>
            <guid>https://velog.io/@cherry_/C-23971-ZOAC-4</guid>
            <pubDate>Sun, 15 Oct 2023 20:42:47 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><a href="https://www.acmicpc.net/problem/23971">23971 - ZOAC 4</a>
<img src="https://velog.velcdn.com/images/cherry_/post/c2f4acc0-4498-4c5c-b06c-9f9e436aadf8/image.png" alt=""></p>
<h2 id="정답">정답</h2>
<pre><code>#include &lt;iostream&gt;

using namespace std;

int main()
{
    int h, w, n, m;
    cin &gt;&gt; h &gt;&gt; w &gt;&gt; n &gt;&gt; m;

    int col, row;

    if(h%(n+1) != 0)    col = (h/(n+1)) + 1;
    else    col = (h/(n+1));
    if((w%(m+1)) != 0)    row = (w/(m+1)) + 1;
    else    row = (w/(m+1));

    cout &lt;&lt; col * row;

    return 0;
}</code></pre><h2 id="생각의-흐름">생각의 흐름</h2>
<p>완전 탐색 돌리면 될 ..듯?
일단 2차 배열 만들고. n번째 칸 줄을 비우고.
예를 들어, 세로로 2칸을 띄워야 한다면 3, 4, 6, 7칸 비우는 식.
그리고 가로도 똑같이 해줌.
비우는 걸 false로 해서, true인 칸 개수를 출력하면 되지 않을까?</p>
<p>생각을 해보자.
20명에서 6명이 나온 원리.
세로 5줄, 가로 4줄. n줄씩 띄어 앉으니까 (h/1+n) + 1 = 앉을 수 있는 세로 줄.
가로줄도 마찬가지겠지?
단, 나누어 떨어지면 +1 안해도 됨
둘이 곱하면 되겠군...</p>
<h2 id="알면-좋을-것">알면 좋을 것</h2>
<p>나누어 떨어지면 +1 하지 않는다는 걸 수학적으로 말하면 반올림;;;이다.
ceil()을 알았으면 한 줄로 끝날 수도 있었던 문제!</p>
<pre><code>cout &lt;&lt; ceil(h / (n+1)) * ceil(w / (m+1));</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 2960 에라토스테네스의 체]]></title>
            <link>https://velog.io/@cherry_/C-2960-%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98-%EC%B2%B4</link>
            <guid>https://velog.io/@cherry_/C-2960-%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98-%EC%B2%B4</guid>
            <pubDate>Sat, 14 Oct 2023 08:55:59 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><a href="https://www.acmicpc.net/problem/2960">2960 에라토스테네스의 체</a>
<img src="https://velog.velcdn.com/images/cherry_/post/e29a1c28-9648-48f3-8117-5e71e9e8802f/image.png" alt=""></p>
<h2 id="정답">정답</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;

int main()
{
    int n, k;
    vector&lt;bool&gt; v;    
    cin &gt;&gt; n &gt;&gt; k;
    v.assign(n+1, true);
    int count = 0;
    int answer = 0;

    for(int i=2; i&lt;=n; i++){
        if(!v[i]){  //false라면
            continue;
        }
        //else
        //erase
        for(int j=i; j&lt;=n; j+=i){
            if(!v[j])    //false
                continue;
            //else
           v[j] = false;
           count++;
           if(count == k){
               cout &lt;&lt; j;
               return 0;
           }

        }

    }
    return 0;
}</code></pre><ul>
<li>핵심은 <code>for(int j=i; j&lt;=n; j+=i)</code> 요 부분이다.</li>
</ul>
<h2 id="생각의-흐름">생각의 흐름</h2>
<p>알고리즘을 충실히 따르자.</p>
<blockquote>
<ol>
<li>2부터 N까지 모든 정수를 적는다.</li>
<li>아직 지우지 않은 수 중 가장 작은 수를 찾는다. 이것을 P라고 하고, 이 수는 소수이다.</li>
<li>P를 지우고, 아직 지우지 않은 P의 배수를 크기 순서대로 지운다.</li>
<li>아직 모든 수를 지우지 않았다면, 다시 2번 단계로 간다.</li>
</ol>
</blockquote>
<p><code>vector&lt;boo&gt; v;</code>를 이용해서.
7, 3이 주어지면 일단 v.assign(8, true);
그리고 v[2] = false로 시작해서 p의 배수를 false로 바꾼다.
계속 그렇게 진행함. true이면서 p의 배수인 것을 false.
이때 바꿀 때마다 count++ , count == 3이면 stop하고 해당 i를 출력한다.</p>
<h3 id="첫번째-시도---틀렸습니다">첫번째 시도 -&gt; 틀렸습니다.</h3>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;

int main()
{
    int n, k;
    vector&lt;bool&gt; v;    
    cin &gt;&gt; n &gt;&gt; k;
    v.assign(n+1, true);
    int count = 0;
    int answer = 0;

    while(count != k){
        //find P
        int p;
        for(int i=2; i&lt;=v.size(); i++){
            if(v[i]){
                p = i;
                break;
            }    
        }
        //erase
        for(int i=1; i&lt;=v.size(); i++){
            if(v[i*p]){
               v[i*p] = false;
               count++;
               if(count == k){
                   cout &lt;&lt; i*p;
                   return 0;
               }
            }  
        }
    }

    return 0;
}</code></pre><p>...? 뭐꼬.. 전혀 틀린 점을 못 짚어내겠다.
그래도 생각을 해보자. 내 조건들은 다 &#39;해당하면&#39; 인데, 이전 문제들에서 &#39;해당하지 않으면&#39;으로 설정하는 게 더 예외 없이 처리 될 확률이 높았으니까.</p>
<h3 id="두-번째-시도---틀렸습니다">두 번째 시도 -&gt; 틀렸습니다.</h3>
<pre><code>int main()
{
    int n, k;
    vector&lt;bool&gt; v;    
    cin &gt;&gt; n &gt;&gt; k;
    v.assign(n+1, true);
    int count = 0;
    int answer = 0;

    while(count != k){
        //find P
        int p;
        for(int i=2; i&lt;=n; i++){
            if(!v[i]){  //false라면
                continue;
            }
            //else
            p = i;
            break;
        }
        //erase
        for(int i=1; i&lt;=n; i++){
            if(!v[i*p])    //false
                continue;
            //else
            {
               v[i*p] = false;
               count++;
               if(count == k){
                   cout &lt;&lt; i*p;
                   return 0;
               }
            }  
        }
    }

    return 0;
}</code></pre><p>while(count != k) 여기서 문제가 생겼을 것 같다. 사실 밑에서 if문으로 count == k 검사를 하고 있기 때문에 조건의 의미가 없다고 생각하긴 했었다.</p>
<h3 id="세-번째-시도---해결">세 번째 시도 -&gt; 해결!</h3>
<pre><code>for(int j=i; j&lt;=n; j+=i){
            if(!v[j])    //false
                continue;
            //else
           v[j] = false;
           count++;
           if(count == k){
               cout &lt;&lt; j;
               return 0;
           }

        }</code></pre><p>ㅋㅋㅋㅋㅋㅋwhile을 바꿔도 해결 X
요 부분이 핵심이었다. 기존의 내 방식은 <code>for(int i=1; i&lt;=n; i++)</code>이 부분에서 v의 선언 범위를 넘어갈 수도 있었다. 즉, p * i &lt;= n 을 대비하지 못한 코드였다는 소리.
n 까지만 검사하면 되니까 이 부분을 잘 고려했어야 함!</p>
<h2 id="참고">참고</h2>
<p>참고한 건 없었다. 그냥.. 정답 한 번 봄ㅎ;</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] Level2 - 기능개발]]></title>
            <link>https://velog.io/@cherry_/C-Level2-%EA%B8%B0%EB%8A%A5%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@cherry_/C-Level2-%EA%B8%B0%EB%8A%A5%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Fri, 13 Oct 2023 09:13:58 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><img src="https://velog.velcdn.com/images/cherry_/post/d37fed5b-e688-4b4a-86c3-f905595db3a3/image.png" alt=""></p>
<h2 id="정답">정답</h2>
<pre><code>#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;queue&gt;
#include &lt;iostream&gt;

using namespace std;

vector&lt;int&gt; calculate(queue&lt;int&gt; &amp;q){
    vector&lt;int&gt; answer;
    int min = q.front();
    int ans = 1;
    q.pop();

    while(!q.empty()){  //2번째 값부터 비교
        if(q.front() &lt;= min){   
            q.pop();
            ans++;
        }
        else{
            //지금까지 값 저장
            answer.push_back(ans);
            //새로 갱신
            min = q.front();
            q.pop();
            ans = 1;
        }
    }
    answer.push_back(ans);  
    return answer;
}

vector&lt;int&gt; solution(vector&lt;int&gt; progresses, vector&lt;int&gt; speeds) {
    queue&lt;int&gt; q;

    for(int i=0; i&lt;progresses.size(); i++){
        int day = 1;
        while((100 - progresses[i])-day*speeds[i] &gt; 0){
            day++;
        }
        q.push(day);        //완성된 일시를 큐에 추가
    }

    return calculate(q);
}</code></pre><ul>
<li>큐를 활용한 간단한 문제.</li>
</ul>
<h2 id="생각의-흐름">생각의 흐름</h2>
<p>[93, 30, 55]    [1, 30, 5]    [2, 1]
100%-progresses 한 값을 보면
[7, 70, 45] 여기서 속도 * 일수 = 7일, 3일, 9일.
앞에 걸 배포할 때 뒤에 애들을 보고 앞에 거보다 적은 일수가 있다면 같이 pop()</p>
<p>스택과 큐.. 둘 중 무얼 써야할까?
선입선출이니까 큐. 넣는 값은 <code>완성된 일시</code>
min = 첫번째 기능의 완성된 일시</p>
<ul>
<li>q.front() &lt;= min -&gt; 같이 pop(), ans++</li>
<li>q.front() &gt; min -&gt; min 갱신, pop(), 이전 ans를 vector에 추가, ans = 1; (초기화)</li>
</ul>
<p>검증해보자.
progresses = [95, 90, 99, 99, 80, 99]
queue = [5, 10, 1, 1, 20, 1]
min = 5, ans = 1;</p>
<p>10 &gt; 5 이므로 v = [1], min = 10, ans=1
1 &lt; 10 -&gt; ans = 2
1 &lt; 10 -&gt; ans = 3
20 &gt; 10 -&gt; v = [1, 3], min = 20, ans=1
1 &lt; 20 -&gt; ans = 2
q.emtpy() 이므로 v = 1, 3, 2</p>
<p>가설 검증 완료!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 1158 요세푸스 문제]]></title>
            <link>https://velog.io/@cherry_/C-1158-%EC%9A%94%EC%84%B8%ED%91%B8%EC%8A%A4-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@cherry_/C-1158-%EC%9A%94%EC%84%B8%ED%91%B8%EC%8A%A4-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Fri, 13 Oct 2023 08:20:59 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><a href="https://www.acmicpc.net/problem/1158">1158 요세푸스 문제</a>
<img src="https://velog.velcdn.com/images/cherry_/post/15818c9c-3fc1-44dd-8768-0cf0a6c6d0e9/image.png" alt=""></p>
<h2 id="정답">정답</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;queue&gt;

using namespace std;

vector&lt;int&gt; calculate(queue&lt;int&gt; &amp;q, int k){
    vector&lt;int&gt; ans;
    int count = 0;
    while(!q.empty()){
        int tmp = q.front();
        q.pop();
        if(++count != k){
            q.push(tmp);
        }   
        else{   //제거
            ans.push_back(tmp);
            count = 0;
        }
    }
    return ans;
}

int main()
{
    int n, k;
    cin &gt;&gt; n &gt;&gt; k;

    queue&lt;int&gt; q;

    for(int i=1; i&lt;=n; i++){
        q.push(i);       
    }

    vector&lt;int&gt; ans = calculate(q, k);

    //출력
    cout &lt;&lt; &quot;&lt;&quot;;
    for(int i=0; i&lt;n-1; i++){
        cout &lt;&lt; ans[i] &lt;&lt; &quot;, &quot;;
    }
    cout &lt;&lt; ans[n-1] &lt;&lt; &quot;&gt;&quot;;

    return 0;
}</code></pre><ul>
<li>queue를 이용하는 간단한 문제</li>
</ul>
<h2 id="생각의-흐름">생각의 흐름</h2>
<p>사골 같은 문제... 근데 매번 풀때마다 고민하게 되는..</p>
<blockquote>
<p>예를 들어 (7, 3)-요세푸스 순열은 &lt;3, 6, 2, 7, 5, 1, 4&gt;이다.</p>
</blockquote>
<p>나 몇 년 전에는 이거 무슨 소리인지 몰라서 ㅇㅅㅇa.. 했는데 지금은 단번에 이해가 된다 휴
일단... 걸린 놈을 pop() 해야함. 그리고 남은 사람들에서 다시 제거할 사람을 축출 반복.
vector[1 2 3 4 5 6 7] 에서 몇 번째 사람을 erase
deque[1 2 3 4 5 6 7] 에서 앞에서 빼고 뒤에서 넣길 반복?
아니 이건 queue에서 해도 되잖아.
queue 2개를 두고 옮기고, 빼고를 반복?</p>
<p>2번째가 제일 나아보인다. <del>근데 나 지금껏 디큐dequeue인줄 앎.. 덱이었네.</del></p>
<blockquote>
<ul>
<li>먼저 1~n까지 큐에 추가하기</li>
</ul>
</blockquote>
<ul>
<li>큐에서 pop()하고 count++<ul>
<li>count!=k라면 push() </li>
<li>else, 축출할 사람이니 answer vector에 추가</li>
</ul>
</li>
<li>큐가 빌 때까지 반복</li>
</ul>
<h2 id="기억하면-좋을-것">기억하면 좋을 것</h2>
<pre><code>//내 코드
    while(!q.empty()){
        int tmp = q.front();
        q.pop();
        if(++count != k){
            q.push(tmp);
        }   
        else{   //제거
            ans.push_back(tmp);
            count = 0;
        }
    }</code></pre><pre><code>//우수 코드
    while(!q.empty()) {
        for(int i = 0; i &lt; k-1; i++) { // k-1번 pop &amp; push
            q.push(q.front());
            q.pop();
        }

        // k번째 사람 pop 후 순열에 추가
        result.push_back(q.front());
        q.pop();
    }</code></pre><p>나는 pop한 걸 저장하고, 그게 k번째인지 아닌지 매번 검사하는 방향을 취했는데
우수 코드는 애초부터 k-1번째까지 pop과 동시에 push하고, k번째라면 벡터에 추가했다.
매번 검사하는 게 아니니까 시간이 훨씬 줄어든다!
항상 느끼지만 코테 할때는 &#39;매번&#39;이냐, &#39;필요할 때&#39; 검사이냐 생각하는 게 중요한 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 1946 신입사원]]></title>
            <link>https://velog.io/@cherry_/C-1946-%EC%8B%A0%EC%9E%85%EC%82%AC%EC%9B%90</link>
            <guid>https://velog.io/@cherry_/C-1946-%EC%8B%A0%EC%9E%85%EC%82%AC%EC%9B%90</guid>
            <pubDate>Fri, 13 Oct 2023 07:38:23 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1946">1946 신입사원</a></p>
<h2 id="문제">문제</h2>
<p><img src="https://velog.velcdn.com/images/cherry_/post/9865ad46-24b0-4895-9737-1a46b758bafe/image.png" alt=""></p>
<h2 id="정답">정답</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;

bool compare(pair&lt;int, int&gt; &amp;p1, pair&lt;int, int&gt; &amp;p2){
    return p1.first &lt; p2.first;
}

int meeting(vector&lt;pair&lt;int, int&gt;&gt; &amp;v){
    int ans = 1;    //서류 1위의 선발
    int min = v[0].second;  //서류 1위의 면접 결과
    for(int i=1; i&lt;v.size(); i++){
        if(v[i].second &lt; min)    //지원자의 면접 순위가 더 높다면
        {
            ans++;
            min = v[i].second;
        }
    }
    return ans;
}

int main()
{
    int t, n;

    cin &gt;&gt; t;

    while(t--){
        vector&lt;pair&lt;int, int&gt;&gt; v;
        int a, b;
        //input
        cin &gt;&gt; n;
        for(int i=0; i&lt;n; i++){
            cin &gt;&gt; a &gt;&gt; b;
            v.push_back(make_pair(a, b));
        }
        //정렬
        sort(v.begin(), v.end(), compare);
        //면접 순위 비교
        cout &lt;&lt; meeting(v) &lt;&lt; &quot;\n&quot;;
    }

    return 0;
}</code></pre><ul>
<li>무난했다. pair로 서류와 면접 순위를 묶는 게 중요한...?</li>
<li>혼자 힘으로 푼 문제라 각별하다!</li>
</ul>
<h2 id="생각의-흐름">생각의 흐름</h2>
<blockquote>
<p>언제나 최고만을 지향하는 굴지의 대기업 진영 주식회사가 신규 사원 채용을 실시한다. 인재 선발 시험은 1차 서류심사와 2차 면접시험으로 이루어진다. </p>
</blockquote>
<p><em>전형 짧고 대규모 채용.. 진짜 부럽다...... 나도 누가 데려가주라 그러려면 코테 열심히 준비하자...!!!!</em></p>
<p>서류-면접 순서 순위
3 2
1 4
4 1
2 3
5 5</p>
<p>서류를 기준으로 정렬하면
1 4
2 3
3 2
4 1
5 5</p>
<p>일단 서류 1위는 무조건 선발. 
1위와 2위를 비교했을 때 2위는 1위보다 면접이 높으니 선발. 
3위는.. 2위보다 면접이 높으니 선발
4위는.. 면접 1등이니까 선발
5위는.. ... 탈락. 면접이 타인보다 높아야하는데 갱신된 최댓값(=1위)보다 낮다.</p>
<p>결론을 정리하자면,</p>
<blockquote>
<ul>
<li>서류 순위대로 정렬 후 면접 순서를 비교한다.</li>
</ul>
</blockquote>
<ul>
<li>서류 1위의 면접 순위를 min로 설정 후, 다음 순위의 면접 순위가 min보다 작다면 선발. -&gt; min 갱신</li>
<li>min과 면접 순위를 비교하여 작다면 선발 및 갱신, 크다면 탈락.</li>
</ul>
<p>검증해보자.
3 6
7 3
4 2
1 4
5 7
2 5
6 1
서류 순위대로 정렬
1 4
2 5
3 6
4 2
5 7
6 1
7 3</p>
<p>min = 4, 1위 선발
2위의 5 &gt; 4이므로 선발 안함, min = 4
3위 : 6 &gt; 4이므로 선발 안함, min = 4
4위 : 2 &lt; 4이므로 선발, min = 2
5위 : 7 &gt; 2이므로 선발 안함, min = 2
6위 : 1 &lt; 2이므로 선발, min = 1
7위 : 3 &gt; 1이므로 이므로 선발 안함, min = 1</p>
<p>선발된 사람은 3.</p>
<h2 id="기억하면-좋을-것">기억하면 좋을 것</h2>
<ul>
<li>vector&lt;pair ~~&gt;의 정렬을 할 때, pair.first의 값으로 정렬을 할 거라면 default sort 만으로도 가능하다! (나처럼 compare을 정의할 필요가 없음)<pre><code>sort(candidates.begin(), candidates.end()); // 서류 심사 순위(first) 순서대로 정렬</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 1620 나는야 포켓몬 마스터 이다솜]]></title>
            <link>https://velog.io/@cherry_/C-1620-%EB%82%98%EB%8A%94%EC%95%BC-%ED%8F%AC%EC%BC%93%EB%AA%AC-%EB%A7%88%EC%8A%A4%ED%84%B0-%EC%9D%B4%EB%8B%A4%EC%86%9C</link>
            <guid>https://velog.io/@cherry_/C-1620-%EB%82%98%EB%8A%94%EC%95%BC-%ED%8F%AC%EC%BC%93%EB%AA%AC-%EB%A7%88%EC%8A%A4%ED%84%B0-%EC%9D%B4%EB%8B%A4%EC%86%9C</guid>
            <pubDate>Thu, 12 Oct 2023 17:05:54 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1620">나는야 포켓몬 마스터 이다솜</a>
<img src="https://velog.velcdn.com/images/cherry_/post/bebd6154-d50d-4777-b997-8d56d3af03c6/image.png" alt=""></p>
<h2 id="정답">정답</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;map&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

using namespace std;

int main()
{
    // 입출력 속도 향상
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int n, m;   //포켓몬 개수, 문제 개수
    map&lt;string, int&gt; smap;
    map&lt;int, string&gt; imap;
    cin &gt;&gt; n &gt;&gt; m;

    for(int i=1; i&lt;=n; i++){
        string name;
        cin &gt;&gt; name;

        smap[name] = i;
        imap[i] = name;
    }

    for(int i=0; i&lt;m; i++){
        string t;
        cin &gt;&gt; t;
        if(isdigit(t[0]) != 0){    //number
            cout &lt;&lt; imap[stoi(t)] &lt;&lt; &quot;\n&quot;;
        }
        else{   //string
            cout &lt;&lt; smap[t] &lt;&lt; &quot;\n&quot;;
        }
    }

    return 0;
}</code></pre><ul>
<li>map 2개를 이용</li>
<li>isdigit()로 숫자인지 문자인지 파악</li>
</ul>
<h2 id="생각의-흐름">생각의 흐름</h2>
<p>번호와 이름을 매칭해서 저장하면 될 거 같다.
map 과 딕셔너리가 떠오르는데 둘의 차이점이 뭐지? (아래 정리함)</p>
<p>맵을 써도 될 듯!</p>
<p>..근데 입력값이 숫자인지 string인지 어떻게 알지?
int로 받거나 auto로 받아서 해보려 했지만 실패.</p>
<p>아! 번호 자체도 string으로 하면 어떨까? map&lt;string, string&gt; m 이렇게!</p>
<h3 id="첫-번째-시도---컴파일-에러">첫 번째 시도 -&gt; 컴파일 에러</h3>
<pre><code>#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;map&gt;

using namespace std;

//map -&gt; key : value
//딕셔너리랑 차이점이 뭐지?

int main()
{
    int n, m;   //포켓몬 개수, 문제 개수
    map&lt;string, string&gt; map;
    cin &gt;&gt; n &gt;&gt; m;

    for(int i=1; i&lt;=n; i++){
        string name;
        cin &gt;&gt; name;
        map[to_string(i)] = name;        //insert to map
    }

    for (int i=0; i&lt;m; i++){
        string t;
        cin &gt;&gt; t;

        for(auto j=map.begin(); j!=map.end(); j++){
            if(map.find(t) == j-&gt; first){
                cout &lt;&lt; j-&gt;second;  
                continue;
            }    
        }
        for(auto j=map.begin(); j!=map.end(); j++){
            if(t == j-&gt;second){
                cout &lt;&lt; j-&gt;first;  
                continue;
            }    
        }
    }

    return 0;
}</code></pre><p>ㅋㅋ... map 조회하는 법 몰라서 삽질했다. 나의 멍청함에 cheers...~⭐</p>
<h2 id="기억할-것">기억할 것</h2>
<ol>
<li><code>맵</code> vs <code>딕셔너리</code></li>
</ol>
<ul>
<li>둘의 차이점이 뭐지?<blockquote>
<p>맵에서는 엔트리들이 유일한 키를 가져야 하는 반면, 딕셔너리에서는 여러 엔트리들이 같은 키를 가질 수 있다. 갖은 단어가 복수의 정의를 가질 수 있는 영어 사전과 같다고 볼 수 있다.</p>
</blockquote>
</li>
<li>파이썬의 딕셔너리는 hash 구조이지만, c++의 <strong>맵은 트리 구조</strong>이다.</li>
</ul>
<ol start="2">
<li><code>맵</code> 사용법<blockquote>
<ul>
<li>중복된 key를 사용하여 입력하면 덮어씌어짐.</li>
<li>추가, 삽입, 삭제 시 자동정렬</li>
</ul>
</blockquote>
</li>
</ol>
<blockquote>
<ul>
<li>선언 : map&lt;key 자료형, value 자료형&gt; m;</li>
<li><strong>추가 : map[키] = 값;</strong></li>
<li>삭제 : m.erase(key);</li>
<li><strong>조회 : m[키];</strong></li>
<li>검색 : m.find(key);<ul>
<li>iter = m.find(key); iter -&gt; second; 이런 식으로 출력 가능</li>
</ul>
</li>
</ul>
</blockquote>
<ol start="3">
<li><code>isdigit(char x)</code>
문자가 숫자인지 판별하는 함수 (문자라면 0 반환)</li>
</ol>
<p><strong>반드시 char을 넣어야 한다! string 넣으면 안 됨!</strong></p>
<h2 id="참고">참고</h2>
<p><a href="https://ya-ya.tistory.com/83">[c언어/c++] isdigit 함수(숫자를 판별하는 함수)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 7785 회사에 있는 사람]]></title>
            <link>https://velog.io/@cherry_/C-7785-%ED%9A%8C%EC%82%AC%EC%97%90-%EC%9E%88%EB%8A%94-%EC%82%AC%EB%9E%8C</link>
            <guid>https://velog.io/@cherry_/C-7785-%ED%9A%8C%EC%82%AC%EC%97%90-%EC%9E%88%EB%8A%94-%EC%82%AC%EB%9E%8C</guid>
            <pubDate>Tue, 10 Oct 2023 13:56:36 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/cherry_/post/c8e3686c-0530-4934-9ad3-96b8172b818e/image.png" alt="">
<a href="https://www.acmicpc.net/problem/7785">7785</a></p>
<h2 id="정답">정답</h2>
<blockquote>
<ul>
<li>핵심은 <code>set</code>과 역순 출력하는 방법 알아두기~</li>
</ul>
</blockquote>
<ul>
<li>rbegin(), rend() 면 역순이다.</li>
</ul>
<pre><code>#include &lt;set&gt;
#include &lt;iostream&gt;
#include &lt;algorithm&gt;

using namespace std;

//이름이 있다는 건 이미 enter했다는 것
//set 사용

int main()
{
    int n;
    cin &gt;&gt; n;

    set&lt;string&gt; s;    //자동 정렬, 자동 중복 제거

    for(int i=0; i&lt;n; i++){
        string name, record;
        cin &gt;&gt; name &gt;&gt; record;
        if(record == &quot;enter&quot;)   s.insert(name);
        else    s.erase(name);
    }

    for(auto i = s.rbegin(); i != s.rend(); i++){
        cout &lt;&lt; *i &lt;&lt; &quot;\n&quot;;
    }

    return 0;
}</code></pre><h3 id="생각의-흐름">생각의 흐름</h3>
<p>벡터를 하나 만들고.
enter -&gt; 추가 push_back
leave -&gt; 제거?
<code>제거</code>가 메인인 문제에서 과연 vector는 좋은 선택인가?</p>
<blockquote>
<p>vector에서 원소 하나를 삭제하는 시간복잡도는 O(N)</p>
</blockquote>
<p>pair&lt;string, bool&gt;을 사용해서 마지막에 true인 친구들만 sort해서 출력하는 방법.
찾아서 삭제하나... 찾아서 변경하나.... 얘도 시간 복잡도가 비슷할 것 같음 </p>
<p>그럼 일단 pair로 가보자. 이게 더 깔끔해보임!
=&gt; 시간 초과</p>
<p>그럼.........
이름이 이미 존재한다는 건 enter 했다는 거 아닌가?
있으면 remove, 없으면 추가?</p>
<p>위랑 다를 게 뭔데.......... </p>
<h4 id="정답을-봤다">정답을 봤다!</h4>
<p><strong>set을 이용하여 출퇴근 하는 사람들을 관리</strong>하는 게 핵심</p>
<h3 id="첫번째-시도---시간-초과">첫번째 시도 -&gt; 시간 초과</h3>
<p><code>pair&lt;이름, 출입기록&gt;</code> 넣고 마지막에 출입기록이 enter인 사람을 sort해서 출력했다.</p>
<pre><code>#include &lt;vector&gt;
#include &lt;iostream&gt;
#include &lt;algorithm&gt;

using namespace std;
bool compare(string a, string b){
    return a &gt; b;
}

int main()
{
    int n;
    vector&lt;pair&lt;string, bool&gt;&gt; v;
    vector&lt;string&gt; answer;

    cin &gt;&gt; n;

    for(int i=0; i&lt;n; i++){
        string name, record;
        cin &gt;&gt; name &gt;&gt; record;
        if(record == &quot;enter&quot;){
            v.push_back(make_pair(name, true));
        }
        else{   //leave
            for(int j=0; j&lt;v.size(); j++){
                if(v[j].first == name)  v[j].second = false;
            }
        }
    }

    for(int i=0; i&lt;v.size(); i++){
        if(v[i].second){
            answer.push_back(v[i].first);
        }
    }

    sort(answer.begin(), answer.end(), compare);

    for(int i=0; i&lt;answer.size(); i++){
        cout &lt;&lt; answer[i] &lt;&lt; &quot;\n&quot;;
    }


    return 0;
}</code></pre><h3 id="두-번째-시도---시간-초과">두 번째 시도 -&gt; 시간 초과</h3>
<p>leave -&gt; vector.erase 사용했다. 장렬하게 시간 초과.</p>
<pre><code>#include &lt;vector&gt;
#include &lt;iostream&gt;
#include &lt;algorithm&gt;

using namespace std;
bool compare(pair&lt;string, bool&gt; a, pair&lt;string, bool&gt; b){
    return a.first &gt; b.first;
}

int main()
{
    int n;
    vector&lt;pair&lt;string, bool&gt;&gt; v;
    vector&lt;string&gt; answer;

    cin &gt;&gt; n;

    for(int i=0; i&lt;n; i++){
        string name, record;
        cin &gt;&gt; name &gt;&gt; record;
        if(record == &quot;enter&quot;){
            v.push_back(make_pair(name, true));
        }
        else{   //leave
            for(int j=0; j&lt;v.size(); j++){
                if(v[j].first == name)  v.erase(v.begin()+j);
            }
        }
    }

    sort(v.begin(), v.end(), compare);

    for(int i=0; i&lt;v.size(); i++){
        cout &lt;&lt; v[i].first &lt;&lt; &quot;\n&quot;;
    }


    return 0;
}</code></pre><h3 id="3번째-시도---틀렸습니다">3번째 시도 -&gt; 틀렸습니다.</h3>
<p>set을 사용했다.</p>
<pre><code>#include &lt;set&gt;
#include &lt;iostream&gt;
#include &lt;algorithm&gt;

using namespace std;

//이름이 있다는 건 이미 enter했다는 것
//set 사용

int main()
{
    int n;
    cin &gt;&gt; n;

    set&lt;string&gt; s;    //자동 정렬, 자동 중복 제거

    for(int i=0; i&lt;n; i++){
        string name, record;
        cin &gt;&gt; name &gt;&gt; record;
        if(record == &quot;enter&quot;)   s.insert(name);
        else    s.erase(s.find(name));
    }

    for(auto i : s) cout &lt;&lt; i &lt;&lt; &quot;\n&quot;;

    return 0;
}</code></pre><p>역순 출력 안 한 거였음. 바보인가?</p>
<h2 id="기억할-것">기억할 것</h2>
<ol>
<li>v.erase()
v[5]를 지우고 싶다면
<code>v.erase(v.begin() + 5);</code>
sort는 앞에 v. 이 안 붙지만 erase()는 붙는다.
그리고 iterator가 &amp;로 들어가는 게 아니라 begin()에서 +i 들어감!</li>
<li>set<blockquote>
<ol>
<li>숫자든 문자든 중복을 없엔다.</li>
<li>삽입하는 순서에 상관없이 정렬되서 입력이 된다.</li>
</ol>
</blockquote>
</li>
</ol>
<p>왜? <code>이진트리</code>니깐,,</p>
<p>당장 기억해야할 사용법은 아래와 같다. (매우 주관적인 판단)</p>
<blockquote>
<ul>
<li>set&lt;자료형&gt; 변수 :      기본적인 선언방법 </li>
</ul>
</blockquote>
<ul>
<li>s.insert() :      s에 값 삽입</li>
<li>s.erase(<code>값/주소값</code>) :       s에 저장된 요소 삭제. 주소값 말고 해당 값이 들어가도 됨!!</li>
<li>s.swap() :     s1과 s2를 서로 교환</li>
<li>s.begin() :     set의 시작이 되는 주소 값 반환</li>
<li>s.end() :     set의 마지막 부분에 대한 주소 값 반환(정확히는 마지막 뒤 공백구간) </li>
<li>s.size() :     s에 저장되어 있는 크기 </li>
<li>s.find(찾을 값) : 찾기 ex) s.find(1) -&gt; 해당 자리 주소값 반환
  -&gt; s.erase(s.find(1)) 이런 식으로 사용 가능!</li>
</ul>
<p><code>insert</code>로 값 삽입하는 것 빼곤 vector랑 비슷함
<code>set&lt;pair&lt;int, int&gt;&gt;</code> 이렇게 사용 가능한 것도 기억해두자!</p>
<p>의외로 출력하는 데 애 먹음;ㅎ</p>
<pre><code>// 출력
for(auto i : s){
    cout &lt;&lt; i;
}
// 리버스 출력
for (auto iter = s.rbegin(); iter != s.rend(); iter++) {
    cout &lt;&lt; *iter &lt;&lt; &#39;\n&#39;;
}</code></pre><h2 id="참고">참고</h2>
<p><a href="https://cho001.tistory.com/164">C++ 벡터 특정 원소 지우는 방법 vector.erase(),remove() 등 Tips
</a>
<a href="https://hwan-shell.tistory.com/130">C++ set 사용법과 설명</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] 10825 국영수]]></title>
            <link>https://velog.io/@cherry_/C-10825-%EA%B5%AD%EC%98%81%EC%88%98</link>
            <guid>https://velog.io/@cherry_/C-10825-%EA%B5%AD%EC%98%81%EC%88%98</guid>
            <pubDate>Tue, 10 Oct 2023 09:13:17 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/cherry_/post/a0e55791-fa82-47b6-b97b-ce706c059bfc/image.png" alt=""></p>
<h2 id="정답">정답</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;

//국어 내림차순. 같으면 아래로
//영어 오름차순. 같으면 아래로
//수학 내림차순. 같으면 아래로.
//이름 오름차순. (아스키 코드 사용)

vector&lt;string&gt; name;

bool compare(vector&lt;int&gt; &amp;v1, vector&lt;int&gt; &amp;v2){
    if(v1[1] == v2[1]){ 
        if(v1[2] == v2[2]){
            if(v1[3] == v2[3]){
                return name[v1[0]] &lt; name[v2[0]];
            }
            return v1[3] &gt; v2[3];
        }
        return v1[2] &lt; v2[2];
    }
    return v1[1] &gt; v2[1];
}


int main()
{
    int t;
    vector&lt;vector&lt;int&gt;&gt; score;

    cin &gt;&gt; t;

    for(int i=0; i&lt;t; i++){
        string n;
        int a, b, c;
        cin &gt;&gt; n &gt;&gt; a &gt;&gt; b &gt;&gt; c;
        name.push_back(n);      //이름 저장
        score.push_back({i, a, b, c});  //
    }

    sort(score.begin(), score.end(), compare);
    for(int i=0; i&lt;t; i++){
        cout &lt;&lt; name[score[i][0]] &lt;&lt; &quot;\n&quot;;
    }

    return 0;
}</code></pre><h3 id="1차-코드">1차 코드</h3>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;

//국어 내림차순. 같으면 아래로
//영어 오름차순. 같으면 아래로
//수학 내림차순. 같으면 아래로.
//이름 오름차순. (아스키 코드 사용)

bool compare(vector&lt;int&gt; &amp;v1, vector&lt;int&gt; &amp;v2){
    if(v1[1] == v2[1]){ 
        if(v1[2] == v2[2]){
            if(v1[3] == v2[3]){
                return v1[4] &lt; v2[4];
            }
            return v1[3] &gt; v2[3];
        }
        return v1[2] &lt; v2[2];
    }
    return v1[1] &gt; v2[1];
}


int main()
{
    int t;
    vector&lt;string&gt; name;
    vector&lt;vector&lt;int&gt;&gt; score;

    cin &gt;&gt; t;

    for(int i=0; i&lt;t; i++){
        string n;
        int a, b, c;
        cin &gt;&gt; n &gt;&gt; a &gt;&gt; b &gt;&gt; c;
        name.push_back(n);      //이름 저장
        score.push_back({i, a, b, c});  //
    }

    sort(score.begin(), score.end(), compare);
    for(int i=0; i&lt;t; i++){
        cout &lt;&lt; name[score[i][0]] &lt;&lt; &quot;\n&quot;;
    }

    return 0;
}</code></pre><p>크게 문제가 없어 보이는데 자꾸 <code>Haebin</code>과 <code>Junkyu</code>이 바뀌어서 나온다.
Junkyu 50 60 100
Haebin 50 60 100
둘의 입력값은 위와 같기에 사전순 정렬에 문제가 있어 보였다.
하지만..... </p>
<pre><code>return s1.name &lt; s2.name; // (국어, 영어, 수학 점수가 같으면) 이름이 사전 순으로 증가하는 순서</code></pre><p>찾다 못해 본 예시 답안도 나랑 똑같이 부등호를 넣었는데 뭐가 문제지?</p>
<h3 id="해결">해결</h3>
<p>ㅋㅋ...
그냥 내가 바보였다.
구조체 쓰는 방법이 기억이 안 나서 (...) 이름과 성적을 서로 다른 벡터에 넣었는데 이름 벡터를 비교하지 않고 있지도 않은 값을 비교하고 있었으니 정렬이 안 될 수 밖에...
없는 값을 조회했는데 에러가 안 나고 00 으로 떠서 00 과 00 값을 비교하고 있었다.</p>
<pre><code>return name[v1[0]] &lt; name[v2[0]];</code></pre><p><code>vector&lt;string&gt; name;</code>을 전역으로 돌리고 이렇게 바꿔서 해결!</p>
<h2 id="기억할-point">기억할 point</h2>
<ol>
<li>2차원 벡터의 사용자 정의 sort
내가 쓴 벡터는 <code>vector&lt;vector&lt;int&gt;&gt;</code></li>
</ol>
<ul>
<li><code>bool</code> compare()</li>
<li>bool compare(<code>vector&lt;int&gt; &amp;v1</code>, <code>vector&lt;int&gt; &amp;v2</code>)<ul>
<li>내부의 벡터를 주소값으로 받아서 넣는다.</li>
</ul>
</li>
<li>sort(score.begin(), score.end(), <code>compare</code>);<ul>
<li>compare() 아님! 괄호 빼고 쓰기.</li>
</ul>
</li>
</ul>
<ol start="2">
<li><p>이름 오름차순. (아스키 코드 사용)
따로 처리해줄 필요 없다. -&#39;A&#39; 이런거 해야하나 했는데.
부등호로 충분히 처리할 수 있다.</p>
</li>
<li><p>조건문에는 &#39;해당하는 값&#39;을 넣는 것보다 <strong>&#39;예외일 때&#39;를 조건으로</strong> 넣는 게 더 좋을 것 같다.</p>
</li>
</ol>
<p>아래 참고. (by.튜터님들 감사합니다..)</p>
<pre><code>// 비교 함수(bad)
bool cmp(const student&amp; s1, const student&amp; s2) {
    if (s1.kor == s2.kor) { // 국어 점수가 같으면
        if (s1.eng == s2.eng) { // 국어, 영어 점수가 같으면
            if (s1.math == s2.math) { // 국어, 영어, 수학 점수가 같으면 
                return s1.name &lt; s2.name; // 이름이 사전 순으로 증가하는 순서
            }
            return s1.math &gt; s2.math; // 수학 점수가 감소하는 순서
        }
        return s1.eng &lt; s2.eng; // 영어 점수가 증가하는 순서
    }
    return s1.kor &gt; s2.kor; // 국어 점수가 감소하는 순서
}

// 비교 함수(good)
bool cmpAdv(const student&amp; s1, const student&amp; s2) {
    if (s1.kor != s2.kor) {// 국어 점수가 감소하는 순서
        return s1.kor &gt; s2.kor;
    }
    if (s1.eng != s2.eng) {// (국어 점수가 같으면) 영어 점수가 증가하는 순서
        return s1.eng &lt; s2.eng;
    }
    if (s1.math != s2.math) {// (국어, 영어 점수가 같으면) 수학 점수가 감소하는 순서
        return s1.math &gt; s2.math;
    }
    return s1.name &lt; s2.name; // (국어, 영어, 수학 점수가 같으면) 이름이 사전 순으로 증가하는 순서
}</code></pre><h2 id="참고한-블로그">참고한 블로그</h2>
<p><a href="https://twinkite.tistory.com/entry/C-2%EC%B0%A8%EC%9B%90-%EB%B2%A1%ED%84%B0-%EC%A0%95%EB%A0%ACsort">링크텍스트</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코테/C++] Level2 - 타겟 넘버]]></title>
            <link>https://velog.io/@cherry_/C%EC%BD%94%ED%85%8C-Level2-%ED%83%80%EA%B2%9F-%EB%84%98%EB%B2%84</link>
            <guid>https://velog.io/@cherry_/C%EC%BD%94%ED%85%8C-Level2-%ED%83%80%EA%B2%9F-%EB%84%98%EB%B2%84</guid>
            <pubDate>Sat, 30 Sep 2023 09:51:19 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/43165">타겟 넘버</a>
<img src="https://velog.velcdn.com/images/cherry_/post/ff18fd0a-9218-4276-b945-bf7196be83a6/image.png" alt=""></p>
<pre><code>#include &lt;string&gt;
#include &lt;vector&gt;

using namespace std;
int answer = 0;

void calculate(vector&lt;int&gt; numbers, int target, int sum, int idx){
    //end
    if( idx == numbers.size()){
        if(sum == target)
            answer++;  
        return;
    }

    //continue
    calculate(numbers, target, sum+numbers[idx], idx+1);
    calculate(numbers, target, sum-numbers[idx], idx+1);    
}

int solution(vector&lt;int&gt; numbers, int target) {
    calculate(numbers, target, 0, 0);
    return answer;
}

//재귀를 만들때, (하나씩 돌 정수들, 목표 타겟, 실행하고 싶은 거, 인덱스+1)로 구성함</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[코테/C++] Level2 - 최댓값과 최솟값]]></title>
            <link>https://velog.io/@cherry_/%EC%BD%94%ED%85%8CC-Level2-%EC%B5%9C%EB%8C%93%EA%B0%92%EA%B3%BC-%EC%B5%9C%EC%86%9F%EA%B0%92</link>
            <guid>https://velog.io/@cherry_/%EC%BD%94%ED%85%8CC-Level2-%EC%B5%9C%EB%8C%93%EA%B0%92%EA%B3%BC-%EC%B5%9C%EC%86%9F%EA%B0%92</guid>
            <pubDate>Sat, 30 Sep 2023 07:34:43 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12939">최댓값과 최솟값</a></p>
<h3 id="문제">문제</h3>
<p><img src="https://velog.velcdn.com/images/cherry_/post/3a3b7c4e-724d-4b09-931e-d5baa4fc4905/image.png" alt=""></p>
<h3 id="풀이">풀이</h3>
<pre><code>#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;

string solution(string s) {
    string answer = &quot;&quot;;
    vector&lt;int&gt; v;
    string tmp;

    for(int i=0; i&lt;s.length(); i++){
        if(s[i] != &#39; &#39;){
            tmp += s[i];        
        }
        else{
            v.push_back(stoi(tmp));
            tmp.clear();
        }
    }
    v.push_back(stoi(tmp));
    sort(v.begin(), v.end());

    answer = to_string(v.front()) + &quot; &quot; + to_string(v.back());
    return answer;
}

//s를 돌면서 i가 공백이 아니면 tmp에 저장 -&gt; 벡터에 추가
//벡터 정렬 -&gt; 첫째가 최소, 마지막이 최대</code></pre><h3 id="기억할-함수">기억할 함수</h3>
<ul>
<li>string to int : <code>stoi()</code></li>
<li>int to string : <code>to_string()</code></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리팩토링 (1) - 손쉽게 자식 div에 CSS 적용하기]]></title>
            <link>https://velog.io/@cherry_/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-1-%EC%86%90%EC%89%BD%EA%B2%8C-%EC%9E%90%EC%8B%9D-div%EC%97%90-CSS-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cherry_/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-1-%EC%86%90%EC%89%BD%EA%B2%8C-%EC%9E%90%EC%8B%9D-div%EC%97%90-CSS-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 05 Sep 2023 05:26:02 GMT</pubDate>
            <description><![CDATA[<p>학회에서 진행한 프로젝트 발표 및 부스 운영이 끝나고, 우리 팀은 출시를 위해 나아가고 있다.
이전에는 촉박한 기한을 맞추느라 &#39;정상 동작&#39;에 초점을 맞춰 코드를 짰다면
이젠 정말 &#39;<strong>출시</strong>&#39;에 초점을 맞춰서 <strong>기존 코드 리팩토링 및 최적화</strong>를 병행하기로 했다.</p>
<h2 id="오늘-진행할-최적화">오늘 진행할 최적화</h2>
<p><a href="https://velog.io/@cherry_/bottomSheet-%EC%BB%A4%EC%8A%A4%ED%85%80#%EB%81%9D%EB%82%9C-%EC%A4%84-%EC%95%8C%EC%95%98%EC%A7%80-css-%EC%A4%91%EC%B2%A9-%EC%A0%81%EC%9A%A9-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0">[React] &#39;react-spring-bottom-sheet&#39;과의 전쟁 (feat. Custom CSS)</a>
[끝난 줄 알았지? CSS 중첩 적용 문제 해결] 부분이다.</p>
<h2 id="기존-코드">기존 코드</h2>
<p>나는 바텀 시트를 만들기 위해 <code>react-spring-bottom-sheet</code>라는 라이브러리를 사용했다.
이를 커스텀하기 위해 <code>custom CSS</code>라는 자체 css 파일을 만들어서 적용했는데, 기본으로 주어지는 style.css를 복붙+수정한 파일이다.
그러나 <code>react-spring-bottom-sheet</code>를 이용한 바텀 시트가 2군데 이상 존재하자 <strong>각각에 적용하던 css 파일 2개가 중첩 적용</strong>을 일으켰다.</p>
<p>중첩 적용을 해결하기 위해 각각의 바텀 시트에 className을 부여하려고 했다.
그러나 문제는 <code>react-spring-bottom-sheet</code>가 <code>portal</code> 형태로 브라우저에 동적으로 생성된다는 것이었다.
createPortal 관련 코드는 모두 라이브러리에 내장되어 있어서 내가 건드릴 수 있는 형태가 아니었다. 그래도 시도해봤지만 장렬히 실패.</p>
<pre><code>&lt;BottomSheet className=&quot;homeSheet&quot;&gt; &lt;/BottomSheet&gt;</code></pre><p>이런 식으로 컴포넌트를 렌더링하는 부분에 className을 적용하면 부모 div에만 적용이 됐다. 자식 div는 className을 이용한 css가 적용되지 않는다는 말씀!
<img src="https://velog.velcdn.com/images/cherry_/post/5f1b9b16-9873-4ded-a72f-b584ae2433e4/image.png" alt="">
<code>&lt;reach-portal&gt;</code>은 자식이 아주 많기 때문에 css 적용이 안 되면 바텀 시트가 보이지 않았다.</p>
<h4 id="부모-id를-찾아서-자식-div에-classname을-적용하면-어떨까">부모 id를 찾아서 자식 div에 className을 적용하면 어떨까?</h4>
<pre><code>// setTimeout을 사용하여  portal이 생성된 후 일정 시간 후에 작업 실행
    const timeoutId = setTimeout(() =&gt; {
      const parentDiv = document.getElementById(&quot;parentDiv-home&quot;);
      if (parentDiv) {
        const childDivs = parentDiv.querySelectorAll(&quot;div&quot;);
        childDivs.forEach((childDiv) =&gt; {
          childDiv.classList.add(&quot;homeSheet&quot;);
        });
        return () =&gt; {
          childDivs.forEach((childDiv) =&gt; {
            childDiv.classList.remove(&quot;homeSheet&quot;);
          });
        };
      }
      const element = window.getComputedStyle(
        document.querySelector(&quot;--rsbs-overlay-h&quot;)
      );

      element.style.setProperty(&quot;--rsbs-overlay-h&quot;, &quot;100px&quot;);
    }, 1);

    // 컴포넌트가 언마운트될 때 clearTimeout으로 타이머 해제
    return () =&gt; {
      clearTimeout(timeoutId);
    };
  }, []);</code></pre><p>해당 코드를 작성했다. 오늘 리팩토링할 코드이기도 하다.</p>
<pre><code>      const parentDiv = document.getElementById(&quot;parentDiv-home&quot;);
      if (parentDiv) {
        const childDivs = parentDiv.querySelectorAll(&quot;div&quot;);
        childDivs.forEach((childDiv) =&gt; {
          childDiv.classList.add(&quot;homeSheet&quot;);
        });</code></pre><p>이 부분을 보면 <code>parentDiv-home</code>을 id로 가진 div(부모)를 찾아서 자식 div에 className <code>homeSheet</code>를 넣어주고 있다.</p>
<p>하지만 여기서 2번째 시련을 맞닥뜨린다.</p>
<h3 id="컴포넌트-렌더링-순서-문제">컴포넌트 렌더링 순서 문제</h3>
<p>내가 생각한 정상 작동 시나리오는 이랬다. </p>
<blockquote>
<p>portal 생성 -&gt; 부모 id 찾기 -&gt; 자식 div에 className 적용 -&gt; css 적용</p>
</blockquote>
<p>그러나 실제 순서는 이랬다.</p>
<blockquote>
<p>부모 id 찾기 -&gt; 해당 id를 가진 div가 없습니다. -&gt; ERROR!</p>
</blockquote>
<p>portal 생성이 내 코드 실행보다 늦었다.
하지만 리액트에서 렌더링 순서를 제어하는 것은 해도 안 되고, 해서도 안 되는 짓이었다.</p>
<blockquote>
<p>리액트에서 컴포넌트의 렌더링 순서는 일반적으로 React의 가상 DOM(Virtual DOM) 및 조화 업데이트(reconciliation) 알고리즘에 따라 자동으로 관리됩니다.</p>
</blockquote>
<p>되지도 않았고... 건드리면 무슨 문제가 일어날 지 몰랐음.
우선 학회 마감 기한이 있어서, &#39;정상 작동&#39;에 초점을 맞추고 임시방편으로 <code>setTimeout(1)</code> 처리를 했다.
className 적용 코드를 0.001초 대기를 걸어두고, 그 사이에 portal이 렌더링되면 그 이후에 부모 id를 찾아서 <del>~</del> 하는 식이다.</p>
<p>다행히 현장에서는 잘 돌아갔으나 이 코드로 출시를 할 순 없었다.
내가 생각한 문제는 아래와 같았다.</p>
<ul>
<li>모든 환경에서 portal이 0.001초 안에 렌더링 된다는 보장이 없다.</li>
<li>비효율적이다. 분명 css 쪽을 건드리면 방법이 있을 것 같았다.</li>
</ul>
<p>그래서 리팩토링을 진행했다!
학회 발표 이후 합류하신 추가 개발자 팀원이 코드 리뷰를 통해 아주 큰 도움을 주셨다.</p>
<h2 id="수정-코드">수정 코드</h2>
<p><strong>없다! 통째로 삭제했기 때문에!</strong>
css 쪽을 수정하니까 자식 div에 className을 적용하기 위해 짰던 코드가 필요 없어졌다!
너무 기쁘다,, 이 맛에 리팩토링 하는구나. <del>(setTimeout이라는 청테이프로 우걱우걱 봉합해놓은 게 마음의 짐이었음)</del></p>
<h3 id="나는-왜-그런-코드를-짰지">나는 왜 그런 코드를 짰지?</h3>
<p>목적을 다시 생각해봤다.
<code>portal</code> 안에 있는 모든 div에 css를 적용하고 싶었기 때문이다.
모든 div! 이게 핵심이었다.</p>
<p><img src="https://velog.velcdn.com/images/cherry_/post/2e0f6864-0f85-48c7-b694-5b6c7739a0fd/image.png" alt="">
이전 css 코드는 이런 형식으로 아주 길게 생겼다. (대부분 defaultCss를 보존하고 필요한 부분만 찾아서 수정하는 식으로 커스텀했다)
className으로 중첩적용을 방지했기 때문에 <code>.homeSheet</code>가 앞에 있는 걸 볼 수 있다.
<img src="https://velog.velcdn.com/images/cherry_/post/52aa81cb-577d-4da1-a2df-8405b2f0cf13/image.png" alt="">
수정한 지금은 이렇게 <code>#parentDeiv-home div</code>가 앞에 있는 것을 볼 수 있다.</p>
<blockquote>
<p>뭐가 바뀌었다고?</p>
</blockquote>
<ul>
<li><code>.homeSheet</code> -&gt; <code>#parentDeiv-home div</code></li>
</ul>
<p>css 선택자에 대한 개념이 부족하고, 뒤에 [data-rsbs-overlay]와 같은 친구들이 붙어 있어서 혼란한 마음에 그 당시에는 사용하지 못했던... ㅇㅏ주 쉬운 방법...... <del>(변명입니다)</del></p>
<h3 id="classname-과-id-div의-차이">.className 과 #id div의 차이</h3>
<p><code>.homeSheet</code> 선택자와 <code>#parentDiv-home div</code> 선택자의 기능적 차이는 무엇일까?</p>
<ul>
<li><code>.homeSheet</code> 선택자는 class가 &quot;homeSheet&quot;인 모든 요소를 선택한다.</li>
<li><code>#parentDiv-home div</code> 선택자는 id가 &quot;parentDiv-home&quot;인 요소 내에 있는 모든 div 요소를 선택한다.</li>
</ul>
<p>내가 사용하는 bottomSheet 라이브러리는 (추가 코드 없이는) 부모 div에 id 혹은 className을 적용하는 게 한계였다.
따라서 <strong>id가 &quot;parentDiv-home&quot;인 요소 내에 있는 모든 div 요소를 선택</strong>하는 두 번째 방법이 정말 알맞았다.</p>
<pre><code>&lt;BottomSheet id=&quot;parentDiv-home&quot;&gt; &lt;/BottomSheet&gt;</code></pre><p>이렇게 하기만 하면 끝!</p>
<h2 id="결론">결론</h2>
<p>부모 div를 찾아서 자식 div에 className을 적용하던 코드를 없애고 css 자체에서 해당 id를 가진 모든 div에 적용하는 식으로 바꿨다.
css 자체에서 <code>id 찾기 -&gt; 이 id의 모든 하위 div에 css 적용</code>을 해주니 당연히 내가 짠 기존 코드는 필요 없을 수 밖에.
더이상 <code>portal</code> 렌더링 순서를 신경쓸 필요도 없다!</p>
<p>css 기초를 좀 더 다지자.
기본 베이스와 기능을 알면 돌아갈 필요가 없다는 걸 절절하게 깨달은 날이었다.,,
<img src="https://velog.velcdn.com/images/cherry_/post/96c8952a-b522-48c1-8abc-cedfb68b26db/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 수많은 버튼들 그중에 너만 CSS를 적용하는 거야]]></title>
            <link>https://velog.io/@cherry_/React-%EC%88%98%EB%A7%8E%EC%9D%80-%EB%B2%84%ED%8A%BC%EB%93%A4-%EA%B7%B8%EC%A4%91%EC%97%90-%EB%84%88%EB%A7%8C-CSS%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B1%B0%EC%95%BC</link>
            <guid>https://velog.io/@cherry_/React-%EC%88%98%EB%A7%8E%EC%9D%80-%EB%B2%84%ED%8A%BC%EB%93%A4-%EA%B7%B8%EC%A4%91%EC%97%90-%EB%84%88%EB%A7%8C-CSS%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B1%B0%EC%95%BC</guid>
            <pubDate>Wed, 16 Aug 2023 19:27:21 GMT</pubDate>
            <description><![CDATA[<p>거두절미하고 오늘의 주제는 무엇이냐.
<img src="https://velog.velcdn.com/images/cherry_/post/5bb8e90b-3ed2-42c7-9389-d3ea9b6ca4d1/image.gif" alt="">
바보가 아니라면 선택한 버튼만 초록색 border가 들어가는 css가 적용되어야 한다는 것을 알 것이다...
<img src="https://velog.velcdn.com/images/cherry_/post/7c6abbb4-525b-4418-8e32-96c9cc597d6c/image.png" alt="">
지금 짠 코드는 엄청 간단함.</p>
<pre><code>    const [selectDeco, setSelectDeco] = useState(false);
    const handleClick = () =&gt; {
        setSelectDeco(true);
    }

    return (
        &lt;div className={selectDeco ? &quot;item_box_click&quot; : &quot;item_box&quot;} onClick={() =&gt; handleClick()}&gt;
            {content}
        &lt;/div&gt;
    )</code></pre><p>사실 저렇게 돌아갈 걸 알고 있었다. 그냥 css 적용 확인하려고 짜본거임..</p>
<h4 id="그럼-수정해보자">그럼 수정해보자!</h4>
<h2 id="어떻게-하면-될까">어떻게 하면 될까?</h2>
<blockquote>
<ol>
<li>버튼 개수만큼 인자를 가진 array 생성. bool값이 인자로 들어갈 것임.</li>
<li>bool = false로 초기화</li>
<li>버튼 선택 시 해당 인자만 true로 바꾸기.</li>
<li>true인 버튼만 css 적용</li>
<li>버튼을 클릭할 때마다 2~4번 반복</li>
</ol>
</blockquote>
<p>짜보자.......
<img src="https://velog.velcdn.com/images/cherry_/post/528a3b6e-e837-4310-85fb-e5e95d4777d3/image.png" alt="">
..나 이거 이렇게 오래 걸릴 줄 몰랐다.
결국 해내긴 햇는데 뭐임?
왜냐면.. 내 js 구조가 복잡하기 때문이다.
나는 기능/박스 별로 js를 분리하는 걸 참 좋아하는데 (한 곳에 코드 몰려있는 거 안 좋아함) 덕분에....... 또다시 부모 &lt;-&gt; 자식을 참조해야하는 일이 생겼던 것이다.</p>
<h2 id="문제-상황">문제 상황</h2>
<p>나는 item.js와 itembox.js를 갖고 있다.
<img src="https://velog.velcdn.com/images/cherry_/post/2e159a34-794d-4e22-9210-0e3d3cf1009c/image.png" alt="">
Item.js에서 map으로 ItemBox를 반복문 생성해주는 구조임.</p>
<pre><code>{ // 아이템 썸네일 박스
    imgArray.map(
        (imgSrc, index) =&gt; {
            return (&lt;Itembox type={typeItemState_cloth}
                imgSrc={imgSrc}
                index={index}/&gt;)
        }
    )
}</code></pre><p><code>imgArray</code>에는 서버에서 받아오는 이미지 url이 있다. <del>(아직 서버 연결 안했다만.. 언제하지?)</del></p>
<p>itembox.js에서는 아래와 같은 코드를 return한다.</p>
<pre><code>return (
    &lt;div className={btnState ? &quot;item_box_click&quot; : &quot;item_box&quot;} onClick={() =&gt; props.handleClick(index)}&gt;
        {content}
    &lt;/div&gt;
)</code></pre><p>..간단하죠?</p>
<p>문제는 여기서 발생한다. 나는 itembox에 모든 버튼의 상태값을 담은 array를 생성한 뒤, 이걸 검사하며 itembox의 클래스네임을 설정해줄 생각이었는데 <strong>itembox가 각각의 js로 생성되며 공통된 변수를 가질 수 없게 된 것</strong>이다!!!!!!
아놔......</p>
<h2 id="해결-과정">해결 과정</h2>
<h3 id="첫-시도-feat-함수-return">첫 시도 (feat. 함수 return)</h3>
<p>내 초반 생각은 이랬다.</p>
<blockquote>
<ol>
<li>버튼의 상태를 담은 array(이하 <code>selectDeco</code>)는 item.js에 만들자.</li>
<li>item.js에 click() 함수를 두고, itemBox에서 버튼을 누르면 click()을 호출하자.</li>
<li>click()에서 <code>selectDeco</code>의 상태를 업데이트 해주자.</li>
<li>itemBox.js에 <code>selectDeco</code>의 상태를 return 하자.</li>
</ol>
</blockquote>
<p>=&gt; 함수 return이 안 돼서 실패.
자식에서 부모의 함수를 호출하는 건 되는데 그 함수의 반환값을 다시 자식으로 받는 건 안 되더라...</p>
<p>하.........................................
공통 변수 두는 무슨 라이브러리 있는 거 같았는데 고작 이런 기능으로 그렇게까지(?) 하고 싶지 않았음.</p>
<h3 id="2차-시도">2차 시도</h3>
<blockquote>
<ol start="4">
<li><code>useEffect</code>를 써볼까? <code>selectDeco</code>가 변하면 &lt; Itembox selectDeco = {selectDeco}/&gt; 이렇게 이 값만 보내는거지!</li>
</ol>
</blockquote>
<p>실패함. 감지를 못 하던데?
디버거까지 써서 해봤는데 보내도 itemBox.js에서 변수가 변경되지 않았다. 분명 useEffect는 실행됐는데.
마찬가지로 itemBox.js에도 useEffect를 두고 해당 변수가 변했을 경우 console.log()를 찍어봐도 나오지 않았다. 그냥 해당 useEffect를 타지 않았다.</p>
<h3 id="3차-시도-해결">3차 시도 (해결)</h3>
<p><strong>그냥 통째로 컴포넌트를 재로드 하자!!!</strong>
아래는 부모. Item.js</p>
<pre><code>//Item.js 부모 

const [selectDeco, setSelectDeco] = useState(false);
const handleClick = (idx) =&gt; {
    const newArr = Array(imgArray.length).fill(false);
    newArr[idx] = true;
    setSelectDeco(newArr);
}
const handleChangeContent = () =&gt; {
        &lt;div className=&quot;itemBoxDiv&quot;&gt;
            {
            // 아이템 썸네일 박스
            imgArray.map((imgSrc, index) =&gt; {
                return (
                    &lt;Itembox type={typeItemState_face}
                        imgSrc={imgSrc}
                        index={index}
                        handleClick={handleClick}
                        selectDeco={selectDeco}/&gt;
                )
            })
        } &lt;/div&gt;
    );
    useEffect(() =&gt; {
        handleChangeContent();
    }, [selectDeco]);</code></pre><p>selectDeco가 변할 때마다 useEffect로 컴포넌트 전체를 재로드했다. 
재로드하면서 업데이트 한 selectDeco={selectDeco} 도 다시 보내줌.</p>
<p>아래는 자식. Itembox.js</p>
<pre><code>useEffect(() =&gt; {
    if (selectDeco[index]) { // 해당 버튼이 클릭됐으면
        setBtnState(true);
    } else {
        setBtnState(false);
    }
}, [selectDeco]);
const [btnState, setBtnState] = useState(false);
return (&lt;div className={
        btnState
            ? &quot;item_box_click&quot;
            : &quot;item_box&quot;
    }
    onClick={
        () =&gt; props.handleClick(index)
}&gt; {content} &lt;/div&gt;)</code></pre><p>업데이트 한 selectDeco가 들어오면 useEffect에서 감지한다.
그래서 selectDeco의 index에 해당하는 인자가 true면 버튼 css를 적용하고, 아니면 해제한다.</p>
<h2 id="결과">결과</h2>
<p><img src="https://velog.velcdn.com/images/cherry_/post/33e9ecdf-6759-4ded-a3e9-8a91e04e02c4/image.gif" alt="">
후.........
이거 진짜 쌩뇌로 짰다.
재랜더링하는 게 메모리 많이 잡아먹을까봐 걱정해서 최대한 피한건데 결국 재랜더링하는 게 좀 마음에 걸리기도 하고.. <del>근데 저 쪼끔 재랜더링한다고 뭐 얼마나 과부화 되겟어? 저게 제일 효율적일수도; (쓰레기 마인드)</del></p>
<p>2달 해외살이 후 토요일에 귀국해서 월화수목 밤을 샜더니 정신이... 어우 어지러워
그래도 뿌듯..하다......웅....</p>
<p><img src="https://velog.velcdn.com/images/cherry_/post/5ff20557-ed40-4cec-8cc9-3a4e10754b3e/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React/Three.js] 특정 GLTF 모델 제거, GLTF에 type 넣기, 특정 data 조회 후 삭제]]></title>
            <link>https://velog.io/@cherry_/ReactThree.js-%ED%8A%B9%EC%A0%95-GLTF-%EB%AA%A8%EB%8D%B8-%EC%A0%9C%EA%B1%B0-GLTF%EC%97%90-type-%EB%84%A3%EA%B8%B0</link>
            <guid>https://velog.io/@cherry_/ReactThree.js-%ED%8A%B9%EC%A0%95-GLTF-%EB%AA%A8%EB%8D%B8-%EC%A0%9C%EA%B1%B0-GLTF%EC%97%90-type-%EB%84%A3%EA%B8%B0</guid>
            <pubDate>Tue, 15 Aug 2023 22:12:42 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/cherry_/post/51028848-1e31-45e4-8e3a-75a0568020c1/image.gif" alt=""></p>
<p>대망의 아바타 옷 꾸미기 기능!</p>
<pre><code>    const groupRef = useRef(props);

  //deco 추가하는 함수
  const putDecoGltf = (gltfPath, setScale, positionX, positionY, positionZ, type) =&gt; {
    const gltfLoader = new GLTFLoader();
    gltfLoader.load(gltfPath, (childGltf) =&gt; {
    const model = childGltf.scene;
    model.scale.set(setScale, setScale, setScale); // 모델 크기 조정
    model.position.set(positionX, positionY, positionZ); // 모델 위치 설정
    groupRef.current.add(model);
    }); 
  } 

    gltfLoader.load(mouthGltfPaht, (childGltf) =&gt; {
      const model = childGltf.scene;
      model.scale.set(1.1, 1.1, 1.1); // 자식 모델 크기 조정
      model.position.set(0,-0.04,0); // 자식 모델 위치 설정
      groupRef.current.add(model);
    });</code></pre><p>나는 이렇게 생긴 <code>gltfLoader.load</code> 함수를 만들어서 gltf 파일을 불러와 <code>Canvas</code>에 넣어주고 있다.
나는 아바타 꾸미기 기능을 만들고 있기 때문에 불러와야 하는 gltf 파일이 많았다,, 그래서 함수로 만들어서 로드하고 있었음.
그리고 아바타 사이즈 조절을 하는데 아바타+옷+악세서리 등이 함께 사이즈 조절, 위치 조절이 되어야 하기 때문에 groupRef로 그룹에 추가해서 묶어주는데,, 이건 나중에 포스팅 하겠음. 오늘 목적은 그게 아님.</p>
<h2 id="오늘의-목적">오늘의 목적</h2>
<p>슈 옷입히기 게임이라도 해보신 분들은 알겠지만 &#39;상의&#39;를 누르면 상의가 바뀌고 &#39;하의&#39;를 누르면 하의가 바뀌여야 한다.</p>
<h4 id="그래-바뀌어야-한다">그래, 바뀌어야 한다.</h4>
<p>gltfLoader.load로 불러오기만 하면 불러온 옷 위에 옷이 덧씌워지고, 덧씌워지고, 덧씌워지고...
처음에는 아무것도 입히지 않은 아바타를 매번 다시 불러오면 되지 않나? 했는데 그럼 로딩도 길어지고, 무엇보다 load도 똑같이 다시 불러와지기 때문에 소용이 없었다.
이 길로 계속 밀고 나가기엔 &#39;상의&#39; 파트에서는 상의만 사라지고 새로운 상의가 올라와야 하는데 저렇게 하면 다른 하의, 악세서리 등도 사라질 것 같아서.
역시 특정 gltf를 없애는 코드를 작성해야겠다고 생각했다.</p>
<h2 id="내가-할-일">내가 할 일</h2>
<p>그룹에 추가하는 형식으로 gltf를 load하기 때문에 그룹에서 빼면 된다고 생각했다.
내가 할일은 다음과 같다.</p>
<blockquote>
<ol>
<li>GLTF remove &amp; 그룹에서 빼기</li>
<li>눈, 입, 상의, 하의 등 특정 type을 가진 GLTF 조회하기 (이것만 삭제해야 하므로)</li>
</ol>
</blockquote>
<p>생각보다 적지?~??!?</p>
<h3 id="1-grouprefcurrentremovemodel">1. groupRef.current.remove(model);</h3>
<pre><code>groupRef.current.remove(model);</code></pre><p>위 코드는 model 객체를 groupRef에서 제거한다.
일단 내가 삭제할 객체들은 groupRef의 자식들이므로 자식 객체를 지우면 되지 않나...</p>
<pre><code>groupRef.current.remove(groupRef.current.children[0]);</code></pre><p>ㅋㅋ
ㅋ
이러면 올라간 gltf가 랜덤으로 지워짐
이때 <strong>glft에 type을 넣어야겠다고 생각</strong>했다. 나는 삭제할 model 객체가 딱 정해진 게 아니었다. 
매 순간마다 그저 &#39;상의&#39; 혹은 &#39;하의&#39; type을 가진 객체를 조회해서 해당 객체만 삭제해야 했다.</p>
<h3 id="2-특정-gltf-type-조회-후-삭제">2. 특정 GLTF &#39;type&#39; 조회 후 삭제</h3>
<pre><code>  //deco 초기화하는 함수
  const removeDecoGltf = (type) =&gt; {
    const childToRemove = groupRef.current.children.find(
      (child) =&gt; child.userData.type === type
    );

    if (childToRemove) {
      groupRef.current.remove(childToRemove);
  }</code></pre><p><code>childToRemove</code> 변수에 <code>userData.type</code>가 <code>type</code>과 일치하는 gltf를 저장한다.
이 gltf는 groupRef 그룹에 속해 있는 자식들 중 하나임
그리고 <code>childToRemove</code>가 존재하면 지운다!</p>
<h3 id="3-그럼-gltf가-type을-가지고-있어야겠지">3. 그럼 GLTF가 type을 가지고 있어야겠지?</h3>
<p><code>type</code>과 일치하는지 살펴보려면 gltf가 type을 가지고 있어야 한다. (당연함)
참고로 <code>type</code>은 <code>var itemTypeArray = [&#39;eye&#39;, &#39;mouth&#39;, &#39;상의&#39;, &#39;하의&#39;, &#39;한벌의상&#39;];</code> 이거임.
이 배열을 돌면서 일치하는지 검사한다... 내가 클릭한 의상의 type이 뭔지는 서버에서 넘어온다. </p>
<p>프론트에서 할일은,</p>
<ul>
<li><p>모델을 로드할 때 해당 모델에 type값 넣어주기</p>
</li>
<li><p>동일한 type 값을 가진 모델이 Canvas에 올라가 있는지</p>
</li>
<li><p>올라가 있다면 지우고 현재 모델을 올려주기</p>
<ul>
<li>없다면 현재 모델을 올리기</li>
</ul>
</li>
</ul>
<pre><code>  //deco 추가하는 함수
  const putDecoGltf = (gltfPath, setScale, positionX, positionY, positionZ, type) =&gt; {
    const gltfLoader = new GLTFLoader();
    gltfLoader.load(gltfPath, (childGltf) =&gt; {
    const model = childGltf.scene;
    model.scale.set(setScale, setScale, setScale); // 모델 크기 조정
    model.position.set(positionX, positionY, positionZ); // 모델 위치 설정

    model.userData.type = type; // 원하는 타입 값을 설정

    itemTypeArray.map((n)=&gt;{
      if(type === n){
        //기존에 있고, 현재 모델의 타입과 일치하는 타입의 모델 지우기
        removeDecoGltf(type)
      }
    })

    groupRef.current.add(model);
    }); 
  } </code></pre><p> userData 객체에 type 속성을 추가한 후, 원하는 타입 값을 설정했다.</p>
<blockquote>
<p> userData는 Three.js의 3D 객체에 사용자 정의 데이터를 저장할 수 있는 속성입니다. 이를 이용하여 특정 자식 객체의 속성을 저장하고 사용할 수 있습니다.
여기서 중요한 점은, Three.js의 userData 속성은 단순한 JavaScript 객체이므로, 어떤 속성이든 저장할 수 있습니다. 따라서 type 외에도 다양한 사용자 정의 데이터를 userData에 저장할 수 있습니다.</p>
</blockquote>
<p>라고 한다.
<img src="https://velog.velcdn.com/images/cherry_/post/9d4bf4f9-a268-4b8f-999e-f76278a384d4/image.gif" alt=""></p>
<p>잘 돌아가서 만족스럽군.........</p>
<h3 id="기타">기타</h3>
<ul>
<li><code>ItemBox.js</code>에서 썸네일 box 클릭 -&gt; <code>AvatarDeco.js</code>의 <code>isClick 함수</code> 호출
이런 식으로 진행했다.</li>
</ul>
<p>문제는 itemBox와 AvatarDeco 사이에 Item.js가 하나 더 있음. 원래는 자식 -&gt; 부모 간의 데이터 전송으로 하려고 했지만 자식 부모간은 괜찮지만 손자와 할아버지? 이건 복잡한 문제가 생기는 거거든요.
그냥 외부함수 호출 <del>(할아버지 외부인 취급)</del> 로 갔더니 간편하고 좋았다. (정말?)</p>
<p>사실 힘들엇음...
주요한 방법은 아래와 같다.</p>
<ul>
<li>전역 함수가 호출되었을 때 해당 호출을 리액트 컴포넌트 내부에서 감지</li>
<li>Custom Event 사용:</li>
</ul>
<pre><code>export function isClick(index, itemtype){ //의상 index 받아오기
  type = itemtype;
  addGltfPath = gltfArray[index];
  const event = new CustomEvent(&#39;globalFunctionCalled&#39;);
  window.dispatchEvent(event);
}

const GltfGroupModels = (props) =&gt; {
  useEffect(() =&gt; {
    function handleGlobalFunctionCall() {
      console.log(type);
      // 원하는 작업 수행
      //removeDecoGltf();
      putDecoGltf(addGltfPath, 1.1, 0,-0.04,0, type);
      console.log(addGltfPath);
    }

    window.addEventListener(&#39;globalFunctionCalled&#39;, handleGlobalFunctionCall);

    return () =&gt; {
      window.removeEventListener(&#39;globalFunctionCalled&#39;, handleGlobalFunctionCall);
    };
  }, []);
}</code></pre><p>급한 사람을 위해 남겨놓고, 나는 다음에 작성해야지... 마감이 얼마 안 남앗다고.... 다시 기능개발로 돌아가야겟다,,
<img src="https://velog.velcdn.com/images/cherry_/post/9b562201-82e9-41d4-89a7-d3c83fc06c58/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Tab Component 구현]]></title>
            <link>https://velog.io/@cherry_/React-Tab-Component-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@cherry_/React-Tab-Component-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Mon, 14 Aug 2023 15:54:40 GMT</pubDate>
            <description><![CDATA[<p>약간 트위터스러운 기능을 구현해야 했다.
탭이 2개있고, 누르면 각각에 맞춰 아래 div를 바꿔주는 기능이다.
아래 div는 분리된 js에서 불러오기 때문에 이번에는 우선 tab component를 만들고, 클릭한 탭을 알아볼 수 있게 하는 부분을 만들 것이다.</p>
<h3 id="코드">코드</h3>
<pre><code>import styles from &#39;../css/header/headerLook.module.css&#39;;
import React, { useState } from &quot;react&quot;;

const HeaderLook = () =&gt; {
    // Tab Menu 중 현재 어떤 Tab이 선택되어 있는지 확인하기 위한 currentTab 상태와 currentTab을 갱신하는 함수가 존재해야 하고, 초기값은 0.
  const [currentTab, clickTab] = useState(&quot;인기&quot;);  //default tap type : 인기 게시물

  const selectMenuHandler = (type) =&gt; {
    // 해당 함수가 실행되면 현재 선택된 Tab Menu 가 갱신.
    //console.log(type);
    clickTab(type);
  };

    return (
        &lt;header className={styles.header}&gt;
            {/* 선택 시 className에 {styles.focused} 추가 */}
            &lt;div className={`${styles.tab} ${currentTab === &quot;인기&quot; ? styles.focused : &#39;&#39;}`}
                 onClick={() =&gt; selectMenuHandler(&quot;인기&quot;)}&gt;
                &lt;img src={currentTab === &quot;인기&quot; ? process.env.PUBLIC_URL + &#39;/img/looking/popular.png&#39; : process.env.PUBLIC_URL + &#39;/img/looking/popular_non.png&#39;}&gt;&lt;/img&gt;
                &lt;text&gt;인기&lt;/text&gt;
            &lt;/div&gt;
            {/* 선택 시 className에 {styles.focused} 추가 */}
            &lt;div className={`${styles.tab} ${currentTab === &quot;최신&quot; ? styles.focused : &#39;&#39;}`}
                 onClick={() =&gt; selectMenuHandler(&quot;최신&quot;)}&gt;
                &lt;img src={currentTab === &quot;최신&quot; ? process.env.PUBLIC_URL + &#39;/img/looking/new.png&#39; : process.env.PUBLIC_URL + &#39;/img/looking/new_non.png&#39;}&gt;&lt;/img&gt;
                &lt;text&gt;최신&lt;/text&gt;
            &lt;/div&gt;
        &lt;/header&gt;
    )
}

export default HeaderLook</code></pre><p>시원하게 전체 공개~
이 코드는 내가 직접 짰다,,ㄷㄷ...
사실 좀 참고 했는데 결국에는 내가 거의 다 뜯어고쳣음ㅎㅅㅎ</p>
<blockquote>
<p>const [currentTab, clickTab] = useState(&quot;인기&quot;);</p>
</blockquote>
<p>디폴트는 인기 게시글로 했다. currentTab 에 인기, 최신 2가지 string이 저장될 것이다.</p>
<blockquote>
<p>onClick={() =&gt; selectMenuHandler(&quot;최신&quot;)}&gt;</p>
</blockquote>
<ul>
<li><p>이걸 누르면
const selectMenuHandler = (type) =&gt; {
  // 해당 함수가 실행되면 현재 선택된 Tab Menu 가 갱신.
  //console.log(type);
  clickTab(type);
};</p>
<p>아래 함수가 호출되면서 clickTab값을 갱신한다.</p>
</li>
</ul>
<pre><code>&lt;div className={`${styles.tab} ${currentTab === &quot;최신&quot; ? styles.focused : &#39;&#39;}`}</code></pre><p>나는 css-module을 사용하기 때문에 className이 2개 이상이려면 저렇게 넣어줘야 한다. tab은 기본으로 갖고 있고, 내가 tab을 선택하면 .focused 클래스네임이 추가된다.</p>
<p>그거에 따라 css를 조절해주면 끝!</p>
<p><img src="https://velog.velcdn.com/images/cherry_/post/cc05aa84-8181-4a15-9fb9-2d299a7b3a64/image.gif" alt="">
뿌듯......
나는 Look.js에서 post를 불러오고 있기 때문에 Look.js에 type값을 전송해주려고 한다.
이건 다음에,, 총총</p>
<p><img src="https://velog.velcdn.com/images/cherry_/post/70cb3cec-1b40-4268-a6fc-09865a802c06/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React/Three.js] GLTF 모델에 Texture 넣기]]></title>
            <link>https://velog.io/@cherry_/ReactThree.js-GLTF-%EB%AA%A8%EB%8D%B8%EC%97%90-Texture-%EB%84%A3%EA%B8%B0</link>
            <guid>https://velog.io/@cherry_/ReactThree.js-GLTF-%EB%AA%A8%EB%8D%B8%EC%97%90-Texture-%EB%84%A3%EA%B8%B0</guid>
            <pubDate>Mon, 14 Aug 2023 13:48:22 GMT</pubDate>
            <description><![CDATA[<p>왜 리액트에서 쓰는 건 잘 없냐
나는 개인 glft 모델을 가져다 쓰고 있었는데 기존 자료는 죄다 제공하는 geometry를 써서.... 거기 있는 라이브러리로 쓰더라.
찾다찾다 없어서 짜내서 쓰다가 에러를 만나고 
암튼 결국 해냈다.</p>
<h2 id="준비-과정">준비 과정</h2>
<p>모델 불러오는 등은 다 되어 있을 거라고 생각하고 생략하겠다.
사실 밑에 전체 코드에 일부분 포함되어 있음ㅋ</p>
<pre><code>import { TextureLoader } from &#39;three&#39;;</code></pre><p>우선 필요한 걸 import를 해준다.</p>
<h2 id="texture-적용-시작">Texture 적용 시작</h2>
<pre><code>gltfLoader.load(AvatarGltfPath, (parentGltf) =&gt; {
      const avatarModel = parentGltf.scene;
      avatarModel.scale.set(1.1, 1.1, 1.1);
      avatarModel.position.set(0,-0.04,0);

      // 텍스처 적용
      avatarModel.material.map = texture;

      groupRef.current.add(avatarModel); 
    });  </code></pre><p>처음에는 이렇게 했는데 실패햇다.</p>
<p><img src="https://velog.velcdn.com/images/cherry_/post/0019619b-fb31-496e-9504-7fbd8354cd07/image.png" alt=""></p>
<blockquote>
<p>Cannot set properties of undefined (setting &#39;map&#39;)</p>
</blockquote>
<p>ㅎㅅㅎ.........</p>
<h2 id="오류-해결">오류 해결</h2>
<p>나와라 챗 지피티.</p>
<pre><code>      //텍스쳐 적용
      avatarModel.traverse((child) =&gt; {
        if (child.isMesh) {
          child.material.map = texture;
        }
      });</code></pre><p>이걸로 코드를 바꾸니까 texture가 그냥 날아가서 투명한 모델이 보이더라.</p>
<h4 id="진짜-챗지피티-리액트에-도움이-하나도-안됨">진짜!!!!!!! 챗지피티 리액트에 도움이 하나도 안됨!!!!!!</h4>
<p><a href="https://discourse.threejs.org/t/child-material-mesh-sets-the-texture-for-the-entire-obj-why/21421">https://discourse.threejs.org/t/child-material-mesh-sets-the-texture-for-the-entire-obj-why/21421</a></p>
<p>대신 나의 영원한 동반자 구글의 바다에서 이 친구가 도움을 줬다...</p>
<blockquote>
<p>child.material = child.material.clone(); //추가!!</p>
</blockquote>
<pre><code>    gltfLoader.load(AvatarGltfPath, (parentGltf) =&gt; {
      const avatarModel = parentGltf.scene;
      avatarModel.scale.set(1.1, 1.1, 1.1); // 부모 모델 크기 조정
      avatarModel.position.set(0,-0.04,0);

      //텍스쳐 적용
      avatarModel.traverse((child) =&gt; {
        if (child.isMesh) {
          child.material = child.material.clone();
          child.material.map = texture;
        }
      });

      groupRef.current.add(avatarModel); 
    });  </code></pre><p>그래서 이렇게 사용하면 된다.
한번 클론해준 다음에 적용하기!</p>
<p>근데 또 웃긴건 저렇게 해서 한 번 성공시켜 놓으니까 클론 부분 지워도 잘 돌아감;; 뭐임;;</p>
<p><img src="https://velog.velcdn.com/images/cherry_/post/7e3aad8f-d7df-41ef-a93b-c37a7b1b1c39/image.png" alt="">
아무튼 귀여운 우드 토끼.....</p>
<p>+) 사실 뺨에 치크 넣으려고 저런건데 UV 편 치크는 왜인지 적용이 안돼서(텍스쳐 문제인듯) 짱 멋진 디자이너님들이 그냥 GLTF 모델에 텍스쳐 입혀서 새로 주셧다
<img src="https://velog.velcdn.com/images/cherry_/post/cbef35ae-42a7-41de-9335-db4d64e60d76/image.png" alt="">
ㄱ껄껄.. 그래서 내가 한 일은 쓸모가 없어졋지만,, 우드 토끼,, 귀여웟으니까 됏다 흥</p>
<p><img src="https://velog.velcdn.com/images/cherry_/post/27d52c36-9961-42e0-b00b-8016b031cafc/image.gif" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>