<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://nu-lungji.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://nu-lungji.github.io/" rel="alternate" type="text/html" /><updated>2026-09-17T16:06:17+00:00</updated><id>https://nu-lungji.github.io/feed.xml</id><title type="html">LungJi Lab</title><subtitle>똥글 싸지릅니다.</subtitle><author><name>LungJi</name></author><entry><title type="html">OOP - Encapsulation</title><link href="https://nu-lungji.github.io/design%20principle/Encapsulation/" rel="alternate" type="text/html" title="OOP - Encapsulation" /><published>2026-09-16T08:10:00+00:00</published><updated>2026-09-14T00:00:00+00:00</updated><id>https://nu-lungji.github.io/design%20principle/Encapsulation</id><content type="html" xml:base="https://nu-lungji.github.io/design%20principle/Encapsulation/"><![CDATA[<p><strong>“캡슐화”는 하나의 클래스가 특정 목적을 위해 연관된 속성과 기능을 하나로 묶어 관리하고, 외부에는 필요한 기능만 제공하여 내부 데이터와 기능을 보호하는 특성</strong>을 “<strong>캡슐화</strong>“라고 말한다. 캡슐화를 적용하면, 연관된 기능과 데이터가 하나의 클래스에서 관리가 되므로, 코드 구조를 파악하는데 도움을 주면서 유지 보수를 용이하게 한다. 또한 클래스의 ‘접근 지정자’를 통해 외부에서 접근과 수정을 제한시키고, 필요한 기능만을 제공시킬 수 있다. 그렇기에, 외부에서는 클래스 내부 구현을 알 필요가 없도록 하고, 제공되는 기능만 사용하도록 만들 수 있다.</p>

<h3 id="접근-지정자">접근 지정자</h3>
<p>C++ 기준으로 접근 지정자는 <code class="language-plaintext highlighter-rouge">public, protected, private</code> 총 3개 있다. 각각 기능을 제공하는 범위가 다르기 때문에 캡슐화를 적절하게 적용하려면 3개의 범위를 정확하게 알 필요가 있다.</p>
<ul>
  <li><strong>public</strong> : 해당 클래스 내부와 외부에서 모두 접근이 가능하고, 해당 클래스를 상속 받는 하위 클래스도 접근이 가능하다. 주로 외부에 제공하기 위한 기능이나 인터페이스를 public을 사용하여 제공한다.</li>
  <li><strong>protected</strong> : 해당 클래스의 내부와 상속 받는 하위 클래스의 내부까지 접근이 가능하다. 외부에서는 접근이 불가하며, 주로 데이터, 기능의 보호와 상속을 목적으로 많이 쓰인다.</li>
  <li><strong>private</strong> : 해당 클래스 내부에서만 사용이 가능하다. 즉, 상속/외부 전부 접근과 수정이 불가하다. 내부 데이터와 기능을 보호하고자 사용한다. (friend는 예외)
    <ul>
      <li>해당 클래스를 상속받는 하위 클래스에도 상위 클래스에 해당하는 부분을 포함하고 있기에, 데이터와 기능은 존재하지만, private 멤버에 접근은 불가하다.</li>
    </ul>
  </li>
</ul>

<h3 id="class--struct">Class &amp; Struct</h3>
<p>C++에는 Class와 Struct가 default로 접근 지정자가 선언이 되어 있다. 즉, 접근 지정자를 아예 선언하지 않고, 변수/함수를 선언하면, 자동으로 Class는 <code class="language-plaintext highlighter-rouge">private</code>, Struct는 <code class="language-plaintext highlighter-rouge">public</code>으로 지정된다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">BaseStruct</span> <span class="p">{</span>
	<span class="kt">int</span>  <span class="n">Data</span><span class="p">;</span>		  <span class="c1">// public</span>
	<span class="kt">void</span> <span class="n">Get_Data</span><span class="p">();</span>  <span class="c1">// public</span>
<span class="p">};</span>

<span class="k">class</span> <span class="nc">BaseClass</span> <span class="p">{</span>
	<span class="kt">int</span>  <span class="n">Data</span><span class="p">;</span>		  <span class="c1">// private</span>
	<span class="kt">void</span> <span class="n">Get_Data</span><span class="p">();</span>  <span class="c1">// private</span>
<span class="p">};</span>
</code></pre></div></div>

<h3 id="getter--setter">Getter / Setter</h3>

<p>Getter / Setter는 데이터의 값을 읽거나 쓸 때 자주 쓰이는 함수로 대체로 외부에서 접근이 가능하게 만들 것이다. 데이터를 보호 하면서, 외부에서 접근할 수 있도록 만드는 브릿지 역할을 하기에, 많이 쓰일 것이다. (본인도 그렇다.) 하지만, 모든 데이터에 대해 Getter / Setter를 만들게 되면, 불필요하게 외부에 데이터를 노출시키고, 수정이 가능해져, 캡슐화의 효과가 약해지게 된다. 그렇기에, Getter / Setter가 ‘기능에 영향을 주는지’, ‘꼭 필요한지’ 다시 한 번 생각해보고, 만드는 것이 좋다. 또한 대안책으로 ‘행동 중심 기능’이 있다.</p>

<p>예를 들어보자. { Monster는 HP를 가지고 있다. 공격을 받으면 HP가 감소하고, 일정 시간 지나면 최대 체력의 N% 회복 시킨다. } 이를 통해서 HP와 관련된 함수를 2개 만들어야 한다는 것을 알게 된다. Getter / Setter도 가능은 하지만, HP를 꼭 읽어와서 Damage만큼 빼준 후, Set을 하는 것 보다. 객체에 행동을 요청하는 방식으로, Damage를 넘겨주면 HP를 감소시키는 기능을 만드는 것이 <strong>데이터 보호, 재사용성, 유지보수</strong> 등에 더 좋을 것이다. 회복도 똑같다. N% 를 넘겨주면 HP가 회복되는 기능을 만드는 것이 여러 방면에서 이점을 준다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">class</span> <span class="nc">Monster</span> <span class="p">{</span>
<span class="nl">public:</span>
	<span class="k">const</span> <span class="kt">float</span><span class="o">&amp;</span> <span class="n">GetHP</span><span class="p">()</span>   <span class="p">{</span> <span class="k">return</span> <span class="n">HP</span><span class="p">;</span> <span class="p">}</span>
	<span class="kt">void</span>  <span class="n">SetHP</span><span class="p">(</span><span class="kt">float</span> <span class="n">_HP</span><span class="p">)</span> <span class="p">{</span> <span class="n">HP</span> <span class="o">=</span> <span class="n">_HP</span><span class="p">;</span>  <span class="p">}</span>
	
	<span class="k">const</span> <span class="kt">float</span><span class="o">&amp;</span> <span class="n">GetMaxHP</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="n">MaxHP</span><span class="p">;</span> <span class="p">}</span>
	
	<span class="kt">void</span>  <span class="n">TakeDamage</span><span class="p">(</span><span class="kt">float</span> <span class="n">_HP</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
	<span class="kt">void</span>  <span class="n">Healing</span><span class="p">(</span><span class="kt">float</span> <span class="n">_Percentage</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
	
<span class="nl">private:</span>
	<span class="kt">float</span> <span class="n">HP</span><span class="p">,</span> <span class="n">MaxHP</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="c1">// Getter / Setter 만 사용할 때</span>
	<span class="p">{</span>
		<span class="n">Monster</span> <span class="n">M1</span><span class="p">;</span>
	
		<span class="kt">float</span> <span class="n">CurHP_A</span> <span class="o">=</span> <span class="n">M1</span><span class="p">.</span><span class="n">GetHP</span><span class="p">();</span>
		<span class="n">M1</span><span class="p">.</span><span class="n">SetHP</span><span class="p">(</span><span class="n">CurHP_A</span> <span class="o">-</span> <span class="mf">10.</span><span class="n">f</span><span class="p">);</span>
	
		<span class="kt">float</span> <span class="n">MaxHP</span> <span class="o">=</span> <span class="n">M1</span><span class="p">.</span><span class="n">GetMaxHP</span><span class="p">();</span>
		<span class="kt">float</span> <span class="n">CurHP_B</span> <span class="o">=</span> <span class="n">M1</span><span class="p">.</span><span class="n">GetHP</span><span class="p">();</span>
	
		<span class="n">M1</span><span class="p">.</span><span class="n">SetHP</span><span class="p">(</span><span class="n">CurHP_B</span> <span class="o">+</span> <span class="n">MaxHP</span> <span class="o">*</span> <span class="mf">0.05</span><span class="n">f</span><span class="p">);</span>
	<span class="p">}</span>
	
	<span class="c1">// 행동 중심 기능을 사용할 때</span>
	<span class="p">{</span>
		<span class="n">Monster</span> <span class="n">M2</span><span class="p">;</span>
		
		<span class="n">M2</span><span class="p">.</span><span class="n">TakeDamage</span><span class="p">(</span><span class="mf">10.</span><span class="n">f</span><span class="p">);</span>
		
		<span class="n">M2</span><span class="p">.</span><span class="n">Healing</span><span class="p">(</span><span class="mf">0.05</span><span class="n">f</span><span class="p">);</span>
	<span class="p">}</span>
	
	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>위 코드와 같이 호출하는 쪽에서도 코드도 간단해지고, 의미도 명확해져, 가독성도 챙길 수 있다. 이렇게 행동 주체를 외부가 아닌 내부로 두면 되면, 코드의 의도를 표현하기도 수월해진다.
단순히 데이터를 감추고(private), Getter / Setter를 외부에 제공하는 것은 내부 구현 방식이나, 상태가 드러나기 때문에, 완전한 정보 은닉이라고 할 수 없다. 그렇기에, 기능의 로직은 내부에 감추고, 인터페이스만 외부에 제공하는 것이 진정한 정보 은닉이면서, 높은 수준의 캡슐화이다.</p>

<h3 id="캡슐화--정보은닉">캡슐화 = 정보은닉?</h3>

<p>캡슐화를 하면 정보은닉이 된다. 라고 하면 모든 데이터와 기능을 public으로 두면 성립하지 않는다. 캡슐화와 정보 은닉을 구분할 필요가 있는데, “캡슐화”는 연관된 속성과 기능을 하나로 묶어 관리하는 속성이고, “정보 은닉”은 외부에서 데이터 접근을 제한시키는 설계 기법이다. 그렇기에 캡슐화는 정보 은닉을 위한 <strong>필요조건</strong>이지만 <strong>충분조건은 아니다.</strong></p>]]></content><author><name>LungJi</name></author><category term="Design Principle" /><category term="#OOP" /><category term="#Encapsulation" /><summary type="html"><![CDATA[“캡슐화”는 하나의 클래스가 특정 목적을 위해 연관된 속성과 기능을 하나로 묶어 관리하고, 외부에는 필요한 기능만 제공하여 내부 데이터와 기능을 보호하는 특성을 “캡슐화“라고 말한다. 캡슐화를 적용하면, 연관된 기능과 데이터가 하나의 클래스에서 관리가 되므로, 코드 구조를 파악하는데 도움을 주면서 유지 보수를 용이하게 한다. 또한 클래스의 ‘접근 지정자’를 통해 외부에서 접근과 수정을 제한시키고, 필요한 기능만을 제공시킬 수 있다. 그렇기에, 외부에서는 클래스 내부 구현을 알 필요가 없도록 하고, 제공되는 기능만 사용하도록 만들 수 있다.]]></summary></entry><entry><title type="html">OOP - Inheritance</title><link href="https://nu-lungji.github.io/design%20principle/Inheritance/" rel="alternate" type="text/html" title="OOP - Inheritance" /><published>2026-09-14T08:10:00+00:00</published><updated>2026-09-14T00:00:00+00:00</updated><id>https://nu-lungji.github.io/design%20principle/Inheritance</id><content type="html" xml:base="https://nu-lungji.github.io/design%20principle/Inheritance/"><![CDATA[<p>“<strong>상속(Inheritance)</strong>“은 기존 클래스의 속성/기능을 기반으로 새로운 클래스를 정의하는 OOP 기능이다. 하나의 클래스가 다른 클래스의 속성을 물려받고 활용하기 위한 목적을 가진다.</p>

<p>[상속의 장점]</p>
<ul>
  <li><strong>코드 재사용성</strong> : 상속을 사용하면, 공통적인 속성이나 메서드를 계속 정의할 필요가 없다. 부모 클래스에서 최초 한 번 구현 후에는, 자식 클래스에서 물려받아 중복 구현을 줄일 수 있다.</li>
  <li><strong>유지보수에 용이</strong> : 자식들의 공통된 로직을 부모 클래스에서 관리하므로, 동일한 코드를 각각 수정할 필요가 없어 빠른 수정이 가능하다.</li>
</ul>

<p>[상속의 단점]</p>
<ul>
  <li><strong>강한 결합도</strong> : 상속 구조가 복잡해지면, 부모 클래스의 수정이 자식 클래스에게 예상치 못한 영향을 주게 된다.</li>
  <li><strong>캡슐화 약화</strong> : 자식 클래스가 부모에 구현된 속성과 메서드에 의존하게 되는 경우, 부모의 구현 세부사항이 자식 클래스에서 노출되어, 캡슐화의 정보 은닉의 특성을 약화시키는 상황을 만든다.
    <h3 id="기본-상속">기본 상속</h3>
  </li>
</ul>

<p>C++ 에서 상속을 받는 것은 ‘접근 제어 지시자’와 ‘부모 클래스 명’ 만 붙여주면 된다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Cat</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Animal</span> <span class="p">{</span>  <span class="c1">// Cat이 자식, Animal이 부모</span>
	<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>접근 제어 지시자는 클래스 속성이나 메서드에 대해 ‘외부 접근 범위’를 제한하는 용도로 쓰이는데, 이 ‘범위’를 자세하게 알 필요가 있다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Parent</span> <span class="p">{</span>
<span class="nl">public:</span>
	<span class="kt">void</span> <span class="n">PublicMethod</span><span class="p">()</span> <span class="p">{}</span>
<span class="nl">protected:</span>
	<span class="kt">void</span> <span class="n">ProtectedMethod</span><span class="p">()</span> <span class="p">{}</span>
<span class="nl">private:</span>
	<span class="kt">void</span> <span class="n">PrivateMethod</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">};</span>

<span class="k">class</span> <span class="nc">PublicChild</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Parent</span> <span class="p">{</span>
	<span class="c1">// PublicMethod(), ProtectedMethod() 접근 가능 / PrivateMethod() 접근 불가</span>
<span class="p">};</span>
<span class="k">class</span> <span class="nc">ProtectedChild</span> <span class="o">:</span> <span class="k">protected</span> <span class="n">Parent</span> <span class="p">{</span>
	<span class="c1">// PublicMethod(), ProtectedMethod() 접근 가능 / PrivateMethod() 접근 불가</span>
<span class="p">};</span>
<span class="k">class</span> <span class="nc">PrivateChild</span> <span class="o">:</span> <span class="k">private</span> <span class="n">Parent</span> <span class="p">{</span>
	<span class="c1">// PublicMethod(), ProtectedMethod() 접근 가능 / PrivateMethod() 접근 불가</span>
<span class="p">};</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">PublicChild</span> <span class="n">PubC</span><span class="p">;</span>
	<span class="n">PubC</span><span class="p">.</span><span class="n">PublicMethod</span><span class="p">();</span>     <span class="c1">// O</span>
	<span class="c1">// PubC.ProtectedMethod();   // X</span>

	<span class="n">ProtectedChild</span> <span class="n">ProC</span><span class="p">;</span>
	<span class="c1">// ProC.PublicMethod();     // X</span>
	<span class="c1">// ProC.ProtectedMethod();   // X</span>
	
	<span class="n">PrivateChild</span> <span class="n">PriC</span><span class="p">;</span>
	<span class="c1">// PriC.PublicMethod();     // X</span>
	<span class="c1">// PriC.ProtectedMethod();   // X</span>

	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>이 예시를 들어보면, public/protected/private 전부 부모의 public, protected에 접근이 가능하다. 하지만, 부모의 private는 오직 부모에서만 사용이 가능하다. 그렇기에 3개의 자식 클래스들은<code class="language-plaintext highlighter-rouge">PublicMethod(), ProtectedMethod()</code>만 사용이 가능한데, 여기서 또 주의를 해야 할 것이 ‘<strong>자식이 어떻게 상속 받았는가</strong>‘이다. main함수에서 이 관계를 간단하게 보여주는데, 자세한건 밑에 표에서 확인할 수 있는데, ‘부모 접근 제어 지시자가 A고, 상속 타입이 B라면 자식에서 C가 된다.’ 라는 순으로 표를 보면 된다.</p>

<table border="1">
  <thead>
  <colgroup> 
	<col style="width: 30%;" /> <col style="width: 40%;" /> <col style="width: 50%;" /></colgroup> 
    <tr>
      <th align="center">상속 타입</th>
      <th align="center">부모 접근 제어 지시자</th>
      <th align="center">자식 접근 제어 지시자</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td rowspan="2" align="center" valign="middle">public</td>
      <td align="center">public</td>
      <td align="center">public</td>
    </tr>
    <tr>
      <td align="center">protected</td>
      <td align="center">protected</td>
    </tr>
    <tr>
      <td rowspan="2" align="center" valign="middle">protected</td>
      <td align="center">public</td>
      <td align="center">protected</td>
    </tr>
    <tr>
      <td align="center">protected</td>
      <td align="center">protected</td>
    </tr>
    <tr>
      <td rowspan="2" align="center" valign="middle">private</td>
      <td align="center">public</td>
      <td align="center">private</td>
    </tr>
    <tr>
      <td align="center">protected</td>
      <td align="center">private</td>
    </tr>
  </tbody>
</table>

<p>다시 처음부터 정리해보면, <strong>“부모의 private 멤버는 자식에서 접근할 수 없다.”</strong> 것이 <strong>상속 관계의 조건.</strong> 외부에서 부모/자식 멤버에 접근하려면 무조건 <strong>“public 선언, public 상속.”</strong> 자식이 또 다른 자식에게 상속은 <strong>“public/proteced 선언 &amp; 상속”</strong> 그 외는 private. 상속 후, 이것이 <strong>접근 제어 지시자 전환.</strong></p>
<h3 id="다중-상속">다중 상속</h3>

<p>C++에서는 두 가지 이상의 상위 클래스를 상속 받을 수 있다. 따라서 여러 상위 클래스의 멤버들을 사용할 수 있어, 유연성과 확장성이 더욱 증가하지만, 반면에 복잡도가 배로 증가한다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">A</span> <span class="p">{</span>
<span class="nl">public:</span>
	<span class="kt">void</span> <span class="n">Update</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">};</span>
<span class="k">class</span> <span class="nc">B</span> <span class="p">{</span>
<span class="nl">public:</span>
	<span class="kt">void</span> <span class="n">Render</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">};</span>
<span class="k">class</span> <span class="nc">C</span> <span class="o">:</span> <span class="k">public</span> <span class="n">A</span><span class="p">,</span> <span class="k">public</span> <span class="n">B</span> <span class="p">{</span>
<span class="nl">public:</span>
	<span class="kt">void</span> <span class="n">Main</span><span class="p">()</span> <span class="p">{</span>
		<span class="n">Update</span><span class="p">();</span>
		<span class="n">Render</span><span class="p">();</span>
	<span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>다중 상속은 장점도 크지만, 단점도 확실하기에 신중하게 써야한다. 그 중에 상속받는 상위 클래스의 ‘메서드 이름 충돌’, ‘다이아몬드 문제’가 있을 수 있는데, ‘다이아몬드 문제’는 해결에 한 번 코드로 보겠다.</p>

<p>※ 원인</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">A</span> <span class="p">{</span> 
<span class="nl">public:</span> 
	<span class="kt">void</span> <span class="n">Render</span><span class="p">(){};</span>
<span class="p">};</span> 

<span class="k">class</span> <span class="nc">B</span> <span class="o">:</span> <span class="k">public</span> <span class="n">A</span> <span class="p">{</span> <span class="p">};</span>
<span class="k">class</span> <span class="nc">C</span> <span class="o">:</span> <span class="k">public</span> <span class="n">A</span> <span class="p">{</span> <span class="p">};</span>
<span class="k">class</span> <span class="nc">D</span> <span class="o">:</span> <span class="k">public</span> <span class="n">B</span><span class="p">,</span> <span class="k">public</span> <span class="n">C</span><span class="p">{</span> <span class="p">};</span>

<span class="c1">//      A</span>
<span class="c1">//     / \</span>
<span class="c1">//    B   C</span>
<span class="c1">//     \ /</span>
<span class="c1">//      D</span>

 <span class="kt">int</span> <span class="n">main</span><span class="p">(){</span> 
	 <span class="n">D</span> <span class="n">dd</span><span class="p">;</span> 
	 <span class="n">dd</span><span class="p">.</span><span class="n">Render</span><span class="p">();</span> <span class="c1">// 에러 : 모호성 발생 </span>
<span class="p">}</span>
</code></pre></div></div>

<p>D객체 가 생성되고 상위 클래스인 B, C도 생성이 되면서 B에도 상위 객체A, C에도 상위 객체A가 생겨 중복이 된다. 생성에는 문제가 없지만, A에 있는 메서드를 사용하면서 어느 객체의 메서드를 사용해야 하는지 모호해지는 일이 발생하여 컴파일 오류를 내게된다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">D</span> <span class="n">dd</span><span class="p">;</span> 
<span class="n">dd</span><span class="p">.</span><span class="n">B</span><span class="o">::</span><span class="n">Render</span><span class="p">();</span>
</code></pre></div></div>
<p>이런 방식으로 어떤 객체의 Render인지 명시해주면 해결은 되지만, 해당 멤버에 대한 접근 권한이 있어야 한다. 충돌한 메서드가 많이 없는 경우에는 괜찮지만, 외부에서는 변수거나 메서드가 public이 아닌 경우 이를 사용하지 못하게 된다.</p>

<p>그래서, 다이아몬드 상속이 발생하고, 부모를 하나만 공유해야 한다면, “<strong>가상 상속</strong>“을 사용 해야한다.</p>

<h3 id="가상-상속">가상 상속</h3>

<p>가상 상속은 다중 상속시에 상위 클래스들이 조상 클래스를 공통으로 가지고 있다면, 조상 클래스의 객체가 둘 이상 생겨 모호성이 발생하는데, 하위 클래스 객체에 동일한 조상 클래스 객체가 중복 생성되는 것을 막고, 하나만 공유하게 만드는 기능이다.</p>

<p>※ 해결</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">A</span> <span class="p">{</span> 
<span class="nl">public:</span> 
	<span class="kt">void</span> <span class="n">Render</span><span class="p">(){};</span>
<span class="p">};</span> 

<span class="k">class</span> <span class="nc">B</span> <span class="o">:</span> <span class="k">virtual</span> <span class="k">public</span> <span class="n">A</span> <span class="p">{</span> <span class="p">};</span>  <span class="c1">// virtual 키워드를 붙여 가상 상속</span>
<span class="k">class</span> <span class="nc">C</span> <span class="o">:</span> <span class="k">virtual</span> <span class="k">public</span> <span class="n">A</span> <span class="p">{</span> <span class="p">};</span>
<span class="k">class</span> <span class="nc">D</span> <span class="o">:</span> <span class="k">public</span> <span class="n">B</span><span class="p">,</span> <span class="k">public</span> <span class="n">C</span><span class="p">{</span> <span class="p">};</span>

 <span class="kt">int</span> <span class="n">main</span><span class="p">(){</span> 
	 <span class="n">D</span> <span class="n">dd</span><span class="p">;</span> 
	 <span class="n">dd</span><span class="p">.</span><span class="n">Render</span><span class="p">();</span>  <span class="c1">// O</span>
<span class="p">}</span>
</code></pre></div></div>
<p>위 코드 같이 객체가 중복으로 생기는 대상 클래스를 상속 받는 쪽에 <code class="language-plaintext highlighter-rouge">virtual</code> 키워드를 붙여 해결할 수 있다.</p>

<p>Q. 그러면 상속을 받는 모든 곳을 가상 상속을 시켜서 초기에 중복을 방지하면 되는거 아닌가?
했을 때, 답은 <strong>“아니다.”</strong> 가상 상속의 단점은 “<strong>복잡도</strong>“와 “<strong>오버헤드</strong>“인데, 기본 상속은 상위 객체의 위치가 비교적 단순하게 정해지는 반면에, 가상 상속은 컴파일러가 상위 객체를 탐색하려고 추가적인 포인터 변수, 테이블이 필요할 수 있어 비효율적이다. 또한 ‘생성자 규칙’이 변하는데, 아래 코드에서 확인해보자.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">A</span>  <span class="p">{</span>  
<span class="nl">public:</span>  
    <span class="n">A</span><span class="p">(</span><span class="kt">int</span> <span class="n">value</span><span class="p">)</span>  <span class="p">{</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"A Constructor : "</span> <span class="o">&lt;&lt;</span> <span class="n">value</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="p">}</span>  
    <span class="o">~</span><span class="n">A</span><span class="p">()</span>          <span class="p">{</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"A Destructor"</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="p">}</span>
<span class="p">};</span>  
  
<span class="k">class</span> <span class="nc">B</span> <span class="o">:</span> <span class="k">virtual</span> <span class="k">public</span> <span class="n">A</span>  <span class="p">{</span>  
<span class="nl">public:</span>  
    <span class="n">B</span><span class="p">()</span> <span class="o">:</span> <span class="n">A</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="p">{</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"B Constructor"</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="p">}</span>  
    <span class="o">~</span><span class="n">B</span><span class="p">()</span>        <span class="p">{</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"B Destructor"</span>  <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="p">}</span>
<span class="p">};</span>  

<span class="k">class</span> <span class="nc">C</span> <span class="o">:</span> <span class="k">virtual</span> <span class="k">public</span> <span class="n">A</span>  <span class="p">{</span>  
<span class="nl">public:</span>  
    <span class="n">C</span><span class="p">()</span> <span class="o">:</span> <span class="n">A</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span> <span class="p">{</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"C Constructor"</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="p">}</span>  
    <span class="o">~</span><span class="n">C</span><span class="p">()</span>        <span class="p">{</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"C Destructor"</span>  <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="p">}</span>
<span class="p">};</span> 

<span class="k">class</span> <span class="nc">D</span> <span class="o">:</span> <span class="k">public</span> <span class="n">B</span><span class="p">,</span> <span class="k">public</span> <span class="n">C</span>  <span class="p">{</span>  
<span class="nl">public:</span>  
    <span class="n">D</span><span class="p">()</span> <span class="o">:</span> <span class="n">A</span><span class="p">(</span><span class="mi">100</span><span class="p">),</span> <span class="n">B</span><span class="p">(),</span> <span class="n">C</span><span class="p">()</span> <span class="p">{</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"D Constructor"</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="p">}</span>  
    <span class="o">~</span><span class="n">D</span><span class="p">()</span>        <span class="p">{</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"D Destructor"</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="p">}</span>
<span class="p">};</span>  
  
<span class="kt">int</span> <span class="n">main</span><span class="p">()</span>  
<span class="p">{</span>  
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"===== Create B ====="</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>  
    <span class="p">{</span> 
	    <span class="n">B</span> <span class="n">b</span><span class="p">;</span>  
    <span class="p">}</span>  
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>  
  
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"===== Create C ====="</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>  
    <span class="p">{</span>        
	    <span class="n">C</span> <span class="n">c</span><span class="p">;</span>  
    <span class="p">}</span>  
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>  
  
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"===== Create D ====="</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>  
    <span class="p">{</span>        
		<span class="n">D</span> <span class="n">d</span><span class="p">;</span>  
    <span class="p">}</span>  
    
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>  
<span class="p">}</span>

<span class="c1">//결과 :</span>

<span class="c1">//===== Create B =====</span>
<span class="c1">//A Constructor : 10</span>
<span class="c1">//B Constructor</span>
<span class="c1">//B Destructor</span>
<span class="c1">//A Destructor</span>

<span class="c1">//===== Create C =====</span>
<span class="c1">//A Constructor : 20</span>
<span class="c1">//C Constructor</span>
<span class="c1">//C Destructor</span>
<span class="c1">//A Destructor</span>

<span class="c1">//===== Create D =====</span>
<span class="c1">//A Constructor : 100</span>
<span class="c1">//B Constructor</span>
<span class="c1">//C Constructor</span>
<span class="c1">//D Constructor</span>
<span class="c1">//D Destructor</span>
<span class="c1">//C Destructor</span>
<span class="c1">//B Destructor</span>
<span class="c1">//A Destructor</span>
</code></pre></div></div>

<p>가상 상속에서는 가장 먼저 공유되는 객체 A가 생성되고, 이후 B &gt; C &gt; D 순으로 생성된다. D클래스가 A클래스를 직접 생성하고 있다는 점에서, 가상 상속은 ‘<strong>최종 자식 클래스가 가상 상위 클래스를 초기화한다.</strong>’ 라고 할 수 있다. 이런 ‘생성자 규칙’ 변화로 인해 예상치 못하는 결과가 나올 수 있다는 위험이 따르기에, 가상 상속은 다중 상속에서 공통 객체를 공유해야 할 때, 쓰는 기능 정도로만 보면 좋을것 같다.</p>]]></content><author><name>LungJi</name></author><category term="Design Principle" /><category term="#OOP" /><category term="#Inheritance" /><summary type="html"><![CDATA[“상속(Inheritance)“은 기존 클래스의 속성/기능을 기반으로 새로운 클래스를 정의하는 OOP 기능이다. 하나의 클래스가 다른 클래스의 속성을 물려받고 활용하기 위한 목적을 가진다.]]></summary></entry><entry><title type="html">SOLID [Object-Oriented Design]</title><link href="https://nu-lungji.github.io/design%20principle/SOLID/" rel="alternate" type="text/html" title="SOLID [Object-Oriented Design]" /><published>2026-09-10T08:10:00+00:00</published><updated>2026-09-10T00:00:00+00:00</updated><id>https://nu-lungji.github.io/design%20principle/SOLID</id><content type="html" xml:base="https://nu-lungji.github.io/design%20principle/SOLID/"><![CDATA[<p>개념 : 객체 지향 프로그래밍 설계의 다섯 가지 기본 원칙.
효과 : 코드 확장의 유연성 향상, 유지 보수성 향상, 불필요한 복잡성 감소 -&gt; 생산성 증가</p>

<h3 id="단일-책임-원칙--srpsingleresponsibilityprinciple">단일 책임 원칙 : SRP(Single Responsibility Principle)</h3>
<blockquote>
  <p>[!tip] 단일 책임 원칙 : SRP(Single Responsibility Principle)
<strong>객체는 오직 하나의 책임을 가져야 한다.</strong></p>
</blockquote>

<p>즉, 하나의 클래스가 하나의 역할을 하도록 분리하는 원칙.
-&gt; SRP가 지켜지지 않는 경우, 수정 사항이 생겼을 때, 많은 코드에 영향을 끼치게 된다. 이는 추가적인 수정 사항/오류를 발생시키는 위험이 있고, 코드가 더 복잡해지게 만든다.</p>

<p>그렇기에, <strong>SRP</strong>는 곧 ‘<strong>구조 단순화</strong>‘와 ‘<strong>유지 보수</strong>‘의 효율을 높이는 설계 방법이다.</p>
<h3 id="개방-폐쇄-원칙--ocpopen-closedprinciple">개방 폐쇄 원칙 : OCP(Open-Closed Principle)</h3>
<blockquote>
  <p>[!tip] 개방 폐쇄 원칙 : OCP(Open-Closed Principle)
<strong>확장에 개방적이어야 하며, 수정에는 폐쇄적이어야 한다.</strong></p>
</blockquote>

<p>클래스 설계 시에는 기존의 코드를 수정하는 대신 새로운 코드 추가를 통해서 기능 확장을 하는 구조여야 한다는 것이다. 그러한 구조를 설계하기 위해서, 대표적으로 인터페이스를 많이 쓴다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Attackable</span> <span class="p">{</span>
	<span class="k">virtual</span> <span class="kt">void</span> <span class="n">Attack</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">class</span> <span class="nc">Movable</span> <span class="p">{</span>
	<span class="k">virtual</span> <span class="kt">void</span> <span class="n">Move</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">class</span> <span class="nc">Flyable</span> <span class="p">{</span>
	<span class="k">virtual</span> <span class="kt">void</span> <span class="n">Fly</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">class</span> <span class="nc">Dragon</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Attackable</span><span class="p">,</span> <span class="k">public</span> <span class="n">Flyable</span>  <span class="p">{</span>
	<span class="kt">void</span> <span class="n">Attack</span><span class="p">()</span> <span class="k">override</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
	<span class="kt">void</span> <span class="n">Fly</span><span class="p">()</span> <span class="k">override</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
<span class="p">};</span>

<span class="k">class</span> <span class="nc">Goblin</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Attackable</span><span class="p">,</span> <span class="k">public</span> <span class="n">Movable</span> <span class="p">{</span>
	<span class="kt">void</span> <span class="n">Attack</span><span class="p">()</span> <span class="k">override</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
	<span class="kt">void</span> <span class="n">Move</span><span class="p">()</span> <span class="k">override</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
<span class="p">};</span>

<span class="k">class</span> <span class="nc">CombatSystem</span> <span class="p">{</span>
	<span class="kt">void</span> <span class="n">Combat</span><span class="p">(</span><span class="n">Attackable</span><span class="o">*</span> <span class="n">_Attackable</span><span class="p">)</span> <span class="p">{</span> <span class="n">_Attackable</span><span class="o">-&gt;</span><span class="n">Attack</span><span class="p">();</span> <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>몬스터를 추가(기능 확장)할 때, 클래스를 만들고 인터페이스를 상속받아 구현하는 것이 끝이다. 인터페이스가 변경되지 않는 이상, CombatSystem도 변경될 일이 없게 된다.
OOP의 핵심 개념인 ‘추상화’를 통해 코드를 확장하여 생산성을 증가시키는 중요 원칙이다.</p>

<h3 id="리스코프-치환-원칙--lspliskovsubstitutionprinciple">리스코프 치환 원칙 : LSP(Liskov Substitution Principle)</h3>
<blockquote>
  <p>[!tip] 리스코프 치환 원칙 : LSP(Liskov Substitution Principle)
<strong>하위 클래스는 상위 클래스에 대해 완전하게 대체할 수 있어야 한다.</strong></p>
</blockquote>

<p>대표적으로 ‘직사각형 - 정사각형 예시’가 이 원칙을 위반하는 사례를 잘 보여주는데,</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Rectangle</span> <span class="p">{</span>
<span class="nl">protected:</span>
    <span class="kt">double</span> <span class="n">width</span><span class="p">;</span>
    <span class="kt">double</span> <span class="n">height</span><span class="p">;</span>
    
<span class="nl">public:</span>
	<span class="k">virtual</span> <span class="kt">double</span> <span class="n">area</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="n">width</span> <span class="o">*</span> <span class="n">height</span><span class="p">;</span> <span class="p">}</span>
	<span class="k">virtual</span> <span class="kt">void</span> <span class="n">setWidth</span><span class="p">(</span><span class="kt">double</span> <span class="n">width</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">this</span><span class="o">-&gt;</span><span class="n">width</span> <span class="o">=</span> <span class="n">width</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="k">virtual</span> <span class="kt">void</span> <span class="n">setHeight</span><span class="p">(</span><span class="kt">double</span> <span class="n">height</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">this</span><span class="o">-&gt;</span><span class="n">height</span> <span class="o">=</span> <span class="n">height</span><span class="p">;</span>
	<span class="p">}</span>
<span class="p">};</span>

<span class="k">class</span> <span class="nc">Square</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Rectangle</span> <span class="p">{</span>
	<span class="kt">void</span> <span class="n">setWidth</span><span class="p">(</span><span class="kt">double</span> <span class="n">width</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="o">-&gt;</span><span class="n">width</span> <span class="o">=</span> <span class="n">width</span><span class="p">;</span>
        <span class="k">this</span><span class="o">-&gt;</span><span class="n">height</span> <span class="o">=</span> <span class="n">width</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="kt">void</span> <span class="n">setHeight</span><span class="p">(</span><span class="kt">double</span> <span class="n">height</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="o">-&gt;</span><span class="n">width</span> <span class="o">=</span> <span class="n">height</span><span class="p">;</span>
        <span class="k">this</span><span class="o">-&gt;</span><span class="n">height</span> <span class="o">=</span> <span class="n">height</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">};</span>

<span class="kt">void</span> <span class="n">Process</span><span class="p">(</span><span class="n">Rectangle</span><span class="o">&amp;</span> <span class="n">_Rect</span><span class="p">){</span>
	<span class="n">_Rect</span><span class="p">.</span><span class="n">setWidth</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
	<span class="n">_Rect</span><span class="p">.</span><span class="n">setHeight</span><span class="p">(</span><span class="mi">4</span><span class="p">);</span>
	<span class="n">assert</span><span class="p">(</span><span class="n">_Rect</span><span class="p">.</span><span class="n">area</span><span class="p">()</span> <span class="o">==</span> <span class="mi">20</span><span class="p">);</span>  <span class="c1">// _Rect가 Square 객체라면, 결과가 16, 오류 발생!</span>
<span class="p">}</span>
</code></pre></div></div>
<p>만약 Process 함수에 Rectangle 클래스 객체가 들어오면 assert에 걸리지 않게 된다. 하지만 Square 클래스 객체가 들어온다면, 결과가 16이 되어 assert에 걸려 에러를 발생시킨다. 중요한 문제는 이 <strong>오류가 런타임에 발생</strong>한다는 것인데, 이는 작업의 효율을 늦추게 만드는 잠재적 원인이 된다.</p>

<p>LSP는 이런 ‘다형성’을 부적절하게 설계하는 문제를 막기 위한 원칙이다. 이를 만족하기 위해서는 인터페이스 클래스로 공통 분모를 나누는 게 좋다.</p>

<h3 id="인터페이스-분리-원칙--ispinterface-segregation-principle">인터페이스 분리 원칙 : ISP(Interface Segregation Principle)</h3>
<blockquote>
  <p>[!tip] 인터페이스 분리 원칙 : ISP(Interface Segregation Principle)
<strong>클라이언트가 사용하지 않는 메서드에 의존하게 만들면 안 된다.</strong></p>
</blockquote>

<p>인터페이스 클래스를 상속받은 클래스가 사용하지도 않을 메서드의 구현을 강제해선 안된다는 생각을 기반으로, 인터페이스를 분리하도록 만드는 원칙이다. 반드시 그런 건 아니지만, 인터페이스 클래스가 비대해지는 것은 위에 다뤘던 SRP가 지켜지지 않을 확률이 높고, OCP에서도 불리해질 수 있다.</p>

<p>그렇기에 비대한 인터페이스보다는 메서드가 하나만 있더라도 가벼운 인터페이스가 선호된다.
이 가벼운 인터페이스는 클라이언트가 불필요한 의존성을 가지는 경우를 줄이게 하고, SRP와 OCP를 지키기 쉽게 한다. 또한 불필요한 메서드 구현이 필요 없어지고, 의존성도 줄일 수 있다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 이런 인터페이스 클래스를 사용하는 대신</span>
<span class="k">class</span> <span class="nc">Machine</span> <span class="p">{</span>
    <span class="k">virtual</span> <span class="kt">void</span> <span class="n">print</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">virtual</span> <span class="kt">void</span> <span class="n">scan</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">virtual</span> <span class="kt">void</span> <span class="n">fax</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>

<span class="c1">// 최소한의 기능만 가진 인터페이스로 만들자 </span>
<span class="k">class</span> <span class="nc">Printer</span> <span class="p">{</span>
    <span class="k">virtual</span> <span class="kt">void</span> <span class="n">print</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">class</span> <span class="nc">Scanner</span> <span class="p">{</span>
    <span class="k">virtual</span> <span class="kt">void</span> <span class="n">scan</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">class</span> <span class="nc">Fax</span> <span class="p">{</span>
    <span class="k">virtual</span> <span class="kt">void</span> <span class="n">fax</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>
<h3 id="의존성-역전-원칙--dipdependency-inversion-principle">의존성 역전 원칙 : DIP(Dependency Inversion Principle)</h3>
<blockquote>
  <p>[!tip] 의존성 역전 원칙 : DIP(Dependency Inversion Principle)
<strong>상위 모듈은 하위 모듈에 의존해선 안 되며, ‘추상화’에 의존해야 한다.</strong></p>
</blockquote>

<p>클라이언트는 인터페이스를 상속받은 구체적인 클래스에 의존하면 안 되고, 인터페이스에 의존해야 한다. 또한 구체적인 클래스도 인터페이스에 의존해야 한다. 결국 상위 모듈은 하위 모듈의 구체적인 구현을 알 필요가 없다. 아래 코드로 이해하면 더 쉽다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">PaymentService</span> <span class="p">{</span>  <span class="c1">// interface</span>
	<span class="k">virtual</span> <span class="kt">void</span> <span class="n">pay</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">class</span> <span class="nc">NaverPay</span> <span class="o">:</span> <span class="k">public</span> <span class="n">PaymentService</span> <span class="p">{</span>
	<span class="kt">void</span> <span class="n">pay</span><span class="p">()</span> <span class="k">override</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
<span class="p">};</span>

<span class="k">class</span> <span class="nc">Client</span> <span class="p">{</span> 
<span class="nl">public:</span>
	<span class="kt">void</span> <span class="n">OrderProduct</span><span class="p">(</span><span class="n">PaymentService</span><span class="o">*</span> <span class="n">_Payment</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">_Payment</span><span class="o">-&gt;</span><span class="n">pay</span><span class="p">();</span>
	<span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>여기서 Client는 ‘상위 모듈’, NaverPay가 ‘하위 모듈’이 된다. PaymentService라는 추상화에 의존하여 결제 수단이 많아져도 Client의 수정이 없고 클래스 추가밖에 없으므로 확장성이 좋다.</p>

<h3 id="정리--회고">정리 &amp; 회고</h3>
<p>SOLID 원칙은 하나의 원칙이 다른 원칙의 목적 달성에도 영향을 준다. 이는 곧 설계를 잘못했을 때, 생산성 저하로 이어진다고 볼 수 있기에, 성공적인 소프트웨어를 개발하려면 SOLID 원칙이 기본이 되어야 하고, 설계에 대해 끊임없이 연구해야 한다고 생각한다.</p>

<p>본인도 이전까지 프로젝트를 할 때, 파일이 많아지면 찾기가 어려우니까 적은 클래스로 하나의 클래스가 역할을 크게 가져가도록 만들었었다. 지금 생각해보면 그건 설계라기보다 편의에 가까웠기에, 고쳐야 할 점이라고 이 글을 쓰면서 느꼈다. 그리고 다형성을 위한 설계를 습관화하려면, 무작정 클래스 먼저 만들고 나중에 고치기보다, 먼저 노트나 패드에 설계도를 그리는 시간을 갖는 게 효율적이었다는 것을 체감한다.</p>]]></content><author><name>LungJi</name></author><category term="Design Principle" /><category term="#SOLID" /><category term="#SRP" /><category term="#OCP" /><category term="#LSP" /><category term="#ISP" /><category term="#DIP" /><category term="#OOP" /><summary type="html"><![CDATA[개념 : 객체 지향 프로그래밍 설계의 다섯 가지 기본 원칙. 효과 : 코드 확장의 유연성 향상, 유지 보수성 향상, 불필요한 복잡성 감소 -&gt; 생산성 증가]]></summary></entry><entry><title type="html">UML [Unified Modeling Language]</title><link href="https://nu-lungji.github.io/software%20modeling/UML/" rel="alternate" type="text/html" title="UML [Unified Modeling Language]" /><published>2026-09-08T08:10:00+00:00</published><updated>2026-09-08T00:00:00+00:00</updated><id>https://nu-lungji.github.io/software%20modeling/UML</id><content type="html" xml:base="https://nu-lungji.github.io/software%20modeling/UML/"><![CDATA[<p>[ Unified Modeling Language ]</p>

<p>UML(Unified Modeling Language)은 SW 개발 과정에서 시스템의 구조와 기능을 시각화/문서화하기 위한 범용 모델링 언어이다. 대규모 프로젝트의 경우, 복잡한 설계들을 개발자와 개발자, 개발자와 클라이언트(기획자, QA, 디자이너)들이 의사소통을 하는 데 있어, 표준화된 방식을 제공한다. 이를 통해서, 프로젝트 진행을 원활하게 만들고, 시각화를 통해서 프로젝트를 빠르게 이해시키는 기능을 가지고 있다.</p>

<hr />

<h2 id="relationships">Relationships</h2>

<h3 id="의존-관계-dependency"><font color="#953734"><font color="#e36c09">의존 관계 (Dependency)</font></font></h3>
<ul>
  <li><strong>개념 :</strong> A클래스가 B클래스를 일시적으로 참조하는 상태, B클래스의 Method를 일시적으로 사용, 필요할 때 잠시 사용하는 관계 -&gt; Decoupling에 매우 좋은 관계, 생명주기와 연관 X
    <ul>
      <li>B클래스 매개변수, A클래스 함수 내부의 B클래스 지역변수 선언, B클래스 return 타입</li>
    </ul>
  </li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CreditCard</span> <span class="p">{...}</span>
<span class="k">class</span> <span class="nc">Receipt</span> <span class="p">{...}</span>
<span class="k">class</span> <span class="nc">PaymentService</span> <span class="p">{</span> 
<span class="nl">public:</span> 
	<span class="c1">// 1. 매개변수로 참조 전달받아 사용</span>
	<span class="kt">void</span> <span class="n">ProcessPayment</span><span class="p">(</span><span class="k">const</span> <span class="n">CreditCard</span><span class="o">&amp;</span> <span class="n">card</span><span class="p">,</span> <span class="kt">int</span> <span class="n">amount</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> 
		<span class="n">card</span><span class="p">.</span><span class="n">pay</span><span class="p">(</span><span class="n">amount</span><span class="p">);</span> 
	<span class="p">}</span> 
	<span class="c1">// 2. 내부 지역 변수로 생성 </span>
	<span class="kt">void</span> <span class="n">IssueReceipt</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> 
		<span class="n">Receipt</span> <span class="n">receipt</span><span class="p">;</span> 
		<span class="n">receipt</span><span class="p">.</span><span class="n">print</span><span class="p">();</span> 
	<span class="p">}</span> 
	<span class="c1">// 3. 리턴 타입으로 반환 </span>
	<span class="n">Receipt</span> <span class="n">CreateReceipt</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Receipt</span><span class="p">();</span> <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<h3 id="연관-관계-association"><font color="#e36c09">연관 관계 (Association)</font></h3>
<ul>
  <li><strong>개념 :</strong> A클래스와 B클래스가 서로 연관되어 있는 관계.
    <ul>
      <li>A클래스의 멤버변수 B클래스 (+ 반대, 양방향)</li>
    </ul>
  </li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Driver</span> <span class="p">{...}</span>
<span class="k">class</span> <span class="nc">Car</span> <span class="p">{</span> 
<span class="nl">private:</span> 
	<span class="n">Driver</span><span class="o">*</span> <span class="n">driver</span><span class="p">;</span>
<span class="nl">public:</span> 
	<span class="n">Car</span><span class="p">(</span><span class="n">Driver</span><span class="o">*</span> <span class="n">driver</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">)</span> <span class="o">:</span> <span class="n">driver</span><span class="p">(</span><span class="n">driver</span><span class="p">)</span> <span class="p">{}</span>  <span class="c1">//생성자로 관계 형성</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="집합-관계-aggregation"><font color="#e36c09">집합 관계 (Aggregation)</font></h3>
<ul>
  <li><strong>개념 :</strong> A클래스(전체)가 여러 B클래스(부분) 객체들을 모아서 관리하는 관계.</li>
  <li><strong>특성 :</strong> 전체가 사라져도(해제) 부분은 사라지지 않고 독립적으로 존재함 (결합도가 낮은 관계)
    <ul>
      <li>A클래스의 멤버변수로 B클래스를 가지지만, 생성자 초기화를 통해 외부에서 생성된 B클래스 객체를 받아와, 멤버변수를 초기화 하는 경우. (A클래스가 해제되어도 B클래스는 영향X)</li>
    </ul>
  </li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Student</span> <span class="p">{...}</span>
<span class="k">class</span> <span class="nc">School</span> <span class="p">{</span> 
<span class="nl">private:</span> 
	<span class="n">vector</span><span class="o">&lt;</span><span class="n">shared_ptr</span><span class="o">&lt;</span><span class="n">Student</span><span class="o">&gt;&gt;</span> <span class="n">students</span><span class="p">;</span>
<span class="nl">public:</span>
	<span class="n">School</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">shared_ptr</span><span class="o">&lt;</span><span class="n">Student</span><span class="o">&gt;&gt;&amp;</span> <span class="n">studentslist</span><span class="p">)</span> <span class="o">:</span> <span class="n">students</span><span class="p">(</span><span class="n">studentslist</span><span class="p">)</span> <span class="p">{}</span>  
<span class="p">}</span>
</code></pre></div></div>

<h3 id="합성-관계-composition"><font color="#e36c09">합성 관계 (Composition)</font></h3>
<ul>
  <li><strong>개념 :</strong> A클래스(전체)가 여러 B클래스(부분) 객체들을 모아서 관리하는 관계.</li>
  <li><strong>특성 :</strong> 전체와 부분이 강한 결합으로 이뤄짐. 전체가 사라지면 부분도 사라지는 특성(집합 관계와 반대).
    <ul>
      <li>A클래스의 멤버변수로 B클래스를 가지는데, A의 생명주기에 B의 생명주기가 종속되는 관계. (A클래스가 해제되면 멤버변수 B클래스도 영향(해제).)</li>
    </ul>
  </li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CPU</span> <span class="p">{...}</span>
<span class="k">class</span> <span class="nc">Computer</span> <span class="p">{</span> 
<span class="nl">private:</span> 
	<span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">CPU</span><span class="o">&gt;</span> <span class="n">cpu</span><span class="p">;</span>
<span class="nl">public:</span> 
	<span class="n">Computer</span><span class="p">()</span> <span class="o">:</span> <span class="n">cpu</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">make_unique</span><span class="o">&lt;</span><span class="n">CPU</span><span class="o">&gt;</span><span class="p">())</span> <span class="p">{</span> <span class="p">}</span> 
	<span class="o">~</span><span class="n">Computer</span><span class="p">()</span> <span class="p">{</span> <span class="p">};</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span> 
	<span class="n">Computer</span><span class="o">*</span> <span class="n">pc</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Computer</span><span class="p">();</span> 
	<span class="c1">// Computer 객체가 만들어지면서 멤버 변수 cpu도 생성자에 의해 동적할당</span>
	<span class="k">delete</span> <span class="n">pc</span><span class="p">;</span>
	<span class="c1">// Computer 객체가 소멸되면서 멤버 변수 cpu도 소멸자 호출</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="일반화-관계-generalization"><font color="#e36c09">일반화 관계 (Generalization)</font></h3>
<ul>
  <li><strong>개념 :</strong> 부모와 자식 관계, 즉 상속 관계(Is-A 관계)를 표현하는 관계이다.</li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Animal</span> <span class="p">{</span>
<span class="nl">public:</span>
	<span class="k">virtual</span> <span class="kt">void</span> <span class="n">Move</span><span class="p">()</span> <span class="p">{}</span>
	<span class="k">virtual</span> <span class="kt">void</span> <span class="n">Sound</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">Gibra</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Animal</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>   <span class="c1">// Gibra is Animal</span>
<span class="k">class</span> <span class="nc">Rabbit</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Animal</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>  <span class="c1">// Rabbit is Animal</span>
</code></pre></div></div>

<h3 id="실체화-관계-realization"><font color="#e36c09">실체화 관계 (Realization)</font></h3>
<ul>
  <li><strong>개념 :</strong> interface 클래스와 interface를 구현하는 클래스와의 관계.</li>
  <li><strong>특성 :</strong> interface 클래스에 정의된 메서드(순수 가상 함수)를 상속받는 클래스가 반드시 interface 클래스의 메서드를 구현해야 한다.</li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Attackable</span> <span class="p">{</span>
<span class="nl">public:</span>
	<span class="k">virtual</span> <span class="kt">void</span> <span class="n">Attack</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">Sword</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Attackable</span> <span class="p">{</span> 
<span class="nl">public:</span>
	<span class="k">virtual</span> <span class="kt">void</span> <span class="n">Attack</span><span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>  <span class="c1">// Attackable 메서드 구현</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">Rifle</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Attackable</span> <span class="p">{</span> 
<span class="nl">public:</span>
	<span class="k">virtual</span> <span class="kt">void</span> <span class="n">Attack</span><span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>  <span class="c1">// Attackable 메서드 구현</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>
    <p><strong>&lt;집합 관계&gt;와 &lt;연관 관계&gt; 모호성 :</strong> &lt;연관 관계&gt;는 두 클래스가 서로 대등한 관계, &lt;집합 관계&gt;는 두 클래스 중에서 전체와 부분 관계로 나뉜다. ex. Car / Person [연관 관계], Car / Wheel [집합 관계] 하지만, &lt;집합 관계&gt;는 &lt;연관 관계&gt;랑 코드 차이도 없고, 전체랑 부분을 나누는 것도 큰 의미를 가지지 않는 이상, &lt;연관 관계&gt;로 대체가 가능하다. 위에서 Car / Wheel을 집합 관계로 설명했지만, 당연히 연관 관계로 나타낼 수 있고, “Wheel은 반드시 Car의 필수 부품이어야 해.”같은 명확한 규칙 없으면 연관 관계로 통일하는게 생각하기 편하다고 한다.</p>
  </li>
  <li>
    <p>관계 표현 방법 : UML에서는 각 관계를 ‘선’으로 표현하는데 각각 다른 선을 사용한다. 조금 헷갈리지만, 구분법은 다음과 같다. 
 <img src="../../assets/images/posts/Software-Architecture/Diagram-Relationship-Line.png" alt="Diagram-Relationship-Line|326" /></p>
    <font color="#595959">※ 출처 - AI 생성형 이미지</font>

    <p>또한 <strong>방향이 중요</strong>한데, 일반화/실체화 경우 { 자식 –&gt; 부모 }, 의존/연관은 { A –&gt; B }, 합성/집합은 { 전체 ◇── 일부 }로 표현할 수 있다.</p>
  </li>
</ul>

<hr />
<h2 id="diagram">Diagram</h2>

<h3 id="클래스-다이어그램-class-diagram">클래스 다이어그램 (Class Diagram)</h3>
<ul>
  <li><strong>개념 :</strong> 클래스의 내부 구성요소들(변수, 함수)과 클래스 간의 관계들을 보여주는 다이어그램.</li>
  <li><strong>사례 :</strong> 아무래도 복잡한 게임 개발에서는 클래스들의 관계 구조화/시각화에 더하여 가시성이 주 목적일 것 같고 , 더 나아가 디자인패턴에서 구조를 나타내는데 잘 쓰인다.
<img src="../../assets/images/posts/Software-Architecture/ClassDiagram_A.png" alt="Diagram2" /></li>
</ul>
<font color="#595959">※ 출처 - AI 생성형 이미지</font>
<ul>
  <li><strong>속성 표현 :</strong> 맨 왼쪽 Product 노드를 보면, 다음과 같이 나타낼 수 있다.</li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Product</span> <span class="p">{</span>
<span class="c1">// + : public, - : private, # : protected로 나타낼 수 있다.</span>
<span class="c1">// 메서드는 [함수명(매개변수) : 리턴타입] 으로 나타낼 수 있고,</span>
<span class="c1">// 변수는 [변수명 : 변수타입] 으로 나타낼 수 있다.</span>
<span class="nl">public:</span>
	<span class="kt">void</span> <span class="n">applyDiscount</span><span class="p">(</span><span class="kt">double</span> <span class="n">rate</span><span class="p">);</span>  
	<span class="kt">double</span> <span class="n">getPrice</span><span class="p">();</span>
	<span class="n">String</span> <span class="n">toString</span><span class="p">();</span>
	
<span class="nl">public:</span>
	<span class="n">String</span> <span class="n">id</span><span class="p">;</span>  
	<span class="n">String</span> <span class="n">name</span><span class="p">;</span>
	<span class="kt">double</span> <span class="n">price</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>두 번째 노드같이, interface, abstract 클래스는 &lt;&lt;interface&gt;&gt;, &lt;&lt;abstract&gt;&gt; 로 클래스명 위에 두어 표시한다. (세 번째 노드는 abstract 클래스의 또 다른 표현방식)</li>
  <li>관계 표현 : 위 에서 다뤘던 #Relationships 의 관계 선으로 표현한 클래스 다이어그램을 다음과 같이 예시로 만들어 보았다.<img src="../../assets/images/posts/Software-Architecture/ClassDiagram_B.png" alt="Diagram2" /></li>
</ul>
<font color="#595959">※ 출처 - AI 생성형 이미지</font>
<ul>
  <li>해석 :
    <ul>
      <li>Customer - Order 연관 관계 : 1명의 고객이 여러 주문을 넣는 관계 
(고객A -&gt; @주문1, @주문2) (고객B -&gt; @주문3)</li>
      <li>Order - OrderItem 합성 관계 : 1개의 주문에는 1개 이상의 주문 정보가 존재하여 종속적인 관계 (@주문1 -&gt; {마우스, 2개, $3}, {키보드, 1개, $2}…)</li>
      <li>Order - PaymentService 의존 관계 : 일시적으로 PaymentService를 매개변수로 받아 processPayment 메서드를 실행시킨다. (@주문1 -&gt; 카카오페이)</li>
      <li>OrderItem - Product 연관 관계 : Product가 하나에는 0개 이상의 OrderItem이 연결되어 있어야 하고, 반복적으로 참조가 가능한 관계. (H마우스를 @주문1 항목, @주문3 항목에서 요구)</li>
    </ul>
  </li>
</ul>

<h3 id="시퀀스-다이어그램-sequence-diagram">시퀀스 다이어그램 (Sequence Diagram)</h3>
<ul>
  <li>개념 : <strong>“시퀀스”</strong> 말 그대로 순서대로 관계를 나타내는 다이어그램. 조금 더 자세하게는 시간이 진행되면서 객체들이 순서대로 상호작용하는 흐름을 표현하는 다이어그램이다.</li>
  <li>사례 : 서버에 패킷을 주고 받는데 순서가 필요하고, 스레드 프로그래밍에서 임계 영역을 해결하기 위해서 사용된다. 게임 서버를 예시로 시퀀스가 어떻게 진행되는지 나타내는 다이어그램이 아래와 같다.</li>
</ul>

<p><img src="../../assets/images/posts/Software-Architecture/Sequence-Diagram.png" alt="Diagram2|361" /></p>
<font color="#595959">  ※ 출처 - https://thebook.io/006884/0321/</font>
<ul>
  <li>관계 표현 : 시퀀스 다이어그램은 위-&gt;아래 순으로 진행이 된다. 사진처럼 ① 방 생성 요청 -&gt; ② 방 생성 성공을 알림 -&gt; ③ … -&gt; ④ … 순서로 이어지는데, 위에서 아래로 흘러가는 것을 볼 수 있다. 클라나 서버에서 밑으로 점선이 내려오는데, 이 점선을 객체의 Lifeline(생명선), 점선 중간에 직사각형 막대가 있는데, 이 막대가 작업을 처리하고 있는 구간으로 ‘활성화’되어 있다고 표현한다.</li>
  <li>메세지 : 객체끼리 상호작용하는 방법은 ‘메시지’를 사용한다. 위 그림에서 가로 화살표들은 전부 메시지다. ‘요청’, ‘응답’으로 나눌 수 있는데, 클라이언트가 서버에 보내는 메시지는 ‘요청’, 서버가 클라이언트에 보내는 메시지는 ‘응답’이다. 그런 메세지도 유형과 ‘선’표현 법이 있는데 이건 표로 정리했다.</li>
</ul>

<table> 
	<colgroup> 
	<col style="width: 20%;" /> <col style="width: 50%;" /> <col style="width: 30%;" /></colgroup> 
	
	<thead> 
	<tr> <th>메세지 유형</th> <th>메세지 내용</th> <th>메세지 표현</th> </tr></thead> 
	
		<tbody> 
		<tr> <td>동기 메세지</td> <td>요청 메세지를 보낸 다음 응답 메세지가 올 때까지 대기</td> <td>실선 + 채운 화살표 : A ㅡ▶ B</td> </tr>
		 <tr> <td>비동기 메세지</td> <td>요청 메세지만 보내고, 응답 메세지를 대기하지 않음.</td> <td>실선 + 열린 화살표 : A → B</td> </tr>
		  <tr> <td>자체 메세지</td> <td>자기 자신에게 보내는 메시지.</td> <td>실선 + 채운 화살표 : A ㅡ▶ A</td> </tr> 
		  <tr> <td>반환 메세지</td> <td>요청에 대해 메시지를 반환. (동기 메세지의 응답 메세지)</td> <td>점선 + 열린 화살표 : B ---&gt; A</td> </tr> 
		  </tbody> 
</table>]]></content><author><name>LungJi</name></author><category term="Software Modeling" /><category term="#UML" /><category term="#Diagram" /><category term="#Software" /><summary type="html"><![CDATA[[ Unified Modeling Language ]]]></summary></entry></feed>