<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발블로그</title>
    <link>https://devmel393.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 24 Sep 2026 03:27:35 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>devmel</managingEditor>
    <item>
      <title>[객체지향] PHP readonly 속성과 클래스</title>
      <link>https://devmel393.tistory.com/61</link>
      <description>&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;의미&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속성을 초기화한 뒤 값을 다시 대입하거나 수정하지 못하도록 제한하는 키워드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;읽기 전용 속성은 PHP8.1부터 지원함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비교&amp;nbsp; : final vs readonly&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구분&lt;/td&gt;
&lt;td&gt;final&lt;/td&gt;
&lt;td&gt;readonly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;클래스에 적용&lt;/td&gt;
&lt;td&gt;클래스 상속 금지&lt;/td&gt;
&lt;td&gt;모든 인스턴스 속성에 읽기 전용 규칙 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;속성에 적용&lt;/td&gt;
&lt;td&gt;자식 클래스에서 속성 재선언 금지&lt;/td&gt;
&lt;td&gt;초기화 이후 속성 변경 제한&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;메서드에 적용&lt;/td&gt;
&lt;td&gt;오버라이딩 금지&lt;/td&gt;
&lt;td&gt;적용 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;문법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788671520043&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ClassName
{
	// 속성 선언, 아직 값은 없음
    public readonly string $propertyName;

	// 첫 값 대입으로 초기화
    public function __construct(string $value)
    {
        $this-&amp;gt;propertyName = $value;
    }
}&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;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788672106105&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    public readonly int $id;

    public function __construct(int $id)
    {
        $this-&amp;gt;id = $id;
    }
}

$user = new UserProfile(1001);

echo $user-&amp;gt;id; // 1001

$user-&amp;gt;id = 2002; // 오류: 초기화된 readonly 속성 변경&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;선언과 초기화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언만으로 값이 생기는 것이 아니라, 허용된 범위에서 처음 값을 대입해야 사용할 수 있음.&lt;/p&gt;
&lt;pre id=&quot;code_1788671926587&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    public readonly int $id;
}

$user = new UserProfile();

echo $user-&amp;gt;id; // 오류: 초기화 전 접근&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;size18&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; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;위치&lt;/td&gt;
&lt;td&gt;PHP 8.1~8.3&lt;/td&gt;
&lt;td&gt;PHP 8.4 이상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;선언한 클래스 내부&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자식 클래스 내부&lt;/td&gt;
&lt;td&gt;불가능&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;클래스 외부&lt;/td&gt;
&lt;td&gt;불가능&lt;/td&gt;
&lt;td&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본값 선언 제한&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 속성 선언 위치에서는 기본값을 지정할 수 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 대신 생성자 매개변수에 기본값을 두고, 그 값을 속성에 대입할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788672241591&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    public readonly int $id = 1001; // 오류
}

// 아래는 가능
class UserProfile
{
    public readonly int $id;

    public function __construct(int $id = 1001)
    {
        $this-&amp;gt;id = $id;
    }
}

$user = new UserProfile();
echo $user-&amp;gt;id; // 1001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;적용타입&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;readonly 속성에는 타입을 반드시 선언해야 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(여러 종류의 값 허용시, mixed사용 가능)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;변경 제한&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단순한 재대입뿐 아니라, 증가 연산이나 배열 요소 수정처럼 속성값을 변경하는 동작도 제한&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788673034279&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    public readonly int $id;
    public readonly array $roles;

    public function __construct(int $id, array $roles)
    {
        $this-&amp;gt;id = $id;
        $this-&amp;gt;roles = $roles;
    }
}

$user = new UserProfile(1001, [&quot;user&quot;]);

echo $user-&amp;gt;id;       // 1001
echo $user-&amp;gt;roles[0]; // user

$user-&amp;gt;id = 2002;          // 재대입 불가
$user-&amp;gt;id = 1001;          // 같은 값을 다시 대입해도 불가
$user-&amp;gt;id++;              // 증가 연산 불가

$user-&amp;gt;roles[] = &quot;admin&quot;;  // 배열 요소 추가 불가
$user-&amp;gt;roles[0] = &quot;guest&quot;; // 배열 요소 변경 불가
unset($user-&amp;gt;roles[0]);    // 배열 요소 삭제 불가&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;값의 조회와 활용&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저장된 값을 읽어 다른 계산이나 처리에 사용하는 것은 가능&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788673046611&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$nextId = $user-&amp;gt;id + 1;

$roles = $user-&amp;gt;roles;
$roles[] = &quot;admin&quot;;

echo $nextId;          // 1002
echo count($roles);    // 2
echo count($user-&amp;gt;roles); // 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 속성과 내부 상태 변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;readonly 속성에 객체를 저장하면, 그 속성이 가리키는 객체를 교체할 수 없음&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;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788673457580&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Address
{
    public string $city;

    public function __construct(string $city)
    {
        $this-&amp;gt;city = $city;
    }
}

class UserProfile
{
    public readonly Address $address;

    public function __construct(Address $address)
    {
        $this-&amp;gt;address = $address;
    }
}

$user = new UserProfile(new Address(&quot;Seoul&quot;));

// 내부 값은 변경 가능
$user-&amp;gt;address-&amp;gt;city = &quot;Busan&quot;;
echo $user-&amp;gt;address-&amp;gt;city; // Busan

// 객체 교체는 불가
$user-&amp;gt;address = new Address(&quot;Jeju&quot;);
// 오류: 초기화된 readonly 속성에 다른 객체 대입&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;읽기 전용 클래스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스에 readonly를 붙이면, 해당 클래스의 모든 인스턴스 속성에 읽기 전용 규칙이 적용됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속성마다 readonly를 반복해서 작성할 필요가 없음&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(8.2 부터 가능)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문법&lt;/h3&gt;
&lt;pre id=&quot;code_1788673683592&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;readonly class ClassName // 읽기 전용 클래스 선언
{
    public string $propertyName; // 자동으로 readonly 규칙이 적용되는 속성
    
    // static propery는 만들 수 없음

    public function __construct(string $value)
    {
        $this-&amp;gt;propertyName = $value;
    }
}&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;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788673700144&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;readonly class UserProfile
{
    public int $id;
    public string $name;

    public function __construct(int $id, string $name)
    {
        $this-&amp;gt;id = $id;
        $this-&amp;gt;name = $name;
    }
}

$user = new UserProfile(1001, &quot;Kim&quot;);

echo $user-&amp;gt;id;   // 1001
echo $user-&amp;gt;name; // Kim

$user-&amp;gt;name = &quot;Lee&quot;; // 오류: 초기화된 readonly 속성 변경

// 선언되지 않은 $email을 추가하면 오류 발생
$user-&amp;gt;email = &quot;kim@example.com&quot;; // 오류: 동적 속성 추가&lt;/code&gt;&lt;/pre&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;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;readonly 클래스를 상속하는 자식 클래스도 readonly로 선언해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788673967323&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;readonly class BaseProfile
{
}

readonly class UserProfile extends BaseProfile
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;주의점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;초기화 누락&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 읽기 전에 초기화되어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- null을 허용하는 타입이라고 해서 자동으로 null이 저장되지는 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;내부 상태의 변경 가능성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;readonly 속성에 객체를 저장하면 그 객체를 교체하는 것은 제한되지만, 내부 상태는 변경될 수 있음.&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;h3 data-ke-size=&quot;size23&quot;&gt;객체 복제 시 재초기화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;php 8.3부터는 __clone()에서 복제본의 readonly 속성을 한 번 재초기화할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;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;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788674323102&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    public readonly Address $address;

    public function __construct(Address $address)
    {
        $this-&amp;gt;address = $address;
    }

    public function __clone(): void
    {
        $this-&amp;gt;address = clone $this-&amp;gt;address;
    }
}

// 앞서 정의만 Address 클래스를 사용하는데, 복제 시 주소 객체도 복제하여 원본과 복제본이 
// 같은 주소 객체를 공유하지 않도록 함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>STUDY/PHP</category>
      <category>final</category>
      <category>PHP</category>
      <category>PHP객체지향</category>
      <category>readonly</category>
      <category>읽기전용속성</category>
      <category>읽기전용클래스</category>
      <author>devmel</author>
      <guid isPermaLink="true">https://devmel393.tistory.com/61</guid>
      <comments>https://devmel393.tistory.com/61#entry61comment</comments>
      <pubDate>Sun, 6 Sep 2026 15:22:25 +0900</pubDate>
    </item>
    <item>
      <title>[객체지향] PHP final 키워드</title>
      <link>https://devmel393.tistory.com/60</link>
      <description>&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;의미&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속을 통한 확장이나 재정의를 제한하는 키워드&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;적용 대상&lt;/td&gt;
&lt;td&gt;제한 내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;클래스&lt;/td&gt;
&lt;td&gt;해당 클래스를 상속할 수 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;메서드&lt;/td&gt;
&lt;td&gt;자식 클래스에서 해당 메서드를 오버라이딩할 수 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상수와 속성&lt;/td&gt;
&lt;td&gt;클래스 상수 (PHP 8.1 이상) - 자식 클래스에서 상수 재정의 금지&lt;br /&gt;속성 (PHP 8.4 이상) - 자식 클래스에서 속성 재정의 금지&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일반 문법&lt;/h3&gt;
&lt;pre id=&quot;code_1788609407658&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * 클래스에 적용
 * =&amp;gt; 해당 클래스를 상속하는 자식 클래스를 만들 수 없음
 */
final class FinalClass
{
    // 클래스 정의
}

/**
 * 메서드에 적용
 * =&amp;gt; 자식 클래스에서 해당 메서드를 재정의할 수 없음
 */
class MethodExample
{
    final public function methodName(): void
    {
        // 메서드 구현
    }
}

/**
 * 상수에 적용 (PHP 8.1 이상)
 * =&amp;gt; 자식 클래스에서 해당 상수를 재정의할 수 없음
 */
class ConstantExample
{
    final public const CONSTANT_NAME = &quot;값&quot;;
}

/**
 * 속성에 적용 (PHP 8.4 이상)
 * =&amp;gt; 자식 클래스에서 해당 속성을 재정의할 수 없음
 * =&amp;gt; 객체의 속성값 변경은 가능
 */
class PropertyExample
{
    final public string $propertyName = &quot;초기값&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex]&lt;/p&gt;
&lt;pre id=&quot;code_1788611297888&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
클래스 적용
*/
final class Calculator
{
    public function add(int $a, int $b): int
    {
        return $a + $b;
    }
}

$calculator = new Calculator();
echo $calculator-&amp;gt;add(10, 20); // 30

// 아래 클래스 선언은 오류 발생
// class CustomCalculator extends Calculator {}


/*
메서드 적용
*/
class Calculator
{
    final public function add(int $a, int $b): int
    {
        return $a + $b;
    }
}

class CustomCalculator extends Calculator
{
    // 아래 메서드를 선언하면 오류 발생
    // public function add(int $a, int $b): int
    // {
    //     return $a + $b + 100;
    // }
}

$calculator = new CustomCalculator();
echo $calculator-&amp;gt;add(10, 20); // 30


/**
상수 적용
*/
class Response
{
    final public const SUCCESS = 200;
}

class CustomResponse extends Response
{
    // 아래 상수를 선언하면 오류 발생
    // public const SUCCESS = 201;
}

echo CustomResponse::SUCCESS; // 200


/**
속성 적용
*/
class UserProfile
{
    final public string $name = &quot;Kim&quot;;
}

class CustomProfile extends UserProfile
{
    // 아래 속성을 선언하면 오류 발생
    // public string $name = &quot;Lee&quot;;
}

$user = new CustomProfile();
$user-&amp;gt;name = &quot;Lee&quot;;

echo $user-&amp;gt;name; // Lee&lt;/code&gt;&lt;/pre&gt;</description>
      <category>STUDY/PHP</category>
      <category>final</category>
      <category>PHP</category>
      <category>PHP객체지향</category>
      <category>상속제한</category>
      <category>오버라이딩</category>
      <author>devmel</author>
      <guid isPermaLink="true">https://devmel393.tistory.com/60</guid>
      <comments>https://devmel393.tistory.com/60#entry60comment</comments>
      <pubDate>Sat, 5 Sep 2026 21:30:25 +0900</pubDate>
    </item>
    <item>
      <title>[객체지향] PHP Trait</title>
      <link>https://devmel393.tistory.com/59</link>
      <description>&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 클래스에서 같은 기능이 필요한 경우가 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 사용자 관리 클래스와 파일 처리 클래스가 모두 로그를 기록한다고 할 때,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 클래스에 같은 메서드를 작성하면 중복이 생기고, 기능이 바뀔 때 여러 곳을 수정해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 클래스에 포함하여 재사용할 수 있는 메서드와 속성의 묶음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Trait을 포함한 클래스에서는 해당 메서드를 자신의 메서드처럼 사용할 수 있음.&lt;/p&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;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;문법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 문법&lt;/h3&gt;
&lt;pre id=&quot;code_1788607583471&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trait TraitName
{
    // 속성 정의
    private string $propertyName = &quot;초기값&quot;;

    // 메서드 정의
    public function methodName(): string
    {
        return $this-&amp;gt;propertyName;
    }
}

class ClassName
{
    use TraitName;
}

$object = new ClassName();
echo $object-&amp;gt;methodName(); // 초기값&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;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788608044436&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trait LoggerTrait
{
    private string $prefix = &quot;[LOG]&quot;;

    public function log(string $message): void
    {
        echo &quot;{$this-&amp;gt;prefix} {$message}\n&quot;;
    }
}

class UserService
{
    use LoggerTrait;
}

class FileService
{
    use LoggerTrait;
}

$userService = new UserService();
$fileService = new FileService();

$userService-&amp;gt;log(&quot;사용자 생성&quot;);
$fileService-&amp;gt;log(&quot;파일 저장&quot;);
/*
[LOG] 사용자 생성
[LOG] 파일 저장
*/&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;여러 클래스에서 여러 Trait을 함께 사용할 수 있음&lt;/h3&gt;
&lt;pre id=&quot;code_1788605283692&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trait FirstTrait
{
    public function firstMethod(): void
    {
        // 첫 번째 기능
    }
}

trait SecondTrait
{
    public function secondMethod(): void
    {
        // 두 번째 기능
    }
}

class ClassName
{
    use FirstTrait, SecondTrait;
}&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;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788605302225&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trait LoggerTrait
{
    public function log(string $message): void
    {
        echo &quot;[LOG] {$message}\n&quot;;
    }
}

trait NotifierTrait
{
    public function notify(string $message): void
    {
        echo &quot;[알림] {$message}\n&quot;;
    }
}

class UserService
{
    use LoggerTrait, NotifierTrait;

    public function register(): void
    {
        $this-&amp;gt;log(&quot;사용자 등록&quot;);
        $this-&amp;gt;notify(&quot;가입 완료&quot;);
    }
}

$service = new UserService();
$service-&amp;gt;register();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메소드 충돌 해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Trait에 같은 이름의 메서드가 있으면, 어떤 구현을 사용할지 명시해야 함.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;키워드&lt;/td&gt;
&lt;td&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;insteadof&lt;/td&gt;
&lt;td&gt;충돌하는 메서드 중 사용할 구현 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;as&lt;/td&gt;
&lt;td&gt;메서드에 별칭 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788605478480&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ClassName
{
    use FirstTrait, SecondTrait {
        FirstTrait::methodName insteadof SecondTrait;
        	// methodName을 호출하면, SecondTrait대신 FirstTrait의 구현을 사용하도록 지정
        SecondTrait::methodName as anotherMethod;
        	// SecondTrait의 methodName를 anotherMethod의 이름으로 사용할 수 있도록 별칭 추가
    }
}&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;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788605501514&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trait ConsoleLoggerTrait
{
    public function log(string $message): void
    {
        echo &quot;[일반] {$message}\n&quot;;
    }
}

trait JsonLoggerTrait
{
    public function log(string $message): void
    {
        echo json_encode([&quot;message&quot; =&amp;gt; $message]) . &quot;\n&quot;;
    }
}

class UserService
{
    use ConsoleLoggerTrait, JsonLoggerTrait {
        ConsoleLoggerTrait::log insteadof JsonLoggerTrait;
        JsonLoggerTrait::log as logJson;
    }
}

$service = new UserService();

$service-&amp;gt;log(&quot;created&quot;);
$service-&amp;gt;logJson(&quot;created&quot;);
/*
[일반] created
{&quot;message&quot;:&quot;created&quot;}
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;접근 제어자와 별칭 지정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Trait을 클래스에 포함할 때 as를 사용하면 메서드의 접근 범위를 변경하거나, 호출할 이름을 추가할 수 있음&lt;/p&gt;
&lt;pre id=&quot;code_1788606675196&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;use TraitName {
    methodName as private;
    // 기존 메서드의 접근 범위 변경
}

use TraitName {
    methodName as aliasName;
    // 기존 메서드에 별칭 추가
}

use TraitName {
    methodName as private aliasName;
    // private 접근 범위를 가진 별칭 추가
}&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;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788606711931&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trait LoggerTrait
{
    public function log(string $message): void
    {
        echo &quot;[LOG] {$message}\n&quot;;
    }
}

/*
접근 범위만 변경
*/
class UserService
{
	// 기존 log()가 private가 됨. 이름은 그대로이며 외부 호출은 제한 됨
    use LoggerTrait {
        log as private;
    }
}

/*
별칭만 추가
*/
class UserService
{
	//기존 log()를 유지하면서 writeLog()라는 호출 이름을 추가한다. 둘 다 public이며 같은 구현을 실행한다.
    use LoggerTrait {
        log as writeLog;
    }
}

/*
별칭의 접근 범위까지 지정
*/
class UserService
{
    use LoggerTrait {
        log as private writeLog;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Trait의 추상메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Trait에 추상 메서드를 선언하면, Trait을 사용하는 클래스가 제공해야 할 메서드를 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; Trait은 해당 메서드를 호출하는 공통 흐름을 구현하고, 구체적인 동작은 사용하는 클래스에 맡긴다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788608162874&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trait TraitName
{
	// abstract = 메서드 구현이 필요함을 선언하는 키워드
    abstract protected function requiredMethod(): string;

}

class ClassName
{
    use TraitName;

    protected function requiredMethod(): string
    {
        return &quot;구현 결과&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 로그 출력 방식은 공통으로 사용하고, 로그에 표시할 서비스 이름은 각 클래스에서 정의하도록 함&lt;/p&gt;
&lt;pre id=&quot;code_1788608350809&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trait LoggerTrait
{
    abstract protected function getLogName(): string;

    public function log(string $message): void
    {
        echo &quot;[{$this-&amp;gt;getLogName()}] {$message}\n&quot;;
    }
}

class UserService
{
    use LoggerTrait;

    protected function getLogName(): string
    {
        return &quot;USER&quot;;
    }
}

class FileService
{
    use LoggerTrait;

    protected function getLogName(): string
    {
        return &quot;FILE&quot;;
    }
}

$userService = new UserService();
$fileService = new FileService();

$userService-&amp;gt;log(&quot;사용자 생성&quot;);
$fileService-&amp;gt;log(&quot;파일 저장&quot;);
/*
[USER] 사용자 생성
[FILE] 파일 저장
*/&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;메서드 우선순위&lt;/h2&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #f6f6f6; border-radius: 15px; solid; padding: 35px;&quot;&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;클래스에&amp;nbsp;직접&amp;nbsp;정의한&amp;nbsp;메서드 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr;&amp;nbsp;우선 &lt;br /&gt;Trait에서&amp;nbsp;포함한&amp;nbsp;메서드 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr;&amp;nbsp;우선 &lt;br /&gt;부모&amp;nbsp;클래스에서&amp;nbsp;상속받은&amp;nbsp;메서드&lt;/p&gt;
&lt;/div&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;부모 메서드가 final이거나 선언이 호환되지 않는 경우에는 우선순위로 해결되지 않고 오류가 발생할 수 있음&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;주의점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이름충돌&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Trait을 포함하면 같은 이름의 메서드나 속성이 겹칠 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; Trait을 추가할 때는 현재 클래스와 다른 Trait에 이미 같은 이름이 있는지 확인해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;숨겨진 의존 관계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Trait이 사용하는 클래스의 특정 속성이나 메서드를 당연히 존재한다고 가정하면,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 조건을 파악하기 어려워짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 필요한 속성을 Trait에 선언하거나, 클래스별 값이 필요하다면 추상 메서드로 요구사항을 명시할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;과도한 기능 포함&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스에 Trait을 많이 넣으면 실제로 어떤 속성과 메서드를 가지는지 확인하기 어려워질 수 있음&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;확인 항목&lt;/td&gt;
&lt;td&gt;판단 기준&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기능의 관련성&lt;/td&gt;
&lt;td&gt;Trait 내부 기능들이 같은 역할에 속하는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용 범위&lt;/td&gt;
&lt;td&gt;포함한 클래스에서 실제로 필요한 기능인가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;코드 추적&lt;/td&gt;
&lt;td&gt;메서드의 정의 위치를 쉽게 찾을 수 있는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;변경 영향&lt;/td&gt;
&lt;td&gt;Trait 수정이 사용하는 클래스들에 어떤 영향을 주는가?&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;h3 data-ke-size=&quot;size23&quot;&gt;객체 조합과의 구분&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Trait은 클래스 정의에 구현을 포함하는 방식이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 중에 Trait을 다른 Trait으로 교체하는 방식으로 사용하지 않음.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 실행 시점에 구현을 선택하거나 교체해야 한다면, 별도의 객체를 전달받아 사용하는 구조가 더 합할 수 있음&lt;/p&gt;</description>
      <category>STUDY/PHP</category>
      <category>insteadof</category>
      <category>PHP</category>
      <category>Trait</category>
      <category>다중Trait</category>
      <category>메서드충돌</category>
      <category>코드재사용</category>
      <category>트레이트</category>
      <author>devmel</author>
      <guid isPermaLink="true">https://devmel393.tistory.com/59</guid>
      <comments>https://devmel393.tistory.com/59#entry59comment</comments>
      <pubDate>Sat, 5 Sep 2026 20:51:54 +0900</pubDate>
    </item>
    <item>
      <title>PHP 변수 대입과 객체 복제</title>
      <link>https://devmel393.tistory.com/58</link>
      <description>&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;기본값 대입&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대입 연산자(=) = 오른쪽 값을 왼쪽 변수에 저장하는 연산자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 숫자나 문자열을 다른 변수에 대입하면, 각 변수의 값은 독립적으로 사용할 수 있음.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 한 변수의 값을 변경해도 다른 변수에는 영향을 주지 않음&lt;/p&gt;
&lt;pre id=&quot;code_1788602603158&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$variableA = 값;
$variableB = $variableA;

// $variableB에는 대입 시점의 $variableA 값이 전달된다. 이후 $variableA의 변경을 자동으로 따라가지는 않는다.&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;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788602655240&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 숫자 대입
$number = 10;
$otherNumber = $number;

$otherNumber = 20;

echo $number;      // 10
echo $otherNumber; // 20


// 문자열 대입
$name = &quot;Kim&quot;;
$otherName = $name;

$name = &quot;Lee&quot;;

echo $name;      // Lee
echo $otherName; // Kim&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;객체 대입&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 다른 변수에 대입하면, 객체가 복제되는 대신 두 변수가 같은 객체를 가리킴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; PHP의 객체 변수에는 특정 객체에 접근하는 객체 식별자가 담긴다. = 로 대입하면 이 식별자가 복사됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 같은 객체를 가리키므로 한 변수를 통해 속성을 변경하면 다른 변수에서도 변경된 값을 확인할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788602825938&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$objectA = new ClassName();
$objectB = $objectA;

/*
$objectA ──┐
           ├──&amp;rarr; 하나의 객체
$objectB ──┘

=&amp;gt;변수는 두 개지만, 객체는 하나
*/&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;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788602974473&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    public string $name;

    public function __construct(string $name)
    {
        $this-&amp;gt;name = $name;
    }
}

$user = new UserProfile(&quot;Kim&quot;);
$other = $user;

$other-&amp;gt;name = &quot;Lee&quot;;
/*
$other-&amp;gt;name = &quot;Lee&quot;
&amp;rarr; 두 변수가 가리키는 객체의 name 변경

$user  ──┐
         ├──&amp;rarr; UserProfile
$other ──┘      name: &quot;Lee&quot;
*/

echo $user-&amp;gt;name;  // Lee
echo $other-&amp;gt;name; // Lee&lt;/code&gt;&lt;/pre&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;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 변수에 새로운 객체를 대입하면 그 변수가 가리키는 대상만 바뀜&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788603209886&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$user = new UserProfile(&quot;Kim&quot;);
$other = $user;

$other = new UserProfile(&quot;Lee&quot;);

echo $user-&amp;gt;name;  // Kim
echo $other-&amp;gt;name; // Lee

/*
$user  ───&amp;rarr; 기존 객체
              name: &quot;Kim&quot;

$other ───&amp;rarr; 새로운 객체
              name: &quot;Lee&quot;
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;참조 대입&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 변수 이름이 같은 변수 내용을 공유하도록 연결하는 방식&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 연결된 이후에는 어느 쪽에 값을 대입하든 다른 변수에서도 변경된 값이 나타남&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문법&lt;/h3&gt;
&lt;pre id=&quot;code_1788603344601&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$variableA = 값;
$variableB =&amp;amp; $variableA;
/*
$variableA ──┐
             ├──&amp;rarr; 공유하는 값
$variableB ──┘

=&amp;gt; $variableB를 $variableA의 또 다른 이름처럼 사용할 수 있다.
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;pre id=&quot;code_1788603399642&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*
기본값의 참조 대입
*/
$number = 10;
$otherNumber =&amp;amp; $number;

$otherNumber = 20;

echo $number;      // 20
echo $otherNumber; // 20

$number = 30;

echo $number;      // 30
echo $otherNumber; // 30


/*
객체의 참조 대입
=&amp;gt; 객체 속성 변경뿐 아니라 새로운 객체를 대입한 결과도 공유함
*/
$user = new UserProfile(&quot;Kim&quot;);
$other =&amp;amp; $user;

$other = new UserProfile(&quot;Lee&quot;);

echo $user-&amp;gt;name;  // Lee
echo $other-&amp;gt;name; // Lee&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의점&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;객체 공유와 참조 재입의 구분&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 함께 수정하기 위해 반드시 &amp;amp;가 필요한 것은 아님&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 일반 대입만으로도 같은 객체를 가리키므로 속성 변경을 함께 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조 대입은 변수에 다른 값을 재대입한 결과까지 공유해야 하는지 따져보고 사용함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;객체 복제&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 객체의 속성값을 바탕으로 새로운 객체를 만드는 동작.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 대입(=)은 같은 객체를 가리키지만, clone은 별도의 객체를 생성함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문법&lt;/h3&gt;
&lt;pre id=&quot;code_1788603644552&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$original = new ClassName();
$copy = clone $original;

/*
$original ───&amp;rarr; 원본 객체
$copy     ───&amp;rarr; 복제된 객체
=&amp;gt; 복제 직후에는 속성값이 같더라도 서로 다른 객체
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788603697771&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$user = new UserProfile(&quot;Kim&quot;);
$copy = clone $user;

$copy-&amp;gt;name = &quot;Lee&quot;;

echo $user-&amp;gt;name; // Kim
echo $copy-&amp;gt;name; // Lee

/* 
[실행과정]
① 원본 생성

$user ───&amp;rarr; UserProfile
             name: &quot;Kim&quot;

② 복제

$user ───&amp;rarr; UserProfile
             name: &quot;Kim&quot;

$copy ───&amp;rarr; UserProfile
             name: &quot;Kim&quot;

③ 복제본의 이름 변경

$user ───&amp;rarr; UserProfile
             name: &quot;Kim&quot;

$copy ───&amp;rarr; UserProfile
             name: &quot;Lee&quot;
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;얕은 복사와 깊은 복사&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 속성에는 문자열이나 숫자뿐 아니라 다른 객체도 들어갈 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p 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; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구분&lt;/td&gt;
&lt;td&gt;복제 범위&lt;/td&gt;
&lt;td&gt;내부 객체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;얕은 복사&lt;/td&gt;
&lt;td&gt;바깥 객체를 복제&lt;/td&gt;
&lt;td&gt;원본과 복제본이 공유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;깊은 복사&lt;/td&gt;
&lt;td&gt;내부 객체까지 복제&lt;/td&gt;
&lt;td&gt;원본과 복제본이 별도로 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;pre id=&quot;code_1788604079335&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
[얕은 복사]
*/
class Address
{
    public string $city = &quot;Seoul&quot;;
}

class UserProfile
{
    public string $name = &quot;Kim&quot;;
    public Address $address;

    public function __construct()
    {
        $this-&amp;gt;address = new Address();
    }
}

$user = new UserProfile();
$copy = clone $user;
/*
$user &amp;rarr; 원본 UserProfile
          address ──┐
                    ├──&amp;rarr; 하나의 Address
$copy &amp;rarr; 복제 UserProfile   city: &quot;Seoul&quot;
          address ──┘
*/

// 복제본의 이름과 주소를 변경해 보기
$copy-&amp;gt;name = &quot;Lee&quot;;
$copy-&amp;gt;address-&amp;gt;city = &quot;Busan&quot;;

echo $user-&amp;gt;name;          // Kim
echo $copy-&amp;gt;name;          // Lee

echo $user-&amp;gt;address-&amp;gt;city; // Busan
echo $copy-&amp;gt;address-&amp;gt;city; // Busan


/**
[깊은 복사]

주소까지 독립적으로 변경하려면 내부의 Address도 별도로 복제해야 함
=&amp;gt; 별도의 deep clone 연산자가 없기 때문에, 필요한 내부 객체를 직접 복제해야 함
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;깊은 복사 방법&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;__clone() 안에서 내부 객체를 추가로 복제하면, 원본과 복제본이 서로 다른 내부 객체를 사용하도록 만들 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788604269871&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public function __clone(): void
{
    $this-&amp;gt;property = clone $this-&amp;gt;property;
}&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;EX]&lt;/p&gt;
&lt;pre id=&quot;code_1788604317989&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Address
{
    public string $city = &quot;Seoul&quot;;
}

class UserProfile
{
    public string $name = &quot;Kim&quot;;
    public Address $address;

    public function __construct()
    {
        $this-&amp;gt;address = new Address();
    }

    public function __clone(): void
    {
        $this-&amp;gt;address = clone $this-&amp;gt;address;
    }
}

$user = new UserProfile();
$copy = clone $user;

$copy-&amp;gt;address-&amp;gt;city = &quot;Busan&quot;;

echo $user-&amp;gt;address-&amp;gt;city; // Seoul
echo $copy-&amp;gt;address-&amp;gt;city; // Busan&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의점&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;복제 범위 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 객체를 무조건 모두 복제하기보다, 복제본에서 독립적으로 변경해야 하는 데이터인지 먼저 판단&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>STUDY/PHP</category>
      <category>Clone</category>
      <category>PHP</category>
      <category>객체복제</category>
      <category>깊은복사</category>
      <category>매직메서드</category>
      <category>변수대입</category>
      <category>얕은복사</category>
      <category>참조대입</category>
      <author>devmel</author>
      <guid isPermaLink="true">https://devmel393.tistory.com/58</guid>
      <comments>https://devmel393.tistory.com/58#entry58comment</comments>
      <pubDate>Sat, 5 Sep 2026 19:40:09 +0900</pubDate>
    </item>
    <item>
      <title>[객체지향] PHP 매직 메서드</title>
      <link>https://devmel393.tistory.com/57</link>
      <description>&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특정 상황이 발생&lt;/b&gt;했을 때 PHP가 &lt;b&gt;자동으로 호출&lt;/b&gt;하는 특수 메서드&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 존재하지 않거나 접근할 수 없는 속성을 읽으려고 하면 =&amp;gt; __get()이 호출될 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름이 __로 시작함&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용목적&lt;/h3&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;종류&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;종류&lt;/td&gt;
&lt;td&gt;메서드&lt;/td&gt;
&lt;td&gt;호출 시점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;생성&lt;/td&gt;
&lt;td&gt;__construct()&lt;/td&gt;
&lt;td&gt;new로 객체 생성 시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;소멸&lt;/td&gt;
&lt;td&gt;__destruct()&lt;/td&gt;
&lt;td&gt;객체 소멸 시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;속성 읽기&lt;/td&gt;
&lt;td&gt;__get()&lt;/td&gt;
&lt;td&gt;존재하지 않거나 접근할 수 없는 속성 읽기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;속성 쓰기&lt;/td&gt;
&lt;td&gt;__set()&lt;/td&gt;
&lt;td&gt;존재하지 않거나 접근할 수 없는 속성에 대입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;객체 메서드 호출&lt;/td&gt;
&lt;td&gt;__call()&lt;/td&gt;
&lt;td&gt;존재하지 않거나 접근할 수 없는 객체 메서드 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정적 메서드 호출&lt;/td&gt;
&lt;td&gt;__callStatic()&lt;/td&gt;
&lt;td&gt;존재하지 않거나 접근할 수 없는 정적 메서드 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;문자열 변환&lt;/td&gt;
&lt;td&gt;__toString()&lt;/td&gt;
&lt;td&gt;객체를 문자열로 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;함수형 호출&lt;/td&gt;
&lt;td&gt;__invoke()&lt;/td&gt;
&lt;td&gt;객체를 함수처럼 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;복제&lt;/td&gt;
&lt;td&gt;__clone()&lt;/td&gt;
&lt;td&gt;clone으로 복제한 후 복제본에서 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;생성과 소멸&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;생성자 __construnct()&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;=&amp;gt; 객체가 처음부터 필요한 데이터를 가진 상태로 사용할 수 있도록 초기 상태를 설정함&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;용도&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;초기값 설정&lt;/td&gt;
&lt;td&gt;객체가 사용할 속성값 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;필수 데이터 전달&lt;/td&gt;
&lt;td&gt;객체 생성에 필요한 정보 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;초기 데이터 검증&lt;/td&gt;
&lt;td&gt;전달받은 값이 올바른지 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 문법 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788595878094&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ClassName
{
    public function __construct($value)
    {
        // 객체 생성 시 수행할 초기화
    }
}

$object = new ClassName($value);
// new ClassName($value)에 전달한 값은 __construct($value)의 매개변수로 전달된다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 예시 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788595959509&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    private string $name;

    public function __construct(string $name)
    {
        $this-&amp;gt;name = $name;
        echo &quot;프로필 생성: {$this-&amp;gt;name}\n&quot;;
    }

    public function getName(): string
    {
        return $this-&amp;gt;name;
    }
}

$user = new UserProfile(&quot;Kim&quot;);
echo $user-&amp;gt;getName();
/* [결과]
프로필 생성: Kim
Kim
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;소멸자 __destruct()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체가 소멸할 때 자동으로 실행되는 메서드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 객체가 사용하던 자원을 정리하는 등의 마무리 작업에 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 문법 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788596014709&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public function __destruct()
{
    // 객체 소멸 시 수행할 처리
}&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;[ 관련 메서드 : unset($변수) ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 해제하는 동작.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 다른 변수가 같은 객체를 참조하고 있다면 바로 소멸하지 않음.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 예시 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788596051982&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public function __destruct()
{
    echo &quot;프로필 소멸: {$this-&amp;gt;name}\n&quot;;
}

$user = new UserProfile(&quot;Kim&quot;);

echo &quot;프로필 사용 중\n&quot;;

unset($user);

/*
프로필 생성: Kim
프로필 사용 중
프로필 소멸: Kim
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;속성 접근&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;속성 읽기 __get()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;존재하지 않거나 현재 위치에서 접근할 수 없는 속성을 읽을 때 자동으로 실행되는 메서드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 읽으려는 속성의 이름을 전달받고, 어떤 값을 반환할지 직접 정의함&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 문법 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788596198541&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public function __get(string $name): mixed 
{
    // 속성을 읽을 때 반환할 값
}
// $name = 읽으려는 속성 이름
// $mixed = 여러 타입의 값을 반환할 수 있다는 의미&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 예시 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788596369816&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    private array $data = [
        &quot;name&quot; =&amp;gt; &quot;Kim&quot;
    ];

    public function __get(string $name): mixed
    {
        return $this-&amp;gt;data[$name] ?? null;
        	// 해당 키가 없거나 값이 null이면 null을 반환함
    }
}

$user = new UserProfile();

echo $user-&amp;gt;name; // Kim
	// UserProfile에는 $data 속성만 있고, $name 속성은 선언되어 있지 않음. 
    // 따라서 $user-&amp;gt;name을 읽으면 __get(&quot;name&quot;)을 호출함 
    // $this-&amp;gt;data[&quot;name&quot;] 을 조회
    // &quot;Kim&quot; 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;속성 쓰기 __set()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;존재하지 않거나 현재 위치에서 접근할 수 없는 속성에 값을 대입할 때 자동으로 실행되는 메서드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 속성 이름과 대입할 값을 전달받고, 그 값을 어떻게 처리할지 정의 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 문법 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788596453064&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public function __set(string $name, mixed $value): void
	// $name = 대입하려는 속성 이름
    // $value = 대입할 값
    // void = 반환값 없음
{
    // 전달받은 값을 저장하거나 검증
}&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;/p&gt;
&lt;pre id=&quot;code_1788596651035&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    private array $data = [
        &quot;name&quot; =&amp;gt; &quot;Kim&quot;
    ];

    public function __get(string $name): mixed
    {
        return $this-&amp;gt;data[$name] ?? null;
    }
    
    // 이와 같은 메서드를 추가하면 =&amp;gt; 외부에서 값을 대입하고 다시 읽을 수 있음
    public function __set(string $name, mixed $value): void
    {
        $this-&amp;gt;data[$name] = $value;
    }
}

$user = new UserProfile();

$user-&amp;gt;name = &quot;Lee&quot;; // __set(&quot;name&quot;, &quot;Lee&quot;)
echo $user-&amp;gt;name;    // __get(&quot;name&quot;) &amp;rarr; Lee
/*
$user-&amp;gt;name = &quot;Lee&quot;
&amp;rarr; __set(&quot;name&quot;, &quot;Lee&quot;)
&amp;rarr; 내부 배열의 &quot;name&quot; 값을 &quot;Lee&quot;로 변경

echo $user-&amp;gt;name
&amp;rarr; __get(&quot;name&quot;)
&amp;rarr; 변경된 값 &quot;Lee&quot; 반환
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메서드 호출&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;객체 메서드 호출 __call()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;존재하지 않거나 현재 위치에서 접근할 수 없는 메서드를 호출할 때 자동으로 실행되는 메서드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 문법 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788596953876&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ClassName
{
    public function __call(string $name, array $arguments): mixed
    {
        // $name과 $arguments를 사용해 호출 처리
    }
}
// $name = 호출하려던 메서드 이름
// $arguments = 전달할 인수를 담은 배열, 인수가 없으면 []
// mixed = 처리 결과로 여러 타입의 값을 반환할 수 있음

$object = new ClassName();
$object-&amp;gt;methodName($argument1, $argument2);

/*
$object-&amp;gt;methodName($argument1, $argument2)
&amp;rarr; 접근 가능한 methodName()이 없는 경우
&amp;rarr; __call(&quot;methodName&quot;, [$argument1, $argument2])
*/&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;/p&gt;
&lt;pre id=&quot;code_1788597021437&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    private array $data = [&quot;name&quot; =&amp;gt; &quot;Kim&quot;];

    public function __call(string $name, array $arguments): mixed
    {
        if ($name === &quot;getName&quot; &amp;amp;&amp;amp; $arguments === []) {
            return $this-&amp;gt;data[&quot;name&quot;];
        }

        throw new BadMethodCallException(&quot;지원하지 않는 호출: $name&quot;);
    }
}

$user = new UserProfile();
echo $user-&amp;gt;getName(); // Kim
	// getName()은 정의되어 있지 않음 =&amp;gt; PHP가 대신 __call()을 호출함
    // __call(&quot;getName&quot;, [])
    // 작성해 둔 조건에 따라 &quot;Kim&quot; 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정적 메서드 호출 __callStatic()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 문법 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788597317894&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ClassName
{
    public static function __callStatic(
        string $name,
        array $arguments
    ): mixed {
        // $name과 $arguments를 사용해 호출 처리
    }
}

ClassName::methodName($argument1, $argument2);
/*
ClassName::methodName($argument1, $argument2)
&amp;rarr; 접근 가능한 정적 methodName()이 없는 경우
&amp;rarr; __callStatic(&quot;methodName&quot;, [$argument1, $argument2])
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[예시]&lt;/p&gt;
&lt;pre id=&quot;code_1788597384163&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static function __callStatic(string $name, array $arguments): mixed
{
    if ($name === &quot;create&quot; &amp;amp;&amp;amp; $arguments === []) {
        return new self();
    }

    throw new BadMethodCallException(&quot;지원하지 않는 호출: $name&quot;);
}

$user = UserProfile::create();
echo $user-&amp;gt;getName(); // Kim
/*
UserProfile::create()
&amp;rarr; 접근 가능한 정적 create() 메서드가 없음
&amp;rarr; __callStatic(&quot;create&quot;, [])
&amp;rarr; new self()로 UserProfile 객체 생성 후 반환
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문자열 반환&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;__toString()&lt;/h4&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;- echo로 객체를 출력하거나 (string)으로 변환할 때 호출 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 문법 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788597601083&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ClassName
{
    public function __toString(): string
    	// public =&amp;gt; 외부에서 접근 가능한 메서드로 선언
        // 매개변수 없음 =&amp;gt; 문자열 변환 시 별도 인수를 받지 않음
        // : string =&amp;gt; 문자열을 반환하도록 선언 
    {
        return &quot;객체를 표현할 문자열&quot;;
    }
}

$object = new ClassName();

echo $object;
$text = (string) $object;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 예시 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788597658280&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    private string $name;

    public function __construct(string $name)
    {
        $this-&amp;gt;name = $name;
    }

    public function __toString(): string
    {
        return &quot;사용자: {$this-&amp;gt;name}&quot;;
    }
}

$user = new UserProfile(&quot;Kim&quot;);

echo $user; // 사용자: Kim
/*
echo $user
&amp;rarr; 객체를 문자열로 표현해야 함
&amp;rarr; PHP가 __toString() 호출
&amp;rarr; &quot;사용자: Kim&quot; 반환
&amp;rarr; echo가 반환된 문자열 출력
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;함수형 호출&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;__invoke()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 함수처럼 호출할 때 실행되는 메서드.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 객체 변수 뒤에 ()를 붙여 호출할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 문법 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788597828551&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ClassName
{
    public function __invoke($parameter)
    	// $parameter = 호출 시 전달받을 값
    {
        // 객체를 함수처럼 호출했을 때 수행할 처리
        return $parameter;
    }
}

$object = new ClassName();
$result = $object($argument); // $object($argument) = 객체를 함수처럼 호출하는 문법
/*
$object($argument)
&amp;rarr; PHP가 __invoke($argument) 호출
&amp;rarr; 메서드 내부 코드 실행
&amp;rarr; 반환값을 $result에 저장
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 예시 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788597878620&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    private array $data = [
        &quot;name&quot; =&amp;gt; &quot;Kim&quot;,
        &quot;role&quot; =&amp;gt; &quot;admin&quot;
    ];

    public function __invoke(string $key): mixed
    {
        return $this-&amp;gt;data[$key] ?? null;
    }
}

$user = new UserProfile();

echo $user(&quot;name&quot;) . &quot;\n&quot;;
echo $user(&quot;role&quot;);
/*
Kim
admin
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 복제&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;__clone()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clone으로 객체를 복제한 뒤, 복제본에서 실행되는 메서드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복제된 객체의 일부 값을 변경하거나, 내부에 포함된 객체를 추가로 복제하는 데 사용함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 문법 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788598034777&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ClassName
{
    public function __clone(): void
    {
        // 복제본에 필요한 추가 처리
    }
}

$original = new ClassName();
$copy = clone $original; // 기존 객체를 복제하여 새 객체 생성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[특징]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- __clone() 안의 $this는 복제본이므로 원본 이름은 변경되지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; clone이 먼저 새로운 객체를 만든다음, __clone()을 그 새 객체에서 실행함&lt;br /&gt;(새로운 객체가 아닌, 같은 객체를 두 변수가 가리킴)&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 복제할 때 __construnct()가 다시 실행되지 않음.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기본적으로 얕은 복사를 수행함.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속성에 다른 객체가 들어 있다면 그 내부 객체까지 자동으로 복제되지는 않는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 객체도 분리해야 한다면 __clone()안에서 해당 객체를 다시 clone해야 함.&lt;/p&gt;
&lt;pre id=&quot;code_1788601632385&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Address
{
    public string $city;

    public function __construct(string $city)
    {
        $this-&amp;gt;city = $city;
    }
}

class User
{
    public string $name;
    // User 객체 안에 Address 객체 있음.
    public Address $address;

    public function __construct(string $name, Address $address)
    {
        $this-&amp;gt;name = $name;
        $this-&amp;gt;address = $address;
    }
}

$address = new Address(&quot;Seoul&quot;);

$user = new User(&quot;Kim&quot;, $address);

$copy = clone $user;
/*
$user -&amp;gt; User 객체 A
$copy -&amp;gt; User 객체 B
*/

// 단순한 속성인 name을 변경하면 서로 영향을 주지 않음
$copy-&amp;gt;name = &quot;Lee&quot;;
echo $user-&amp;gt;name; // Kim
echo $copy-&amp;gt;name; // Lee

// address의 경우
/*
$user
└─ User 객체 A
   ├─ name = &quot;Kim&quot;
   └─ address ──────┐
                    &amp;darr;
               Address 객체 C
               city = &quot;Seoul&quot;

$copy
└─ User 객체 B
   ├─ name = &quot;Kim&quot;
   └─ address ──────┘
   
=&amp;gt; 두 속성이 같은 Address 객체를 바라보고 있음.
*/

// 복제본에서 주소를 변경하면
$copy-&amp;gt;address-&amp;gt;city = &quot;Busan&quot;;

// 결과는 =&amp;gt; 둘 다 바뀜 
// why) Address 객체 자체는 복제되지 않음 
echo $user-&amp;gt;address-&amp;gt;city; // Busan
echo $copy-&amp;gt;address-&amp;gt;city; // Busan&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 예시 ]&lt;/p&gt;
&lt;pre id=&quot;code_1788598066307&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserProfile
{
    public string $name;

    public function __construct(string $name)
    {
        $this-&amp;gt;name = $name;
    }

    public function __clone(): void
    {
        $this-&amp;gt;name .= &quot; (복사본)&quot;;
    }
}

$user = new UserProfile(&quot;Kim&quot;);
$copy = clone $user;

echo $user-&amp;gt;name . &quot;\n&quot;;
echo $copy-&amp;gt;name;
/*
Kim
Kim (복사본)
*/&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;주의점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;호출 흐름의 명확성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매직 메서드는 코드에 메서드 이름이 직접 나타나지 않아, 실제로 어떤 코드가 실행되는지 파악하기 어려울 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;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;ex]&lt;/p&gt;
&lt;pre id=&quot;code_1788601725705&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$user-&amp;gt;name;       // __get()으로 연결될 수 있음
$user-&amp;gt;getRole();  // __call()로 연결될 수 있음
$user(&quot;role&quot;);     // __invoke() 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;오타와 잘못된 호출&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;존재하지 않는 속성이나 메서드를 처리할 때, 모든 요청을 받아 주면 오타를 발견하기 어려워질 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상태 변경 규칙&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;__set()을 정의했다고 해서 데이터가 자동으로 보호되는 것은 아님&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 허용할 항목, 값의 타입, 수정 가능 여부가 중요하다면 메서드 내부에 검증 규칙을 직접 작성해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동작의 예측 가능성&lt;/h3&gt;
&lt;p 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; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;메서드&lt;/td&gt;
&lt;td&gt;설계 시 고려사항&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__get()&lt;/td&gt;
&lt;td&gt;단순 조회에 예상치 못한 상태 변경이 포함되지 않는지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__set()&lt;/td&gt;
&lt;td&gt;저장할 값과 수정 가능한 항목을 검증하는지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__call()&lt;/td&gt;
&lt;td&gt;지원하는 호출과 잘못된 호출을 구분하는지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__toString()&lt;/td&gt;
&lt;td&gt;객체를 표현하는 문자열을 일관되게 반환하는지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__invoke()&lt;/td&gt;
&lt;td&gt;객체를 함수처럼 호출하는 의미가 명확한지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__clone()&lt;/td&gt;
&lt;td&gt;내부 객체도 별도로 복제해야 하는지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;소멸 시점의 의존성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체는 다른 곳에서 참조하는 동안 유지될 수 있으므로, 원하는 업무 시점과 소멸 시점이 일치한다고 가정하면 안 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 저장이나 작업완료처럼 실행 시점이 중요한 처리는 일반 메서드로 명시적으로 호출하고, 소멸자에는 적절한 정리 자겅븡ㄹ 두는 편이 좋음.&lt;/p&gt;</description>
      <category>STUDY/PHP</category>
      <category>Clone</category>
      <category>PHP</category>
      <category>객체복제</category>
      <category>매직메서드</category>
      <category>메서드오버로딩</category>
      <category>문자열변환</category>
      <category>생성자</category>
      <category>소멸자</category>
      <category>속서엊ㅂ근</category>
      <author>devmel</author>
      <guid isPermaLink="true">https://devmel393.tistory.com/57</guid>
      <comments>https://devmel393.tistory.com/57#entry57comment</comments>
      <pubDate>Sat, 5 Sep 2026 18:55:13 +0900</pubDate>
    </item>
    <item>
      <title>[객체지향] PHP static과 스코프 해석 연산자</title>
      <link>https://devmel393.tistory.com/56</link>
      <description>&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;static&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체가 아니라 &lt;b&gt;클래스 자체에 속한&lt;/b&gt; 속성이나 메서드를 정의할 때 사용하는 키워드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static으로 정의한 속성이나 메서드는 &lt;b&gt;객체를 만들지 않고 클래스 이름으로 접근 가능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 멤버]&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구분&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;객체 멤버&lt;/td&gt;
&lt;td&gt;객체마다 따로 가지는 속성이나 메서드&lt;/td&gt;
&lt;td&gt;자동차의 색상, 무게&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;클래스 멤버&lt;/td&gt;
&lt;td&gt;클래스 자체에 속하는 속성이나 메서드&lt;/td&gt;
&lt;td&gt;생성된 자동차 수, 공통 설정값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;클래스 상수&lt;/td&gt;
&lt;td&gt;클래스 안에 정의한 변하지 않는 값&lt;/td&gt;
&lt;td&gt;상태 코드, 타입 값&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스코프 해석 연산자&lt;/h3&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;gt; 클래스, 부모 클래스, 현재 클래스의 정적 멤버나 상수에 접근할 때 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교]&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;연산자&lt;/td&gt;
&lt;td&gt;사용 대상&lt;/td&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&amp;gt;&lt;/td&gt;
&lt;td&gt;객체&lt;/td&gt;
&lt;td&gt;$car-&amp;gt;startEngine()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;::&lt;/td&gt;
&lt;td&gt;클래스, 정적 멤버, 상수&lt;/td&gt;
&lt;td&gt;Car::getCount()&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;h4 data-ke-size=&quot;size20&quot;&gt;관련키워드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[self]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 클래스 자신을 가리킴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 메서드나 클래스 상수에 접근할 때 자주 사용함&lt;/p&gt;
&lt;pre id=&quot;code_1788589671257&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Car
{
    public const TYPE = &quot;vehicle&quot;;

    public static function getType()
    {
        return self::TYPE;
    }
}&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;size18&quot;&gt;[parent]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 클래스를 가리킴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자식 클래스에서 부모 클래스의 메서드나 생성자, 상수에 접근할 때 사용함&lt;/p&gt;
&lt;pre id=&quot;code_1788589810955&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Animal
{
    public function speak()
    {
        return &quot;동물이 소리를 냅니다.&quot;;
    }
}

class Dog extends Animal
{
    public function speak()
    {
    	// 부모클래스인 Animal의 speak() 메서드를 호출함 
        return parent::speak() . &quot; 그리고 멍멍&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;self와 비슷해 보이지만 상속 관계에서 차이가 있음&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;키워드&lt;/td&gt;
&lt;td&gt;기준&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;self&lt;/td&gt;
&lt;td&gt;코드가 작성된 클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;static&lt;/td&gt;
&lt;td&gt;실제 호출한 클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788589967853&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ParentClass
{
    public static function who()
    {
        return &quot;부모&quot;;
    }

    public static function test()
    {
        return static::who();
    }
}

class ChildClass extends ParentClass
{
    public static function who()
    {
        return &quot;자식&quot;;
    }
}

echo ChildClass::test(); // 자식&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정적 속성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체가 아니라 클래스 자체에 속하는 속성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속성 앞에 static을 붙여 정의&lt;/p&gt;
&lt;pre id=&quot;code_1788589218863&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Car
{
    public static $count = 0;
    public $color = &quot;&quot;;
    
    public function __construct()
    {
        self::$count++;
    }
}

// 접근 : 객체를 생성하지 않고, 클래스 이름으로 접근
echo Car::$count;

// 일반속성은 객체마다 따로 존재함
$car1 = new Car();
$car2 = new Car();

$car1-&amp;gt;color = &quot;black&quot;;
$car2-&amp;gt;color = &quot;white&quot;;

// 정적속성은 모든 객체가 공유함
echo Car::$count; // 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정적 메서드&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;개념/문법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 만들지 않고 클래스 이름으로 호출할 수 있는 메서드&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788589288972&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Calculator
{
    public static function add($a, $b)
    {
        return $a + $b;
    }
}

echo Calculator::add(10, 20); // 30&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;정적 메서드 안에서든 $this 사용 불가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;why) $this는 현재 객체를 의미하는데, 정적 메서드는 객체 없이 이름으로 호출될 수 있기 때문에 객체가 존재하지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 정적메서드에서 정적 속성에 접근하려면 self:: 사용&lt;/p&gt;
&lt;pre id=&quot;code_1788589356999&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Car
{
    public static $count = 0;

    public static function getCount()
    {
        return self::$count;
    }
}&lt;/code&gt;&lt;/pre&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;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구분&lt;/td&gt;
&lt;td&gt;객체 멤버&lt;/td&gt;
&lt;td&gt;정적 멤버&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;소속&lt;/td&gt;
&lt;td&gt;객체&lt;/td&gt;
&lt;td&gt;클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;접근 방식&lt;/td&gt;
&lt;td&gt;$object-&amp;gt;method()&lt;/td&gt;
&lt;td&gt;ClassName::method()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상태&lt;/td&gt;
&lt;td&gt;객체마다 다를 수 있음&lt;/td&gt;
&lt;td&gt;클래스 단위로 공유됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;$this&lt;/span&gt; 사용&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;늦은 정적 바인딩&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속 관계에서 static:: 이 실제로 호출한 클래스 기준으로 동작하게 하는 PHP의 기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 어떤 클래스를 기준으로 할지 코드 작성 시점에 고정하지 않고 실행 시점에 결정 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;비교&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;키워드&lt;/td&gt;
&lt;td&gt;기준&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;self::&lt;/td&gt;
&lt;td&gt;코드가 작성된 클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;static::&lt;/td&gt;
&lt;td&gt;실제로 호출한 클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속 구조에서는 부모 클래스에 공통 정적 메서드를 두고, 자식 클래스마다 일부 정적 동작만 다르게 만들고 싶을 때가 있음.&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;EX) 부모 클래스에는 공통 조회 메서드가 있고, 자식 클래스마다 다른 테이블 이름을 반환해야 함&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #f6f6f6; border-radius: 15px; solid; padding: 35px;&quot;&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Model &lt;br /&gt;&amp;rarr;&amp;nbsp;User &lt;br /&gt;&amp;rarr;&amp;nbsp;Post&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788590939987&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?php

class ParentClass
{
    public static function identify()
    {
        return &quot;부모&quot;;
    }

    public static function test()
    {
        echo &quot;self 결과: &quot; . self::identify() . PHP_EOL;
        echo &quot;static 결과: &quot; . static::identify() . PHP_EOL;
    }
}

class ChildClass extends ParentClass
{
    public static function identify()
    {
        return &quot;자식&quot;;
    }
}

ChildClass::test();
/*
[결과]
self 결과: 부모
static 결과: 자식

[실행흐름]
ChildClass::test()
        &amp;darr;
ParentClass에 작성된 test() 실행
        &amp;darr;
self::identify()
        &amp;darr;
ParentClass::identify()
        &amp;darr;
&quot;부모&quot;

static::identify()
        &amp;darr;
ChildClass::identify()
        &amp;darr;
&quot;자식&quot;
*/&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;주의점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static과 스코프 해석 연산자는 클래스 단위의 값이나 기능을 다룰 때 유용하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 정적 멤버를 무분별하게 사용하면 객체지향적인 구조가 흐려질 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;static 남용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 메서드는 객체를 만들지 않고 바로 호출할 수 있어 편리하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 상태와 무관한 유틸리성 기능이라면 정적 메서드가 잘 맞을 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 객체가 가져야 할 상태나 책임까지 정적 메서드로 처리하며 코드가 절차적인 함수 모음처럼 변할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EX]&lt;/p&gt;
&lt;pre id=&quot;code_1788591233821&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserService
{
    public static function createUser($name)
    {
        // 사용자 생성
    }

    public static function updateUser($id, $name)
    {
        // 사용자 수정
    }

    public static function deleteUser($id)
    {
        // 사용자 삭제
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전역 상태&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;테스트 어려움&lt;/h3&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;ex]&lt;/p&gt;
&lt;pre id=&quot;code_1788591342546&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class OrderService
{
    public function createOrder($name)
    {
        Logger::log(&quot;주문 생성: &quot; . $name);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Logger에 직접 의존함.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트할 때 가짜 로거로 교체하거나, 로그 호출 여부만 따로 확인하기 어려울 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 필요한 객체를 외부에서 전달받으면 더 유연해짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1788592185176&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class OrderService
{
    private $logger;

    public function __construct($logger)
    {
        $this-&amp;gt;logger = $logger;
    }

    public function createOrder($name)
    {
        $this-&amp;gt;logger-&amp;gt;log(&quot;주문 생성: &quot; . $name);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 시 원하는 로거 객체를 넣을 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;고려사항&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;질문&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;객체 상태와 무관한 기능인가?&lt;/td&gt;
&lt;td&gt;정적 메서드가 적합한지 판단한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;여러 객체가 공유해야 하는 값인가?&lt;/td&gt;
&lt;td&gt;정적 속성이 필요한지 확인한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;어디서든 값이 바뀌어도 괜찮은가?&lt;/td&gt;
&lt;td&gt;전역 상태 문제가 없는지 본다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;테스트에서 쉽게 대체할 수 있는가?&lt;/td&gt;
&lt;td&gt;정적 의존성이 테스트를 어렵게 만들지 않는지 확인한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;객체의 책임을 정적 메서드로 빼앗고 있지는 않은가?&lt;/td&gt;
&lt;td&gt;객체지향 구조가 흐려지는지 점검한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>STUDY/PHP</category>
      <category>LateStaticBinding</category>
      <category>parent</category>
      <category>PHP</category>
      <category>PHP객체지향</category>
      <category>Self</category>
      <category>static</category>
      <category>스코프해석연산자</category>
      <category>정적메서드</category>
      <category>정적속성</category>
      <author>devmel</author>
      <guid isPermaLink="true">https://devmel393.tistory.com/56</guid>
      <comments>https://devmel393.tistory.com/56#entry56comment</comments>
      <pubDate>Sat, 5 Sep 2026 16:13:21 +0900</pubDate>
    </item>
    <item>
      <title>PHP 네임스페이스와 use</title>
      <link>https://devmel393.tistory.com/55</link>
      <description>&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;네임스페이스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 이름의 충돌을 막기 위한 이름 공간 (이름 앞에 소속 공간)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네임스페이스를 두면 같은 클래스 이름이라도 서로 다른 공간에 둘 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;namespace&lt;/b&gt; 키워드로 클래스가 속할 이름 공간을 선언&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 보통 파일 경로와 비슷하게 맞춰 사용함 =&amp;gt; 클래스가 어느 영역에 속하는지 파악하기 쉽고, 자동 로딩 설정과도 잘 어울림.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다, 네임스페이스와 파일 경로가 자동으로 연결되는 것은 아님. 파일 안에 직접 선언해야 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;as&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EX]&lt;/p&gt;
&lt;pre id=&quot;code_1788587448408&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?php

namespace App\Service;

class Logger
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; Logger 클래스의 전체 이름 = App\Service\Logger&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;use&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 네임스페이스에 있는 클래스를 현재 파일에서 짧게 사용하기 위한 문법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788587261969&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$logger = new App\Service\Logger();
 // 매번 전체 이름으로 쓰면 코드가 길어짐
 
use App\Service\Logger;
// 별칭
use External\Library\Logger as ExternalLogger;

$logger = new Logger(); // 짧은 파일 이름으로 사용 가능
$externalLogger = new ExternalLogger();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>STUDY/PHP</category>
      <category>AS</category>
      <category>namespace</category>
      <category>use</category>
      <category>네임스페이스</category>
      <author>devmel</author>
      <guid isPermaLink="true">https://devmel393.tistory.com/55</guid>
      <comments>https://devmel393.tistory.com/55#entry55comment</comments>
      <pubDate>Sat, 5 Sep 2026 14:59:20 +0900</pubDate>
    </item>
    <item>
      <title>[디자인패턴] 싱글톤 패턴</title>
      <link>https://devmel393.tistory.com/54</link>
      <description>&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 만들다 보면 어떤 객체는 여러 개 만들어질 필요가 없거나, 여러 개 만들어지면 문제가 되는 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EX] 애플리케이션 설정 정보를 관리하는 객체&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #f6f6f6; border-radius: 15px; solid; padding: 35px;&quot;&gt;
&lt;pre id=&quot;code_1788501543878&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;config1 = AppConfig()
config2 = AppConfig()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 설정 객체가 여러 개 만들어지고, 각각 다른 상태를 가지게 된다면 프로그램 안에서 사용하는 설정값이 달라질 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 이런 상황에서는 어느 설정값이 실제 기준인지 헷갈릴 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 데이터베이스 연결 관리자, 로거, 캐시 관리자처럼 애플리케이션 전체에서 하나의 기준으로 관리하는 객체도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 객체가 불필요하게 여러 개 만들어지면 상태 관리가 어려워지거나 자원이 낭비될 수 있음&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;의미&lt;/h3&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;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구분&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;하나의 인스턴스&lt;/td&gt;
&lt;td&gt;특정 클래스의 객체가 프로그램 안에서 하나만 존재한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;접근 지점 제공&lt;/td&gt;
&lt;td&gt;그 하나의 객체를 사용할 수 있는 방법을 제공한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;생성 제어&lt;/td&gt;
&lt;td&gt;외부에서 객체를 마음대로 여러 개 만들지 못하게 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 구조&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구성 요소&lt;/td&gt;
&lt;td&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Singleton Class&lt;/td&gt;
&lt;td&gt;하나의 인스턴스만 생성되도록 관리하는 클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instance 저장 공간&lt;/td&gt;
&lt;td&gt;생성된 하나의 인스턴스를 저장하는 변수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;접근 메서드&lt;/td&gt;
&lt;td&gt;저장된 인스턴스를 반환하는 메서드&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름]&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #f6f6f6; border-radius: 15px; solid; padding: 35px;&quot;&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;인스턴스&amp;nbsp;요청 &lt;br /&gt;&amp;rarr;&amp;nbsp;이미&amp;nbsp;만들어진&amp;nbsp;인스턴스가&amp;nbsp;있는지&amp;nbsp;확인 &lt;br /&gt;&amp;rarr;&amp;nbsp;없으면&amp;nbsp;새로&amp;nbsp;생성 &lt;br /&gt;&amp;rarr;&amp;nbsp;있으면&amp;nbsp;기존&amp;nbsp;인스턴스&amp;nbsp;반환&lt;/p&gt;
&lt;/div&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EX]&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #f6f6f6; border-radius: 15px; solid; padding: 35px;&quot;&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;애플리케이션 설정을 관리하는 AppConfig&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788502975126&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class AppConfig:
    def __init__(self):
        self.database_url = None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 클래스를 여러 곳에서 각각 생성하면 서로 다른 설정 객체가 만들어짐&lt;/p&gt;
&lt;pre id=&quot;code_1788502997206&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;config1 = AppConfig()
config2 = AppConfig()

config1.database_url = &quot;db-main&quot;
config2.database_url = &quot;db-test&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 config1과 config2는 서로 다른 상태를 가짐&lt;/p&gt;
&lt;br /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;객체&lt;/td&gt;
&lt;td&gt;database_url&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;config1&lt;/td&gt;
&lt;td&gt;db-main&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;config2&lt;/td&gt;
&lt;td&gt;db-test&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;p data-ke-size=&quot;size16&quot;&gt;문제는 애플리케이션 안에서 어떤 설정 객체를 기준으로 봐야 하는지 모호해질 수 있음&lt;/p&gt;
&lt;pre id=&quot;code_1788503060066&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def connect_database(config):
    return f&quot;{config.database_url}에 연결&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1788503087767&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;connect_database(config1)  # db-main에 연결
connect_database(config2)  # db-test에 연결&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 곳에서는 config1을 넘기고, 다른 곳에서는 config2를 넘기면 실행 위치에 따라 다른 설정값을 사용할 수 있음&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;br /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;문제&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상태 불일치&lt;/td&gt;
&lt;td&gt;같은 설정 객체라고 생각했지만 서로 다른 값을 가질 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기준 모호&lt;/td&gt;
&lt;td&gt;어떤 인스턴스가 실제 기준인지 알기 어렵다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자원 낭비&lt;/td&gt;
&lt;td&gt;데이터베이스 연결 관리자처럼 무거운 객체가 여러 번 생성될 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;변경 추적 어려움&lt;/td&gt;
&lt;td&gt;어느 객체의 상태가 언제 바뀌었는지 추적하기 어렵다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우는 하나의 인스턴스만 생성되도록 관리할 필요가 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤으로 인스턴스 하나만 관리할 필요가 있음.&lt;/p&gt;
&lt;pre id=&quot;code_1788503360286&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class AppConfig:
	# _instance = 생성된 객체를 저장하는 변수
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.database_url = None
        return cls._instance&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 변 생성하더라도 같은 객체가 반환됨&lt;/p&gt;
&lt;pre id=&quot;code_1788503415940&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;config1 = AppConfig()
config2 = AppConfig()

config1.database_url = &quot;db-main&quot;

print(config2.database_url)  # db-main
print(config1 is config2)    # True&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;장점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;장점&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인스턴스 중복 생성 방지&lt;/td&gt;
&lt;td&gt;같은 객체가 여러 번 생성되는 것을 막을 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;일관된 상태 관리&lt;/td&gt;
&lt;td&gt;여러 곳에서 같은 인스턴스를 사용하므로 기준 상태를 통일할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;공통 자원 접근 관리&lt;/td&gt;
&lt;td&gt;설정, 로거, 연결 관리자 같은 공용 자원을 한곳에서 관리할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;접근 지점 단순화&lt;/td&gt;
&lt;td&gt;필요한 곳에서 같은 인스턴스에 접근하는 방식을 제공할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;효과적인 상황&lt;/h3&gt;
&lt;table style=&quot;letter-spacing: 0px; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;상황&lt;/td&gt;
&lt;td&gt;이유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;설정 정보를 한곳에서 관리해야 하는 경우&lt;/td&gt;
&lt;td&gt;여러 객체가 서로 다른 설정값을 가지는 문제를 줄일 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;로거처럼 공통으로 사용하는 객체가 필요한 경우&lt;/td&gt;
&lt;td&gt;프로그램 전체에서 같은 로깅 기준을 사용할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터베이스 연결 관리자처럼 생성 비용이 큰 객체가 있는 경우&lt;/td&gt;
&lt;td&gt;불필요한 중복 생성을 줄일 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;하나의 상태를 기준으로 여러 곳에서 접근해야 하는 경우&lt;/td&gt;
&lt;td&gt;같은 인스턴스를 공유해 일관성을 유지할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;주의점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의점&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;주의점&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;전역 상태 문제&lt;/td&gt;
&lt;td&gt;여러 곳에서 같은 상태를 공유하면 변경 영향이 넓게 퍼질 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;테스트 어려움&lt;/td&gt;
&lt;td&gt;테스트마다 독립적인 상태를 만들기 어렵고, 이전 테스트의 상태가 남을 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;의존 관계 숨김&lt;/td&gt;
&lt;td&gt;코드 안에서 싱글톤을 직접 호출하면 어떤 의존성이 필요한지 겉으로 드러나지 않는다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동시성 문제&lt;/td&gt;
&lt;td&gt;여러 흐름에서 동시에 같은 객체를 수정하면 상태 충돌이 생길 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;생명주기 관리 어려움&lt;/td&gt;
&lt;td&gt;언제 생성되고 언제 초기화되는지 명확하지 않으면 관리가 어려워질 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;고려사항&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;질문&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정말 하나만 존재해야 하는 객체인가?&lt;/td&gt;
&lt;td&gt;단순히 편해서 싱글톤을 쓰는 것은 아닌지 확인한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상태 변경이 여러 곳에 영향을 줘도 괜찮은가?&lt;/td&gt;
&lt;td&gt;공유 상태의 변경 범위를 고려한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;테스트마다 상태를 분리할 수 있는가?&lt;/td&gt;
&lt;td&gt;테스트 독립성을 해치지 않는지 확인한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;의존성이 코드 밖으로 드러나는가?&lt;/td&gt;
&lt;td&gt;숨겨진 의존성이 생기지 않도록 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동시 접근 상황에서 안전한가?&lt;/td&gt;
&lt;td&gt;여러 흐름이 동시에 사용할 때 문제가 없는지 확인한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>STUDY/CS</category>
      <category>SingletonPattern</category>
      <category>디자인패턴</category>
      <category>소프트웨어설계</category>
      <category>싱글톤패턴</category>
      <category>전역상태</category>
      <author>devmel</author>
      <guid isPermaLink="true">https://devmel393.tistory.com/54</guid>
      <comments>https://devmel393.tistory.com/54#entry54comment</comments>
      <pubDate>Fri, 4 Sep 2026 19:06:48 +0900</pubDate>
    </item>
    <item>
      <title>[디자인패턴] 데코레이터 패턴</title>
      <link>https://devmel393.tistory.com/53</link>
      <description>&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 만들다 보면 기존 기능은 유지하면서, 그 앞뒤로 부가 기능을 추가해야 하는 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EX]&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #f6f6f6; border-radius: 15px; solid; padding: 35px;&quot;&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;결제 기능&lt;/p&gt;
&lt;pre id=&quot;code_1788498879982&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;payment.pay(10000)&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;ex) 로깅, 권한 확인, 캐싱, 알림&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능들이 기존 결제 클래스 안에 계속 추가되면, 클래스의 책임이 점점 커짐.&lt;/p&gt;
&lt;pre id=&quot;code_1788498954996&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class PaymentService:
    def pay(self, amount):
        check_permission()
        write_log(&quot;결제 시작&quot;)

        result = process_payment(amount)

        send_notification()
        write_log(&quot;결제 완료&quot;)

        return result&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 결제 기능 자체와 부가 기능이 강하게 묶인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 로깅만 제거하거나, 알림만 다른 상황에서 추가하고 싶을 때 유연하게 조합하기 어려움.&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot; 기존 객체를 직접 수정하지 않고, 기능을 덧붙일 수 없을까?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;의미&lt;/h3&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;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.1395%;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 44.5349%;&quot;&gt;역할&lt;/td&gt;
&lt;td style=&quot;width: 22.2093%;&quot;&gt;예시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.1395%;&quot;&gt;Component&lt;/td&gt;
&lt;td style=&quot;width: 44.5349%;&quot;&gt;공통으로 사용할 기능의 형태&lt;/td&gt;
&lt;td style=&quot;width: 22.2093%;&quot;&gt;Payment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.1395%;&quot;&gt;Concrete Component&lt;/td&gt;
&lt;td style=&quot;width: 44.5349%;&quot;&gt;기본 기능을 수행하는 실제 객체&lt;/td&gt;
&lt;td style=&quot;width: 22.2093%;&quot;&gt;BasicPayment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.1395%;&quot;&gt;Decorator&lt;/td&gt;
&lt;td style=&quot;width: 44.5349%;&quot;&gt;기존 객체를 감싸고 부가 기능을 추가하는 객체&lt;/td&gt;
&lt;td style=&quot;width: 22.2093%;&quot;&gt;LoggingPayment, PermissionPayment, NotificationPayment&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;p data-ke-size=&quot;size16&quot;&gt;핵심은 기존 객체와 데코레이터가 같은 형태의 메서드를 제공함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 사용하는 쪽에서는 기본 객체인지, 데코레이터로 감싼 객체인지 같은 방식으로 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 객체를 내부에 가지고 있다가, 필요할 때 기존 객체의 기능을 호출함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 호출 전후에 자신의 부가 기능을 추가함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EX]&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #f6f6f6; border-radius: 15px; solid; padding: 35px;&quot;&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;결제 기능을 담당하는 BasicPayment&lt;/p&gt;
&lt;pre id=&quot;code_1788499278185&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class BasicPayment:
    def pay(self, amount):
        return f&quot;{amount}원 결제 완료&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 기능이 추가될 때, 가장 간단한 방법은 기존 클래스 안에 부가 기능을 직접 넣는 것&lt;/p&gt;
&lt;pre id=&quot;code_1788499322356&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class BasicPayment:
    def pay(self, amount):
        check_permission()
        write_log(&quot;결제 시작&quot;)

        result = f&quot;{amount}원 결제 완료&quot;

        send_notification()
        write_log(&quot;결제 완료&quot;)

        return result&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;br /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;문제&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;책임 혼합&lt;/td&gt;
&lt;td&gt;결제 처리, 권한 확인, 로깅, 알림이 한곳에 섞인다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;조합 어려움&lt;/td&gt;
&lt;td&gt;어떤 상황에서는 로깅만, 다른 상황에서는 알림만 적용하기 어렵다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수정 범위 증가&lt;/td&gt;
&lt;td&gt;부가 기능이 바뀔 때 결제 클래스도 함께 수정된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;클래스 증가 가능성&lt;/td&gt;
&lt;td&gt;상속으로 조합하면 경우의 수만큼 클래스가 늘어날 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 기능과 부가 기능을 분리하고, 필요한 기능만 조합할 수 있는 구조가 필요함.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 데코레이터로 기능 감싸기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결제 객체가 공통으로 제공할 동작을 정한다&lt;/p&gt;
&lt;pre id=&quot;code_1788500077785&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Payment:
    def pay(self, amount):
        pass&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;기본 결제 기능은 BasicPayment가 담당&lt;/p&gt;
&lt;pre id=&quot;code_1788500096754&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class BasicPayment(Payment):
    def pay(self, amount):
        return f&quot;{amount}원 결제 완료&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부가 기능은 데코레이터로 분리&lt;/p&gt;
&lt;pre id=&quot;code_1788500113448&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class LoggingPayment(Payment):
    def __init__(self, payment):
        self.payment = payment

    def pay(self, amount):
        print(&quot;결제 시작 로그&quot;)
        result = self.payment.pay(amount)
        print(&quot;결제 완료 로그&quot;)
        return result&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부에 다른 Payment 객체를 가지고 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 pay()를 호출할 때 기존 객체의 pay()를 실행하면서 앞뒤로 로그를 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한 확인도 별도의 데커레이터로 만들 수 있음&lt;/p&gt;
&lt;pre id=&quot;code_1788500191679&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class PermissionPayment(Payment):
    def __init__(self, payment):
        self.payment = payment

    def pay(self, amount):
        print(&quot;권한 확인&quot;)
        return self.payment.pay(amount)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 기능을 감싸서 조합할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1788500224572&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;payment = BasicPayment()
payment = LoggingPayment(payment)
payment = PermissionPayment(payment)

payment.pay(10000)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행흐름)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PermissionPayment.pay() &lt;br /&gt;&amp;rarr;&amp;nbsp;권한&amp;nbsp;확인 &lt;br /&gt;&amp;rarr;&amp;nbsp;내부에&amp;nbsp;감싼&amp;nbsp;LoggingPayment.pay()&amp;nbsp;호출 &lt;br /&gt;&amp;rarr;&amp;nbsp;결제&amp;nbsp;시작&amp;nbsp;로그 &lt;br /&gt;&amp;rarr;&amp;nbsp;내부에&amp;nbsp;감싼&amp;nbsp;BasicPayment.pay()&amp;nbsp;호출 &lt;br /&gt;&amp;rarr;&amp;nbsp;실제&amp;nbsp;결제&amp;nbsp;실행 &lt;br /&gt;&amp;rarr;&amp;nbsp;결제&amp;nbsp;완료&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;데코레이터들의 pay()는 같은 결제를 여러 번 실행하는 것이 아니라, 요청을 안쪽 객체로 전달하면서 앞뒤에 부가 기능을 추가하는 역할을 한다.&lt;/p&gt;
&lt;/div&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;장점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;장점&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기존 코드 변경 최소화&lt;/td&gt;
&lt;td&gt;기본 기능을 담당하는 클래스를 직접 수정하지 않고 기능을 추가할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기능 조합 유연성&lt;/td&gt;
&lt;td&gt;필요한 부가 기능을 원하는 순서로 조합할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;책임 분리&lt;/td&gt;
&lt;td&gt;기본 기능과 부가 기능을 각각 다른 객체로 나눌 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상속보다 유연한 확장&lt;/td&gt;
&lt;td&gt;기능 조합마다 새로운 자식 클래스를 만들 필요가 줄어든다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;실행 시점 조합 가능&lt;/td&gt;
&lt;td&gt;어떤 데코레이터를 적용할지 실행 중에 선택할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;효과적인 상황&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;상황&lt;/td&gt;
&lt;td&gt;이유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기존 코드를 직접 수정하기 어려운 경우&lt;/td&gt;
&lt;td&gt;기본 객체를 감싸서 기능을 추가할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;부가 기능 조합이 여러 가지인 경우&lt;/td&gt;
&lt;td&gt;상속보다 유연하게 조합할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기능을 실행 시점에 선택해야 하는 경우&lt;/td&gt;
&lt;td&gt;필요한 데코레이터를 조건에 따라 적용할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기본 기능과 부가 기능을 분리하고 싶은 경우&lt;/td&gt;
&lt;td&gt;각 기능의 책임을 나눌 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;주의점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;주의점&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;흐름 추적 어려움&lt;/td&gt;
&lt;td&gt;여러 객체가 겹쳐 있으면 실제 실행 순서를 파악하기 어려울 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;순서 의존성&lt;/td&gt;
&lt;td&gt;권한 확인, 로깅, 알림처럼 실행 순서가 중요한 기능은 조합 순서를 신경 써야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;디버깅 어려움&lt;/td&gt;
&lt;td&gt;문제가 발생했을 때 어느 데코레이터에서 발생했는지 추적해야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;과한 구조 가능성&lt;/td&gt;
&lt;td&gt;단순한 기능 추가에는 데코레이터보다 직접 구현이 더 명확할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;책임 혼합 위험&lt;/td&gt;
&lt;td&gt;데코레이터가 부가 기능을 넘어 핵심 비즈니스 로직까지 담당하면 구조가 흐려진다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의점&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;질문&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기존 객체를 직접 수정하지 않아야 하는 이유가 있는가?&lt;/td&gt;
&lt;td&gt;데코레이터를 둘 필요가 있는지 확인한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;부가 기능을 여러 방식으로 조합해야 하는가?&lt;/td&gt;
&lt;td&gt;조합의 이점이 있는지 판단한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;실행 순서가 중요한가?&lt;/td&gt;
&lt;td&gt;데코레이터를 감싸는 순서를 명확히 해야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데코레이터가 너무 많이 겹치지 않는가?&lt;/td&gt;
&lt;td&gt;구조가 과하게 복잡해지는지 확인한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;부가 기능과 핵심 기능의 책임이 분리되어 있는가?&lt;/td&gt;
&lt;td&gt;데코레이터가 핵심 로직을 침범하지 않게 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>STUDY/CS</category>
      <category>객체지향</category>
      <category>기능조합</category>
      <category>기능확장</category>
      <category>데코레이터패턴</category>
      <category>디자인패턴</category>
      <category>소프트웨어설계</category>
      <author>devmel</author>
      <guid isPermaLink="true">https://devmel393.tistory.com/53</guid>
      <comments>https://devmel393.tistory.com/53#entry53comment</comments>
      <pubDate>Fri, 4 Sep 2026 14:56:52 +0900</pubDate>
    </item>
    <item>
      <title>[디자인패턴] 어댑터 패턴</title>
      <link>https://devmel393.tistory.com/52</link>
      <description>&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 만들다 보면 이미 존재하는 코드와 새로 사용해야 하는 코드의 형식이 맞지 않는 경우가 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;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;ex)&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #f6f6f6; border-radius: 15px; solid; padding: 35px;&quot;&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;결제 객체가 pay(amount)라는 메서드를 제공&lt;/p&gt;
&lt;pre id=&quot;code_1788494935455&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;payment.pay(10000)&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;그런데 새로 연동해야 하는 외부 결제 API는 다른 이름과 다른 형식의 메서드를 제공 할 수 있음&lt;/p&gt;
&lt;pre id=&quot;code_1788494975483&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;external_payment.request_payment(price=10000)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능은 비슷함 (둘 다 결제를 처리)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BUT 사용하는 방식이 다르기 때문에 기존 코드에서는 바로 사용할 수 없음.&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;gt; 외부 API를 기존 코드 형식에 맞게 감싸기&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;의미&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어댑터 패턴은 서로 맞지 않는 인터페이스를 중간에서 변환해 연결하는 패턴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 기존 코드는 익숙한 방식 그대로 사용할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 구조&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구성 요소&lt;/td&gt;
&lt;td&gt;역할&lt;/td&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client&lt;/td&gt;
&lt;td&gt;기존 인터페이스를 사용하는 코드&lt;/td&gt;
&lt;td&gt;주문 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target Interface&lt;/td&gt;
&lt;td&gt;Client가 기대하는 인터페이스&lt;/td&gt;
&lt;td&gt;pay(amount)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adaptee&lt;/td&gt;
&lt;td&gt;실제 연결해야 하는 기존 객체 또는 외부 API&lt;/td&gt;
&lt;td&gt;외부 결제 API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adapter&lt;/td&gt;
&lt;td&gt;Target Interface에 맞게 Adaptee를 감싸는 객체&lt;/td&gt;
&lt;td&gt;결제 어댑터&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 외부 API나 기존 코드 자체를 억지로 바꾸는 것이 아니라,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간에 어댑터를 두어, 서로 다른 사용 방식을 맞춰주는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EX]&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #f6f6f6; border-radius: 15px; solid; padding: 35px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주문로직 : 결제 객체가 apy(amount) 메서드를 제공한다고 가정&lt;/p&gt;
&lt;pre id=&quot;code_1788495373708&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class OrderService:
    def __init__(self, payment):
        self.payment = payment

    def order(self, amount):
        return self.payment.pay(amount)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서 OrderService는 구체적인 결제 방식이 무엇인지 알 필요가 없음.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그저 pay(amount)를 호출할 수 있으면 됨&lt;/p&gt;
&lt;pre id=&quot;code_1788495569418&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CardPayment:
    def pay(self, amount):
        return &quot;카드 결제&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 새로 연동해야 하는 외부 결제 API는 다른 형식을 사용한다고 할 때&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788495601773&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ExternalPaymentAPI:
    def request_payment(self, price):
        return f&quot;{price}원 외부 결제 요청&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능은 결제지만, 메서드 이름이 pay()가 아니라 request_payment()다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터 이름도 amount가 아니라 price&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 이 상태에서는 OrderService에 바로 넣을 수 없음&lt;/p&gt;
&lt;pre id=&quot;code_1788495701327&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;external_payment = ExternalPaymentAPI()
order_service = OrderService(external_payment)

order_service.order(10000)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OrderService는 내부에서 self.payment.pay(amount)를 호출한다.&lt;br /&gt;하지만 ExternalPaymentAPI에는 pay()가 없기 때문에 오류가 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해 어댑터로 인터페이스 변환&lt;/p&gt;
&lt;pre id=&quot;code_1788495771056&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ExternalPaymentAdapter:
    def __init__(self, external_payment):
        self.external_payment = external_payment

    def pay(self, amount):
        return self.external_payment.request_payment(price=amount)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 외부 API를 내부 코드가 기대하는 pay(amount) 형태로 바꿔줌&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;이제 OrderService는 수정하지 않아도 됨&lt;/p&gt;
&lt;pre id=&quot;code_1788495828840&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;external_payment = ExternalPaymentAPI()
payment = ExternalPaymentAdapter(external_payment)

order_service = OrderService(payment)
print(order_service.order(10000))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행흐름)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OrderService.order(10000) &lt;br /&gt;&amp;rarr;&amp;nbsp;payment.pay(10000) &lt;br /&gt;&amp;rarr;&amp;nbsp;ExternalPaymentAdapter.pay(10000) &lt;br /&gt;&amp;rarr;&amp;nbsp;ExternalPaymentAPI.request_payment(price=10000)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구분&lt;/td&gt;
&lt;td&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OrderService&lt;/td&gt;
&lt;td&gt;pay(amount)를 기대하는 기존 코드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ExternalPaymentAPI&lt;/td&gt;
&lt;td&gt;request_payment(price)를 제공하는 외부 API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ExternalPaymentAdapter&lt;/td&gt;
&lt;td&gt;pay(amount) 호출을 request_payment(price) 호출로 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;장점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;장점&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기존 코드 변경 최소화&lt;/td&gt;
&lt;td&gt;기존 코드가 기대하는 인터페이스를 유지한 채 새로운 객체를 연결할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;외부 구현과 내부 코드 분리&lt;/td&gt;
&lt;td&gt;외부 API의 메서드 이름이나 응답 구조를 내부 코드가 직접 알 필요가 줄어든다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;호환성 확보&lt;/td&gt;
&lt;td&gt;서로 다른 인터페이스를 가진 객체들을 같은 방식으로 사용할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;변경 영향 축소&lt;/td&gt;
&lt;td&gt;외부 API 형식이 바뀌어도 어댑터만 수정하면 되는 구조를 만들 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;일관된 사용 방식 제공&lt;/td&gt;
&lt;td&gt;내부 코드에서는 여러 외부 객체를 동일한 인터페이스로 다룰 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;효과적인 상황&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;상황&lt;/td&gt;
&lt;td&gt;이유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;외부 API를 연동해야 하는 경우&lt;/td&gt;
&lt;td&gt;외부 API 형식을 내부 코드에 맞게 감쌀 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기존 코드를 크게 바꾸기 어려운 경우&lt;/td&gt;
&lt;td&gt;기존 인터페이스를 유지하면서 새 구현을 연결할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;서로 다른 라이브러리를 같은 방식으로 사용하고 싶은 경우&lt;/td&gt;
&lt;td&gt;어댑터를 통해 일관된 사용 방식을 제공할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;외부 변경 영향을 줄이고 싶은 경우&lt;/td&gt;
&lt;td&gt;변경 지점을 어댑터로 제한할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;div style=&quot;border-bottom: 5px solid #009a87; width: 100px; margin: -20px auto 0; position: relative; bottom: -15px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 style=&quot;margin-bottom: .2em; color: #000; text-align: center; font-weight: bold;&quot; data-ke-size=&quot;size26&quot;&gt;주의점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어댑터는 기존 코드와 외부 코드 사이의 형식 차이를 흡수한다.&amp;nbsp;&lt;/p&gt;
&lt;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;ex)&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #f6f6f6; border-radius: 15px; solid; padding: 35px;&quot;&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;외부 API의 request_payment()가 실제로는 결제 요청뿐 아니라 포인트 적립, 알림 발송, 정산 요청까지 함께 처리한다고 할 때&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788498478862&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ExternalPaymentAPI:
    def request_payment(self, price):
        process_payment(price)
        add_point(price)
        send_notification(price)
        create_settlement(price)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 API를 단순히 pay(amount)로 감싸면 겉으로는 결제 인터페이스처럼 보임&lt;/p&gt;
&lt;pre id=&quot;code_1788498589212&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ExternalPaymentAdapter:
    def pay(self, amount):
        return self.external_payment.request_payment(price=amount)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 내부적으로는 결제 외의 여러 작업까지 함께 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 pay()라는 이름만 보고 사용하는 코드는 실제로 어떤 부작용이 발생하는지 알기 어려움.&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;주의점&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;의미 차이를 숨기지 않기&lt;/td&gt;
&lt;td&gt;메서드 이름만 맞추고 실제 동작 의미가 다르면 오해가 생길 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;어댑터 비대화&lt;/td&gt;
&lt;td&gt;변환 로직이 너무 많아지면 어댑터가 복잡해질 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;오류 처리 방식 차이&lt;/td&gt;
&lt;td&gt;외부 API의 예외, 실패 응답, 재시도 정책을 내부 기준에 맞게 정리해야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;성능 비용&lt;/td&gt;
&lt;td&gt;변환 과정이 많아지면 불필요한 처리 비용이 생길 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;책임 범위 확인&lt;/td&gt;
&lt;td&gt;어댑터가 단순 변환을 넘어 비즈니스 로직까지 담당하지 않도록 주의해야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>STUDY/CS</category>
      <category>객체지향</category>
      <category>디자인패턴</category>
      <category>소프트웨어설계</category>
      <category>어댑터패턴</category>
      <category>외부API연동</category>
      <category>인터페이스변환</category>
      <category>호환성</category>
      <author>devmel</author>
      <guid isPermaLink="true">https://devmel393.tistory.com/52</guid>
      <comments>https://devmel393.tistory.com/52#entry52comment</comments>
      <pubDate>Fri, 4 Sep 2026 14:12:53 +0900</pubDate>
    </item>
  </channel>
</rss>