<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Patrick's Devlog</title>
    <link>https://patrick-jy.tistory.com/</link>
    <description>공부를 통해 터득한 지식을 정리하고 공유하는 곳입니다.</description>
    <language>ko</language>
    <pubDate>Tue, 14 Apr 2026 04:29:46 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Patrick_</managingEditor>
    <item>
      <title>[BOJ/C++] 주사위 굴리기(14499번)</title>
      <link>https://patrick-jy.tistory.com/180</link>
      <description>&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 개요&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14499&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/14499&lt;/a&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1-1. 설명&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;크기가 N X M인 지도가 존재한다. 지도의 오른쪽은 동쪽, 위쪽은 북쪽이다. 지도 위에 주사위가 놓여져 있으며 전개도는 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1211&quot; data-origin-height=&quot;112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAcHkj/btsLWwELoxZ/U3EkauuAbzXUAxi1q7ui90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAcHkj/btsLWwELoxZ/U3EkauuAbzXUAxi1q7ui90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAcHkj/btsLWwELoxZ/U3EkauuAbzXUAxi1q7ui90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAcHkj%2FbtsLWwELoxZ%2FU3EkauuAbzXUAxi1q7ui90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1211&quot; height=&quot;112&quot; data-origin-width=&quot;1211&quot; data-origin-height=&quot;112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지도의 좌표는 (r, c)로 r은 북쪽으로부터 떨어진 칸의 개수, c는 서쪽으로부터 떨어진 칸의 개수다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;주사위의 가장 처음에는 모든 면에 0이 적혀있고, 지도에는 각 칸의 정수가 쓰여져 있다. 주사위를 굴렸을 때, 이동한 칸에 쓰여있는 수가 0이면, 주사위의 바닥면에 쓰여있는 수가 칸에 복사된다. 이동한 칸이 0이 아닌 경우에는 쓰여있는 수가 주사위 바닥면으로 복사되며, 칸에 쓰여있는 수는 0이 된다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;주사위를 놓은 곳의 좌표와 이동시키는 명령이 주어졌을 때, 주사위가 이동했을 때마다 상단에 쓰여있는 값을 구하는 프로그램을 작성한다. 단, 주사위는 지도 바깥으로 이동시킬 수 없으며 이러한 경우 명령을 무시해야 한다.&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1-2. 제한 사항&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 첫줄의 지도 세로 크기 N, 가로 크기 M, 주사위를 놓은 곳의 좌표 x, y, 명령어의 개수 K가 주어짐&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- N과 M은 1 이상 20 이하, x는 0 이상 N-1 이하, y는 0 이상 M-1 이하, K는 1 이상 1,000 이하&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 둘째 줄부터 N개의 줄에 지도에 쓰여있는 수가 북쪽 -&amp;gt; 남쪽, 각 줄은 서쪽 -&amp;gt; 동쪽 순서대로 주어짐&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 주사위를 놓는 칸에 쓰여있는 수는 항상 0&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 지도 각 칸에 쓰여있는 수는 0 이상 10 미만&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 명령은 1, 2, 3, 4로 이루어져 있으며 각각 동쪽, 서쪽, 북쪽, 남쪽을 의미&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 구현&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2-1. 풀이&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문제를 잘 읽고 그대로 구현하면 된다. 헤맸던 부분은 주사위의 전개도를 어떻게 저장하고 이동할지 고민하였다. 풀이 과정을 나열하면 아래와 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;span&gt;&amp;nbsp;1. K개의 명령어를 차례로 확인&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;2. 지도 밖으로 나가는 경우는 continue&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;3. 방향에 따른 주사위 전개도 인덱스 변경&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;4. 이동칸이 0이 아닌 경우 주사위 바닥면으로 복사 후 이동칸은 0으로 변경&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;5. 이동칸이 0인 경우 주사위 바닥면에 쓰여있는 숫자를 이동칸으로 복사&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3번은 어렵게 생각할 필요없이 이동했을 때 저장한 주사위 눈금을 변경해주면 된다. 전개도는 1차원 배열로 저장하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1031&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oMXDn/btsLV7kURDB/MEOQS1k1vftPYGK2C5rmgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oMXDn/btsLV7kURDB/MEOQS1k1vftPYGK2C5rmgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oMXDn/btsLV7kURDB/MEOQS1k1vftPYGK2C5rmgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoMXDn%2FbtsLV7kURDB%2FMEOQS1k1vftPYGK2C5rmgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1031&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1031&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;조금 더 이해하기 쉽게끔 그림으로 정리해보았다. 초기 주사위를 [1, 2, 3, 4, 5, 6]으로 가정하면 각각의 움직였을 때의 인덱스는 위의 그림처럼 반환된다.&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2-2. 코드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1737519728463&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;iterator&amp;gt;

using namespace std;


void MoveDice(pair&amp;lt;int, int&amp;gt; start, int N, int M);

const int MaxSize = 21;
const int DiceIdx = 6;
const int Dir = 4;

int map[MaxSize][MaxSize];
queue&amp;lt;int&amp;gt; directions;

int dirX[Dir] = { 0, 0, -1, 1 };
int dirY[Dir] = { 1, -1, 0, 0 };

int dice[DiceIdx] = { 0, 0, 0, 0, 0, 0 };
int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int N, M, x, y, K;
    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; M &amp;gt;&amp;gt; x &amp;gt;&amp;gt; y &amp;gt;&amp;gt; K;

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

    int num;

    while (K--) {
        cin &amp;gt;&amp;gt; num;
        directions.push(num);
    }

    MoveDice({x, y}, N, M); // 주사위 이동 함수
}

void MoveDice(pair&amp;lt;int, int&amp;gt; start, int N, int M) {
    pair&amp;lt;int, int&amp;gt; cur = start, next;
    int temp, dir;

    while (!directions.empty()) {

        dir = directions.front();
        directions.pop();

        next = { cur.first + dirX[dir - 1], cur.second + dirY[dir - 1] };
        if (next.first &amp;lt; 0 || next.first &amp;gt;= N || next.second &amp;lt; 0 || next.second &amp;gt;= M) continue;
        // 지도밖으로 나갔을 때
        
        int temp[DiceIdx];
        copy_n(dice, DiceIdx, temp);
        if (dir == 1) { // 동쪽
            int idx[DiceIdx] = { 2, 0, 5, 3, 4, 1 };
            for (int i = 0; i &amp;lt; DiceIdx; i++) dice[i] = temp[idx[i]];
        }
        else if (dir == 2) { // 서쪽
            int idx[DiceIdx] = { 1, 5, 0, 3, 4, 2 };
            for (int i = 0; i &amp;lt; DiceIdx; i++) dice[i] = temp[idx[i]];
        }
        else if (dir == 3) { // 북쪽
            int idx[DiceIdx] = { 4, 1, 2, 0, 5, 3 };
            for (int i = 0; i &amp;lt; DiceIdx; i++) dice[i] = temp[idx[i]];
        }
        else { // 남쪽
            int idx[DiceIdx] = { 3, 1, 2, 5, 0, 4 };
            for (int i = 0; i &amp;lt; DiceIdx; i++) dice[i] = temp[idx[i]];
        }

        if (map[next.first][next.second] == 0) map[next.first][next.second] = dice[5];
        else {
            dice[5] = map[next.first][next.second];
            map[next.first][next.second] = 0;
        }

        cout &amp;lt;&amp;lt; dice[0] &amp;lt;&amp;lt; &quot;\n&quot;;

        cur = next;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/Algorithms Practice</category>
      <category>BOJ</category>
      <category>Gold</category>
      <author>Patrick_</author>
      <guid isPermaLink="true">https://patrick-jy.tistory.com/180</guid>
      <comments>https://patrick-jy.tistory.com/180#entry180comment</comments>
      <pubDate>Wed, 22 Jan 2025 13:23:50 +0900</pubDate>
    </item>
    <item>
      <title>[운영체제] 프로세스 상태</title>
      <link>https://patrick-jy.tistory.com/178</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프로세스 상태&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스는 빠르게 번갈아가면서 실행된다. 그 과정에서 하나의 프로세스는 여러 상태를 거치며 실행된다. 그리고 운영체제는 프로세스의 상태를 PCB를 통해 인식하고 관리한다. 프로세스의 상태 표현 방식은 운영체제마다 다르나, 대표적으로는 아래의 그림과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2dWqt/btsKlrTVfRJ/Ep0pDnO7AY89PlHkT3shB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2dWqt/btsKlrTVfRJ/Ep0pDnO7AY89PlHkT3shB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2dWqt/btsKlrTVfRJ/Ep0pDnO7AY89PlHkT3shB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2dWqt%2FbtsKlrTVfRJ%2FEp0pDnO7AY89PlHkT3shB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;836&quot; height=&quot;437&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 생성 상태&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 메모리에 적재되어 PCB를 할당받은 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성 상태를 거쳐 실행할 준비가 완료되면 준비 상태가 되어 CPU의 할당을 기다리게 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 준비 상태&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당장 CPU를 할당받아 실행할 수 있으나, 자신의 차례가 아니므로 기다리는 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 준비 상태인 프로세스가 실행 상태로 전환되는 것을 디스패치라고 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 실행 상태&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU를 받아 프로세스가 실행중인 상태. 할당된 일정 시간동안만 CPU 사용 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 프로세스가 할당된 시간을 모두 사용(Time Interrupt)한다면 다시 준비 상태가 되며, 실행 도중 입출력장치를 사용해 입출력 장치 작업이 끝날 때까지 기다려야 하면 대기상태가 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4. 대기 상태&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스가 실행 도중 입출력장치를 사용할 때 대기 상태로 넘어감&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 작업은 CPU에 비해 느리므로 입출력 작업을 요청한 프로세스는 입출력장치가 입출력을 끝낼때까지 기다려야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 작업이 완료되면 CPU 할당을 기다리는 준비 상태로 전환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;5. 종료 상태&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스가 종료된 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 PCB와 프로세스가 사용한 메모리를 정리&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고 문서&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/wz9C_vqME8g?si=eEjR_wzirauMaXR_&quot;&gt;강민철, 혼자 공부하는 컴퓨터 구조 + 운영체제, 한빛 미디어 - 10강 프로세스&lt;/a&gt;&lt;/p&gt;</description>
      <category>Computer Science/Operating System</category>
      <category>프로세스 상태</category>
      <author>Patrick_</author>
      <guid isPermaLink="true">https://patrick-jy.tistory.com/178</guid>
      <comments>https://patrick-jy.tistory.com/178#entry178comment</comments>
      <pubDate>Mon, 28 Oct 2024 16:23:33 +0900</pubDate>
    </item>
    <item>
      <title>[운영체제] 프로세스</title>
      <link>https://patrick-jy.tistory.com/176</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로세스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스는 쉽게 설명하면 실행 중인 프로그램이다. 보통 보조기억장치에 저장된 프로그램을 메모리에 적재하고 실행하면 해당 프로그램은 프로세스가 된다. 이러한 과정을 프로세스를 생성한다고 생각하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;종류&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 포그라운드 프로세스(Foreground Process)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 사용자가 볼 수 있는 공간에서 실행되는 프로세스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 백그라운드 프로세스(Background Process)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 사용자가 볼 수 없는 공간에서 실행되는 프로세스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 사용자와 상호작용이 가능한 프로세스, 상호작용 하지 않고 일만 하는 프로세스(데몬, 서비스) 두 가지로 나뉨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프로세스 제어 블록(PCB, Process Control Block)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 프로세스는 실행을 위해 CPU를 필요로 하나, CPU 자원은 한정되어 있다. 쉽게 말해 모든 프로세스가 CPU를 동시에 사용할 수 없다. 따라서 프로세스들은 차례대로 돌아가며 정해진 시간동안 CPU를 이용한다. 이 때, 타이머 인터럽트(클럭 신호를 발생시키는 장치에 의해 주기적으로 발생하는 하드웨어 인터럽트) 발생 시 차례를 양보한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠르게 번갈아 수행되는 프로세스를 관리하기 위해 생겨난 자료구조가 PCB이다. 프로세스 관련 정보를 저장하는 자료 구조이며, 상품에 달린 태그와 같은 정보로 생각하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;286&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/riYcu/btsKfrFF5io/o4sIDOSEv8yPT7lGIkHGe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/riYcu/btsKfrFF5io/o4sIDOSEv8yPT7lGIkHGe1/img.png&quot; data-alt=&quot;PCB에 담기는 정보&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/riYcu/btsKfrFF5io/o4sIDOSEv8yPT7lGIkHGe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FriYcu%2FbtsKfrFF5io%2Fo4sIDOSEv8yPT7lGIkHGe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;286&quot; height=&quot;403&quot; data-origin-width=&quot;286&quot; data-origin-height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PCB에 담기는 정보&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 PCB에 담기는 정보는 위의 그림과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 프로세스 ID : 특정 프로세스를 식별하기 위해 부여하는 고유 번호&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 프로세스 상태 : 프로세스의 현재 상태 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 레지스터의 값 : 프로그램 카운터, 스택 포인터 등 프로세스가 실행하며 사용했던 레지스터의 값들&lt;br /&gt;- CPU 스케줄링 정보 : 프로세스가 언제, 어떤 순서로 CPU를 할당 받을지에 대한 정보&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 메모리 정보 : 프로세스가 어느 주소에 저장되어 있는지에 대한 정보, 페이지 테이블 정보&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사용할 파일과 입출력 장치 정보 : 할당된 입출력장치, 사용중인(열려있는) 파일 정보&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 운영체제마다 상이하며, PCB는 커널 영역에 생성된다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문맥 교환(Context Switching)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU는 한정되어 있으므로 돌아가면서 사용하기 위해 문맥 교환을 이용한다. 예를 하나 들어보면 A 프로세스가 B 프로세스로 순서가 넘어갈 때, A 프로세스는 지금까지 중간 정보(문맥)를 백업한다. 뒤이어 실행할 B 프로세스 문맥을 복구하면 자연스레 실행 중인 프로세스가 바뀐다. 이처럼 기존에 실행 중인 프로세스 문맥을 백업하고 새로운 프로세스 실행을 위해 문맥을 복구하는 과정을 &lt;b&gt;문맥 교환&lt;/b&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/97o0W/btsKgh3isDQ/b01yIbHG8eMfC5M5qf1ad0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/97o0W/btsKgh3isDQ/b01yIbHG8eMfC5M5qf1ad0/img.png&quot; data-alt=&quot;A 프로세스 레지스터 정보들 메모리에 저장 후 B로 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/97o0W/btsKgh3isDQ/b01yIbHG8eMfC5M5qf1ad0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F97o0W%2FbtsKgh3isDQ%2Fb01yIbHG8eMfC5M5qf1ad0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;509&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;A 프로세스 레지스터 정보들 메모리에 저장 후 B로 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용자 영역에서의 프로세스 메모리 구조&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널 영역에 PCB가 생성되는 반면, 사용자 영역에서는 프로세스 메모리 영역이 아래의 그림 처럼 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 나뉘게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;974&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4RZSZ/btsKeengZPt/5am3b16h9AkLywmiaP6Fu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4RZSZ/btsKeengZPt/5am3b16h9AkLywmiaP6Fu1/img.png&quot; data-alt=&quot;메모리 영역 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4RZSZ/btsKeengZPt/5am3b16h9AkLywmiaP6Fu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4RZSZ%2FbtsKeengZPt%2F5am3b16h9AkLywmiaP6Fu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;428&quot; height=&quot;607&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;974&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메모리 영역 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 코드 영역(= 텍스트 영역)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행할 수 있는 코드, 기계어로 이루어진 명령어 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터가 아닌 CPU가 실행할 명령어가 담기기에 쓰기가 금지된 영역&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 데이터 영역&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 프로그램의 전역 변수와 정적(static) 변수가 저장되는 영역&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 임시로 쓰는 데이터가 아니므로 프로그램 시작과 함께 할당되며, 프로그램 종료 시 소멸&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 힙 영역&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사용자(프로그래머)가 직접 관리할 수 있는 그리고 반드시 그렇게 해야만 하는 메모리 영역&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사용자에 의해 메모리 공간이 동적으로 할당되고 해제 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 스택과 다르게 낮은 주소에서 높은 주소 방향으로 할당&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex&amp;gt; C의 malloc, C++의 new 등으로 동적 할당 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4. 스택 영역&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 함수의 호출과 관계되는 지역 변수와 매개 변수가 저장되는 영역&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 함수 호출 시 할당되며 함수 호출 완료 시 소멸&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 스택은 push를 통해 데이터 저장, pop을 통해 데이터 인출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;rarr; LIFO 방식에 따라 동작하므로 가장 늦게 저장된 데이터가 가장 먼저 인출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 메모리 주소가 높은 주소에서 낮은 주소의 방향으로 할당됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 힙과 스택의 영역의 크기는 가변적으로 변할 수 있다. 일반적으로 힙은 낮은 주소 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rarr;&amp;nbsp;&lt;/span&gt;높은 주소 순으로 할당이 되고 스택은 높은 주소 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rarr;&amp;nbsp;&lt;/span&gt;낮은 주소순으로 할당된다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;힙 VS 스택 영역&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 119px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;스택&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;힙&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;매우 빠른 액세스&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;상대적으로 느린 액세스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;변수를 명시적으로 할당 해제할 필요 X&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;변수 할당 및 해제의 책임 有&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;지역 변수만 저장&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;전역적으로 변수 저장 및 액세스 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;CPU에 의해 관리 &amp;rarr; 메모리 단편화 X&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;메모리 관리해야 함 &amp;rarr; 효율적 공간 사용 보장하지 않으면 할당 후 메모리 조각화되어 강제 해제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;스택 크기 제한(OS에 따라 다름)&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;메모리 크기 제한 X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;변수 크기 조정 X&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;malloc, realloc, new 로 할당 및 크기 조정 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고 문서 및 자료&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/Jjfah3t_xWk?feature=shared&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;강민철, 혼자 공부하는 컴퓨터 구조 + 운영체제, 한빛 미디어 - 10강 프로세스&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tcpschool.com/c/c_memory_structure#google_vignette&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TCP School, 메모리의 구조&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://junghyun100.github.io/%ED%9E%99-%EC%8A%A4%ED%83%9D%EC%B0%A8%EC%9D%B4%EC%A0%90/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;스택과 힙의 차이점&lt;/a&gt;&lt;/p&gt;</description>
      <category>Computer Science/Operating System</category>
      <category>메모리</category>
      <category>운영체제</category>
      <category>프로세스</category>
      <author>Patrick_</author>
      <guid isPermaLink="true">https://patrick-jy.tistory.com/176</guid>
      <comments>https://patrick-jy.tistory.com/176#entry176comment</comments>
      <pubDate>Tue, 22 Oct 2024 17:53:08 +0900</pubDate>
    </item>
    <item>
      <title>[Unity] DirtyFlag Pattern</title>
      <link>https://patrick-jy.tistory.com/175</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DirtyFlag Pattern&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DirtyFlag 패턴은 메모리를 효과적으로 관리하기 위해 실시간으로 필요한 객체는 활성화하고, 필요하지 않을 때 비활성화 하는 패턴이다. 예를 들어 오픈 월드일 때 캐릭터가 이동 시 캐릭터 주변에 있는 사물 및 환경만 로딩이 되고 캐릭터가 벗어나면 로딩이 내려가는 것으로 생각하면 된다. 오픈 월드를 한번에 로딩하면 메모리 과부하가 일어나므로, 청크 개념 단위로 나눠 월드를 쪼개어 캐릭터의 시야정도까지만 로딩해야 한다. 패턴이긴 하나, 최적화 방법에 조금 더 가깝다. DirtyFlag는 비주얼적인 면, 데이터적인 면 등 여러 방면에서 광범위하게 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성이 복잡하고 비용이 많이드는 환경에 적합하며, 장면에서 많은 계산이나 업데이트가 발생할때 유용하다. 객체의 상태 변경 여부를 나타내는 단순한 boolean flag이다. 대표적인 예로는 Unity에서 하이어라키가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;888&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eapc5u/btsJ5yr5Ypa/JWMoQjFekqzIaw3O2cmCE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eapc5u/btsJ5yr5Ypa/JWMoQjFekqzIaw3O2cmCE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eapc5u/btsJ5yr5Ypa/JWMoQjFekqzIaw3O2cmCE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feapc5u%2FbtsJ5yr5Ypa%2FJWMoQjFekqzIaw3O2cmCE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;345&quot; height=&quot;476&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;888&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하위 transform은 상위 transform에 업데이트가 필요할 때까지 업데이트를 무시한다. 쉽게 설명하면 하위transform에서 변경 연산이 이루어져야 할 때, 바로 연산이 이루어지는 것이 아닌 변경 연산이 필요하다는 플래그를 활성화하게 된다. 그 이후에 모든 계층 구조에서 연산이 끝나고 나면(한 프레임 종료) 플래그를 수집하여 플래그가 활성화된 컴포넌트를 한번에 업데이트가 이루어진다. 이러한 업데이트는 엔진 내부에서 자동적으로 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DirtyFlag의 주 목적은 상태가 자주 변경되는 동적 개체에 대한 불필요한 계산을 최소화하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 그림처럼 구역을 청크 단위로 나누고, 플레이어가 이동했을 때 가까운 거리에 있는 영역에서 로딩이 안된 부분은 DirtyFlag 활성화처리해준다.(2번) 플래그에 따라 load, unload를 해준다.(3번)&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZFMyv/btsJ5fGpiNO/PHb4g54BGUfKoFfE8LJ5hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZFMyv/btsJ5fGpiNO/PHb4g54BGUfKoFfE8LJ5hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZFMyv/btsJ5fGpiNO/PHb4g54BGUfKoFfE8LJ5hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZFMyv%2FbtsJ5fGpiNO%2FPHb4g54BGUfKoFfE8LJ5hk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1430&quot; height=&quot;808&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 코드로 구현하면 아래와 같다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728974275831&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class GameSectors : MonoBehaviour {
    public PlayerController m_Player; // player
    public Sector[] m_Sectors; // sector 정보
    
    private void Update() {
        foreach (Sector sector in m_Sectors) {
            bool isPlayerClose = sector.IsPlayerClose(m_Player.transform.position);
            // 거리 측정
            
            if (isPlayerClose != sector.IsLoaded) 
                sector.MarkDirty(); // 더티 플래그 세팅
            
            if (sector.IsDirty) { // 활성화
                if (isPlayerClose) 
                    sector.LoadContent();
                else
                    sector.UnloadContent();
                
                sector.Clean();
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고 문서&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/6lCrgdLBIGw?feature=shared&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[유니티&amp;nbsp;TIPS]&amp;nbsp;어디에서나&amp;nbsp;사용하는&amp;nbsp;DirtyFlag&amp;nbsp;패턴&amp;nbsp;|&amp;nbsp;프로그래밍&amp;nbsp;디자인패턴&lt;/a&gt;&lt;/p&gt;</description>
      <category>Unity/Design Pattern</category>
      <category>dirtyflag pattern</category>
      <category>Unity</category>
      <author>Patrick_</author>
      <guid isPermaLink="true">https://patrick-jy.tistory.com/175</guid>
      <comments>https://patrick-jy.tistory.com/175#entry175comment</comments>
      <pubDate>Tue, 15 Oct 2024 15:44:11 +0900</pubDate>
    </item>
    <item>
      <title>[Unity] Flyweight Pattern</title>
      <link>https://patrick-jy.tistory.com/174</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Flyweight Pattern&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 패턴과 다르게 메모리 절약 등 성능 및 최적화에 초점을 맞춘 패턴이다. 여러 객체들이 리소스 공유를 통해 이들을 효율적으로 지원하는 패턴이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 복제된 여러 GameObject가 씬에 존재할 때, 각 개별 필드는 상대적으로 작을 수 있으나 다양한 구성 요소와 저장된 데이터 필드(예 : 구조체, 배열 등 Value Type 유형)도 복사된다. 이러한 부분을 해결하기 위해 Scriptable Object를 사용하면 해결된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;633&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XcAGU/btsJ1FkTSd8/kMis5fpBytF6RKZaOjRKZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XcAGU/btsJ1FkTSd8/kMis5fpBytF6RKZaOjRKZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XcAGU/btsJ1FkTSd8/kMis5fpBytF6RKZaOjRKZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXcAGU%2FbtsJ1FkTSd8%2FkMis5fpBytF6RKZaOjRKZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;507&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;633&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티에서 Flyweight Pattern을 사용한다고 하면 Scriptable Object를 사용한다고 생각하면 된다. Scriptable Object의 궁극적인 목표는 공유된 데이터는 데이터로서만 지니고 있고 여러 오브젝트가 참조한다. 이러한 목표에 대해 Flyweight Pattern의 목적과 동일하다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리팩토링 되지 않은 코드가 있다고 예를 들자&lt;/p&gt;
&lt;pre id=&quot;code_1728628089458&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class UnrefactoredUnitInstance : MonoBehaviour {
    // 인스턴스가 달라도 동일한 변수 정보
    // 인스턴스마다 복제됨
    public string factionName;
    public Sprite factionIcon;
    public int baseHealth;
    public int baseAttack;
    public int baseDefence;
    public int baseMovement;
    
    // 인스턴스마다 다른 변수 정보
    public int health;
    public int attack;
    public int defence;
    public int movement;
    public Vector3 position;
    
    private void Start() {
        RefreshunitStats();
    }
    
    private void RefreshUnitStats() {
        health = baseHealth;
        attack = baseAttack;
        defense = baseDefense;
        movement = baseMovement;
    }
    
    public void SetFactionData(string factionName, Sprite factionIcon, int baseHealth,
     int baseAttack, int baseDefense, int baseMovement) {
        this.factionName = factionName;
        this.factionIcon = factionIcon;
        this.baseHealth = baseHealth;
        this.baseAttack = baseAttack;
        this.baseDefense = baseDefense;
        this.baseMovement = baseMovement;
        
        RefreshUnitStats();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 클래스에 이렇게 정보를 기입하면 인스턴스마다 달라야 하는 변수는 상관없으나 기본적인 객체의 정보는 모두 복제되므로 좋은 코드가 되지 않는다. 이를 ScriptableObject로 변수 정보를 저장하면 아래의 코드와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1728628289009&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[CreateAssetMenu]
public class FactionData : ScriptableObject {
    public string factionName;
    public Sprite factionIcon;
    public int baseHealth;
    public int baseAttack;
    public int baseDefence;
    public int baseMovement;
}

// ---

public class UnitInstance : MonoBehaviour {
    public FactionData factionData;
    
    private void Start() {
        RefreshunitStats();
    }
    
    private void RefreshUnitStats() {
        health = factionData.baseHealth;
        attack = factionData.baseAttack;
        defencse = factionData.baseDefense;
        movement = factionData.baseMovement;
    }
    
    public int health;
    public int attack;
    public int defense;
    public int movement;
    
    public Vector3 position;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구현하게되면 인스턴스별로 달라야하는 값들은 메모리에 인스턴스당 1개씩 저장되고, 공통적으로 저장되는 FactionData는 메모리의 하나만 저장된다. Flyweight Pattern은 코드도 간결해지고 메모리도 절약할 수 있다는 장점이 존재한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고 문서&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/0j3Y_YUV_Mw?feature=shared&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; [유니티&amp;nbsp;TIPS]&amp;nbsp;대량의&amp;nbsp;인스턴스가&amp;nbsp;가진&amp;nbsp;속성을&amp;nbsp;최적화하는&amp;nbsp;Flyweight&amp;nbsp;패턴&amp;nbsp;|&amp;nbsp;프로그래밍&amp;nbsp;디자인패턴&lt;/a&gt;&lt;/p&gt;</description>
      <category>Unity/Design Pattern</category>
      <category>Flyweight Pattern</category>
      <category>Unity</category>
      <author>Patrick_</author>
      <guid isPermaLink="true">https://patrick-jy.tistory.com/174</guid>
      <comments>https://patrick-jy.tistory.com/174#entry174comment</comments>
      <pubDate>Fri, 11 Oct 2024 15:36:44 +0900</pubDate>
    </item>
    <item>
      <title>[Unity] Strategy Pattern</title>
      <link>https://patrick-jy.tistory.com/173</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Strategy&amp;nbsp;Pattern&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일 계열의 알고리즘을 정의하고 각각의 알고리즘을 캡슐화하여 상호교환이 가능하도록 만드는 패턴이다. 쉽게 설명하면 객체 내에서 알고리즘이나 동작을 캡슐화 하고 이를 실질적으로 교환이 가능하게끔 만들어 준다는 것에 초점을 두면 된다. 각 객체는 동적으로 실행될 수 있는 고유한 동작을 캡슐화하고, 알고리즘을 사용하는 사용자와 상관없이 독립적으로 알고리즘을 다양하게 런타임시 변경할 수 있게 하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MYEhf/btsJ0twoUNf/nzIYfKngFvUtKGhRscrDH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MYEhf/btsJ0twoUNf/nzIYfKngFvUtKGhRscrDH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MYEhf/btsJ0twoUNf/nzIYfKngFvUtKGhRscrDH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMYEhf%2FbtsJ0twoUNf%2FnzIYfKngFvUtKGhRscrDH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;527&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;624&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림을 살펴보면 베이스 클래스인 Strategy가 존재하고, 이를 구체화해서 만든 파생 클래스가 ConcreteStrategy 클래스들이다. 실시간으로 Context에서 A나 B를 자유롭게 변경할 수 있도록 만들어준다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임에서 어빌리티 시스템을 만든다고 예를 들어보자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728527192285&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class AbilityRunner : MonoBehaviuor {
    public enum Ability {
        RadarPulse,
        AirSupport,
        FirstAid
    }
    
    public Ability currentAbility;
    
    void ActivateAbility(Ability ability) {
        switch (ability) {
            case Ability.RadarPulse:
                Debug.Log(&quot;Activating Radar Pulse&quot;);
                break;
            case Ability.AirSupport:
                Debug.Log(&quot;Calling in Air Support&quot;);
                break;
            case Ability.FirstAid:
                Debug.Log(&quot;Using First Aid&quot;);
                break;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 속성마다 switch case문을 만들게 된다면 컨텐츠가 업데이트 될 때 switch case문이 계속해서 늘어나게 될 것이다. 이를 클래스로 빼고 보완해두면 아래의 코드와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1728528151289&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public abstract class Ability : ScriptableObject {
    // Ability라는 Base Class
    [SerializeField] protected string m_AbilityName;
    ...
    public virtual void Use(GameObject gameObject) {
        // 공통 기능
        Debug.Log(&quot;$Using ability : {m_AbilityName}&quot;);
        PlaySound();
        PlayParticleFX();
    }
}
    
public class AirSupportAbility : Ability {
    // Ability를 상속
    public override void Use(GameObject gameObject) { 
        base.Use(gameObject);
        // 추가 기능 구현
    }
}
    
public class RadarPulseAbility : Ability { ... }
public class FirstAidAbility : Ability { ... }

// --- 

public class AbilityRunner : MonoBehaviuor {
    private Ability m_CurrentAbility;
    ...
    public void OnAbilityButtonClicked() {
        if (m_CurrentAbility != null) {
            m_CurrentAbility.Use(this.gameObject);
            // 실행
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스를 추가하여도 기존의 Runner는 건들이지 않고 추가하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행동이 조금씩 다를뿐 개념적으로 관련된 많은 클래스들이 존재할 때 해당 패턴은 유용하게 쓰인다. 하나의 클래스에 몰아서 사용하는 것이 아닌, 상속을 이용해 여러 클래스를 생성하고 사용하면 된다. 알고리즘의 변형이 필요할 때, 이러한 변형물들이 알고리즘의 상속 관계도 구현될 때 사용 가능하다. 만약 사용자가 몰라야 하는 알고리즘이 있을 때, 노출하지 말아야 할 복잡한 자료구조는 Strategy 클래스로 적용한다. 많은 행동들이 하나의 클래스 연산안에서 복잡한 다중 조건문의 모습을 취할 때 프로토타입에서는 상관없으나, 확장될 우려가 있으면 리팩토링해서 Strategy Pattern을 사용하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고 문서&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/ic2tGGQrq1c?feature=shared&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[유니티&amp;nbsp;TIPS]&amp;nbsp;객체지향의&amp;nbsp;기본기&amp;nbsp;Strategy&amp;nbsp;패턴&amp;nbsp;|&amp;nbsp;프로그래밍&amp;nbsp;디자인패턴&lt;/a&gt;&lt;/p&gt;</description>
      <category>Unity/Design Pattern</category>
      <category>strategy pattern</category>
      <category>Unity</category>
      <author>Patrick_</author>
      <guid isPermaLink="true">https://patrick-jy.tistory.com/173</guid>
      <comments>https://patrick-jy.tistory.com/173#entry173comment</comments>
      <pubDate>Thu, 10 Oct 2024 11:53:08 +0900</pubDate>
    </item>
    <item>
      <title>[Unity] MVC, MVP, MVVM Pattern</title>
      <link>https://patrick-jy.tistory.com/172</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC, MVP, MVVM 패턴은 UI와 로직의 분리가 목적인 패턴이다. 로직을 분리하여 불필요한 종속 관계를 줄인다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘날의 UI는 과거보다 훨씬 정교해지고 수가 많아짐에 따라 혼자 작업할 수 없다. 여러명의 공동 작업을 진행할 때 구조를 얼마나 잘 짤 수 있을지가 관건이며, 구조의 짜임새가 중요하므로 다양한 패턴이 탄생하고 다양한 UI 프레임워크가 탄생했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 디자인 패턴은 클래스 다이어그램으로 표현이 가능하나, 해당 패턴은 SoC(Separation Of Concerns, 관심사 분리) 측면이다. 즉, 하나 하나의 클래스가 아닌 어떤 로직 단위들의 관계를 나타내는 개념으로 생각하면 된다. 그러다보니 디자인 패턴보다는 아키텍쳐 패턴으로 분류된다. 유니티에서의 툴셋 기반으로 진행되는 도구는 모두 해당 패턴이 사용되었다고 보면 된다.&amp;nbsp;해당 패턴의 주 목표는 잠재적인 스파게티 코드를 방지하며, UI/UX 프레임워크에 의존적이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;529&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n3y8S/btsJXZvNlbk/JxmnlNLN8okYSTQnlOsz70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n3y8S/btsJXZvNlbk/JxmnlNLN8okYSTQnlOsz70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n3y8S/btsJXZvNlbk/JxmnlNLN8okYSTQnlOsz70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn3y8S%2FbtsJXZvNlbk%2FJxmnlNLN8okYSTQnlOsz70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;843&quot; height=&quot;529&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;529&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MVC(Model View Controller)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;705&quot; data-origin-height=&quot;471&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bALB7z/btsJYG4i5UI/4RcxeKFcxC5e72cPLQdTWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bALB7z/btsJYG4i5UI/4RcxeKFcxC5e72cPLQdTWk/img.png&quot; data-alt=&quot;Model View Controller&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bALB7z/btsJYG4i5UI/4RcxeKFcxC5e72cPLQdTWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbALB7z%2FbtsJYG4i5UI%2F4RcxeKFcxC5e72cPLQdTWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;705&quot; height=&quot;471&quot; data-origin-width=&quot;705&quot; data-origin-height=&quot;471&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Model View Controller&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어에서 UI를 개발할 때 일반적으로 사용되는 디자인패턴이다. 소프트웨어의 논리적인 부분을 데이터와 분리하는 것이 궁극적인 목표이며, 결국 종속성과 스파게티 코드를 줄이는 것이 최종 목표이다. MVC 디자인은 각 레이어의 기능을 제한하여 단일 책임 원칙을 준수한다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Model : 애플리케이션 데이터를 관리하고 값을 보관하는 데이터 컨테이너, 게임 플레이 로직을 수행하거나 계산 실행 X&lt;/li&gt;
&lt;li&gt;View : 화면에 데이터 그래픽을 사용자에게 표시하는 인터페이스 &amp;rarr; UI Section&lt;/li&gt;
&lt;li&gt;Controller : 게임 데이터를 처리하고 런타임에 값이 어떻게 변하는지 계산, 입력을 처리하고 결과를 모델로 다시 전송, 그 자체로 게임 데이터를 포함하지 않음 &amp;rarr; 입력 및 로직 처리 Section&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰와 컨트롤러로 서로 역할을 나누게 되면 의존 관계 및 종속성을 줄일 수 있고 유지보수가 쉬워진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC를 적용하면 뷰자체는 문제가 없다(UI Toolkit or Unity UI가 제공되므로). 그러나, 모델 데이터의 변경사항을 수신하기 위한 Vew별 코드가 필요하다. 이러한 문제로 인해 Unity에서는 MVC보다는 MVP 모델을 채용한다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MVP(Model View Presenter)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwbW47/btsJXXS1zEE/BsCdNZMaLFJ8iiL0ea2J4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwbW47/btsJXXS1zEE/BsCdNZMaLFJ8iiL0ea2J4k/img.png&quot; data-alt=&quot;Model View Presenter&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwbW47/btsJXXS1zEE/BsCdNZMaLFJ8iiL0ea2J4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwbW47%2FbtsJXXS1zEE%2FBsCdNZMaLFJ8iiL0ea2J4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;510&quot; height=&quot;672&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Model View Presenter&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Model : 데이터와 해당 데이터를 관리하는 규칙 포함(example&amp;gt; 게임의 현재 상태, 게임 속성, 캐릭터 레벨업, 체력, 점수 등) 데이터에 대한 로직), Model은 View에 대한 정보가 없음 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&amp;rarr;&lt;span&gt; Model과 View를 분리시키는 것이 목표&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Presenter : Model과 View 사이에서 중재, View에서 사용자의 입력 이벤트를 처리, 게임의 조건이 변경되면 Model 및 View를 업데이트&lt;/li&gt;
&lt;li&gt;View : 사용자에게 데이터를 표시하는 애플리케이션의 UI, 사용자 상호작용을 Presenter로 전송, MVP 패턴의 View는 Model과 직접 상호작용하지 않음, UI 로직에 대한 별도 스크립트가 존재할 수 있으나, 게임의 비즈니스 로직 처리 X&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC에서는 컨트롤러가 사용자 입력을 처리하고 Model을 조작하나, MVP에서는 뷰가 사용자의 입력을 처리하고 Model과 View를 중재하는 것이 Presenter가 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰는 UI Event를 통해 입력을 Presenter에게 전송하고, Presenter는 모델을 변경한다. 그리고 모델의 상태가 변경됐을 때 Presenter에게 이벤트로 통지하고, Presenter는 수정된 데이터를 뷰로 전달에 뷰는 UI를 갱신하는 프로세스로 진행된다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;장점&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 확장성 : 특정 작업을 처리하는 부분들이 나뉘어 있으면 코드 베이스 관리 및 확장 용이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 모듈성 : 한 구성요소의 변경사항이 전체 시스템에 영향을 미치지 않음, 각 구성요소는 독립적으로 개발, 테스트 및 수정 될 수 있음, 코드 디버깅 용이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 재사용성 및 유지 관리성 : 코드의 일부는 애플리케이션의 다른 부분에서 재사용 가능, 스파게티 코드가 될 확률 &amp;darr;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MVVM(Model View ViewModel)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVP를 보완하여 생성된 패턴이 MVVM이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;671&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KzK1N/btsJXZ4qnHh/zelG0LMAP89uJaZm3MfFzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KzK1N/btsJXZ4qnHh/zelG0LMAP89uJaZm3MfFzK/img.png&quot; data-alt=&quot;Model View ViewModel&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KzK1N/btsJXZ4qnHh/zelG0LMAP89uJaZm3MfFzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKzK1N%2FbtsJXZ4qnHh%2FzelG0LMAP89uJaZm3MfFzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;510&quot; height=&quot;671&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;671&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Model View ViewModel&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 MVP에서 뷰와 Controller/Presenter의존성을 약화한 모델이다. &lt;u&gt;데이터 바인딩&lt;/u&gt;이 주 핵심이며, 뷰와 뷰모델 간의 데이터 바인딩을 통해 뷰모델 속성 변경이 &lt;u&gt;자동으로 뷰에 반영&lt;/u&gt;된다. 데이터 바인딩 시스템은 프레임워크에 따라 제공해주는지 결정된다. 또한 뷰모델에서 UI 상호작용을 처리하는 커맨드 패턴을 사용한다. 마지막으로 뷰모델은 뷰에 대한 참조를 가지지 않으므로 테스트가 용이하다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Model : 모델은 비즈니스 로직에서 사용되는 데이터를 포함하는 데이터 구조, 어떤 객체라도 될 수 있음 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;rarr;&lt;span&gt; ScriptableObject, MonoBegaviuor&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;View : 사용자에게 데이터를 표시하고 상호작용 하는 UI, 일반적으로 UI Element로 구성, UI Toolkit에서는 USS 스타일 시트와 UXML 파일로 구성&lt;/li&gt;
&lt;li&gt;ViewModel : Model과 View 사이에서 중재, 사용자가 View와 상호작용할 때 Model을 업데이트 하는 역할, Model이 변경될 때 View를 업데이트하는 로직도 포함, ViewModel과 View는 &lt;b&gt;데이터 바인딩&lt;/b&gt;을 통해 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터 바인딩&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 바인딩은 UI가 아닌 개체의 속성과 UI 요소간의 자동으로 동기화를 보장한다. exmple&amp;gt; MonoBehaviuor의 문자열 속성과 TextField 값의 속성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바인딩은 본질적으로 UI가 아닌 속성과 이를 수정하는 UI 요소간의 링크이며, 바인딩을 설정하면 기본 데이터와 해당 시각적 요소 간의 변경 사항이 자동으로 동기화 된다. &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&amp;rarr; 각 UI 업데이트에 대해 수동으로 이벤트 핸들러 작성할 필요X&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;Unity6의 UI Toolkit은 런타임 데이터 바인딩을 지원한다. 해당 기능을 사용할 시 런타임 UI 작업 중에 C# 개체의 속성을 UI 컨트롤 속성에 바인딩이 가능하다. 웹개발에서의 영감을 얻어 구현된 시스템으로 생각하면 된다. 해당 글은 디자인 패턴에 대한 내용이므로, UI Toolkit에 대한 튜토리얼은 공개 강의가 많으니 &lt;a href=&quot;https://www.youtube.com/watch?v=KrjclAb5XvU&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;를 참조하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;MVP VS MVVM&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MVP&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Presenter는 상태 변경을 감지하기 위해 모델의 이벤트를 구독하고, 변경 사항이 통보되면 Presenter는 데이터를 View에 업데이트한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MVVM&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델에 필요한 Converter를 등록하는 것부터 시작하며, ViewModel에서 데이터 바인딩을 설정한다. 설정을 사용하게 되면 모델의 변경 사항이 기존 바인딩을 통해 자동으로 보기를 업데이트 한다. 사용자 입장에서는 신경쓸 부분이 줄어든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 바인딩을 사용해 UI를 데이터와 동기화하는데 필요한 코드의 양을 줄일 수 있으며, 그러다보면 읽고 유지하기 쉬운 간결한 코드가 된다. 또한 데이터 바인딩을 통해 오래되거나 잘못된 데이터가 표시될 위험을 줄여 UI 일관성을 향상시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 각 데이터 바인딩을 설정하는데 따른 추가 오버헤드가 발생한다. 데이터 소스, 데이터 소스 경로, 바인딩 모드, 컨버터 등을 설정하려면 번거로울 수 있다. 성능적으로 중요한 부분(시스템의 도움을 받는 등)에서는 좋지 않을 수 있다. 이러한 패턴은 간단한 UI 환경보다는 조금 더 복잡한 대규모 사용자 인터페이스에 적합할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고 문서&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/fxlYxhhf83s?feature=shared&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[유니티&amp;nbsp;TIPS]&amp;nbsp;UI에&amp;nbsp;걸맞는&amp;nbsp;MVC,&amp;nbsp;MVP,&amp;nbsp;MVVM&amp;nbsp;패턴&amp;nbsp;|&amp;nbsp;프로그래밍&amp;nbsp;디자인패턴&lt;/a&gt;&lt;/p&gt;</description>
      <category>Unity/Design Pattern</category>
      <category>MVC</category>
      <category>MVP</category>
      <category>MVVM</category>
      <category>Unity</category>
      <author>Patrick_</author>
      <guid isPermaLink="true">https://patrick-jy.tistory.com/172</guid>
      <comments>https://patrick-jy.tistory.com/172#entry172comment</comments>
      <pubDate>Tue, 8 Oct 2024 16:14:36 +0900</pubDate>
    </item>
    <item>
      <title>[Unity] Singleton Pattern</title>
      <link>https://patrick-jy.tistory.com/171</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Singleton Pattern&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유일무이한 존재의 인스턴스, 즉 하나의 프로젝트에서 하나의 객체만 존재하는 것이다. 보통은 클래스를 생성할 때 인스턴스화한 후 여러 개 생성이 가능하나, 클래스가 자신의 인스턴스 하나만 인스턴스화할 수 있도록 보장하는 것이 싱글톤 패턴이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 곳에서 불러와야 하므로 해당 단일 인스턴스에 대한 손쉬운 전역 액세스를 제공하고 하나의 객체만 존재할 필요가 있을 때 사용된다. (example&amp;gt; 상태창, 게임 매니저, 오디오 매니저, 파일 관리자 UI Setting etc)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 모든 곳에서 접근해야 하다보니 안티 패턴(비효율적이거나 비생산적인 패턴)으로 취급되기도 하며, 커플링이 발생하기 쉬운구조이다. 생성 시점이 명확하지 않다보니 테스트나 디버깅이 불편하다는 점이다. 싱글톤 패턴을 쓰되 주의하면서 사용해야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤의 클래스 다이어그램을 확인해보면 구현 자체가 어렵진 않다. 인스턴스가 본인을 잡고있다는 것을 알 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcedex/btsJVIt3Bfv/7CRFQ2fujmJu1L6jbiEr31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcedex/btsJVIt3Bfv/7CRFQ2fujmJu1L6jbiEr31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcedex/btsJVIt3Bfv/7CRFQ2fujmJu1L6jbiEr31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbcedex%2FbtsJVIt3Bfv%2F7CRFQ2fujmJu1L6jbiEr31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;291&quot; height=&quot;276&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 다이어그램을 코드로 나타내면 아래와 같다. 싱글톤을 생성하는 코드의 버전은 Scene에 올리기 위한 MonoBehaviour를 상속했을 때와 상속하지 않았을 때 두 가지로 구현되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MonoBehaviuor 상속&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728028209904&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SimpleSingleton : MonoBehaviour { // Scene에 올려야할 때
    public static SimpleSingleton instance;
    
    private void Awake() {
        if(instance == null)
            instance =  this;
        else 
            Destroy(gameObject);
    }
    // 새로운 scene을 부르면 GameObject가 지워짐
    // 사용되기 전 하이어라키에서 셋업되어야 함
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;해당 클래스들은 자기 자신의 인스턴스를 정적으로 지니고 있다. 이렇게 되면 여러 인스턴스가 생기더라도 메모리 공간은 하나만 잡게되고, 하나의 인스턴스만 존재하게 하도록 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MonoBehaviour를 상속받는 SimpleSingleton의 코드의 문제점은 새로운 Scene을 부르게 되면 GameObject가 지워지게 되며, 사용되기 전에 하이어라키에 무조건 셋업이 되어야 한다. 셋업이 되지 않으면 사용될 때 객체가 없을 수 있으므로 문제가 생긴다. 이러한 문제를 해결하기 위해 아래 코드처럼 수정하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728028610275&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Singleton : MonoBehaviuor {
    private static Singleton instance;
    public static Singleton Instance {
        get {
            if (instance == null) Setupinstance();
            return instance;
        }
    }
    
    private void Awake() {
        if(instance == null) {
            instance = this;
            DontDestroyOnLoad(this.gameObject);
        }
        else Destroy(gameObject);
    }
    
    private static void SetupInstance() {
        instance = FindObjectOfType&amp;lt;Singleton&amp;gt;(); 
        // singleton을 찾음
        
        if (instance == null) { // 존재하지 않을 때 생성
            GameObject gameObj = new GameObject();
            gameObj.name = &quot;Singleton&quot;;
            instance = gameObj.AddComponent&amp;lt;Singleton&amp;gt;();
            DontDestroyOnLoad(gameObj);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 DontDestroyOnLoad를 추가하여 새로운 신을 로딩했을 때, 해당 객체를 죽이지 않고 살리라고 코드에 명시해야 한다. 간혹 싱글톤이 게임매니저, 오디오 매니저 등 여러가지가 존재할 때 generic 타입으로 명시하여 구현하는 경우도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1728028860725&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Singleton&amp;lt;T&amp;gt; : MonoBehaviuor where T : Component {
    private static T instance;
    public static T Instance {
        get {
            if (instance == null) {
                instance = (T)FindObjectOfType(typeof(T));
                if (instance == null) SetupInstance();
            }
            return instance;
        }
    }
    
    private void Awake() {
        RemoveDuplicates();
    }
    
    private static void SetupInstance() {
        instance = (T)FindObjectOfType(typeof(T)); 
        
        if (instance == null) {
            GameObject gameObj = new GameObject();
            gameObj.name = typeof(T).Name;
            instance = gameObj.AddComponent&amp;lt;T&amp;gt;();
            DontDestroyOnLoad(gameObj);
        }
    }
    
    private RemoveDuplicates() {
        if (instance == null) {
            instance = this as T;
            DontDestroyOnLoad(gameObject);
        }
        else Destroy(gameObject);
    }
}

// ---

public class GameManager : Singleton&amp;lt;GameManager&amp;gt; { ... }
// Generic 타입 Singleton 사용 예제&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여러 싱글톤을 사용할 수 있으며, 코드 재사용성도 높아지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MonoBehaviour 비상속&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728029058719&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SimpleSingleton { // Scene에 올리는 객체가 아닐 때
    public static SimpleSingleton Instance;
    
    public static SimpleSingleton Instance {
        get {
            if (instance == null) instance = new Singleton(); // thread safe X
            return instance;
        }
        
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 작동에는 큰 문제가 없으나, 멀티 스레드 환경에서는 문제가 될 수 있다. 두 스레드에서 동시에 new Singleton()에 접근할 수 있으므로 두개가 동시에 생성될 수 있다. MonoBehaviuor에서는 해당 부분이 문제가 되지 않는 이유가 유니티에서는 멀티 스레드 환경을 사용할 수 없으므로, 싱글 스레드만 요구하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 해당 문제를 해결하기 위해 아래 코드처럼 lock을 사용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1728029352841&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SimpleSingleton {
    private static volatile SimpleSingleton instance = null;
    // volatile : 멀티 스레드 환경에서 안정성을 위해 사용하는 키워드
    // -&amp;gt; 캐시 최적화를 건너뛰는 대신 멀티 스레드 환경에서 안정적으로 접근
    private static readonly object padlock = new object();
    ...
    public static SimpleSingleton Instance {
        get {
            if (instance == null) {
                lock(padlock) { // critical section
                    if (instance == null) {
                        instance = new SimpleSingleton();
                    }
                }
            }
            return instance;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lock을 사용하게 되면 A 스레드가 먼저 접근했을 때, lock이 풀릴 때까지 B 스레드는 접근하지 못한다. instance 확인을 두번 하는 이유는 한번만 단순히 확인했을 때, 간발의 차로 A가 생성하고 있을 때 B는 instance가 존재하지 않는다고 판단하는 경우도 있기 때문이다. 이러한 경우를 회피하기 위해 instance 확인을 두번 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드로는 멀티 스레드 환경에서 안정성을 완전히 보장하기 어려우므로, 아래의 코드를 통해 더 안정성을 충족할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728029905789&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public sealed class SimpleSingleton {
    private static readonly SimpleSingleton instance = new SimpleSingleton();
    
    private SimpleSingleton() { }
    
    public static SimpleSingleton Instance {
        get { return instance; }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Singleton 선언 중 생성하면 문제를 해결할 수 있다. 하지만 클래스가 생성될 때, 바로 생성되다 보니 로딩시간이 조금 길어질 수 있는 단점이 존재할 수 있다. 상황에 맞춰서 코드를 구현하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고 문서&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/Tf_VZEgnag0?feature=shared&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[유니티&amp;nbsp;TIPS]&amp;nbsp;유일무이한&amp;nbsp;객체&amp;nbsp;인스턴스가&amp;nbsp;필요하다면?&amp;nbsp;Singleton&amp;nbsp;패턴&amp;nbsp;|&amp;nbsp;프로그래밍&amp;nbsp;디자인패턴&lt;/a&gt;&lt;/p&gt;</description>
      <category>Unity/Design Pattern</category>
      <category>Singleton pattern</category>
      <category>Unity</category>
      <author>Patrick_</author>
      <guid isPermaLink="true">https://patrick-jy.tistory.com/171</guid>
      <comments>https://patrick-jy.tistory.com/171#entry171comment</comments>
      <pubDate>Fri, 4 Oct 2024 17:24:46 +0900</pubDate>
    </item>
    <item>
      <title>[Unity] Object Pool Pattern</title>
      <link>https://patrick-jy.tistory.com/170</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Object Pool&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수많은 오브젝트를 다루는 개념이며, 오브젝트의 풀을 생성하고 필요할때 꺼내서 사용하면 된다. GoF의 디자인 패턴에서는 Object Pool을 따로 설명하지 않았다. 현대에서는 많이 쓰이는 개념이 되다보니 디자인 패턴으로 취급하는 경향이 있다. 전통적인 디자인 패턴하고는 컨셉이 다를 수 있으므로, 디자인 패턴보다는 최적화 기법으로 생각하면 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Pool&quot;에서 준비된 상태로 대기 중인 초기화된 오브젝트 세트를 사용한다. 그리고 오브젝트가 필요할 때 새로 인스턴스화하는 대신 풀에서 오브젝트를 활성화하여 사용한다. 해당 객체의 사용이 끝나면 Destroy 대신 비활성화하고 풀에 반환한다. 객체 생성 및 삭제로 인한 가비지 급증으로 발생할 수 있는 끊김 현상(Hiccup, GC spike)을 예방할 수 있다. 주로 로딩 화면에서 미리 생성한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFWTBz/btsJTJG25ki/5APiq5ObVIhVEe1INNWi60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFWTBz/btsJTJG25ki/5APiq5ObVIhVEe1INNWi60/img.png&quot; data-alt=&quot;오브젝트 풀의 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFWTBz/btsJTJG25ki/5APiq5ObVIhVEe1INNWi60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFWTBz%2FbtsJTJG25ki%2F5APiq5ObVIhVEe1INNWi60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;788&quot; height=&quot;470&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오브젝트 풀의 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오브젝트가 부족할 땐 어느정도 생성이 되나, 오브젝트가 많아지면 더이상 생성되지 않고 있는 오브젝트를 활성화, 비활성화한다. 오브젝트 풀을 생성할 때, Set Parent를 통해 부모 자식관계를 가지게 되면 위험할 수 있으므로 권장하지 않는다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1727922871353&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ObjectPool : MonoBehaviour {
    [SerializeField] private uint initPoolSize;
    [SerializeField] private PooledObject objectToPool;
    private Stack&amp;lt;PooledObject&amp;gt; stack; // Pool Object를 뺐다 넣는 자료구조
    
    private void Start() {
        SetupPool();
    }
    
    private void SetupPool() { // 로딩 화면에서 미리 생성
        stack = new Stack&amp;lt;PooledObject&amp;gt;();
        PooledObject instance = null;
        
        for (int i = 0; i &amp;lt; initPoolSize; i++) { 
            instance = Instantiate(objectToPool);
            instance.Pool = this;
            instance.gameObject.SetActive(false);
            stack.Push(instance);
        }
    }
    
    // 오브젝트를 가져옴
    public PooledObject GetPooledObject() {
        if (stack.Count == 0) { // 풀에 없을 때
            PooledObject newInstance = Instantiate(objectToPool); // 새로 생성
            newInstance.Pool = this;
            return newInstance;
        }
        
        PooledObject nextInstance = stack.Pop();
        nextInstance.gameObject.SetActive(true); // 생성이 아닌 활성화
        return nextInstance;
    }
    
    // 오브젝트를 반환
    public void ReturnToPool(PooledObject pooledObject) {
        stack.Push(pooledObject);
        pooledObject.gameObject.SetActive(false); // Destroy가 아닌 비활성화
    }
}

// ---

public class PooledObject : MonoBehaviour { // 오브젝트 풀 관리 대상
    private ObjectPool pool;
    public ObjectPool Pool { get =&amp;gt; pool; set =&amp;gt; pool = value; }
    
    public void Release() { 
        pool.ReturnToPool(this); // 풀에 들어갈 수 있도록 구현
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오브젝트 풀에 대한 예시 코드이다. 오브젝트 풀을 생성하고, Stack에 저장하여 해당 오브젝트를 Stack에서 가져오거나 저장하는 방식으로 진행된다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ObjectPool API&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오브젝트 풀은 많이 사용되다보니 Unity 2021부터 API를 제공한다. 해당 API의 &lt;a href=&quot;https://docs.unity3d.com/ScriptReference/Pool.ObjectPool_1.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;를 참조하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1884&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddea7b/btsJTSw2AIl/epoIp3tKDsbmZFEMsCHdD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddea7b/btsJTSw2AIl/epoIp3tKDsbmZFEMsCHdD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddea7b/btsJTSw2AIl/epoIp3tKDsbmZFEMsCHdD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fddea7b%2FbtsJTSw2AIl%2FepoIp3tKDsbmZFEMsCHdD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1884&quot; height=&quot;694&quot; data-origin-width=&quot;1884&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작동 원리는 똑같지만 직접 구현하는 것보다는 API를 이용해서 구현하는 것이 수월하다. ObjectPool API를 사용했을 때의 코드 예시이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1727923981991&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class RevisedGun : MonoBehaviour {
    ...
    [SerializeField] private RevisedProjectile projectilePrefab;
    
    private IObjectPool&amp;lt;RevisedProjectile&amp;gt; objectPool;
    
    private void Awake() {
        objectPool = new ObjectPool&amp;lt;RevisedProjectile&amp;gt;(CreateProjectile, OnGetFromPool
        , OnReleaseToPool, OnDestroyPooledObject, collectionCheck, defaultCapacity, maxSize);
        // new ObjectPool&amp;lt;T&amp;gt;(list, create function, action on get, action on release, action on destroy)
    }
    
    private RevisedProjectile CreateProjectile() { // 오브젝트 생성
        RevisedProjectile projectileInstance = Instantiate(projectilePrefab);
        projectileInstance.ObjectPool = objectPool;
        return projectileInstance;
    }
    
    private void OnGetFromPool(RevisedProjectile pooledObject) { // 풀에서 가져올 때
        pooledObject.gameObject.SetActive(true);
    }
    
    private void FixedUpdate() {
        RevisedProjectile bulletObject = objectPool.Get();
        // 오브젝트를 가져옴
        ...
    }
    
}

public class RevisedProjectile : MonoBehaviuor {
    private IObjectPool&amp;lt;RevisedProjectile&amp;gt; objectPool;
    public IObjectPool&amp;lt;RevisedProjectile&amp;gt; ObjectPool { set =&amp;gt; objectPool = value; }
    
    public void Deactivate() {
        ...
        objectPool.Release(this);
        // 오브젝트를 반환
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고 문서&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/jvJ1Vd7PGwk?feature=shared&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[유니티&amp;nbsp;TIPS]&amp;nbsp;수많은&amp;nbsp;오브젝트를&amp;nbsp;다루는데&amp;nbsp;적합한&amp;nbsp;Objectpool&amp;nbsp;패턴&amp;nbsp;|&amp;nbsp;프로그래밍&amp;nbsp;디자인패턴&lt;/a&gt;&lt;/p&gt;</description>
      <category>Unity/Design Pattern</category>
      <category>Object pool</category>
      <category>Unity</category>
      <author>Patrick_</author>
      <guid isPermaLink="true">https://patrick-jy.tistory.com/170</guid>
      <comments>https://patrick-jy.tistory.com/170#entry170comment</comments>
      <pubDate>Thu, 3 Oct 2024 13:24:10 +0900</pubDate>
    </item>
    <item>
      <title>[Unity] Factory Pattern</title>
      <link>https://patrick-jy.tistory.com/169</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Factory Pattern&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 생성을 위한 패턴이다. 아이템을 직접 생성하는 것이 아닌, 팩토리에서 호출하여 생성하는 것이 팩토리 패턴이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2L6Om/btsJS53iS1s/mC1gkPeSBENWqlLDkE8ze1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2L6Om/btsJS53iS1s/mC1gkPeSBENWqlLDkE8ze1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2L6Om/btsJS53iS1s/mC1gkPeSBENWqlLDkE8ze1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2L6Om%2FbtsJS53iS1s%2FmC1gkPeSBENWqlLDkE8ze1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;578&quot; height=&quot;386&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 생성에 대한 인터페이스와 구현의 분리가 가능하고, 객체 생성과 관련된 많은 세부 정보를 캡슐화할 수 있다. 또한 새&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로운 객체를 생성 방식에 대한 확장이 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Key-Value Dictionary를 이용한 탐색을 적용해, 아이템의 등급 등을 풀로 만들어 적용하는 경우가 많다. 또한 아이템을 많이 생성하다 보니 Object Pool과 병행해서 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이템 생성을 위한 아이템 매니저를 제작한다고 가정하고 코드를 예시로 들어보자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1727747077544&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ItemManager : MonoBehaviour {
    ...
    public void SpawnItem(ItemType type) {
        switch(type) {
            case ItemType.Health:
            ...
            case ItemType.Speed;
            ...
            case ItemType.Shield;
            ...
            case ItemType.Heart:
            ...
        }
    }
    
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;switch 문을 통해 아이템을 생성하게 되면 추후에 아이템이 추가될 때 아이템 매니저를 당연히 수정할 수 밖에 없게 된다. 이를 손대지 않고 개선하기 위해 팩토리 패턴을 적용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1727747176801&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ItemManager : MonoBehaviour {
    ItemFactory itemFactory;
    public void SpawnItem(ItemType type) {
        ...
        GameObject item = itemFactory.CreateItem(...);
        ...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적인 아이템의 상황은 팩토리에 전달을 해주면 된다. 해당 코드는 아이템 매니저를 굳이 건들일 필요없이, 아이템만 추가하여 팩토리만 생성해주면 된다. 해당 코드 또한 추상화에 의존하고 있으며 개방 폐쇄 원칙을 지키게 되는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;579&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p6PmJ/btsJQHpPIyR/mCZweK1KQnjSyL4VmPOGak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p6PmJ/btsJQHpPIyR/mCZweK1KQnjSyL4VmPOGak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p6PmJ/btsJQHpPIyR/mCZweK1KQnjSyL4VmPOGak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp6PmJ%2FbtsJQHpPIyR%2FmCZweK1KQnjSyL4VmPOGak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1242&quot; height=&quot;579&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;579&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적인 예시를 코드로 살펴보자. 우선 예시의 설계는 위의 그림과 같다. Factory는 IProduct만 의존하므로, 구체적인 클래스나 아이템에 의존하지 않고 추상화에 의존하므로 확장에 용이하다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1727748366918&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface IProduct {
    public string Productname { get; set; }
    public void Initialize();
}

public abstract class Factory : MonoBehaviour { // 추상클래스 팩토리
    public abstract IProduct GetProduct(Vector3 pos); // 아이템을 돌려주는 메소드
    
}

public class ProductA : MonoBehaviour, IProduct { // IProduct 구체화
    ...
    public void Initialize() { ... }
 }
 
 // ---
 
 public class ConcreteFactoryA : Factory {
     [SerializeField] private ProductA productPrefab;
     
     public override IProduct GetProduct(Vector3 position) {
         GameObject instance = Instantiate(productPrefab.gameObject, ... ); // 생성
         ProductA newProduct = instance.GetComponent&amp;lt;ProductA&amp;gt;();
         newProduct.Initialize(); // 초기화
         return newProduct; // 반환
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고 문서&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/qhtL9EYtB3Q?feature=shared&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[유니티&amp;nbsp;TIPS]&amp;nbsp;객체&amp;nbsp;생성을&amp;nbsp;위한&amp;nbsp;Factory&amp;nbsp;패턴&amp;nbsp;|&amp;nbsp;프로그래밍&amp;nbsp;디자인패턴&lt;/a&gt;&lt;/p&gt;</description>
      <category>Unity/Design Pattern</category>
      <category>Factory Pattern</category>
      <category>Unity</category>
      <author>Patrick_</author>
      <guid isPermaLink="true">https://patrick-jy.tistory.com/169</guid>
      <comments>https://patrick-jy.tistory.com/169#entry169comment</comments>
      <pubDate>Tue, 1 Oct 2024 11:12:22 +0900</pubDate>
    </item>
  </channel>
</rss>