<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>portuga_code.log</title>
        <link>https://velog.io/</link>
        <description>Keep Learning!</description>
        <lastBuildDate>Fri, 08 Aug 2025 04:59:37 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>portuga_code.log</title>
            <url>https://velog.velcdn.com/images/portuga_code/profile/4a51ae7a-2d78-4e53-a870-4c42e44c6019/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. portuga_code.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/portuga_code" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Unity] 유니티에 HTTP 통신하는 방법]]></title>
            <link>https://velog.io/@portuga_code/Unity-%EC%9C%A0%EB%8B%88%ED%8B%B0%EC%97%90-HTTP-%ED%86%B5%EC%8B%A0%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@portuga_code/Unity-%EC%9C%A0%EB%8B%88%ED%8B%B0%EC%97%90-HTTP-%ED%86%B5%EC%8B%A0%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Fri, 08 Aug 2025 04:59:37 GMT</pubDate>
            <description><![CDATA[<h2 id="unity-network-유니티에-http-통신하는-방법-post-get">[Unity Network] 유니티에 HTTP 통신하는 방법 (POST, GET)</h2>
<br>

<blockquote>
<p><strong>www</strong></p>
</blockquote>
<br>

<p>www 클래스는 유니티에서 제공하는 간단한 방법으로 웹 서버와 통신할 수 있게 해주는 클래스입니다. 웹 페이지의 데이터를 다운로드하거나, 이미지 등을 불러올 때 사용됩니다.</p>
<p>현재 www는 유니티에서 권장하지 않고 있습니다.</p>
<br>

<blockquote>
<p><strong>UnityWebRequest</strong>  </p>
</blockquote>
<br>

<p>UnityWebRequest 클래스는 유니티에서 HTTP 통신을 할 수 있게 해주는 클래스입니다. GET, POST 등 다양한 HTTP 요청 방식을 지원하며, 파일 다운로드, 업로드 등의 작업에 사용됩니다. 유니티에서 www를 사용하기 보다는 UnityWebRequest를 사용하는 것을 권장합니다.</p>
<br>

<br>

<ul>
<li><strong>GET 방식</strong><ul>
<li>GET 방식은 주로 서버로부터 정보를 <strong>==조회==</strong>하기 위해 사용되는 메서드입니다.</li>
<li>데이터를 요청할 때 필요한 매개변수를 URL의 일부로 포함시켜 서버에 전송합니다.</li>
<li>URL에 데이터가 붙기 때문에, 전송할 수 있는 데이터의 크기가 제한적이며, 보안상 민감한 데이터를 전송하기에는 부적합할 수 있습니다.</li>
</ul>
</li>
</ul>
<br>

<h3 id="get-소스-코드">GET 소스 코드</h3>
<br>

<pre><code>IEnumerator GetTextData()
{
    string url = &quot;https://example.com/data.json&quot;;
    UnityWebRequest request = UnityWebRequest.Get(url);

    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        Debug.Log(&quot;받은 데이터: &quot; + request.downloadHandler.text);
    }
    else
    {
        Debug.LogError(&quot;요청 실패: &quot; + request.error);
    }
}</code></pre><br>

<pre><code>// Json 다운로드 형식

UnityWebRequest request = UnityWebRequest.Get(&quot;https://example.com/data.json&quot;);
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
    string jsonText = request.downloadHandler.text;
    Debug.Log(jsonText);
}</code></pre><br>

<pre><code>// Texture2D 다운로드 형식

UnityWebRequest request = UnityWebRequestTexture.GetTexture(&quot;https://example.com/image.png&quot;);
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
    Texture2D tex = DownloadHandlerTexture.GetContent(request);
    myRenderer.material.mainTexture = tex;
}

// byte[] -&gt; Texture2D 변환
IEnumerator GetTextureFromBytes()
{
    UnityWebRequest request = UnityWebRequest.Get(&quot;https://example.com/image.png&quot;);
    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        byte[] imageBytes = request.downloadHandler.data;

        Texture2D texture = new Texture2D(2, 2); // 임의 크기, LoadImage가 자동 리사이즈함
        texture.LoadImage(imageBytes); // PNG/JPG 포맷 해석

        // 결과 사용
        GetComponent&lt;Renderer&gt;().material.mainTexture = texture;
    }
    else
    {
        Debug.LogError(request.error);
    }
}</code></pre><br>

<br>

<pre><code>// AudioClip 다운로드 형식

UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(&quot;https://example.com/sound.mp3&quot;, AudioType.MPEG);
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
    AudioClip clip = DownloadHandlerAudioClip.GetContent(request);
    audioSource.clip = clip;
    audioSource.Play();
}</code></pre><br>

<pre><code>// zip 파일 조회 후 압축 해제
// zip 압축 해제를 위해 ICSharpCode.SharpZipLib 설치 필요 (Nuget 통해 설치 가능)

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;

IEnumerator DownloadAndUnzip()
{
    string zipUrl = &quot;https://example.com/files/archive.zip&quot;;
    string zipPath = Path.Combine(Application.persistentDataPath, &quot;archive.zip&quot;);
    string extractPath = Path.Combine(Application.persistentDataPath, &quot;unzipped&quot;);

    UnityWebRequest request = UnityWebRequest.Get(zipUrl);
    request.downloadHandler = new DownloadHandlerFile(zipPath);
    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        Debug.Log(&quot;ZIP 파일 다운로드 완료&quot;);

        // 압축 해제
        using (FileStream fs = File.OpenRead(zipPath))
        using (ZipInputStream zipStream = new ZipInputStream(fs))
        {
            ZipEntry entry;
            while ((entry = zipStream.GetNextEntry()) != null)
            {
                string filePath = Path.Combine(extractPath, entry.Name);
                Directory.CreateDirectory(Path.GetDirectoryName(filePath));

                using (FileStream streamWriter = File.Create(filePath))
                {
                    byte[] buffer = new byte[4096];
                    int size;
                    while ((size = zipStream.Read(buffer, 0, buffer.Length)) &gt; 0)
                    {
                        streamWriter.Write(buffer, 0, size);
                    }
                }

                Debug.Log($&quot;파일 추출: {entry.Name}&quot;);
            }
        }
    }
    else
    {
        Debug.LogError(&quot;ZIP 다운로드 실패: &quot; + request.error);
    }
}</code></pre><br>

<br>

<br>

<br>

<ul>
<li><strong>POST 방식</strong><ul>
<li>POST 방식은 서버에 데이터를 <code>전송</code>할 때 사용됩니다. 일반적으로 로그인, 업로드 등에 사용합니다.</li>
</ul>
</li>
</ul>
<br>

<h3 id="post-소스-코드">POST 소스 코드</h3>
<br>

<pre><code>// 기초 소스 코드

IEnumerator PostJsonData()
{
    string url = &quot;https://example.com/upload&quot;;
    string json = &quot;{\&quot;name\&quot;:\&quot;chatgpt\&quot;,\&quot;score\&quot;:100}&quot;;

    UnityWebRequest request = new UnityWebRequest(url, &quot;POST&quot;);
    byte[] jsonToSend = new System.Text.UTF8Encoding().GetBytes(json);
    request.uploadHandler = new UploadHandlerRaw(jsonToSend);
    request.downloadHandler = new DownloadHandlerBuffer();
    request.SetRequestHeader(&quot;Content-Type&quot;, &quot;application/json&quot;);

    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        Debug.Log(&quot;응답: &quot; + request.downloadHandler.text);
    }
    else
    {
        Debug.LogError(&quot;요청 실패: &quot; + request.error);
    }
}</code></pre><br>

<br>

<pre><code>// 로컬에 있는 Json 파일 전송

IEnumerator PostLocalJsonFile()
{
    string filePath = Path.Combine(Application.streamingAssetsPath, &quot;myData.json&quot;);
    string jsonData = File.ReadAllText(filePath);

    UnityWebRequest request = new UnityWebRequest(&quot;https://example.com/upload&quot;, &quot;POST&quot;);
    byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
    request.uploadHandler = new UploadHandlerRaw(bodyRaw);
    request.downloadHandler = new DownloadHandlerBuffer();
    request.SetRequestHeader(&quot;Content-Type&quot;, &quot;application/json&quot;);

    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        Debug.Log(&quot;JSON 전송 완료: &quot; + request.downloadHandler.text);
    }
    else
    {
        Debug.LogError(&quot;전송 실패: &quot; + request.error);
    }
}</code></pre><br>

<br>

<pre><code>// 로컬에 있는 zip 파일 전송

IEnumerator PostLocalZipFile()
{
    string zipFilePath = Path.Combine(Application.persistentDataPath, &quot;archive.zip&quot;);
    byte[] zipBytes = File.ReadAllBytes(zipFilePath);

    UnityWebRequest request = new UnityWebRequest(&quot;https://example.com/upload&quot;, &quot;POST&quot;);
    request.uploadHandler = new UploadHandlerRaw(zipBytes);
    request.downloadHandler = new DownloadHandlerBuffer();
    request.SetRequestHeader(&quot;Content-Type&quot;, &quot;application/zip&quot;);

    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        Debug.Log(&quot;ZIP 전송 완료: &quot; + request.downloadHandler.text);
    }
    else
    {
        Debug.LogError(&quot;전송 실패: &quot; + request.error);
    }
}</code></pre><br>

<pre><code>// 텍스처(Texture 2D) 전송 (png 변환 후)

IEnumerator PostTexture(Texture2D texture)
{
    byte[] pngData = texture.EncodeToPNG();

    UnityWebRequest request = new UnityWebRequest(&quot;https://example.com/upload&quot;, &quot;POST&quot;);
    request.uploadHandler = new UploadHandlerRaw(pngData);
    request.downloadHandler = new DownloadHandlerBuffer();
    request.SetRequestHeader(&quot;Content-Type&quot;, &quot;image/png&quot;);

    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        Debug.Log(&quot;텍스처 전송 완료&quot;);
    }
    else
    {
        Debug.LogError(&quot;전송 실패: &quot; + request.error);
    }
}</code></pre><br>

<br>

<pre><code>// 비디오 또는 오디오 전송 (바이너리 그대로)

IEnumerator PostMediaFile(string mediaPath, string mimeType)
{
    byte[] mediaBytes = File.ReadAllBytes(mediaPath);

    UnityWebRequest request = new UnityWebRequest(&quot;https://example.com/upload&quot;, &quot;POST&quot;);
    request.uploadHandler = new UploadHandlerRaw(mediaBytes);
    request.downloadHandler = new DownloadHandlerBuffer();
    request.SetRequestHeader(&quot;Content-Type&quot;, mimeType); // 예: &quot;video/mp4&quot; 또는 &quot;audio/mpeg&quot;

    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        Debug.Log(&quot;미디어 파일 전송 완료&quot;);
    }
    else
    {
        Debug.LogError(&quot;전송 실패: &quot; + request.error);
    }
}

// 사용 예시
StartCoroutine(PostMediaFile(Application.streamingAssetsPath + &quot;/sample.mp4&quot;, &quot;video/mp4&quot;));
StartCoroutine(PostMediaFile(Application.streamingAssetsPath + &quot;/sound.mp3&quot;, &quot;audio/mpeg&quot;));</code></pre><br>

<br>

<br>

<br>

<br>

<br>

<blockquote>
<p><strong>UploadHandler</strong></p>
</blockquote>
<br>

<ul>
<li>UploadHandlerRaw<ul>
<li>바이트 배열을 그대로 서버에 전송할 때 사용됩니다. Json, 바이너리 등</li>
</ul>
</li>
</ul>
<br>

<hr>
<br>

<ul>
<li>UploadHandlerFile<ul>
<li>로컬 파일을 그대로 서버에 업로드할 때 사용합니다.</li>
</ul>
</li>
</ul>
<br>

<pre><code>UnityWebRequest request = UnityWebRequest.Put(&quot;https://example.com/upload&quot;, &quot;Assets/Data/file.txt&quot;);
request.uploadHandler = new UploadHandlerFile(&quot;Assets/Data/file.txt&quot;);
yield return request.SendWebRequest();</code></pre><br>

<hr>
<br>

<ul>
<li>UploadHanlderFormData<ul>
<li>폼 데이터를 서버로 보낼 때 사용합니다.</li>
</ul>
</li>
</ul>
<br>

<pre><code>List&lt;IMultipartFormSection&gt; formData = new List&lt;IMultipartFormSection&gt;();
formData.Add(new MultipartFormDataSection(&quot;field1&quot;, &quot;value1&quot;));
formData.Add(new MultipartFormDataSection(&quot;field2&quot;, &quot;value2&quot;));

UnityWebRequest request = UnityWebRequest.Post(&quot;https://example.com/form&quot;, formData);
yield return request.SendWebRequest();</code></pre><br>

<hr>
<br>

<blockquote>
<p><strong>DownloadHandler</strong></p>
</blockquote>
<br>

<ul>
<li>DownloadHandlerBuffer<ul>
<li>텍스트나 Json 등의 일반 데이터를 메모리로 바로 가져올 때 사용합니다.</li>
</ul>
</li>
</ul>
<br>

<hr>
<br>

<ul>
<li>DownloadHandlerFile<ul>
<li>다운로드 데이터를 직접 디스크에 저장할 때 사용합니다.</li>
</ul>
</li>
</ul>
<br>

<pre><code>string savePath = Application.persistentDataPath + &quot;/file.json&quot;;
UnityWebRequest request = UnityWebRequest.Get(&quot;https://example.com/data.json&quot;);
request.downloadHandler = new DownloadHandlerFile(savePath);
yield return request.SendWebRequest();</code></pre><br>

<hr>
<br>

<ul>
<li>DownloadHandlerAssetBundle<ul>
<li>에셋 번들을 다운로드하고 메모리상에서 바로 로드할 때 사용합니다.</li>
</ul>
</li>
</ul>
<br>

<pre><code>string url = &quot;https://example.com/bundle&quot;;
UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(url);
yield return request.SendWebRequest();

if (request.result == UnityWebRequest.Result.Success)
{
    AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
    GameObject prefab = bundle.LoadAsset&lt;GameObject&gt;(&quot;MyPrefab&quot;);
    Instantiate(prefab);
}</code></pre><br>

<br>

<blockquote>
<p><strong>📘 UnityWebRequest의 작업 흐름</strong></p>
</blockquote>
<br>

<p><img src="blob:https://velog.io/d6887d25-01e0-4413-8539-908a8e200043" alt="업로드중.."></p>
<br>

<ol>
<li>요청 생성 (<code>UnityWebRequest.Get/Post/Put/...</code>)</li>
<li>필요 시 <code>uploadHandler</code>, <code>downloadHandler</code> 지정</li>
<li>요청 보내기 (<code>SendWebRequest</code>)</li>
<li>완료되면 <code>result</code>, <code>downloadHandler.text</code> 또는 <code>GetContent()</code> 등으로 결과 확인</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Coding] Rest API]]></title>
            <link>https://velog.io/@portuga_code/Rest-API</link>
            <guid>https://velog.io/@portuga_code/Rest-API</guid>
            <pubDate>Fri, 08 Aug 2025 03:24:17 GMT</pubDate>
            <description><![CDATA[<p>Rest API에서 GET 과 POST는 가장 기본적이면서도 중요한 HTTP 메서드입니다. 목적과 차이점을 정리하겠습니다.</p>
<br>

<h2 id="🔹-1-목적-차이">🔹 1. 목적 차이</h2>
<table>
<thead>
<tr>
<th>메서드</th>
<th>용도</th>
</tr>
</thead>
<tbody><tr>
<td><strong>GET</strong></td>
<td>서버에서 **데이터를 조회(읽기)**할 때 사용</td>
</tr>
<tr>
<td><strong>POST</strong></td>
<td>서버에 **데이터를 전송(생성/처리)**할 때 사용</td>
</tr>
</tbody></table>
<br>

<hr>
<h2 id="🔹-2-예시">🔹 2. 예시</h2>
<ul>
<li><strong>GET 예시</strong></li>
</ul>
<pre><code>http
복사편집
GET /users/123</code></pre><p>👉 사용자 123번의 정보를 요청 (서버는 데이터를 <strong>읽어서 반환</strong>)</p>
<br>

<ul>
<li><strong>POST 예시</strong></li>
</ul>
<pre><code>http
복사편집
POST /users
Body: { &quot;name&quot;: &quot;JKOH&quot;, &quot;email&quot;: &quot;test@example.com&quot; }</code></pre><p>👉 서버에 새 사용자 정보를 <strong>보내서 생성</strong>하는 요청</p>
<br>

<hr>
<h2 id="🔹-3-주요-차이점">🔹 3. 주요 차이점</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>GET</th>
<th>POST</th>
</tr>
</thead>
<tbody><tr>
<td><strong>데이터 전송 위치</strong></td>
<td>URL (쿼리스트링 등)</td>
<td>HTTP Body</td>
</tr>
<tr>
<td><strong>데이터 용량</strong></td>
<td>적음 (URL 길이 제한 있음)</td>
<td>큼 (Body에 자유롭게 담을 수 있음)</td>
</tr>
<tr>
<td><strong>캐싱</strong></td>
<td>가능 (브라우저/프록시 등에서 캐싱됨)</td>
<td>기본적으로 캐싱 안 됨</td>
</tr>
<tr>
<td><strong>안전성</strong></td>
<td>안전함 (단순 조회)</td>
<td>안전하지 않을 수 있음 (서버 상태 변경 가능)</td>
</tr>
<tr>
<td><strong>멱등성</strong></td>
<td>있음 (같은 요청 반복해도 결과 동일)</td>
<td>없음 (요청 반복 시 데이터 중복 생성 등 부작용 가능)</td>
</tr>
</tbody></table>
<br>

<br>

<hr>
<h2 id="🔹-4-정리된-핵심-요약">🔹 4. 정리된 핵심 요약</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>GET</th>
<th>POST</th>
</tr>
</thead>
<tbody><tr>
<td>목적</td>
<td>데이터 &quot;조회&quot;</td>
<td>데이터 &quot;전송/생성&quot;</td>
</tr>
<tr>
<td>데이터 위치</td>
<td>URL</td>
<td>Request Body</td>
</tr>
<tr>
<td>캐싱 가능 여부</td>
<td>가능</td>
<td>불가능 (기본적으로)</td>
</tr>
<tr>
<td>멱등성</td>
<td>있음</td>
<td>없음</td>
</tr>
<tr>
<td>일반 사용 예</td>
<td>조회, 리스트 불러오기 등</td>
<td>회원가입, 로그인, 주문하기 등</td>
</tr>
</tbody></table>
<br>

<br>

<hr>
<h3 id="✅-결론적으로">✅ 결론적으로:</h3>
<ul>
<li>데이터를 <strong>읽어오려면 <code>GET</code></strong></li>
<li>서버에 데이터를 <strong>보내서 등록하거나 처리하려면 <code>POST</code></strong></li>
</ul>
<br>

<br>

<br>

<br>

<h2 id="📌-put과-delete는-무엇을-위한-메서드인가요">📌 <code>PUT</code>과 <code>DELETE</code>는 무엇을 위한 메서드인가요?</h2>
<table>
<thead>
<tr>
<th>메서드</th>
<th>목적</th>
</tr>
</thead>
<tbody><tr>
<td><strong>PUT</strong></td>
<td><strong>리소스를 &quot;수정&quot;하거나 &quot;전체 대체&quot;</strong> 할 때 사용</td>
</tr>
<tr>
<td><strong>DELETE</strong></td>
<td><strong>리소스를 삭제</strong>할 때 사용</td>
</tr>
</tbody></table>
<br>

<br>

<hr>
<h2 id="🔹-put--업데이트-요청">🔹 <code>PUT</code> – 업데이트 요청</h2>
<h3 id="✅-사용-목적">✅ 사용 목적</h3>
<ul>
<li>서버의 특정 리소스를 <strong>지정된 값으로 업데이트</strong> (보통 전체 수정)</li>
</ul>
<h3 id="✅-예시">✅ 예시</h3>
<pre><code>http
복사편집
PUT /users/123
Body: { &quot;name&quot;: &quot;JKOH&quot;, &quot;email&quot;: &quot;new@example.com&quot; }</code></pre><p>➡️ 사용자 123의 정보를 주어진 값으로 <strong>완전히 교체</strong></p>
<h3 id="✅-특징">✅ 특징</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>멱등성</strong></td>
<td>O (같은 요청을 여러 번 보내도 같은 결과)</td>
</tr>
<tr>
<td><strong>전체 대체</strong></td>
<td>보통은 전체 리소스를 덮어씀</td>
</tr>
<tr>
<td><strong>부분 수정은?</strong></td>
<td><code>PATCH</code>를 사용하는 것이 더 적절</td>
</tr>
</tbody></table>
<br>

<br>

<hr>
<h2 id="🔹-delete--삭제-요청">🔹 <code>DELETE</code> – 삭제 요청</h2>
<h3 id="✅-사용-목적-1">✅ 사용 목적</h3>
<ul>
<li>서버에서 특정 리소스를 <strong>삭제</strong>할 때 사용</li>
</ul>
<h3 id="✅-예시-1">✅ 예시</h3>
<pre><code>http
복사편집
DELETE /users/123</code></pre><p>➡️ 사용자 123번 계정 삭제 요청</p>
<h3 id="✅-특징-1">✅ 특징</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>멱등성</strong></td>
<td>O (이미 삭제된 리소스를 다시 삭제해도 결과는 동일)</td>
</tr>
<tr>
<td><strong>Body 포함 여부</strong></td>
<td>보통은 없음, 필요 시 포함 가능 (권장 X)</td>
</tr>
</tbody></table>
<br>

<br>

<hr>
<h2 id="✅-4가지-메서드-전체-비교-요약">✅ 4가지 메서드 전체 비교 요약</h2>
<table>
<thead>
<tr>
<th>메서드</th>
<th>목적</th>
<th>요청 데이터 위치</th>
<th>멱등성</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>GET</code></td>
<td>리소스 조회</td>
<td>URL (쿼리)</td>
<td>✅ 있음</td>
<td><code>/users/123</code></td>
</tr>
<tr>
<td><code>POST</code></td>
<td>리소스 생성</td>
<td>Body</td>
<td>❌ 없음</td>
<td><code>/users</code></td>
</tr>
<tr>
<td><code>PUT</code></td>
<td>리소스 전체 수정</td>
<td>Body</td>
<td>✅ 있음</td>
<td><code>/users/123</code></td>
</tr>
<tr>
<td><code>DELETE</code></td>
<td>리소스 삭제</td>
<td>(Body 거의 없음)</td>
<td>✅ 있음</td>
<td><code>/users/123</code></td>
</tr>
</tbody></table>
<br>

<br>

<h2 id="🩹-patch--리소스-일부만-수정할-때-사용하는-메서드">🩹 PATCH – 리소스 <strong>일부만 수정</strong>할 때 사용하는 메서드</h2>
<h3 id="✅-사용-목적-2">✅ 사용 목적</h3>
<ul>
<li>기존 리소스의 <strong>일부 필드만 업데이트</strong>할 때 사용</li>
<li><code>PUT</code>이 전체 교체라면, <code>PATCH</code>는 <strong>부분 변경</strong></li>
</ul>
<hr>
<h3 id="✅-예시-2">✅ 예시</h3>
<pre><code>http
복사편집
PATCH /users/123
Body: { &quot;email&quot;: &quot;newemail@example.com&quot; }</code></pre><p>➡️ 사용자 123의 <code>email</code>만 <strong>변경</strong>, 나머지 정보는 그대로 유지</p>
<hr>
<h3 id="✅-특징-2">✅ 특징</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td><strong>목적</strong></td>
<td><strong>일부 필드만 변경</strong></td>
</tr>
<tr>
<td><strong>데이터 위치</strong></td>
<td>Body</td>
</tr>
<tr>
<td><strong>멱등성</strong></td>
<td>❌ 보장되지 않음 (서버 구현 방식에 따라 다름)</td>
</tr>
<tr>
<td><strong>용도 예시</strong></td>
<td>사용자 이름만 변경, 비밀번호만 재설정 등</td>
</tr>
<tr>
<td><strong>성능</strong></td>
<td><code>PUT</code>보다 <strong>가볍고 효율적</strong> (변경 필드만 전송)</td>
</tr>
</tbody></table>
<br>

<br>

<hr>
<h2 id="📌-5가지-rest-api-메서드-최종-비교-정리표">📌 5가지 REST API 메서드 최종 비교 정리표</h2>
<table>
<thead>
<tr>
<th>메서드</th>
<th>목적</th>
<th>데이터 위치</th>
<th>멱등성</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>GET</strong></td>
<td>조회</td>
<td>URL</td>
<td>✅ 있음</td>
<td>데이터 가져오기</td>
</tr>
<tr>
<td><strong>POST</strong></td>
<td>생성</td>
<td>Body</td>
<td>❌ 없음</td>
<td>새 데이터 등록</td>
</tr>
<tr>
<td><strong>PUT</strong></td>
<td>전체 수정</td>
<td>Body</td>
<td>✅ 있음</td>
<td>리소스 전체 교체</td>
</tr>
<tr>
<td><strong>DELETE</strong></td>
<td>삭제</td>
<td>(Body 없음)</td>
<td>✅ 있음</td>
<td>리소스 제거</td>
</tr>
<tr>
<td><strong>PATCH</strong></td>
<td>부분 수정</td>
<td>Body</td>
<td>❌ 없음 (보통)</td>
<td>리소스 일부 필드만 변경</td>
</tr>
</tbody></table>
<br>

<br>

<hr>
<h2 id="💬-예시-비교-users123-대상">💬 예시 비교 (<code>/users/123</code> 대상)</h2>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>GET</strong></td>
<td>사용자 123의 정보를 가져온다</td>
</tr>
<tr>
<td><strong>POST</strong></td>
<td>새 사용자를 등록한다</td>
</tr>
<tr>
<td><strong>PUT</strong></td>
<td>사용자 123의 정보를 통째로 새로 쓴다</td>
</tr>
<tr>
<td><strong>PATCH</strong></td>
<td>사용자 123의 일부 정보만 수정한다 (예: 이름만 바꿈)</td>
</tr>
<tr>
<td><strong>DELETE</strong></td>
<td>사용자 123을 삭제한다</td>
</tr>
</tbody></table>
<br>

<br>

<hr>
<h3 id="✅-개발-팁">✅ 개발 팁</h3>
<ul>
<li>사용자 이름만 바꾸거나 설정만 수정할 땐 <code>PATCH</code>가 가장 적합합니다.</li>
<li>대부분의 RESTful API 서버(예: Django REST, Express.js, FastAPI)는 <code>PATCH</code> 메서드 지원합니다.</li>
<li><strong>모바일·프론트엔드에서는 PATCH보다 PUT만 쓰는 경우</strong>도 있는데, 서버가 명확히 구분해주는 게 중요합니다.</li>
</ul>
<br>

<br>

<blockquote>
<p>예제</p>
</blockquote>
<br>

<ul>
<li>URL Json 구조</li>
</ul>
<pre><code>{
  &quot;id&quot;: 123,
  &quot;name&quot;: &quot;JKOH&quot;,
  &quot;email&quot;: &quot;test@example.com&quot;,
  &quot;created_at&quot;: &quot;2025-07-02T09:20:00Z&quot;
}</code></pre><br>

<ul>
<li> Unity에서 해당 구조 받을 때 Class</li>
</ul>
<pre><code>[System.Serializable]
public class UserResponse
{
    public int id;
    public string name;
    public string email;
    public string created_at;
}

// 받을 때
// UserResponse user = JsonUtility.FromJson&lt;UserResponse&gt;(request.downloadHandler.text);</code></pre><br>

<br>

<ul>
<li>정리</li>
</ul>
<pre><code>using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

[System.Serializable]
public class UserResponse
{
    public int id;
    public string name;
    public string email;
    public string created_at;
}

[System.Serializable]
public class UserRequest
{
    public string name;
    public string email;
}

[System.Serializable]
public class EmailOnly
{
    public string email;
}</code></pre><br>

<ul>
<li>GET : 사용자 조회</li>
</ul>
<pre><code>IEnumerator GetUser(int userId)
{
    string url = $&quot;https://example.com/api/users/{userId}&quot;;

    using (UnityWebRequest request = UnityWebRequest.Get(url))
    {
        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
            string json = request.downloadHandler.text;
            UserResponse user = JsonUtility.FromJson&lt;UserResponse&gt;(json);
            Debug.Log($&quot;[GET] 이름: {user.name}, 이메일: {user.email}&quot;);
        }
        else
        {
            Debug.LogError(&quot;[GET] 실패: &quot; + request.error);
        }
    }
}</code></pre><br>

<ul>
<li>POST : 새 사용자 생성 (id, created_at은 서버 로직에서 값 넣는다는 가정)</li>
</ul>
<pre><code>IEnumerator CreateUser(string name, string email)
{
    string url = &quot;https://example.com/api/users&quot;;
    UserRequest data = new UserRequest { name = name, email = email };
    string jsonData = JsonUtility.ToJson(data);

    using (UnityWebRequest request = new UnityWebRequest(url, &quot;POST&quot;))
    {
        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
        request.uploadHandler = new UploadHandlerRaw(bodyRaw);
        request.downloadHandler = new DownloadHandlerBuffer();
        request.SetRequestHeader(&quot;Content-Type&quot;, &quot;application/json&quot;);

        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
            string json = request.downloadHandler.text;
            UserResponse user = JsonUtility.FromJson&lt;UserResponse&gt;(json);
            Debug.Log($&quot;[POST] 생성된 ID: {user.id}&quot;);
        }
        else
        {
            Debug.LogError(&quot;[POST] 실패: &quot; + request.error);
        }
    }
}</code></pre><br>

<br>

<ul>
<li>PUT : 전체 사용자 정보 수정</li>
</ul>
<pre><code>IEnumerator UpdateUser(int userId, string name, string email)
{
    string url = $&quot;https://example.com/api/users/{userId}&quot;;
    UserRequest data = new UserRequest { name = name, email = email };
    string jsonData = JsonUtility.ToJson(data);

    using (UnityWebRequest request = new UnityWebRequest(url, &quot;PUT&quot;))
    {
        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
        request.uploadHandler = new UploadHandlerRaw(bodyRaw);
        request.downloadHandler = new DownloadHandlerBuffer();
        request.SetRequestHeader(&quot;Content-Type&quot;, &quot;application/json&quot;);

        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
            string json = request.downloadHandler.text;
            UserResponse user = JsonUtility.FromJson&lt;UserResponse&gt;(json);
            Debug.Log($&quot;[PUT] 수정된 이름: {user.name}&quot;);
        }
        else
        {
            Debug.LogError(&quot;[PUT] 실패: &quot; + request.error);
        }
    }
}</code></pre><br>

<ul>
<li>PATCH : 정보 일부 수정 (이메일 변경할 시)</li>
</ul>
<pre><code>IEnumerator PatchUser(int userId, string newEmail)
{
    string url = $&quot;https://example.com/api/users/{userId}&quot;;
    EmailOnly data = new EmailOnly { email = newEmail };
    string jsonData = JsonUtility.ToJson(data);

    using (UnityWebRequest request = new UnityWebRequest(url, &quot;PATCH&quot;))
    {
        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
        request.uploadHandler = new UploadHandlerRaw(bodyRaw);
        request.downloadHandler = new DownloadHandlerBuffer();
        request.SetRequestHeader(&quot;Content-Type&quot;, &quot;application/json&quot;);

        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
            string json = request.downloadHandler.text;
            UserResponse user = JsonUtility.FromJson&lt;UserResponse&gt;(json);
            Debug.Log($&quot;[PATCH] 새 이메일: {user.email}&quot;);
        }
        else
        {
            Debug.LogError(&quot;[PATCH] 실패: &quot; + request.error);
        }
    }
}</code></pre><br>

<ul>
<li>DELETE : 사용자 삭제</li>
</ul>
<pre><code>IEnumerator DeleteUser(int userId)
{
    string url = $&quot;https://example.com/api/users/{userId}&quot;;

    using (UnityWebRequest request = UnityWebRequest.Delete(url))
    {
        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
            Debug.Log(&quot;[DELETE] 성공적으로 삭제됨&quot;);
        }
        else
        {
            Debug.LogError(&quot;[DELETE] 실패: &quot; + request.error);
        }
    }
}</code></pre><br>

<br>

<ul>
<li>사용 예시</li>
</ul>
<pre><code>void Start()
{
    StartCoroutine(GetUser(123));
    StartCoroutine(CreateUser(&quot;JKOH&quot;, &quot;test@example.com&quot;));
    StartCoroutine(UpdateUser(123, &quot;JKOH&quot;, &quot;update@example.com&quot;));
    StartCoroutine(PatchUser(123, &quot;patch@email.com&quot;));
    StartCoroutine(DeleteUser(123));
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Resolve] iOS 배포 오류 ITMS-90109]]></title>
            <link>https://velog.io/@portuga_code/iOS-%EB%B0%B0%ED%8F%AC-%EC%98%A4%EB%A5%98-ITMS-90109</link>
            <guid>https://velog.io/@portuga_code/iOS-%EB%B0%B0%ED%8F%AC-%EC%98%A4%EB%A5%98-ITMS-90109</guid>
            <pubDate>Fri, 08 Aug 2025 03:19:25 GMT</pubDate>
            <description><![CDATA[<h2 id="ios-배포-오류-itms-90109">iOS 배포 오류 ITMS-90109</h2>
<br>

<p><a href="https://developer.apple.com/library/archive/qa/qa1623/_index.html">https://developer.apple.com/library/archive/qa/qa1623/_index.html</a>  </p>
<br>

<p>앱 업데이트는 <strong>기존에 해당 앱을 사용하던 모든 디바이스</strong>에서 여전히 작동해야 합니다.<br><code>UIRequiredDeviceCapabilities</code>를 통해 지원 기기를 줄이는 것은 허용되지 않습니다.</p>
<br>

<h2 id="실제-사례-정리">실제 사례 정리</h2>
<p>Stack Overflow 사례에서는 이전에 <code>armv7</code>만 지원했던 버전에, 업데이트 버전에서 <code>metal</code>이 추가되어 <strong>지원 범위가 줄어들었다는 이유</strong>로 이 오류가 발생했습니다.<br><a href="https://gameneo.medium.com/ios-itms-90109-this-bundle-is-invalid-%EC%98%A4%EB%A5%98-580bd8bb33f9?utm_source=chatgpt.com">b4x.com+6Medium+6Stack Overflow+6</a></p>
<p>또 다른 사례에서는 <code>armv7</code>만 있던 <code>UIRequiredDeviceCapabilities</code> 항목을 <strong>아예 제거</strong>했더니 오류가 해결되었다는 보고도 있습니다.</p>
<br>

<br>

<h2 id="해결-방법-요약">해결 방법 요약</h2>
<table>
<thead>
<tr>
<th>조치 항목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>UIRequiredDeviceCapabilities</code>에 <strong>불필요한 항목 제거</strong></td>
<td>예: <code>metal</code>, <code>arkit</code>, <code>armv7</code> 등 스마트폰 기능을 직접 요구하는 항목</td>
</tr>
<tr>
<td><strong>가능하면 해당 키를 제거</strong></td>
<td>특별한 하드웨어 기능이 필수이지 않으면 완전히 삭제하세요</td>
</tr>
<tr>
<td>새로운 요구사항 추가는 피하기</td>
<td>지원되는 기기를 줄이는 변경은 App Store 정책 위반입니다</td>
</tr>
<tr>
<td>만약 필수 기능이라면</td>
<td>iOS 최소 버전 상향이나 앱을 별도 번들 ID로 재출시 방안 고려</td>
</tr>
</tbody></table>
<br>

<br>

<h4 id="수동-제거">수동 제거:</h4>
<ol>
<li><code>Xcode &gt; Info.plist</code> 열기</li>
<li>아래 항목이 있으면 제거:</li>
</ol>
<pre><code>&lt;key&gt;UIRequiredDeviceCapabilities&lt;/key&gt;
&lt;array&gt;
    &lt;string&gt;arkit&lt;/string&gt;
    &lt;string&gt;metal&lt;/string&gt;
&lt;/array&gt;</code></pre><br>]]></description>
        </item>
        <item>
            <title><![CDATA[[Resolve] iOS 빌드에서 TTS 안되는 현상]]></title>
            <link>https://velog.io/@portuga_code/iOS-%EB%B9%8C%EB%93%9C%EC%97%90%EC%84%9C-TTS-%EC%95%88%EB%90%98%EB%8A%94-%ED%98%84%EC%83%81</link>
            <guid>https://velog.io/@portuga_code/iOS-%EB%B9%8C%EB%93%9C%EC%97%90%EC%84%9C-TTS-%EC%95%88%EB%90%98%EB%8A%94-%ED%98%84%EC%83%81</guid>
            <pubDate>Fri, 08 Aug 2025 03:18:12 GMT</pubDate>
            <description><![CDATA[<p>Microsoft.CognitiveServices.Speech.csharp으로 TTS를 구현하였습니다. 안드로이드 환경에서는 TTS가 아주 잘 작동하였으나, iOS 빌드를 하니 TTS가 작동하지 않았습니다. Mac 에디터에서도 잘 작동하고, 에디터에서도 잘 작동하였지만 iOS 빌드에서만 TTS 부분이 작동하지 않았습니다.<br><br>
에러 로그는 다음과 같았습니다.<br><br></p>
<pre><code>Unable to load DLL &#39;Microsoft.CognitiveServices.Speech.core.dll&#39;. Tried the load the following dynamic libraries: Unable to load dynamic library &#39;/Microsoft.CognitiveServices.Speech.core.dll&#39; because of &#39;Failed to open the requested dynamic library</code></pre><br>

<p>그렇다 .dll 파일을 찾을 수 없다고 한다. iOS에서는 dll 파일이 iOS에서 지원되지 않는다. .a 파일 혹은 .framework 파일을 찾아야 하는데 .dll 파일을 찾고 있으니 생긴 문제였다.<br><br>
먼저 시도해본 것은</p>
<br>

<p>Plugin/Microsoft.CognitiveServices.Speech.core.a 파일을 복사해서 넣고 빌드 후 시도해보는 것이었다.<br>하지만 역시나 똑같은 오류가 발생했다.<br><br>
계속 찾아본 결과 <strong>Nuget에서 다운받은 패키지는 iOS가 불안정</strong>하다는 정보가 있었다. 그래서 Unity 공식 Speech SDK를 다운받아서 사용하라는 해결 답변이 있었고 이를 실행에 옮겼다.</p>
<p><a href="https://github.com/Azure-Samples/cognitive-services-speech-sdk/tree/master/quickstart/csharp/unity" title="https://github.com/Azure-Samples/cognitive-services-speech-sdk/tree/master/quickstart/csharp/unity">공식 Speech SDK</a></p>
<p>이를 다운 받으면 Speech SDK라는 폴더에 각 플랫폼 별 파일들이 존재합니다. 이는 그냥 나두고 기존 Nuget으로 다운 받은 Microsoft.CognitiveServices.Speech를 삭제해줬습니다.<br>하지만 여기서 Nuget Package에서 Remove 해버리면 의존성 파일들도 삭제가 됩니다. 그러면 꽤 많은 문제를 발생시켰기 때문에 직접 Speech 폴더만 삭제해줍니다. </p>
<br>

<ul>
<li>Azure.Core</li>
<li>System.ClientModel</li>
<li>Microsoft.Bcl.AsyncInterfaces</li>
<li>System.Text.Json</li>
<li>System.Memory.Data <em>(일부 Azure SDK 버전만 해당)</em></li>
<li>System.Diagnostics.DiagnosticSource</li>
</ul>
<br>

<p>해당 의존성 폴더들을 삭제하지 않았습니다.<br><br>
이렇게하고 빌드 테스트하니 TTS 기능이 잘 작동하였습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] Input System에 대하여]]></title>
            <link>https://velog.io/@portuga_code/Unity-Input-System%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@portuga_code/Unity-Input-System%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Fri, 25 Jul 2025 04:54:37 GMT</pubDate>
            <description><![CDATA[<h2 id="new-input-system">New Input System</h2>
<br>

<p><a href="https://www.youtube.com/watch?v=ONlMEZs9Rgw">Unity&#39;s NEW input system in 13 minutes</a>  </p>
<br>

<p>오늘은 Unity에 Old Input System과 New Input Sytem의 차이점을 알아보고 간략하게 New Input System을 사용하는 방법을 알아보자</p>
<br>

<table>
<thead>
<tr>
<th>항목</th>
<th>Old Input System</th>
<th>New Input System</th>
</tr>
</thead>
<tbody><tr>
<td><strong>기반 구조</strong></td>
<td><code>Input</code> 클래스 기반</td>
<td>이벤트 기반 및 C# InputAction 기반</td>
</tr>
<tr>
<td><strong>확장성</strong></td>
<td>낮음 (플랫폼, 디바이스 확장 어려움)</td>
<td>높음 (멀티 디바이스 대응, 리맵, 커스터마이징 가능)</td>
</tr>
<tr>
<td><strong>코드 스타일</strong></td>
<td><code>Input.GetKey()</code>, <code>Input.GetTouch()</code></td>
<td><code>InputAction</code>, <code>PlayerInput</code> 등 사용</td>
</tr>
<tr>
<td><strong>기기 대응</strong></td>
<td>키보드, 마우스, 터치 등 제한적</td>
<td>게임패드, 모바일, VR/AR, 커스텀 디바이스 대응</td>
</tr>
<tr>
<td><strong>입력 설정</strong></td>
<td>Unity Editor &gt; Project Settings &gt; Input</td>
<td>InputActionAsset 파일(.inputactions)로 시각적 편집</td>
</tr>
<tr>
<td><strong>복수 입력 처리</strong></td>
<td>멀티터치, 조이패드 등 복잡함</td>
<td>다중 디바이스 / 플레이어 인식 우수</td>
</tr>
<tr>
<td><strong>학습 난이도</strong></td>
<td>낮음 (빠르게 사용 가능)</td>
<td>높음 (구조 파악 필요)</td>
</tr>
<tr>
<td><strong>지원 여부</strong></td>
<td>유지보수만 진행 중 (레거시)</td>
<td>공식 권장 (Unity 2021 이후 권장)</td>
</tr>
</tbody></table>
<br>

<h2 id="old-input-system의-특징"><strong>Old Input System의 특징</strong></h2>
<br>

<pre><code>if (Input.GetMouseButtonDown(0)) {
    Debug.Log(&quot;마우스 클릭!&quot;);
}

float h = Input.GetAxis(&quot;Horizontal&quot;);</code></pre><br>

<ul>
<li><code>Input.GetKey()</code>, <code>Input.GetMouseButton()</code>, <code>Input.GetAxis()</code> 등을 통해 입력 감지</li>
<li>사용이 매우 간단하고 직관적</li>
<li><strong>단점</strong>: 키 리맵, 멀티디바이스, VR/AR 확장 등에 제약 많음</li>
</ul>
<br>

<h2 id="br"><br></h2>
<h2 id="new-input-system의-특징"><strong>New Input System의 특징</strong></h2>
<br>

<pre><code>public class PlayerController : MonoBehaviour
{
    public InputAction moveAction;
    public InputActionReference move;

    private Vector2 _moveDirection;

    private void OnEnable() =&gt; moveAction.Enable();
    private void OnDisable() =&gt; moveAction.Disable();

    void Update()
    {
        // 방법 1
        Vector2 move = moveAction.ReadValue&lt;Vector2&gt;();
        Debug.Log(move);

        // 방법 2 (미리 만들어둔 래퍼런스를 사용하는 방법)
        _moveDirection = move.action.ReadValue&lt;Vector2&gt;();
    }
}
//PlayerInput 컴포넌트 방식도 존재함</code></pre><br>

<ul>
<li>Input Actions를 SO로 시각적으로 구성<ul>
<li>.inputactions 파일에서 키맵핑, 액션, 디바이스, 조합 등을 구성</li>
</ul>
</li>
</ul>
<br>

<h2 id="✅-어떤-걸-써야-할까">✅ 어떤 걸 써야 할까?</h2>
<table>
<thead>
<tr>
<th>상황</th>
<th>추천 시스템</th>
</tr>
</thead>
<tbody><tr>
<td>빠른 프로토타입, 간단한 입력만 필요</td>
<td><strong>Old Input System</strong> (빠름, 쉬움)</td>
</tr>
<tr>
<td>멀티 플랫폼, 키 설정 지원, 게임패드/VR 지원 필요</td>
<td><strong>New Input System</strong> (확장성, 유지보수 용이)</td>
</tr>
<tr>
<td>Unity 2022 이상, 협업 프로젝트</td>
<td><strong>New Input System</strong> 권장</td>
</tr>
</tbody></table>
<br>

<br>]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] CocoaPods (Unity iOS 빌드 관련)]]></title>
            <link>https://velog.io/@portuga_code/CocoaPods-Unity-iOS-%EB%B9%8C%EB%93%9C-%EA%B4%80%EB%A0%A8</link>
            <guid>https://velog.io/@portuga_code/CocoaPods-Unity-iOS-%EB%B9%8C%EB%93%9C-%EA%B4%80%EB%A0%A8</guid>
            <pubDate>Fri, 25 Jul 2025 04:43:08 GMT</pubDate>
            <description><![CDATA[<h2 id="cocoapods-unity-ios-관련">CocoaPods (Unity IOS 관련)</h2>
<br>

<h2 id=""></h2>
<blockquote>
<p> 🍎 CocoaPods란?</p>
</blockquote>
<ul>
<li>iOS와 macOS 앱에서 외부 라이브러리를 쉽게 관리하고 설치할 수 있도록 도와주는 <strong>“의존성 관리 도구”</strong></li>
</ul>
<br>

<p>마치 Unity에서 <code>Package Manager</code>로 <code>TextMeshPro</code>나 <code>Addressables</code> 같은 걸 설치하는 것처럼,<br>iOS에서는 <code>CocoaPods</code>를 사용해서 <code>Firebase</code>, <code>Kakao SDK</code>, <code>GoogleSignIn</code>, <code>Alamofire</code> 등 수많은 외부 라이브러리를 쉽게 가져다 쓸 수 있다.</p>
<br>

<blockquote>
<h2 id="🔧-cocoapods가-왜-필요한가"><strong>🔧 CocoaPods가 왜 필요한가?</strong></h2>
</blockquote>
<br>

<ol>
<li><strong>라이브러리 설치 자동화</strong><ul>
<li>예전엔 라이브러리 .framework, .a, .h 파일들을 수동으로 복사하고, Build Setting도 직접 설정해야 했다.</li>
<li>CocoaPods는 한 줄 명령어 (pod install)로 이 모든 걸 자동으로 해준다.</li>
</ul>
</li>
</ol>
<br>

<hr>
<br>

<ol start="2">
<li><strong>버전 관리 용이</strong><ul>
<li>PodFile에 설치하고 싶은 라이브러리와 버전을 명시하면, 매번 동일한 환경을 누구나 재현할 수 있다. (협업, 배포 시 매우 중요)</li>
</ul>
</li>
</ol>
<br>

<pre><code>pod &#39;Firebase/Auth&#39;, &#39;10.3.0&#39;</code></pre><br>

<hr>
<br>

<ol start="3">
<li><strong>의존성 트리 자동 해결</strong><ul>
<li>예: Firebase를 쓰면 내부적으로 여러 모듈(FirebaseCore, FirebaseInstallations 등)이 필요함.</li>
<li>CocoaPods가 이걸 알아서 찾아서 설치한다.</li>
</ul>
</li>
</ol>
<br>

<hr>
<br>

<ol start="4">
<li><strong>통합된 빌드 환경 제공</strong><ul>
<li><strong>pod install</strong> 후 생성되는 <strong>.xcworkspace</strong> 파일을 열면, Xcode 프로젝트와 외부 라이브러리가 하나의 워크스페이스에서 통합되어 빌드됨.</li>
<li>수동 링크 없이도 안정적으로 작동</li>
</ul>
</li>
</ol>
<br>

<hr>
<br>

<ol start="5">
<li><strong>커뮤니티 및 생태계 기반</strong><ul>
<li>iOS 오픈 소스 라이브러리의 대부분이 CocoaPods를 통해 배포됨.</li>
<li>예: Kakao SDK도 <code>pod &#39;KakaoSDK&#39;</code>, Firebase도 <code>pod &#39;Firebase/Core&#39;</code></li>
</ul>
</li>
</ol>
<br>

<blockquote>
<h2 id="📦-cocoapods-작동-방식-간단-요약"> <strong>📦 CocoaPods 작동 방식 간단 요약</strong></h2>
</blockquote>
<br>

<ul>
<li><code>pod init</code>: <strong>Podfile이 없을 때</strong> 사용하는 명령어 (초기 설정용)</li>
<li><code>pod install</code>: <strong>Podfile이 이미 존재할 때</strong> 실행하여 라이브러리를 설치함</li>
</ul>
<br>

<ol>
<li><code>Podfile</code> 파일 생성: 내가 사용할 라이브러리를 선언.</li>
<li><code>pod install</code>: 선언한 라이브러리를 다운로드 + 설치.</li>
<li><code>.xcworkspace</code> 생성: Xcode 프로젝트 + Pods 통합된 워크스페이스.</li>
<li><code>.xcworkspace</code> 열기: 실제 앱 실행은 이 파일 기준으로 이뤄져야 함.</li>
</ol>
<br>

<pre><code>cd MyUnityIOSProject/
pod init             # → Podfile 생성 (빈 상태)
vim Podfile          # → 원하는 SDK 추가 (예: pod &#39;Firebase/Auth&#39;)
pod install          # → SDK 실제 설치 및 .xcworkspace 생성
open MyProject.xcworkspace</code></pre><h2 id="br"><br></h2>
<h3 id="1️⃣-pod-init-실행-전">1️⃣ <code>pod init</code> 실행 전</h3>
<pre><code>bash
복사편집
$ ls
Unity-iPhone.xcodeproj</code></pre><h3 id="2️⃣-pod-init-실행-후">2️⃣ <code>pod init</code> 실행 후</h3>
<pre><code>bash
복사편집
$ ls
Unity-iPhone.xcodeproj
Podfile         👈 생성됨</code></pre><h3 id="3️⃣-podfile-내용-추가-후">3️⃣ <code>Podfile</code> 내용 추가 후</h3>
<pre><code>ruby
복사편집
target &#39;Unity-iPhone&#39; do
  pod &#39;Firebase/Auth&#39;
end</code></pre><h3 id="4️⃣-pod-install-실행-후">4️⃣ <code>pod install</code> 실행 후</h3>
<pre><code>bash
복사편집
$ ls
Unity-iPhone.xcodeproj
Podfile
Podfile.lock
Pods/                      👈 설치된 라이브러리
Unity-iPhone.xcworkspace   👈 이걸로 Xcode 열어야 함</code></pre><ul>
<li>Unity의 <code>Packages/manifest.json</code> → iOS의 <code>Podfile</code></li>
<li>Unity Package Manager가 패키지 설치 → CocoaPods의 <code>pod install</code></li>
<li>Unity는 패키지 설치 후 에디터에 반영됨 → CocoaPods는 <code>.xcworkspace</code> 생성으로 반영됨</li>
<li><br>

</li>
</ul>
<h2 id="✅-언제-반드시-써야-하나">✅ 언제 반드시 써야 하나?</h2>
<ul>
<li>Firebase, Kakao, GoogleSignIn 등 네이티브 SDK를 연동할 때</li>
<li>Unity + iOS 네이티브 기능을 같이 써야 할 때 (예: 푸시알림, SNS 로그인 등)</li>
</ul>
<hr>
<h2 id="🚫-안-써도-되는-경우">🚫 안 써도 되는 경우?</h2>
<ul>
<li>오직 Unity만으로 구현하고, iOS Native SDK와의 연동이 필요 없는 경우</li>
<li>별도 외부 프레임워크가 필요 없는 최소 iOS 빌드일 경우</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] Json 사용 방법]]></title>
            <link>https://velog.io/@portuga_code/Json-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@portuga_code/Json-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 26 May 2025 05:37:58 GMT</pubDate>
            <description><![CDATA[<h2 id="today-i-learned">Today I Learned</h2>
<h3 id="json">Json</h3>
<br>

<blockquote>
<p>설치 방법</p>
</blockquote>
<p>패키지 매니저의 Add Name을 통한 설치 <br></p>
<p>com.unity.nuget.newtonsoft-json</p>
 <br>

<blockquote>
<p>Json?</p>
</blockquote>
<p>웹이나 네트워크에서 서버와 클라이언트 사이에  데이터를 주고받을 때 사용하는 개방형 표준 포맷, 쉽게 말하면 서버와 클라이언트가 어떻게 데이터를 주고받을지에 대한 약속같은 것입니다.</p>
<p>XML에 비해 가독성 및 데이터 파싱이 간편합니다.</p>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/c5778970-57f4-4ca0-9050-75fe49040c21/image.png" alt=""></p>
<p>중요한 점은 Json을 작성 시 Key - Value 형식으로 작성하고 문법 및 오타를 신경써야하며, JsonData와 ClassData의 형식이 정확히 일치해야합니다.</p>
<br>

<blockquote>
<p>직렬화와 역직렬화</p>
</blockquote>
<p>직렬화 : 바이트 배열로 변환 (Json 데이터로 변환)
역직렬화 : 직렬화된 데이터를 객체의 상태로 변환</p>
<pre><code class="language-csharp">//데이터 직렬화
string jsonData = JsonConvert.SerializeObject(testClass);
Debug.Log(jsonData);

//데이터 역직렬화
JsonTestClass jsonData2 = JsonConvert.DeserializeObject&lt;JsonTestClass&gt;(jsonData);
jsonData2.PrintJsonTestClass();</code></pre>
<br>

<blockquote>
<p><strong>Save &amp; Load</strong></p>
</blockquote>
<pre><code class="language-csharp">using Newtonsoft.Json;
using System;
using System.IO; //파일 입출력
using System.Text; //문자열 처리
     
        //Json으로 Save
        private void JsonSave&lt;T&gt;(T data)
    {
        FileStream stream = new FileStream(Application.dataPath + &quot;/test.json&quot;, FileMode.OpenOrCreate);

        string jsonData = JsonConvert.SerializeObject(data);
        byte[] bt = Encoding.UTF8.GetBytes(jsonData);
        stream.Write(bt, 0, bt.Length);
        stream.Close();
    }

        //Json을 데이터로 Load
    private T JsonLoad&lt;T&gt;(string path)
    {
        FileStream stream = new FileStream(Application.dataPath + &quot;/&quot; + path, FileMode.Open);
        byte[] data = new byte[stream.Length];
        stream.Read(data, 0, data.Length);
        stream.Close();

        string jsonData = Encoding.UTF8.GetString(data);
                T testclass = JsonConvert.DeserializeObject&lt;T&gt;(jsonData);

        return testclass;
         }</code></pre>
<p>추가 지식 : using 문을 사용하면 Dispose or Close를 안해줘도 해당 구문을 빠져나올 때 Dispose or Close를 해줘 메모리 해제 작업을 진행할 수 있다.</p>
<pre><code class="language-csharp">private T JsonLoad&lt;T&gt;(string path)
{
    T testclass;

    using (FileStream stream = new FileStream(Application.dataPath + &quot;/&quot; + path, FileMode.Open))
    {
        try
        {
            byte[] data = new byte[stream.Length];
            stream.Read(data, 0, data.Length);
            string jsonData = Encoding.UTF8.GetString(data);
            testclass = JsonConvert.DeserializeObject&lt;T&gt;(jsonData);
        }
                //혹시나 모를 오류에 대비한 예외처리
        finally
        { 
            stream?.Close();
        }
    }

    return testclass;
}</code></pre>
<br>

<blockquote>
<h3 id="json-utillity"><strong>Json Utillity</strong></h3>
</blockquote>
<h3 id="1-jsondata-클래스-만들기">1. JsonData 클래스 만들기</h3>
<p>[Serializable]를 입력하여 직렬화를 해주고 <a href="https://GetType().Name" title="https://GetType().Name">GetType().Name</a> 메서드를 통해 Class 이름을 가져오는 생성자를 만들어줍니다.</p>
<pre><code>[Serializable]
public class JsonData
{
    public string typeName;

    public JsonData()
    {
        typeName = GetType().Name;
    }
}</code></pre><hr>
<p>JsonData 클래스를 상속받아 저장할 클래스를 만듭니다.</p>
<p>생성자 옆에 base()를 붙여주어 부모클래스의 생성자가 실행되도록 합니다.</p>
<pre><code>[Serializable]
public class PlayerData : JsonData
{
    public Vector3 pos;
    public int level;

    public PlayerData() : base()
    {

    }
}</code></pre><br>

<h3 id="2-json-data-저장하기-write">2. Json Data 저장하기 (Write)</h3>
<p>JsonUtility.ToJson() 함수를 이용하여 데이터를 Json으로 바꿔줍니다.</p>
<p>file.Directory.Create()는 폴더가 없을 경우 폴더를 만들어줍니다.</p>
<br>

<pre><code>private void SaveJsonData(JsonData data)
{
    string jsonData = JsonUtility.ToJson(data);
    string path = GetJsonSavePath(data.typeName);
    var file = new System.IO.FileInfo(path);
    file.Directory.Create();
    System.IO.File.WriteAllText(file.FullName, jsonData);
}</code></pre><hr>
<pre><code>private string GetJsonSavePath(string typeName)
{
    return string.Format(&quot;Assets/JsonResource/{0}.json&quot;, typeName);
}

//Application.dataPath를 이용해서 리턴값을 수정해주면 똑같은곳에 저장됩니다.
//return Application.dataPath + string.Format(&quot;/JsonResource/{0}.json&quot;, typeName);</code></pre><hr>
<pre><code>private void SavePlayerData()
{
    var playeData = new PlayerData();
    playeData.pos = new Vector3(1, 2, 3);
    playeData.level = 2;

    SaveJsonData(playeData);
}</code></pre><br>

<h3 id="3-json-data-불러오기-read">3. Json Data 불러오기 (Read)</h3>
<p>default(T) 는 참조 형식일때 null을 반환합니다.</p>
<pre><code>private T LoadJsonData&lt;T&gt;(string typeName)
{
    string path = GetJsonSavePath(typeName);
    var file = new System.IO.FileInfo(path);

    if (!file.Exists)
    {
        Debug.LogError(&quot;파일 없음: &quot; + path);
        return default(T);
    }

    string jsonData = System.IO.File.ReadAllText(file.FullName);

    return JsonUtility.FromJson&lt;T&gt;(jsonData);
}</code></pre><hr>
<p>위 함수를 활용해 데이터를 불러옵니다.</p>
<pre><code>private void LoadPlayerData()
{
    PlayerData playerData = new PlayerData();
    playerData = LoadJsonData&lt;PlayerData&gt;(playerData.typeName);

    if(playerData == null) 
    {
        return; 
    }

    Debug.Log(&quot;player pos: &quot; + playerData.pos);
    Debug.Log(&quot;player level: &quot; + playerData.level);
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Resolve] 2D 텍스처가 깨질 때 해결 방법]]></title>
            <link>https://velog.io/@portuga_code/Unity-2D-%ED%85%8D%EC%8A%A4%EC%B2%98%EA%B0%80-%EA%B9%A8%EC%A7%88-%EB%95%8C-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@portuga_code/Unity-2D-%ED%85%8D%EC%8A%A4%EC%B2%98%EA%B0%80-%EA%B9%A8%EC%A7%88-%EB%95%8C-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 26 May 2025 04:34:43 GMT</pubDate>
            <description><![CDATA[<p>2D 이미지를 가지고 왔는데 이미지 상태가 깨지거나 좋지 못한 경우가 있다.</p>
<p>대부분의 원인은 압축을 하고 가져와서 그렇다.</p>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/44ee5b76-fb00-49e8-918f-c4c13f78a9fa/image.png" alt=""></p>
<p>Fillter Mode : No fillter
Compression : None</p>
<p>으로 설정하면 텍스처를 원본 상태로 가지고 올 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] 가비지 컬렉터란?]]></title>
            <link>https://velog.io/@portuga_code/%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%ED%84%B0%EB%9E%80</link>
            <guid>https://velog.io/@portuga_code/%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%ED%84%B0%EB%9E%80</guid>
            <pubDate>Fri, 08 Nov 2024 08:17:05 GMT</pubDate>
            <description><![CDATA[<p>참고자료</p>
<p>1) <a href="https://velog.io/@dohui/C-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BD%9C%EB%A0%89%ED%84%B0">C# - 가비지 컬렉션 (Garbage Collection, GC)</a></p>
<h1 id="garbage-collection이란"><strong>Garbage Collection이란?</strong></h1>
<p>스택 메모리는 함수가 실행되는 순간부터 종료까지 얼만큼 공간을 사용하는지 계속 추적된다. 스택 영역은 알아서 줄어들고 늘어나기 때문에 따로 신경 쓸 필요가 없다.</p>
<p>하지만 힙 영역의 경우 메모리를 할당하고 어떤 행동도 하지 않으면 계속 메모리에 올라온 상태로 유지가 된다. C와 C++의 경우 프로그래머가 직접 메모리를 코드 상에서 관리해야 하지만 <strong>C#은 CLR이 자동적으로 메모리를 관리</strong>해준다. <strong>Garbage Collection은 이러한 자동 메모리 관리의 중심이 되는 기능</strong>이다. 그리고 이 <strong>Garbage Collection을 담당하는 역할을 하고 있는 것을 Garbage Collector</strong>라고 한다.</p>
<br>


<h3 id="garbage란-무엇일까"><strong>Garbage란 무엇일까?</strong></h3>
<p>Reference Type 변수들을 살펴보면 <strong>스택 메모리에 주소 값을 담아두고 힙 메모리에 접근하는 방식으로 저장</strong>되어 있다. 여기서 함수가 종료되고 지역 변수들이 제거되면서 스택 메모리에서 힙 메모리를 가리키고 있는 일부 주소값들이 같이 Pop된다. 그러면 해당 주소값에 있는 힙 메모리의 데이터에는 더 이상 접근할 수 없는 상태가 된다. 이렇게 <strong>참조할 수 없게 된 객체들을 Garbage</strong>라고 부른다.</p>
<p>마이크로소프트 공식 문서에서 박싱과 언박싱을 설명할 때 사용하는 그림을 살펴보자.</p>
<pre><code class="language-csharp">void static Main()
{
    int i = 123;      // a value type
    object o = i;     // boxing
    int j = (int)o;   // unboxing
}</code></pre>
<p>여기서 Main 함수가 끝나게 되면 o는 Stack 메모리에서 Pop된다. 그럼 이것이 가리키는 Boxing된 i는 힙 메모리에서 Garbage로 남아있게 되는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/ebbb5c2f-b011-4325-86b8-875a56322a93/image.png" alt=""></p>
<h3 id="우리는-왜-garbage-collection을-알아야-하는가"><strong>우리는 왜 Garbage Collection을 알아야 하는가?</strong></h3>
<p>Garbage Collector 역시 리소스를 사용하는 소프트웨어이다. 즉, 프로그램에서 사용하는 자원을 함께 사용하고 있다는 점을 명심해야 한다. 유한한 자원 안에서 Garbage Collector가 사용하고 있는 부분을 최소화 할 수 있다면 우리 프로그램의 성능이 더 늘 수 있다. 이것이 바로 Garbage Collector가 어떻게 동작하는 지 이해해야 하는 이유이며 이를 바탕으로어떻게 코드를 작성해나갈지 고민해봐야 한다.</p>
<br>


<h1 id="managed-heap-관리되는-힙"><strong>Managed Heap (관리되는 힙)</strong></h1>
<p>힙 메모리는 프로세스 별로 고유하게 가진다. 새 프로세스가 생성될 때 인접한 공간의 메모리를 할당받게 되는데 이를 Managed Heap이라 부른다.</p>
  <br>

<ul>
<li>동작 방식</li>
</ul>
<p>Managed Heap은 포인터로 힙 메모리에 할당된 object의 위치를 가리킨다. 런타임에서 개체를 메모리에 할당할 때 스택과 유사하게 포인터에 값을 더하는 식으로 진행되기 때문에 메모리 할당 측면에서 스택만큼 효율적이다.</p>
<p>이 Managed Heap의 경우 효율성을 위해 <strong>공간을 3개로 나눈다.</strong></p>
<h4 id="왜-공간을-3개로-나눌까">왜 공간을 3개로 나눌까?</h4>
<p>Grabage Collection이 필요한 순간 실질적으로 오래 사용되는 개체가 있고 잠깐 사용되는 개체가 있다. 오래 사용되는 개체의 경우 메모리가 찼을 때 굳이 계속 봐야 하는가에 대해 고민해봐야 한다. 이를 고려하지 않고 전체를 도는 것은 상당히 부담되는 일이다.  그래서 <strong>개체가 얼마나 오래 살아남았느냐를 기준으로 해당 개체를 보는 빈도수를 구분하기 위해 ‘세대’라는 개념을 도입</strong>한다.</p>
 <br> 

<h3 id="garbage-collection-generation-세대"><strong>Garbage Collection Generation (세대</strong>)</h3>
<p>Generation (세대)는 몇 번의 Garbage Collection을 거쳤는지 나타낸다. <strong>Garbage Collection이 일어나고 Stack 메모리에 아직 참조되고 있는 Object들에 대해서는 세대 수를 증가</strong>시킨다. [<strong>여러 번 거쳤을 때 남아있다면 프로그램에서 계속해서 사용되고 있는 것이라고 볼 수 있기 때문에 이 Object들은 매번 Garbage Collector가 확인할 필요가 없다는 것을 의미]</strong>한다.</p>
<p>구체적으로 어떻게 처리되는지 살펴보자. 먼저 새로 힙 메모리에 할당되는 Object의 경우 0세대에 저장한다. 이후 0세대가 꽉차게 되면 Garbage Collection이 일어나고 사용이 되고 있는 Object들은 1세대에 저장된다. 동일한 과정으로 1세대에 대해 Garbage Collection이 일어날 때 살아남은 Object들은 2세대에 저장된다. 2세대로 옮겨진 Object들은 더 이상 다른 곳으로 옮겨가지 않는다. 만약 2세대도 꽉차서 Garbage Collection이 수행된다면 Garbage Collector는 모든 세대에 대해 Garbage Collection을 진행한다. 이것을 Full GC라고 부르는데 이때 일정 시간 프로그램을 멈추고 Garbage Collection을 하기 때문에 Garbage를 잘 관리할 필요가 있다.</p>
  <br>

<h4 id="garbage-collection-과정"><strong>Garbage Collection 과정</strong></h4>
<ul>
<li>Garbage Collection은 어떤 과정으로 진행될까?</li>
</ul>
<p>크게 3가지 과정을 거치는데 간단히 정리하면 다음과 같다.</p>
<ol>
<li>사용되고 있는 개체들을 연결하는 작업 (Mark) </li>
<li>사용되지 않는 개체들을 식별하는 작업 (Relocate)</li>
<li>필요 없는 객체들을 지우고 살아있는 객체들을 모으는 작업 (Compact)</li>
</ol>
  <br>

<blockquote>
<h5 id="1-marking-단계-표시-단계"><strong>1) Marking 단계 (표시 단계)</strong></h5>
</blockquote>
<p>Garbage Collection 주기가 시작될 때 Garbage Collector는 일단 모든 Object를 Garbage로 가정한다. 즉, 루트 목록 내 어떤 루트도 메모리를 가리키고 있지 않다고 가정한다. 이 상태에서 Garbage Collecor는 루트 목록을 돌면서 각 루트가 참조하는 것들을 마킹한다. 좀 더 면밀히 말하면 마킹이란 루트 목록에서 시작되는 연결된 그래프를 만드는 것이다.</p>
<h4 id="루트-목록이란">루트 목록이란?</h4>
<p>루트 목록에는 스택, 가비지 수집 핸들, 정적 데이터 등으로 구성된다.</p>
<p>이걸 General하게 표현한 그림이다. Stack 메모리에서 참조하고 있는 Heap Object들과 그 Object들이 참조하고 있는 다른 Object들 등 루트 목록에서 도달할 수 있는 Object들에 대해 그래프를 만든다.</p>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/de115276-9f2b-45a7-ba38-e4712b86d1e1/image.png" alt=""></p>
<br>


<blockquote>
<h5 id="2-relocation-단계-재배치-단계"><strong>2) Relocation 단계 (재배치 단계)</strong></h5>
</blockquote>
<p>루트 목록에서 도달할 수 없는 Object들을 Garbage로 간주한다. Garbage가 차지한 공간은 비어있는 공간으로 간주하게 된다.</p>
  <br>

<blockquote>
<h5 id="3-compaction-단계-압축-단계"><strong>3) Compaction 단계 (압축 단계)</strong></h5>
</blockquote>
 <br> 

<p>루트 목록에 대한 조사가 끝나면 Garbage Collector는 Heap을 순회하며 Garbage가 차지했던 비어있는 공간에 인접한 도달할 수 있는 Object들을 메모리에 복사를 통해 덮어 씌운다. 도달 가능한 모든 Object에 대한 이동이 끝나면 Garbage Collector는 포인터의 위치 또한 적절하게 수정한다.(Application root, Managed Heap Pointer)</p>
<p>최종적으로 다음과 같이 깨끗한 상태의 메모리를 얻게 되고 이것이 Garbage Collection의 한 주기에 대한 과정이다.</p>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/8caac933-39bb-497b-92fd-88ec686a1ddd/image.png" alt=""></p>
<ul>
<li>메모리 단편화</li>
</ul>
<p>메모리 할당과 해제가 반복적으로 일어나면 Haep에 듬성듬성 메모리가 배치되어 메모리 단편화가 발생하고 이는 할당 속도를 느리게 하는 원인이 된다. Garbage Collection의 Compacting 과정에서 비어 있는 공간에 Object를 당겨와 차곡차곡 쌓기에 메모리 공간 낭비 없이 사용할 수 있게되므로 메모리 단편화를 해결해준다.</p>
<br>


<h1 id="the-largest-object-heap-대형-개체-힙"><strong>The Largest Object Heap (대형 개체 힙)</strong></h1>
<p>여태까지 살펴본 Managed Heap은 소형 개체 힙을 가리킨다. Garbage Collection은 큰 개체에 대한 할당을 담당하는 Largest Object Heap (대형 개체 힙)을 따로 가지고 있다. 여기서 큰 개체의 경우 85KB 이상의 개체를 가리킨다.</p>
<p>왜 대형 개체 힙을 가지는 것이 맞는건지는 앞서 어떤 과정으로 Garbage Collection이 이루어졌는지를 이해하면 알 수 있다. 대형 개체의 경우 잡아먹는 공간 자체가 크기 때문에 더 빨리 메모리를 가득 채우게 될 것이고 이는 Garbage Collection을 훨씬 빈번하게 일으키게 된다. 따라서 <a href="https://.Net" title="https://.Net">.Net</a>에서는 이러한 대형 개체들을 대형 개체 힙에 관리한다. </p>
<ul>
<li>대형 개체 힙의 동작 방식</li>
</ul>
<p>대형 개체 힙은 동작 방식이 소형 개체 힙과는 다르다. 대형 개체 힙은 포인터로 메모리들을 복사하는 방식처럼 동작하기에는 개체 복사 자체에서 발생하는 비용이 너무 크다. 따라서 대형 개체 힙은 object의 크기를 계산한 뒤 힙에 여유 공간이 있는 지 탐색하여 할당하게 된다.</p>
<p>Garbage Collection이 일어난 후 대형 개체 힙에서는 해제된 공간을 그대로 둔다. 메모리 복사를 통해 Object를 압축하는 과정의 비용이 크기 때문이다. 그래서 위에서 설명한 메모리 단편화 문제가 발생하게 된다. 또한 CLR에서는 대형 개체 힙을 2세대 힙으로 간주하기 때문에 대형 개체 힙의 Garbage Collection을 하려면 Full GC가 일어나야 한다.</p>
<p><em>ps. 그래서 큰 Object 할당은 늘 신중히 또 신중히 한다.</em></p>
 <br> 

<h1 id="garbage-collection을-줄이기-위한-노력"><strong>Garbage Collection을 줄이기 위한 노력</strong></h1>
<ul>
<li>객체를 너무 많이 할당하지 않는다.<ul>
<li>너무 많은 Object를 생성하면 그만큼 메모리가 금방 차고 Garbage Collection이 빈번히 발생하게 될 것이다. 따라서 필요 이상으로 많이 만들지 않는다.</li>
</ul>
</li>
</ul>
<ul>
<li>너무 큰 객체를 할당하지 않는다.<ul>
<li>대형 개체 힙에 대해 이해했으면 자연스레 깨닫게 되는 내용이다.</li>
</ul>
</li>
</ul>
<ul>
<li>복잡한 참조 관계 만들지 않는다.<ul>
<li>Garbage Collector는 Garbage Collection 후 도달할 수 있는 Object의 세대를 옮기기 위해 메모리 복사를 수행한다. 이때 참조 관계가 복잡하다면 단순히 메모리 복사가 아니라 참조하고 있는 모든 메모리 주소를 수정하는 과정을 거쳐야 한다. 또한 2세대 메모리에 있는 개체가 0세대 메모리를 참조하게 되는 경우를 보자. root에서 해당 0세대 메모리 object를 참조하고 있지 않음에도 2세대 메모리가 참조를 하고 있기 때문에 Garbage Collection 과정에서 CLR은 쓰기 장벽(Write Barrier)을 통해 root에서 도달할 수 있는 것처럼 동작하게 해준다. 이 쓰기 장벽도 비용이 큰 편이라 참조 관계는 복작하지 않게 만드는게 Garbage Collection 비용을 많이 줄여준다.</li>
</ul>
</li>
</ul>
<ul>
<li>루트를 많이 만들지 않는다.<ul>
<li>루트 목록을 기반으로 Garbage를 찾아내기 때문에 많지 않다면 그만큼 루트 목록을 순회하는 시간을 줄일 수 있다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Coding] 객체 지향의 4대 특징 및 SOLID]]></title>
            <link>https://velog.io/@portuga_code/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5%EC%9D%98-4%EB%8C%80-%ED%8A%B9%EC%A7%95-%EB%B0%8F-SOLID</link>
            <guid>https://velog.io/@portuga_code/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5%EC%9D%98-4%EB%8C%80-%ED%8A%B9%EC%A7%95-%EB%B0%8F-SOLID</guid>
            <pubDate>Fri, 13 Sep 2024 02:29:34 GMT</pubDate>
            <description><![CDATA[<h3 id="객체지향-4대-특징"><strong>객체지향 4대 특징</strong></h3>
<p>객체 지향 (OOP, Object-Oriented Programming)은 프로그래밍 패러다임 중 하나로, 현실 세계의 사물이나 개념을 객체(object)라는 단위로 나누어서 이들 간의 상호작용을 프로그래밍하는 방법입니다. 대표적인 장점으로는 마치 고장 난 기계의 부품만 교체하면 되는 것처럼 프로그램을 보다 유연하고 변경이 용이하게 만들 수 있다는 점입니다. 객체 지향 프로그래밍의 4대 특징은</p>
<br>

<p>1. <strong>추상화</strong> : 객체의 공통적인 속성과 기능을 추출하여 정의하는 것입니다. (추상 클래스, 인터페이스 등)</p>
 <br> 

<p>2. <strong>상속</strong> : 기존의 클래스를 재활용하여 새로운 클래스를 작성하는 것, 상속된 하위 클래스는 상위 클래스의 속성과 기능들을 간편하게 사용 가능합니다.</p>
  <br>

<p>3. <strong>다형성</strong> : 어떤 객체의 속성이나 기능이 상황에 따라 여러가지 형태를 가질 수 있습니다. 방식으로는 오버라이딩과 오버로딩이 있습니다. 나는 집에서 아들 학원에서는 학생</p>
  <br>

<p>4. <strong>캡슐화</strong> : 클래스 안에 서로 연관 있는 속성과 기능들 및 데이터를 외부로부터 보호하는 것을 말합니다. 접근제어자 Private, public, Protected로 접근 범위를 지정합니다. (프로퍼티도 지정 가능)</p>
 <br> 

<blockquote>
<h3 id="객체지향의-5원칙-solid"><strong>[객체지향의 5원칙 (SOLID)]</strong></h3>
</blockquote>
<pre><code>SRP(Single Responsibility Principle): 단일 책임 원칙
: 클래스(객체)는 단 하나의 책임만 가져야 한다는 원칙
: 하나의 클래스는 하나의 기능을 담당하여 하나의 책임을 수행한다.
: 프로그램의 유지보수 성을 높이기 위한 설계 기법
.
OCP(Open Closed Priciple): 개방 폐쇄 원칙
: 확장에 열려있어야 하며, 수정에는 닫혀있어야 한다
: 추상화 사용을 통한 관계 구축을 권장하는 의미
.
LSP(Listov Substitution Priciple): 리스코프 치환 원칙
: 서브 타입은 언제나 부모 타입으로 교체 가능해야 한다.
: 다형성의 원리를 이용하기 위한 원칙
.
ISP(Interface Segregation Principle): 인터페이스 분리 원칙
: 인터페이스를 각각 사용에 맞게 잘게 분리해야한다는 설계 원칙
: 인터페이스의 단일 책임을 강조
.
DIP(Dependency Inversion Principle): 의존 역전 원칙
: 어떤 클래스를 참조해야할 시 그 Class를 참조하는 것이 아닌 그 대상의 상위 요소로 참조하라는 원칙 (모노비헤이비어)
: 쉽게 말해 추상 클래스 및 인터페이스에 의존하라는 것
: 자주 변화하는 것이 아닌 변화가 거의 없는 것에 의존하라는 것</code></pre><p>출처: <a href="https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EC%9D%98-5%EA%B0%80%EC%A7%80-%EC%9B%90%EC%B9%99-SOLID">💠-객체-지향-설계의-5가지-원칙-SOLID</a> [Inpa Dev 👨‍💻:티스토리]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Coding] Command Pattern : 메서드를 객체화하다.]]></title>
            <link>https://velog.io/@portuga_code/Command-Pattern-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B0%9D%EC%B2%B4%ED%99%94%ED%95%98%EB%8B%A4</link>
            <guid>https://velog.io/@portuga_code/Command-Pattern-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B0%9D%EC%B2%B4%ED%99%94%ED%95%98%EB%8B%A4</guid>
            <pubDate>Fri, 13 Sep 2024 02:27:28 GMT</pubDate>
            <description><![CDATA[<h2 id="커맨드-패턴에-대하여">커맨드 패턴에 대하여</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=wAmnVmFeDEM">참고</a></li>
</ul>
<blockquote>
<p>Command Pattern
<img src="https://velog.velcdn.com/images/portuga_code/post/861b1130-3926-4100-b606-8cb0612d2ea2/image.png" alt=""></p>
</blockquote>
<ul>
<li>커맨드 패턴</li>
</ul>
<p><b>요청을 객체의 형태로 캡슐화</b>하여 사용자가 보낸 요청을 나중에 이용할 수 있도록 메서드 이름, 매개변수 등 요청에 필요한 정보를 저장 또는 로깅, 취소할 수 있게하는 패턴</p>
<blockquote>
<p>Client</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/4ba3d1b7-b8c3-4350-be79-c02ab2f2e32d/image.png" alt=""></p>
<p>Client는 주문하는 역할</p>
<blockquote>
<p>ICommand</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/118ec28e-1f01-4aac-9d83-ca855ea6c4c2/image.png" alt=""></p>
<p>Command는 넓은 의미의 커맨드로 Excute() 함수만을 넣어 오버라이딩하게 한다.</p>
<blockquote>
<p>Concrete Commands (구체적인)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/6e1cd373-f68a-4208-a6b6-5b347af6fa87/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/82d655b6-4476-4d8f-8ec7-6a267352300c/image.png" alt=""></p>
<p>구체적으로 객체화할 명령이다.
이러한 명령들은 대리자를 통해 관리된다.</p>
<blockquote>
<p>대리자 (Agent)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/44ce62d7-b8f7-487c-a10e-283a125ce81e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/b50910a9-dbe7-4f23-a0c4-9bd3f3adba13/image.png" alt=""></p>
<p>대리자는 객체화한 명령을 실행시킬 객체다.</p>
<blockquote>
<p>Command</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/cbf55e10-7cf6-4f97-a1d8-aa2bb8c6420d/image.png" alt=""></p>
<p>커맨드 패턴의 핵심인 Command Manager</p>
<p>해당 스크립트에서는 객체화된 요청을 자료구조에 담아서 관리하는 역할을 한다.</p>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/ef722f3c-034c-445f-8863-a231cc1ad428/image.png" alt=""></p>
<p>등록하는 예시는 위 이미지와 같다.</p>
<blockquote>
<p>정리</p>
</blockquote>
<ul>
<li><p>커맨드 패턴은 메서드를 객체화 시킨다는 것이 핵심으로 객체화된 메서드는 담아두고 유연하게 관리할 수 있다는 장점이 있다.</p>
</li>
<li><p>담아두기 때문에 뒤로 돌리거나 취소 또한 할 수 있으며 특정 오브젝트에게 명령을 하는 등 여러 오브젝트를 관리할 때 용이하다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Coding] '전략(Strategy) 패턴'과 '상태(State) 패턴'의 차이]]></title>
            <link>https://velog.io/@portuga_code/%EC%A0%84%EB%9E%B5Strategy-%ED%8C%A8%ED%84%B4%EA%B3%BC-%EC%83%81%ED%83%9CState-%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@portuga_code/%EC%A0%84%EB%9E%B5Strategy-%ED%8C%A8%ED%84%B4%EA%B3%BC-%EC%83%81%ED%83%9CState-%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Wed, 11 Sep 2024 01:31:39 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>구분</p>
</blockquote>
<p>전략(Strategy) 패턴 : 한 번 인스턴스를 생성하고 나면, 상태가 거의 바뀌지 않는 경우에 사용한다.</p>
<p>상태(State) 패턴 : 한 번 인스턴스를 생성하고 난 뒤, 상태를 바꾸는 경우가 빈번한 경우에 사용한다.</p>
 <br> 

<blockquote>
<p>공통점</p>
</blockquote>
<p>인터페이스를 사용함으로써 캡슐화를 한다. 따라서 Context Class에서 인터페이스를 인자로 받아와서 그대로 메서드를 수행하는 코드가 있다. 즉 두 패턴 모두 실행중인 클래스의 영향을 받지 않고 유연한 변환이 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/38a42b02-bcf4-402c-b488-8c6ea3c1395e/image.png" alt=""></p>
<p align="center">
 _두 패턴의 기본 구조_
</p>



<p>🦊 표면상으로는 같으나 굳이 분리를 한 이유는 다음과 같다.</p>
<br> 

<blockquote>
<p>차이</p>
</blockquote>
<p>State는 로직의 끝에, setState(new State())를 호출해 스스로 변환할 수 있다.</p>
<p>Strategy의 경우 외부에서 데이터 입력이 필요하다. (카카오페이, 네이버페이)</p>
 <br> 


<p>따라서 다음과 같이 정리할 수 있다.</p>
<blockquote>
<p>정리</p>
</blockquote>
 <br> 

<p>Strategy Pattern : 행동(알고리즘)을 소유한 객체를 실행 시에 선택할 수 있게 상황에 맞는 행동을 하는데 유용</p>
<p>=&gt; 다양한 알고리즘이 사용되는 경우 고려</p>
 <br> 

<p>State Pattern : 다양한 상태변화가 아주 복잡한 로직을 제어하는데 유용</p>
<p>=&gt; if, else 문에 의한 분기점이 많아 복잡한 경우 고려</p>
<blockquote>
<p>ver2.0</p>
</blockquote>
<p>해당 글은 저의 공부를 낙서하듯 적어놓은 것이기 때문에 정확하지 않을 수 있습니다! 혹시 틀리거나 다른 의견이 있다면 댓글 주시면 감사하겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] 컴퓨터 부품에 대해서]]></title>
            <link>https://velog.io/@portuga_code/%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%B6%80%ED%92%88%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</link>
            <guid>https://velog.io/@portuga_code/%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%B6%80%ED%92%88%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</guid>
            <pubDate>Wed, 11 Sep 2024 01:21:15 GMT</pubDate>
            <description><![CDATA[<ol>
<li>CPU</li>
</ol>
<ul>
<li><p>CPU(Central Processing Unit)는 컴퓨터 연산을 담당하는 하드웨어 장치로 컴퓨터 디바이스의 두뇌 역할을 수행한다.</p>
</li>
<li><p>컴퓨터는 다양한 프로그램을 실행해야 하기 때문에 CPU는 복잡하고 다양한 연산을 할 수 있도록 설계되어 있다.</p>
</li>
<li><p>컴퓨터의 기본적인 처리 속도를 담당하기 때문에 중요한 부품이다.</p>
</li>
</ul>
<ol start="2">
<li>GPU</li>
</ol>
<ul>
<li><p>GPU(Graphics Processing Unit)는 비디오, 즉 픽셀로 이루어진 영상을 처리하는 용도로 탄생했다.</p>
</li>
<li><p>CPU에 비해 반복적이고 비슷한, 대량의 연산을 병렬적으로 수행하기 때문에 속도가 매우 빠르다.- 영상, 렌더링을 비롯한 그래픽 작업의 경우 픽셀 하나하나에 대해 연산을 하기 때문에 CPU가 GPU로 데이터를 보내 처리한다.- 게임과 같이 그래픽 연산을 많이 처리할 때 중요하다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/portuga_code/post/bab244b0-4513-4996-a557-fa67ef4ad314/image.png" alt=""></p>
<ol start="3">
<li>메인보드</li>
</ol>
<ul>
<li>컴퓨터 본체에서 메인보드란 각 부품들이 하나로 연결되어 컴퓨터의 기능을 할 수 있게 해주는 회로이다. 이러한 특징으로 인해 간혹 마더 보드나 시스템보드라고 불리기도 한다.- 다른 부품들에 비해 메인보드를 업그레이드 했다고 해서 크게 성능을 좌우하지 않는다.</li>
</ul>
<ol start="4">
<li>파워</li>
</ol>
<ul>
<li>파워는 컴퓨터의 전원을 공급하는 장치로 컴퓨터의 심장이라고 할 수 있다.- 파워가 고장이 나면 다른 부품에도 영향을 미칠 수 있어 일정 용량 이상의 파워를 사는 것이 중요하다. 전력 효율에 따라 등급이 나뉘며 컴퓨터 사양에 맞는 등급으로 구매하는 것이 적절하다.</li>
</ul>
<ol start="5">
<li>RAM</li>
</ol>
<ul>
<li>랜덤 액세스 메모리(Random Access Memory)의 약자이다.- 전원 공급이 끊기면 저장된 정보가 사라지는 휘발성 메모리이다.- RAM의 역할은 CPU와 ROM 사이에 위치하여 데이터를 저장하고 처리하는 역할을 한다.</li>
</ul>
<ol start="6">
<li>ROM (HDD, SSD)</li>
</ol>
<ul>
<li>주요 저장 장치로 사용되며 RAM과 다르게 전원 공급이 중단되어도 저장된 내용을 잃지 않는다.- SSD는 HDD와 달리 기계적으로 구동하지 않아 읽기/쓰기 처리 속도가 빠르다.- 그러나 SSD는 HDD에 비해 가격이 비싼 편이다.</li>
</ul>
<p>※ 프로그래머에게 가장 중요한 부품</p>
<p>◆ 프로그래밍을 할 때 생각할 부분    - 실행 속도 : CPU, GPU 성능 / RAM 성능과 용량    - 소비 자원 : RAM 용량 / 디스크 용량</p>
<p>실행 속도와 소비 자원 관리에서 모두 중요한 것이 RAM이기 때문에 개인적으로 RAM이 중요하다고 생각하며 실제 프로그래밍을 할 때도 다른 요소 보다 RAM 용량 관리에 신경쓰는 경우가 많다.</p>
<p>이상 용무 끝!</p>
]]></description>
        </item>
    </channel>
</rss>