<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>ice rabbit programming</title>
    <link>https://icerabbit.tistory.com/</link>
    <description>NC SOFT Software Programmer</description>
    <language>ko</language>
    <pubDate>Fri, 21 Aug 2026 01:52:59 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>판교토끼</managingEditor>
    <image>
      <title>ice rabbit programming</title>
      <url>https://tistory1.daumcdn.net/tistory/3831415/attach/3d73938419634bafaa1f6870944b7449</url>
      <link>https://icerabbit.tistory.com</link>
    </image>
    <item>
      <title>[C#] 컬렉션, 코루틴, LINQ, 스레딩, GC</title>
      <link>https://icerabbit.tistory.com/169</link>
      <description>&lt;h2 data-path-to-node=&quot;61&quot; data-ke-size=&quot;size26&quot;&gt;1. 컬렉션, 코루틴, LINQ&lt;/h2&gt;
&lt;h3 data-path-to-node=&quot;62&quot; data-ke-size=&quot;size23&quot;&gt;Collection&lt;/h3&gt;
&lt;p data-path-to-node=&quot;63&quot; data-ke-size=&quot;size16&quot;&gt;동일 타입의 객체를 보관하는 자료구조다. C#에서는 성능과 타입 안정성을 위해 주로 제네릭 컬렉션(List&amp;lt;T&amp;gt;, Dictionary&amp;lt;K,V&amp;gt;)을 사용하며, 멀티 스레드 환경에서는 Concurrent 컬렉션을 사용한다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;64&quot; data-ke-size=&quot;size23&quot;&gt;Enumerator (열거자)와 Co-Routine (코루틴)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;65&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;65,0,0&quot;&gt;열거자:&lt;/b&gt; IEnumerator 인터페이스를 구현하며, MoveNext(), Current를 통해 컬렉션을 순회한다. foreach 문은 내부적으로 열거자를 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;65,1,0&quot;&gt;코루틴 (yield return):&lt;/b&gt; 컬렉션을 순회할 때 값을 한 번에 리턴하지 않고, yield return을 만날 때마다 호출부로 돌아갔다가 다음 호출 시 중단점부터 다시 실행되는 상태 머신 구조다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;66&quot; data-ke-size=&quot;size23&quot;&gt;LINQ (Language Integrated Query)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;67&quot; data-ke-size=&quot;size16&quot;&gt;컬렉션 데이터를 SQL 쿼리처럼 다룰 수 있게 해주는 C#의 강력한 기능이다. (주로 IEnumerable&amp;lt;T&amp;gt;를 반환)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;68&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;68,0,0&quot;&gt;지연된 실행(Deferred Execution):&lt;/b&gt; 쿼리를 정의한 시점이 아니라, 실제로 데이터에 접근(foreach 등)할 때 연산이 수행된다.&lt;/li&gt;
&lt;li&gt;람다식, 확장 메서드, 코루틴, 델리게이트가 총동원된 기술이다.&lt;/li&gt;
&lt;li&gt;문법 형태: 확장 메서드 방식 (Fluent Syntax)과 쿼리 식 방식 (Query Syntax)을 모두 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwj-jejYwoGWAxUAAAAAHQAAAAAQVg&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;var e = arr.Where(n =&amp;gt; n % 2 == 1).Select(n =&amp;gt; n * 10).Skip(3);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-path-to-node=&quot;71&quot; data-ke-size=&quot;size26&quot;&gt;2. 스레딩과 비동기 (Threading &amp;amp; Task)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;72&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;72,0,0&quot;&gt;Thread:&lt;/b&gt; Thread t = new Thread(메서드); t.Start();로 실행한다. 매개변수가 필요하면 람다식을 활용한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;72,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;72,0,1,0,0&quot;&gt;Foreground 스레드:&lt;/b&gt; 주 스레드가 종료되어도 스레드 작업이 끝날 때까지 프로세스가 살아있다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;72,0,1,1,0&quot;&gt;Background 스레드:&lt;/b&gt; 주 스레드가 종료되면 즉시 강제 종료된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;72,1,0&quot;&gt;Task:&lt;/b&gt; 시스템이 관리하는 스레드 풀(Thread Pool)을 활용하여 최적화된 스레딩을 제공한다. 최신 C#에서는 원시적인 Thread보다 Task.Run() 사용을 적극 권장한다. 기본적으로 Background 스레드로 동작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;74&quot; data-ke-size=&quot;size26&quot;&gt;3. 가비지 컬렉션 (GC)과 Disposal 패턴&lt;/h2&gt;
&lt;h3 data-path-to-node=&quot;75&quot; data-ke-size=&quot;size23&quot;&gt;Garbage Collection (GC) 원리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;76&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C#에서는 메모리가 연속적으로 할당되며, 힙 영역에 더 이상 참조되지 않는 객체(쓰레기)가 쌓여 메모리가 부족해지면 GC가 동작한다.&lt;/li&gt;
&lt;li&gt;사용 중인 메모리를 압축(Compacting)하여 파편화를 막는다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;76,2,0&quot;&gt;세대(Generation) 기반 수집:&lt;/b&gt; 0, 1, 2세대로 나뉘며 최근에 생성된 객체(0세대)일수록 먼저, 자주 검사한다. 오래 살아남은 객체는 다음 세대로 승격된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;77&quot; data-ke-size=&quot;size23&quot;&gt;자원 해제와 Dispose 패턴&lt;/h3&gt;
&lt;p data-path-to-node=&quot;78&quot; data-ke-size=&quot;size16&quot;&gt;네트워크, 파일, DB 커넥션 등의 비관리(Unmanaged) 자원은 GC가 언제 수거할지 알 수 없다(Non-deterministic). 따라서 사용을 마친 후에는 반드시 명시적으로 자원을 해제해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;79&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dispose() 메서드를 구현하여 명시적으로 자원을 해제한다.&lt;/li&gt;
&lt;li&gt;실무에서는 GC.Collect()를 직접 호출하는 것은 웬만하면 피하고, 프레임워크의 관리에 맡기는 것이 좋다. 대신 자원 해제는 앞서 언급한 using 블록을 활용하여 안전하게 처리하자.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Development/C#</category>
      <author>판교토끼</author>
      <guid isPermaLink="true">https://icerabbit.tistory.com/169</guid>
      <comments>https://icerabbit.tistory.com/169#entry169comment</comments>
      <pubDate>Sun, 2 Aug 2026 17:30:43 +0900</pubDate>
    </item>
    <item>
      <title>[C++] GoF 패턴 간단 정리(3) -  생성 및 컬렉션 패턴</title>
      <link>https://icerabbit.tistory.com/168</link>
      <description>&lt;h2 data-path-to-node=&quot;39&quot; data-ke-size=&quot;size26&quot;&gt;생성 및 컬렉션 패턴&lt;/h2&gt;
&lt;h3 data-path-to-node=&quot;40&quot; data-ke-size=&quot;size23&quot;&gt;Container &amp;amp; Iterator (반복자)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;41&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;41,0,0&quot;&gt;Thin Template Container:&lt;/b&gt; void* 기반의 컨테이너를 상속받는 얇은 템플릿(Thin Template) 기법을 사용하면, 강력한 타입 안정성을 챙기면서도 과도한 템플릿 인스턴스화에 따른 코드 폭발(Code Bloat)을 차단할 수 있어 하드웨어 제약이 큰 모바일이나 임베디드 라이브러리에서 매우 유용하다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;41,1,0&quot;&gt;Iterator 패턴:&lt;/b&gt; 컨테이너 내부 구현(배열, 연결 리스트 등)을 노출하지 않고 요소에 순차적으로 접근할 수 있도록 돕는다. Java/C#의 인터페이스 기반 다형성 처리와 달리, C++의 STL 반복자는 런타임 가상 함수 호출 성능 저하를 방지하기 위해 &lt;b data-index-in-node=&quot;144&quot; data-path-to-node=&quot;41,1,0&quot;&gt;연산자 오버로딩(++, *)과 컴파일 타임 템플릿 매칭&lt;/b&gt; 기법을 사용하며, 모든 관련 동작은 고성능 인라인 함수로 처리된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;42&quot; data-ke-size=&quot;size23&quot;&gt;객체 생성의 추상화 패턴&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;43&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;43,0,0&quot;&gt;Factory Method 패턴:&lt;/b&gt; 객체 생성을 위한 추상 인터페이스는 부모 클래스에 정의하되, 실제 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 파생 클래스에 위임한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;43,1,0&quot;&gt;Abstract Factory 패턴:&lt;/b&gt; 구체적인 클래스를 명시하지 않고, 서로 관련이 있거나 의존적인 객체들의 군(Family)을 생성하기 위한 인터페이스를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjG5Mv6pdeVAxUAAAAAHQAAAAAQVg&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;C++&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;// Abstract Factory 설계 구현 예시
struct IButton {
  virtual ~IButton() = default;
};

struct IFactory {
  virtual ~IFactory() = default;
  virtual IButton* CreateButton() = 0;
};

struct WinFactory : public IFactory {
  IButton* CreateButton() override {
    // 내부적으로 WinButton 인스턴스 반환
    return nullptr; 
  }
};

int main() {
  IFactory *pFactory = nullptr;
  
  if (/* 런타임 조건 분기 */ true) {
    pFactory = new WinFactory();
  }
  
  IButton *pBtn = pFactory-&amp;gt;CreateButton();
  
  delete pBtn;
  delete pFactory;
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;45&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;45,0,0&quot;&gt;Builder 패턴:&lt;/b&gt; 동일한 생성 절차(Director)를 따르되, 생성 각 단계의 세부 표현(Builder 인터페이스 구현체)을 분리하여 생성 결과가 각기 달라질 수 있도록 제어한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;45,1,0&quot;&gt;Prototype 패턴:&lt;/b&gt; 구체적인 클래스를 상속하여 생성하지 않고, 미리 등록되어 있는 견본 객체의 복사본을 만드는 Clone() 가상 함수를 호출하여 새로운 인스턴스를 생성한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;45,2,0&quot;&gt;Singleton 패턴:&lt;/b&gt; 애플리케이션 내에 오직 하나의 인스턴스만 보장되도록 제어한다. 생성자를 private화하고 복사 생성 및 대입을 금지(= delete) 처리한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;45,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i data-index-in-node=&quot;0&quot; data-path-to-node=&quot;45,2,1,0,0&quot;&gt;DCLP (Double-Checked Locking Pattern):&lt;/i&gt; 과거 락 최소화를 위해 쓰였으나 컴파일러 재정렬(Reordering) 버그 위험이 있었다. C++11 이후부터는 std::atomic 메모리 모델을 적용하여 안전하게 구현할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;i data-index-in-node=&quot;0&quot; data-path-to-node=&quot;45,2,1,1,0&quot;&gt;Meyers' Singleton:&lt;/i&gt; static 지역 변수를 선언하여 객체를 반환하는 방식이다. C++11 규격에 의해 최초 접근 시 스레드 세이프(Thread-safe)한 지연 초기화(Lazy Initialization)가 보장되므로, 현재 C++ 환경에서 가장 안전하고 선호되는 형태다.&lt;/li&gt;
&lt;li&gt;&lt;i data-index-in-node=&quot;0&quot; data-path-to-node=&quot;45,2,1,2,0&quot;&gt;CRTP (Curiously Recurring Template Pattern):&lt;/i&gt; 싱글톤 로직을 재사용하기 위해 자식 클래스 타입을 템플릿 인자로 넘겨 상속받는 형태(class MyClass : public Singleton&amp;lt;MyClass&amp;gt;)를 말한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://icerabbit.tistory.com/166&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;첫 글&lt;/a&gt;을 쓴지도 1년 반이라는 시간이 흘렀다.(아무래도 의도한 바는 아니지만...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시에도 copilot, chatgpt 등 AI를 이용한 개발이 막 태동하던 시기였지만 업무에 사용할 정도는 아니었는데, 그 사이에 이제는 AI agent 없이는 개발에 차질이 생길 정도로 많은 부분을 차지하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 날 사용해보고 고민도 해보면서 얻은 결론은, 코드를 읽고 이해하고 검증하는 능력이 더욱 중요해졌다는 것이다. 그리고 코드를 잘 읽기 위해서는 많이 써봐야 읽고 이해하는 능력이 길러진다고 생각한다. 또한 그러기 위해서는 설계나, 이번 포스팅에서 다룬 데이터를 논리적으로 처리하기 위한 구조(패턴)에 익숙할수록 더 빠르게 읽고 생산성을 높일 수 있다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 한 가지는 몇 년 뒤에는 모르겠지만 아직까지는 사용자의 역량에 따라 AI가 낼 수 있는 퍼포먼스가 좌우되는 경향이 있다고 생각한다. 그러므로 잘 사용하는 개발자가 될 수 있도록 기초와 이론을 계속 힘써야할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 주제는 C++ Basic 강좌를 들으며 정리한 내용을 해볼까 한다.(명색이 C++ 개발자인데 JS 글이 더 많다.) 이제 구글링하며 직접 블로그를 드나드는 일이 많이 줄어든 것 같지만, 누군가에게는 도움이 되고 어떤 AI의 검색 베이스가 되어 잘 활용되기를 바라며...&lt;/p&gt;</description>
      <category>Development/C++</category>
      <author>판교토끼</author>
      <guid isPermaLink="true">https://icerabbit.tistory.com/168</guid>
      <comments>https://icerabbit.tistory.com/168#entry168comment</comments>
      <pubDate>Fri, 17 Jul 2026 15:32:48 +0900</pubDate>
    </item>
    <item>
      <title>[C++] GoF 패턴 간단 정리(2) - 간접층의 원리, 행동 패턴</title>
      <link>https://icerabbit.tistory.com/167</link>
      <description>&lt;h3 data-path-to-node=&quot;27&quot; data-ke-size=&quot;size23&quot;&gt;간접층의 원리를 활용한 패턴들&lt;/h3&gt;
&lt;p data-path-to-node=&quot;28&quot; data-ke-size=&quot;size16&quot;&gt;간접층을 도입하여 의존성을 제어하며, 목적에 따라 아래와 같이 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;29&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;29,0,0&quot;&gt;Adapter 패턴:&lt;/b&gt; 기존 클래스의 인터페이스가 요구 사항과 맞지 않을 때 인터페이스를 변경(호환성 제공)하기 위해 사용한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;29,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i data-index-in-node=&quot;0&quot; data-path-to-node=&quot;29,0,1,0,0&quot;&gt;Class Adapter:&lt;/i&gt; 다중 상속 또는 값으로 포함하여 수정.&lt;/li&gt;
&lt;li&gt;&lt;i data-index-in-node=&quot;0&quot; data-path-to-node=&quot;29,0,1,1,0&quot;&gt;Object Adapter:&lt;/i&gt; 객체를 포인터/참조로 포함(Composition)하여 수정. STL의 std::stack 및 std::reverse_iterator가 이에 해당한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;29,1,0&quot;&gt;Proxy 패턴:&lt;/b&gt; 특정 객체에 대한 접근을 제어하거나 대행하기 위해 대리 객체를 제공한다. (예: Remote Proxy, Virtual Proxy, Protected Proxy).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;29,2,0&quot;&gt;Facade 패턴:&lt;/b&gt; 복잡한 서브시스템 구조의 의존성을 감추고, 호출하기 단순한 단일 포괄 인터페이스를 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;29,3,0&quot;&gt;Bridge 및 PIMPL Idiom:&lt;/b&gt; 구현부(Implementation)와 추상화(Abstraction) 개념을 분리하여 각각 독립적으로 확장할 수 있도록 설계한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;29,3,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;29,3,1,0,0&quot;&gt;PIMPL (Pointer to IMPLementation):&lt;/b&gt; 헤더 파일에 전방 선언(Forward Declaration)만 남기고 상세 구현체 클래스는 소스 파일(.cpp)로 완전히 격리하여 컴파일 속도를 향상시키고 정보 은닉을 제공하는 C++ 핵심 기법이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;33&quot; data-ke-size=&quot;size26&quot;&gt;행동 패턴 (Behavioral Patterns)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;34&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;34,0,0&quot;&gt;State 패턴:&lt;/b&gt; 객체의 내부 상태에 따라 스스로 행동을 변경할 수 있도록 유도한다. 조건에 따른 if-else 분기 처리를 상태 클래스로 분리해 내어 OCP를 극대화한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;34,1,0&quot;&gt;Observer 패턴:&lt;/b&gt; 1:N 종속 관계를 정의하여 Subject의 상태가 변할 때 등록된 모든 Observer 객체들에게 자동으로 변경을 통보한다. 데이터를 함께 보내는 Push 방식과, 통보 후 옵저버가 직접 조회하는 Pull 방식이 존재하며 일반적으로 느슨한 결합도를 유지할 수 있는 Pull 방식이 더 선호된다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;34,2,0&quot;&gt;Visitor 패턴:&lt;/b&gt; 객체 구조를 변경하지 않고도, 내부 요소들을 순회하며 새로운 기능(오퍼레이션)을 손쉽게 정의할 수 있도록 설계된 패턴이다. accept와 visit 함수를 통한 이중 디스패치(Double Dispatch) 메커니즘을 사용한다. 타입을 추가하기는 어려우나 함수를 추가하기는 매우 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;35&quot; data-ke-size=&quot;size23&quot;&gt;Menu Event 처리 (C++ 표준 라이브러리 활용)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;36&quot; data-ke-size=&quot;size16&quot;&gt;과거에는 가상 함수 기반의 리스너(Listener) 객체나 커스텀 함수 래퍼 클래스를 직접 구현했으나, 모던 C++(C++11~) 환경에서는 표준 라이브러리 &amp;lt;functional&amp;gt;의 std::function을 사용하여 일관된 함수 바인딩 인터페이스를 구축한다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjG5Mv6pdeVAxUAAAAAHQAAAAAQVQ&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;functional&amp;gt;

void Foo() {
  std::cout &amp;lt;&amp;lt; &quot;Foo called\n&quot;;
}

class Dialog {
 public:
  void Close() {
    std::cout &amp;lt;&amp;lt; &quot;Dialog closed\n&quot;;
  }
};

int main() {
  // std::function을 활용한 호출 인터페이스 통일
  std::function&amp;lt;void()&amp;gt; f;
  
  f = &amp;amp;Foo; 
  f(); 
  
  Dialog dlg;
  // 기존 std::bind 방식
  f = std::bind(&amp;amp;Dialog::Close, &amp;amp;dlg);
  f(); 
  
  // 모던 C++14/17 권장 방식: std::bind 대신 람다 캡처를 활용
  // 가독성이 높고 컴파일러 최적화 효율이 우수하다.
  f = [&amp;amp;dlg]() { 
    dlg.Close(); 
  };
  f();
  
  return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Development/C++</category>
      <category>C++</category>
      <category>디자인패턴</category>
      <author>판교토끼</author>
      <guid isPermaLink="true">https://icerabbit.tistory.com/167</guid>
      <comments>https://icerabbit.tistory.com/167#entry167comment</comments>
      <pubDate>Thu, 16 Jul 2026 22:34:04 +0900</pubDate>
    </item>
    <item>
      <title>[C++] GoF 패턴 간단 정리(1) - 공통성과 가변성의 분리, 재귀적 포함</title>
      <link>https://icerabbit.tistory.com/166</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;지난 &lt;a href=&quot;https://icerabbit.tistory.com/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;정규표현식 간단 정리 글&lt;/a&gt;에 이어서 이번에는 디자인 패턴에 관해 강의를 들으며 정리했던 필기를 정리하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약 4년 반 전에 포스팅했던 &lt;a href=&quot;https://icerabbit.tistory.com/83&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Typescript 강의를 듣고 정리했던 글&lt;/a&gt;과 비슷하겠네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느새 처음 입사한지 만 5년이 흐른 지금, 기존 코드도 나름 많이 읽었고 선배 및 동료 개발자들과 리뷰를 하면서 더 나은 코드를 작성할 수 있도록 노력해오기도 했습니다. (개인적으로는 아직 많이 부족하다고 생각합니다만...)&lt;br /&gt;C#, 파이썬과 Typescript를 거쳐서 현재는 C++을 주로 사용하고 있어서 GoF 패턴을 C++ 강좌로 접할 기회가 있어 듣고 간략하게 정리한 내용입니다.&lt;br /&gt;대부분 한 번씩 사용해봤을 법한 패턴들입니다. 다만 패턴에 너무 매몰되지 않고 생산성과 현재 프로덕트가 처한 상황에 맞게 적용하면 된다고 생각하고 있습니다. 여러 가지가 중요하긴 하지만 저는 개인적으로는 &amp;lt;코드 중복의 방지&amp;gt;와 &amp;lt;함수와 클래스의 명확함(한 번에 하나의 일)&amp;gt;이 가장 근본적이고, 이후에 필요에 맞게 쌓아 올려지는 것이 아닌가 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌거나 약 세 편에 나뉘어서 이전에 썼던 필기들을 올려볼까 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h1&gt;들어가기 전에&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Protected&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Protected &lt;/span&gt;생성자&lt;span&gt;: &lt;/span&gt;상속받은 자식 클래스로만 인스턴스 생성 가능&lt;span&gt;(&lt;/span&gt;부모 클래스 인스턴스 직접 생성 불가&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Protected &lt;/span&gt;소멸자&lt;span&gt;: &lt;/span&gt;객체를 스택에 만들 수 없게 할 때&lt;span&gt;(&lt;/span&gt;즉&lt;span&gt;, Heap&lt;/span&gt;에만 만들게&lt;span&gt;) &lt;/span&gt;사용한다&lt;span&gt;. &lt;/span&gt;참조 계수 기반의 객체 수명 관리 기법에서 주로 사용한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가상 함수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상 함수로 만들지 않고 부모 타입 포인터에 자식 객체를 넣을 경우 오버라이딩 되지 않음&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상함수에&lt;span&gt; =0&lt;/span&gt;을 붙이면 순수 가상 함수가 된다&lt;span&gt;. &lt;/span&gt;구현부가 없고 구현 클래스에서 오버라이딩이 필수이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상 클래스&lt;span&gt;: &lt;/span&gt;순수 가상함수를 한 개 이상 가지고 있다&lt;span&gt;. &lt;/span&gt;인스턴스를 생성할 수 없고&lt;span&gt;, &lt;/span&gt;포인터 변수는 생성할 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;공통성과 가변성의 분리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변하지 않는 전체 흐름 내에 있는 변하는 부분을 분리하여&lt;span&gt;, &lt;/span&gt;정책이 변경되었을 때 코드의 수정을 최소화 하도록 한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개방 폐쇄의 법칙&lt;span&gt;(OCP: Open Close Principle)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;기능 확장&lt;span&gt;(&lt;/span&gt;모듈&lt;span&gt;, &lt;/span&gt;클래스&lt;span&gt;, &lt;/span&gt;함수 추가&lt;span&gt;)&lt;/span&gt;에 열려 있고 수정&lt;span&gt;(&lt;/span&gt;기존 코드 수정&lt;span&gt;)&lt;/span&gt;에는 닫혀 있어야 한다는 원칙&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;새로운 클래스가 추가되어도 기존 클래스의 코드를 수정하지 않도록 만들어야 한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Template Method &lt;/span&gt;패턴 &lt;span&gt;&amp;ndash; &lt;/span&gt;가상 함수로 분리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속 기반 패턴&lt;span&gt;. &lt;/span&gt;전체 흐름 중 변하는 부분을 가상 함수로 정의한다&lt;span&gt;. &lt;/span&gt;즉&lt;span&gt;, &lt;/span&gt;해당 부분의 변경이 필요하면 파생 클래스를 새로 만들어서 적용한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;컴파일 타임에 정책을 정해야 한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;파생 클래스에서 오버라이딩하므로 해당 클래스에서만 접근이 가능하다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;참고로&lt;span&gt; final &lt;/span&gt;키워드를 붙이면 파생 클래스에서 원 함수를 변경할 수 없다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Strategy &lt;/span&gt;패턴 &lt;span&gt;&amp;ndash; &lt;/span&gt;인스턴스로 교체&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변하는 것을 인터페이스&lt;span&gt;(&lt;/span&gt;약한 결합&lt;span&gt;)&lt;/span&gt;로 설계하여 별도의 클래스로 정의한다&lt;span&gt;. &lt;/span&gt;즉&lt;span&gt;, &lt;/span&gt;해당 부분의 변경이 필요하면 인터페이스를 구현하는 클래스를 만들어서 적용한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;런타임에 정책을 정하므로 실행 시간에도 변경 적용할 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;가상 함수 기반이므로 상대적으로 느리다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;별개의 클래스이므로 전혀 다른 클래스에서도 정책을 같이 사용할 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Policy Base Design(&lt;/span&gt;단위 전략 디자인&lt;span&gt;) &amp;ndash; &lt;/span&gt;템플릿 인자로 교체&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Template &lt;/span&gt;인자를 사용하여 정책 클래스를 교체하는 기법이다&lt;span&gt;. GoF &lt;/span&gt;패턴에 있지는 않으나&lt;span&gt; C++&lt;/span&gt;에서 널리 사용되는 전략이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;typename ThreadModel&amp;gt;&lt;/span&gt;과 같이 사용하여&lt;span&gt;, template &lt;/span&gt;인자로 사용되는 클래스를 바꿀 수 있도록 한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;함수 내에 변하는 것과 변하지 않는 것이 혼재해 있을 경우에도 사용이 가능하다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Strategy(&lt;/span&gt;전략&lt;span&gt;) &lt;/span&gt;패턴과 비교했을 때&lt;span&gt; inline &lt;/span&gt;함수를 사용하므로 실행 속도가 조금 더 빠르지만&lt;span&gt;, &lt;/span&gt;컴파일 타임에 정책을 정해야 한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Application Framework&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Main &lt;/span&gt;함수에 전체적인 흐름을 담아서 라이브러리 내부에 감추는 방식이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;모든 것을 객체로 한다&lt;span&gt;. (&lt;/span&gt;단&lt;span&gt;, C++&lt;/span&gt;은&lt;span&gt; Main&lt;/span&gt;은 무조건 일반 함수&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;특정 분야의 프로그램은 전체적 흐름이 항상 유사하다&lt;span&gt;. (GUI, &lt;/span&gt;게임 등&lt;span&gt;)&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1737462111414&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CWinApp* g_app = 0;
int main() {
  if (g_app-&amp;gt;InitInstance() == true)
    g_app-&amp;gt;Run();
  g_app-&amp;gt;ExitInstance();
}

// 프로그램 전체 흐름 프레임워크
class CWinApp {
    public:
     CWinApp() { g_app = this; } // 전역 객체에 어플리케이션 정보 저장
     virtual bool InitInstance(() { return false; }
     virtual bool Run() { return false; }
     virtual Int ExitInstance() { return 0; }
)
// 파생 클래스
class MyApp : public CWinApp {
    public:
     virtual bool InitInstance { &amp;hellip; }
     virtual int ExitInstance() { &amp;hellip; }
}
MyApp theApp; // 전역 객체 생성자가 main보다 먼저 실행된다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;프레임워크 정의하는 추상 클래스가 있고 사용자는 그 틀에 맞춰서 파생 클래스를 만든다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;MFC, QT, WxWidget, iOS, Android &lt;/span&gt;등에서 사용하는 형태이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;일반 함수와 가변성 &lt;span&gt;&amp;ndash; &lt;/span&gt;함수 인자화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정책이 변경될 경우 라이브러리나 일반 함수 내부를 수정하지 않아도 되도록 설계하는 것이 필요하다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;멤버 함수가 변할 경우&lt;span&gt;: &lt;/span&gt;가상 함수나&lt;span&gt; strategy &lt;/span&gt;패턴을 사용할 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;일반 함수의 경우&lt;span&gt;: &lt;/span&gt;변해야 하는 것&lt;span&gt;(&lt;/span&gt;정책&lt;span&gt;)&lt;/span&gt;을 함수 인자화한다&lt;span&gt;.&lt;br /&gt;&lt;/span&gt;함수 포인터 &lt;span&gt;&amp;ndash; &lt;/span&gt;코드 메모리가 증가하지 않지만 인라인 치환 불가&lt;span&gt;&lt;br /&gt;&lt;/span&gt;함수 객체&lt;span&gt;, &lt;/span&gt;람다 표현식 &lt;span&gt;&amp;ndash; &lt;/span&gt;인라인 치환이 되지만 코드 메모리가 증가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;State &lt;/span&gt;패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태에 따라 다른 행동을 해야 할 때 구현할 수 있는 방법이다&lt;span&gt;. &lt;/span&gt;아래는 각 기법의 비교이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;분기&lt;span&gt;: &lt;/span&gt;여러 함수에 조건에 따른 분기가 들어갈 경우&lt;span&gt;, &lt;/span&gt;모든 동작 함수에 분기가 필요하고 새 조건 추가 시 모든 곳에 분기가 추가되는 문제가 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;가상 함수&lt;span&gt;: &lt;/span&gt;처리는 되나 새 인스턴스를 사용한 것으로 처리된다&lt;span&gt;. &lt;/span&gt;즉&lt;span&gt;, &lt;/span&gt;객체의 변화가 아니라 새 인스턴스가 생성된 것이므로 클래스에 의한 변화이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;State &lt;/span&gt;패턴&lt;span&gt;: &lt;/span&gt;변경되는 동작들을 다른 클래스로 분리&lt;span&gt;(&lt;/span&gt;인터페이스 정의&lt;span&gt;)&lt;/span&gt;한다&lt;span&gt;. &lt;/span&gt;즉&lt;span&gt;, &lt;/span&gt;객체의 속성은 유지하면서 동작을 변경할 수 있다&lt;span&gt;. Strategy &lt;/span&gt;패턴과 유사하다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1737462125679&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct IState {
  virtual void run() = 0;
  virtual void attack() = 0;
  virtual ~IState() {}
};

class NormalState : public IState { &amp;hellip; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;State &lt;/span&gt;패턴은 객체 자신의 내부 상태에 따라 행위를 변경&lt;span&gt;, &lt;/span&gt;객체는 마치 클래스를 변경하는 것처럼 동작한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Strategy &lt;/span&gt;패턴은 다양한 알고리즘이 존재하면 이들 각각을 하나의 클래스로 캡슐화하여 알고리즘을 대체하는 식으로 동작한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;eth;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;클래스 다이어그램은 유사하나 의도가 다르다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;재귀적 포함&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Composite &lt;/span&gt;패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다형성을 이용하여 실행할 수 있도록 한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Leaf node&lt;/span&gt;가 다른&lt;span&gt; composite node&lt;/span&gt;들과 같은&lt;span&gt; layer&lt;/span&gt;에 존재하는&lt;span&gt; tree &lt;/span&gt;구조에서 유용하게 사용할 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개별 객체와 복합 객체를 구별하지 않고 동일한 방법으로 다룰 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Ex) &lt;/span&gt;메뉴 속 메뉴를 누르거나 메뉴 속 항목을 눌렀을 때&lt;span&gt;, &lt;/span&gt;같은&lt;span&gt; Base&lt;/span&gt;를 가지고 있다면&lt;span&gt;, &lt;/span&gt;즉 공통된 실행 함수&lt;span&gt;(&lt;/span&gt;하위 메뉴를 보여 주거나 항목 실행하는 게 동일한 함수라면&lt;span&gt;)&lt;/span&gt;가 있다면 타입에 관계없이 호출하면 된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Cf) &lt;/span&gt;자식 클래스들 중 한 곳에서만 사용하는 함수이더라도 기반 클래스에 가상 함수로 선언하고&lt;span&gt; dummy &lt;/span&gt;구현을 해주면 동일하게 사용이 가능하다&lt;span&gt;. &lt;/span&gt;만약 모두가 사용한다면 순수 가상함수로 선언하면 된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 든&lt;span&gt; Menu Event&lt;/span&gt;를 살펴보면 다음과 같다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;하는 일&lt;span&gt;(&lt;/span&gt;변하는 부분&lt;span&gt;)&lt;/span&gt;을 가상 함수로 분리한다&lt;span&gt;.&lt;br /&gt;-&amp;gt; &lt;/span&gt;메뉴의 개수만큼 파생 클래스를 만들어야 한다&lt;span&gt;. (&lt;/span&gt;너무 많아질 우려&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;하는 일&lt;span&gt;(&lt;/span&gt;변하는 부분&lt;span&gt;)&lt;/span&gt;을 다른 클래스로 분리한다&lt;span&gt;. (Strategy &lt;/span&gt;패턴&lt;span&gt;, Listener&lt;/span&gt;라는 이름을 사용하는 기술&lt;span&gt;)&lt;br /&gt;-&amp;gt; &lt;/span&gt;어떤 메뉴인지 정보를 전달받는&lt;span&gt;(&lt;/span&gt;주로 인자&lt;span&gt;) &lt;/span&gt;절차가 필요하다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;메뉴에 객체가 아닌 함수를 연결한다&lt;span&gt;. &lt;/span&gt;즉&lt;span&gt;, &lt;/span&gt;같은 인터페이스를 사용하는 것이 아니라 함수 포인터를 전달 받아 사용한다&lt;span&gt;.&lt;br /&gt;-&amp;gt; &lt;/span&gt;단&lt;span&gt;, &lt;/span&gt;함수 포인터는 일반 함수와 멤버 함수의 포인터를 동시에 담을 수 없다&lt;span&gt;.&lt;br /&gt;-&amp;gt; &lt;/span&gt;각 함수 포인터를&lt;span&gt; wrapping&lt;/span&gt;하는 클래스를 공통의 기반 클래스를 가지도록 선언하여 사용한다&lt;span&gt;. &lt;/span&gt;이 때 함수 템플릿&lt;span&gt;(&lt;/span&gt;클래스 템플릿은&lt;span&gt; C++17~)&lt;/span&gt;은 타입을 명시적으로 지정하지 않아도 묵시적 추론이 되므로 생략 가능하다&lt;span&gt;. ~C++14&lt;/span&gt;에서는 클래스 템플릿을 생성하는 함수 템플릿을 도움 함수로 만들면 실제 사용 시 일반 함수와 멤버 함수가 통합된 형태로 사용이 가능하다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Decorator&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 시간에 객체에 기능을 추가할 때 사용하는 패턴이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;(&lt;/span&gt;반대는 상속을 통한 객체 기능 추가이다&lt;span&gt;. &lt;/span&gt;상속을 통해 추가하는 건 인스턴스가 새로 생성되는 것으로 객체에 기능을 추가한 게 아니라 클래스에 기능을 추가하여 인스턴스를 변경한 개념이다&lt;span&gt;.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Composition&lt;/span&gt;을 통한 기능을 추가하면&lt;span&gt;(&lt;/span&gt;새 클래스가 기존 클래스를 멤버 변수로 가짐&lt;span&gt;) &lt;/span&gt;기존 객체에 기능이 추가된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교하자면 상속에 의한 추가는 클래스에 추가가 되고 코드 작성 시 추가의 개념이고&lt;span&gt;, &lt;/span&gt;구성을 통한 추가는 인스턴스에 추가가 되고 런타임에 추가의 개념이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능을 연속해서 추가하기 위해서는 기능이 추가되는 주체 객체와 기능 추가 객체들은 동일한 기반 클래스를 가져야 한다&lt;span&gt;. (ex. Base, Par1, Par2, Par3 &lt;/span&gt;등등이 공통의 기반 클래스를 가짐&lt;span&gt;).&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여기서 기능 추가 클래스들은 다시 공통의 기반 클래스&lt;span&gt;(IDecotator)&lt;/span&gt;로 묶을 수 있다&lt;/span&gt;&lt;/p&gt;</description>
      <category>Development/C++</category>
      <category>C++</category>
      <category>gof 패턴</category>
      <category>디자인 패턴</category>
      <author>판교토끼</author>
      <guid isPermaLink="true">https://icerabbit.tistory.com/166</guid>
      <comments>https://icerabbit.tistory.com/166#entry166comment</comments>
      <pubDate>Tue, 21 Jan 2025 21:22:42 +0900</pubDate>
    </item>
    <item>
      <title>정규표현식 간단 정리</title>
      <link>https://icerabbit.tistory.com/165</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 할 때 종종 정규표현식을 사용할 일이 있습니다. 특정 패턴의 버전을 입력해야 하는 유효성 검사를 하거나, string으로 넘어온 인자들을 파싱할 때 등 여러 경우에 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에는 언어에서 지원해주는 라이브러리들도 많고, 자주 쓰이는 패턴의 경우에는 검색하면 금방 나오기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 코딩과 마찬가지로 긁어서 가져다 쓰는 것만이 아니라 이해하고 활용하고, 수정하고 나아가서는 직접 만들 줄도 알아야 한다고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 근래에 정규표현식 관련해서 책을 읽으면서 나름대로 정리한 내용입니다.(근래라고는 썼지만 몇 달 되긴 했네요.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://regexr.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://regexr.com/&lt;/a&gt; 와 같은 곳에서 직접 만들고 테스트해볼 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(혼자 정리용으로 필기한 글이라 최대한 가독성 좋게 수정하기는 했습니다만 잘 읽히지 않는 부분이 있을 수도 있습니다.)&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식&lt;span&gt;(Regular Expression, regex) : &lt;/span&gt;일치하는 텍스트를 찾거나&lt;span&gt;(&lt;/span&gt;검색&lt;span&gt;), &lt;/span&gt;찾은 텍스트를 치환하는&lt;span&gt;(&lt;/span&gt;치환&lt;span&gt;) &lt;/span&gt;도구&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;가장 쉽게는 문자를 그대로 찾을 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;This my car. &lt;/span&gt;에서 &lt;span&gt;my &lt;/span&gt;를 구문으로 찾으면 &lt;span&gt;my&lt;/span&gt;가 검색된다&lt;span&gt;. &lt;/span&gt;여러 일치된 결과물을 받을 수도 있다&lt;span&gt;. &lt;/span&gt;대소문자 구분은 하는 것이 디폴트이지만 옵션으로 구분하지 않을 수도 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;모든 문자에 대응하는 마침표&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와일드카드로 마침표&lt;span&gt;(.)&lt;/span&gt;가 있다&lt;span&gt;. &lt;/span&gt;예를 들어 &lt;span&gt;c.t &lt;/span&gt;로 검색하면 &lt;span&gt;cat, cot &lt;/span&gt;등이 모두 검색된다&lt;span&gt;. &lt;/span&gt;마침표 한 개는 한 개의 아무 문자를 의미한다&lt;span&gt;. Sales.. &lt;/span&gt;로 검색하면 &lt;span&gt;Sales.hi, sales.hello &lt;/span&gt;등이 모두 검색된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 진짜 마침표를 찾고 싶다면 &lt;span&gt;escape&lt;/span&gt;문자로 &lt;span&gt;\. &lt;/span&gt;를 써주면 된다&lt;span&gt;. &lt;/span&gt;이것을 정규표현식에서는(다른 곳에서도 많이 쓰는 표현이지만) 메타 문자라고 한다&lt;span&gt;. &lt;/span&gt;역슬래시 자체는 &lt;span&gt;\\ &lt;/span&gt;이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;여러 문자의 집합&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 문자가 아니라 일부 문자만을 넣고 싶다면&lt;span&gt;, &lt;/span&gt;대괄호&lt;span&gt;([])&lt;/span&gt;로 일치하는 문자들을 넣을 수 있다&lt;span&gt;. &lt;/span&gt;특정 구역에서 대소문자를 구분하지 않을 때 많이 사용한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 &lt;span&gt;[Rr]rg[Ee]X&lt;/span&gt;와 같은 식이다&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대괄호에 넣은 문자가 많을 경우에는 다 적기가 번거로우므로&lt;span&gt;, &lt;/span&gt;하이픈&lt;span&gt;(-)&lt;/span&gt;으로 범위를 지정할 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;span&gt;[ns]a[0-9]\.xls&lt;/span&gt;와 같은 식이다&lt;span&gt;. A-Z, a-z, A-F, A-z &lt;/span&gt;등으로 활용 가능하다&lt;span&gt;. &lt;/span&gt;여기서 하이픈은 대괄호 내에서만 유효한 메타 문자이므로&lt;span&gt;, &lt;/span&gt;대괄호 안이 아닌 이상 &lt;span&gt;\&lt;/span&gt;를 붙여줄 필요가 없다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;[A-Za-z0-9]&lt;/span&gt;는 모든 영문자&lt;span&gt;, &lt;/span&gt;숫자를 의미한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 몇 개만 빼고 모두 찾아야할 경우에는 제외하는 것이 빠르다&lt;span&gt;. &lt;/span&gt;그럴 때에는 캐럿 문자&lt;span&gt;(^) &lt;/span&gt;를 사용한다&lt;span&gt;. &lt;/span&gt;다음과 같이 사용하면 숫자를 제외할 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;[ns]a[^0-9]\.xls&lt;/span&gt;이다&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;메타 문자&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상술했듯이 역슬래시&lt;span&gt;(\)&lt;/span&gt;를 활용한 &lt;span&gt;escape &lt;/span&gt;문자를 이용하여&lt;span&gt;, &lt;/span&gt;대괄호나 마침표 등을 검색할 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공백 문자는 &lt;span&gt;[\b] &amp;ndash; &lt;/span&gt;백스페이스&lt;span&gt;, \f &amp;ndash; &lt;/span&gt;페이지 넘김&lt;span&gt;, \n &amp;ndash; &lt;/span&gt;개행&lt;span&gt;, \t &amp;ndash; &lt;/span&gt;탭 등으로 검색할 수 있다&lt;span&gt;. &lt;/span&gt;덧붙여 마침표&lt;span&gt;(.)&lt;/span&gt;는 거의 모든 문자를 탐색하지만 대부분의 엔진에서 공백 문자는 제외된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 윈도우에서는 &lt;span&gt;\r\n&lt;/span&gt;이 줄의 끝을 나타내고&lt;span&gt;, &lt;/span&gt;유닉스&lt;span&gt;/&lt;/span&gt;리눅스 계열에서는 &lt;span&gt;\n&lt;/span&gt;이 줄의 끝을 나타낸다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자 집합&lt;span&gt;, &lt;/span&gt;숫자 집합 등은 굉장히 자주 쓰이는 조합이기 때문에&lt;span&gt;, &lt;/span&gt;편의를 이유로 메타 문자로 해당 클래스들을 만들어 놓는다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;\d &amp;ndash; &lt;/span&gt;숫자 하나&lt;span&gt;, \D &amp;ndash; &lt;/span&gt;숫자를 제외한 문자 하나&lt;span&gt;, \w &amp;ndash; &lt;/span&gt;대소문 영문자&lt;span&gt;, &lt;/span&gt;숫자&lt;span&gt;, &lt;/span&gt;밑줄&lt;span&gt;, \W &amp;ndash; &lt;/span&gt;대소문 영문자 및 숫자 및 밑줄을 제외한 문자&lt;span&gt;, \s &amp;ndash; &lt;/span&gt;모든 공백 문자&lt;span&gt;, \S &lt;/span&gt;공백 문자가 아닌 문자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진수도 메타 문자로 쓸 수 있다&lt;span&gt;. 16&lt;/span&gt;진수는 &lt;span&gt;\x&lt;/span&gt;로 표시&lt;span&gt;, 8&lt;/span&gt;진수는 &lt;span&gt;\0&lt;/span&gt;으로 표기한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;POSIX &lt;/span&gt;문자 클래스로도 줄여 쓰기를 제공하는데&lt;span&gt;, [:alpha:]&lt;/span&gt;와 같이 대괄호와 콜론 사이에 단어가 들어간다&lt;span&gt;. &lt;/span&gt;단&lt;span&gt;, &lt;/span&gt;이는 자바스크립트에서는 지원하지 않는다&lt;span&gt;. POSIX&lt;/span&gt;는 자체로 대괄호를 문법에 포함하므로&lt;span&gt;, &lt;/span&gt;집합을 나타내는 대괄호와 함께 &lt;span&gt;[[:alpha:]]&lt;/span&gt;와 같이 사용되는 것이 일반적이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;반복 찾기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전까지는 한 글자에 대한 찾기였으나 둘 이상의 글자를 찾는 것이 필요하다&lt;span&gt;. &lt;/span&gt;그럴 때 더하기&lt;span&gt;(+)&lt;/span&gt;를 뒤에 붙이면 하나 이상의 문자와 일치하는지를 찾는다&lt;span&gt;(ex. a+&lt;/span&gt;이면 연속되는 &lt;span&gt;a&lt;/span&gt;를 모두 찾는다&lt;span&gt;).&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더하기는 최소한 하나의 일치하는 문자가 존재해야 하지만&lt;span&gt;, &lt;/span&gt;별표&lt;span&gt;(*)&lt;/span&gt;는 일치하는 문자가 존재하거나 아예 없거나이다&lt;span&gt;(&lt;/span&gt;불일치하는 경우만 걸러진다&lt;span&gt;).&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더해서 물음표&lt;span&gt;(?)&lt;/span&gt;는 더하기와 별표의 중간인데&lt;span&gt;, &lt;/span&gt;딱 한 문자가 일치하거나 문자가 존재하지 않거나이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중괄호&lt;span&gt;({})&lt;/span&gt;를 사용해서 바로 앞 문자의 구간을 지정해 줄 수도 있다&lt;span&gt;. &lt;/span&gt;하나만 적으면 정확한 개수&lt;span&gt;, &lt;/span&gt;두 개를 적으면 개수의 범위이다&lt;span&gt;. &lt;/span&gt;쉼표를 적으면 최소한의 의미가 된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Ex) \d{1,2}\d{3}\d{3,}&lt;br /&gt;1&lt;/span&gt;개 이상 &lt;span&gt;2&lt;/span&gt;개 이하의 숫자&lt;span&gt;, 3&lt;/span&gt;개의 숫자&lt;span&gt;, &lt;/span&gt;최소한 &lt;span&gt;3&lt;/span&gt;개의 숫자&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과도하게 일치하는 것을 방지하기 위해 &lt;span&gt;lazy &lt;/span&gt;수량자를 이용한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;*&lt;/span&gt;는 &lt;span&gt;*?, +&lt;/span&gt;는&lt;span&gt; +?, {n,}&lt;/span&gt;는 &lt;span&gt;{n,}?&lt;/span&gt;이다&lt;span&gt;. &lt;/span&gt;일치하는 최소한의 문자만을 찾는다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;위치 찾기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치 찾기는 문자열 내에서 반드시 일치해야 하는 위치를 지정할 때 사용한다&lt;span&gt;. Cat&lt;/span&gt;을 검색했는데 &lt;span&gt;cat&lt;/span&gt;과 &lt;span&gt;scatter&lt;/span&gt;가 모두 검색되는 등의 문제를 방지하기 위함&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문제는 앞뒤로 단어의 끝을 나타내는 &lt;span&gt;\b&lt;/span&gt;를 삽입하여 해결할 수 있다&lt;span&gt;(\bcat\b). \b&lt;/span&gt;는 단어가 아니라 위치를 가리키기 때문에&lt;span&gt;, &lt;/span&gt;찾은 문자열의 길이는 &lt;span&gt;3&lt;/span&gt;이 된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단어 경계를 반대로 제외시키고 싶을 때에는 &lt;span&gt;\B&lt;/span&gt;를 사용한다&lt;span&gt;. &lt;/span&gt;메타 문자에서 대소문자는 주로 반대의 역할을 가진다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐럿&lt;span&gt;(^) &lt;/span&gt;문자는 문자열의 시작을 의미한다&lt;span&gt;. &lt;/span&gt;즉&lt;span&gt;, &lt;/span&gt;반드시 &lt;span&gt;1&lt;/span&gt;째 줄의 시작이어야 하는 경우에 사용할 수 있다&lt;span&gt;. [^&lt;/span&gt;로 쓰일 경우에는 부정을 의미하지만&lt;span&gt;, &lt;/span&gt;대괄호 바깥에서는 문자열의 시작으로 쓰인다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;$ &lt;/span&gt;문자는 반대로 문자열의 끝이다&lt;span&gt;. &lt;/span&gt;즉&lt;span&gt;, &lt;/span&gt;마지막 줄의 끝이어야 하는 경우에 사용한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;?m&lt;/span&gt;을 정규표현식 맨 앞에 붙여 다중 행을 지원한다면&lt;span&gt;, ^&lt;/span&gt;와 &lt;span&gt;$&lt;/span&gt;는 줄바꿈 문자를 문자열의 시작&lt;span&gt;/&lt;/span&gt;끝으로 같이 인식하게 된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;하위 표현식 사용하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복 메타 문자는 자신의 바로 앞 한 글자에 대해서만 작동한다&lt;span&gt;. &lt;/span&gt;예를 들어 &lt;span&gt;&amp;amp;nbsp;{2,}&lt;/span&gt;의 경우에는 &lt;span&gt;&amp;amp;nbsp;&lt;/span&gt;가 반복되는 것을 찾고 싶었으나 &lt;span&gt;;&lt;/span&gt;만의 반복을 찾게 된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 하위 표현식을 사용하면 된다&lt;span&gt;. &lt;/span&gt;소괄호로 묶으면 된다&lt;span&gt;. (&amp;amp;nbsp;){2,}&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;괄호 안에는 정규 표현식이 들어갈 수도 있다&lt;span&gt;. (\d{1,3}\.){3}&lt;/span&gt;과 같은 식&lt;span&gt;. &lt;/span&gt;반복하려는 패턴의 영역을 정의하거나 &lt;span&gt;OR &lt;/span&gt;조건을 적절하게 정의할 때 주로 사용된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 응용하여 중첩&lt;span&gt;(nested)&lt;/span&gt;도 가능하다&lt;span&gt;. &lt;/span&gt;괄호 안의 괄호&lt;/p&gt;
&lt;h1&gt;역참조 사용하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역참조는 패턴 중 하나로&lt;span&gt;, &lt;/span&gt;앞서 일치한 부분을 다시 가리키는 패턴이다&lt;span&gt;. &lt;/span&gt;하위표현식의 맨 끝에 &lt;span&gt;\1&lt;/span&gt;을 붙이면 앞에 일치했던 패턴과 일치하는지를 찾게 된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Ex) [ ]+(\w+)[ ]+\1 : &lt;/span&gt;앞뒤로 공백이 하나 이상이며&lt;span&gt;, &lt;/span&gt;문자가 하나 이상 등장하는 하위 표현식이 등장한다&lt;span&gt;. &lt;/span&gt;마지막의 &lt;span&gt;\1&lt;/span&gt;는 앞서 일치한 하위 표현식이 또 있는지를 확인한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;\1&lt;/span&gt;은 처음 사용한 하위 표현식과 일치하는지를 확인한다&lt;span&gt;. \2&lt;/span&gt;는 두 번째&lt;span&gt;, \3&lt;/span&gt;는 세 번째&lt;span&gt;&amp;hellip;&lt;/span&gt;와 같은 사용이 가능하다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역참조 문법은 정규 표현식 구현에 따라서 상이한 경우가 많으므로&lt;span&gt;, &lt;/span&gt;해당 스택에서의 용법을 확인하고 사용할 것&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;l&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;치환 작업을 할 때에는 정규 표현식이 두 개 필요하다&lt;span&gt;. &lt;/span&gt;검색할 때 사용하는 표현식&lt;span&gt;, &lt;/span&gt;치환할 때 사용하는 표현식&lt;span&gt;. &lt;/span&gt;아래 예제를 보자&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이메일을 검색하여 &lt;span&gt;href &lt;/span&gt;형태로 바꿀 때 역참조로 간단하게 할 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 패턴 &lt;span&gt;: (\w+[\w\.]&lt;a href=&quot;mailto:*@[\w\.%5d+\.\w&quot;&gt;*@[\w\.]+\.\w&lt;/a&gt;+)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;치환 패턴 &lt;span&gt;: &amp;lt;A HREF=&lt;a href=&quot;mailto:$1&quot;&gt;mailto:$1&lt;/a&gt;&amp;gt;$1&amp;lt;/A&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 치환 패턴에서 &lt;span&gt;$1&lt;/span&gt;이 역참조를 담당하고 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;전후방탐색&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 자체가 아닌&lt;span&gt;, &lt;/span&gt;문자열의 위치를 찾고 싶은 경우에 탐색&lt;span&gt;(look around)&lt;/span&gt;를 사용한다&lt;span&gt;. &lt;/span&gt;예를 들어&lt;span&gt;, &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt; &lt;/span&gt;태그 사이에 있는 문자만을 결과로 조회하고 싶은 경우&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전방 탐색은 일치 영역을 발견해도 그 값을 반환하지 않는 패턴이다&lt;span&gt;. &lt;/span&gt;실제로는 하위 표현식이다&lt;span&gt;. ?=&lt;/span&gt;로 시작하고 &lt;span&gt;= &lt;/span&gt;다음에 일치할 텍스트가 온다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Ex) .+(?=:) &lt;/span&gt;와 같이 작성을 하면&lt;span&gt;, &lt;/span&gt;콜론이 등장하기 전까지의 텍스트가 반환된다&lt;span&gt;. &lt;/span&gt;만일 전방 탐색을 빼면 콜론까지 포함한 값이 반환된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후방 탐색은 뒤쪽을 탐색하는데&lt;span&gt;, ?&amp;lt;=&lt;/span&gt;이다&lt;span&gt;. &lt;/span&gt;전방 탐색과 사용법이 같고&lt;span&gt;, &lt;/span&gt;일치하는 뒷부분을 반환한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Ex) (?&amp;lt;=/$)[0-9.]+&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 첫 태그 내 텍스트를 가져오는 경우와 같이&lt;span&gt;, &lt;/span&gt;전후방탐색을 함께 사용할 수도 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 적은 전후방탐색의 반대 표현인 부정형 전후방탐색도 있는데 &lt;span&gt;= &lt;/span&gt;대신에 &lt;span&gt;!&lt;/span&gt;를 사용하면 된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;?! &lt;/span&gt;와 &lt;span&gt;?&amp;lt;!&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;조건 달기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건을 사용할 때에는 &lt;span&gt;?&lt;/span&gt;를 사용하는데&lt;span&gt;, &lt;/span&gt;이전에 썼던 것들 중 역참조&lt;span&gt;, &lt;/span&gt;전후방탐색을 사용하여 조건 처리가 가능하다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역참조 조건은 &lt;span&gt;(?(&lt;/span&gt;역참조 표현&lt;span&gt;)true)&lt;/span&gt;로 쓸 수 있다&lt;span&gt;. (?(&lt;/span&gt;역참조 표현&lt;span&gt;)true|false)&lt;/span&gt;로 하면 &lt;span&gt;else&lt;/span&gt;문과 같이 동작한다&lt;span&gt;. &lt;/span&gt;즉&lt;span&gt;, ?&lt;/span&gt;로 시작한 역참조 표현이 존재하면 &lt;span&gt;true &lt;/span&gt;위치에 있는 표현식이&lt;span&gt;, &lt;/span&gt;없으면 &lt;span&gt;false &lt;/span&gt;위치의 표현식이 동작한다&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전후방탐색은 역참조와 동일하게 사용하면 된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Development</category>
      <category>Regex</category>
      <category>정규표현식</category>
      <author>판교토끼</author>
      <guid isPermaLink="true">https://icerabbit.tistory.com/165</guid>
      <comments>https://icerabbit.tistory.com/165#entry165comment</comments>
      <pubDate>Tue, 14 Jan 2025 21:30:35 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 344. Reverse String</title>
      <link>https://icerabbit.tistory.com/164</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/reverse-string/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/reverse-string/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;char 배열로 들어있는 string을 뒤바꾸는 간단명료한 문제이다. 참고로 일부 언어들에서 .resverse를 지원하기도 하지만 이를 방지하기 위해 모든 언어에서 char 배열로 input을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인은 stack에 넣고 앞부터 차례대로 넣어주도록 간단하게 구현하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1729684228127&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
public:
    void reverseString(vector&amp;lt;char&amp;gt;&amp;amp; s) {
        stack&amp;lt;char&amp;gt; stack;
        for(char ch : s) {
            stack.push(ch);
        }
        int i=0;
        while(!stack.empty()) {
            s[i++] = stack.top();
            stack.pop();
        }
    }
};&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/LeetCode</category>
      <category>C++</category>
      <category>leetcode</category>
      <author>판교토끼</author>
      <guid isPermaLink="true">https://icerabbit.tistory.com/164</guid>
      <comments>https://icerabbit.tistory.com/164#entry164comment</comments>
      <pubDate>Wed, 23 Oct 2024 20:51:16 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 144. Binary Tree Preorder Traversal</title>
      <link>https://icerabbit.tistory.com/163</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/binary-tree-preorder-traversal/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/binary-tree-preorder-traversal&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tree가 주어질 경우 이 값을 root부터 순차적으로 값을 배열에 저장하는 심플한 문제이다.&lt;/p&gt;
&lt;pre id=&quot;code_1729683897540&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector&amp;lt;int&amp;gt; preorderTraversal(TreeNode* root) {
        vector&amp;lt;int&amp;gt; answer;
        TreeNode* current = root;
        vector&amp;lt;TreeNode*&amp;gt; parents;
        int count = 0;
        if(root == nullptr) {
            return answer;
        }
        do {
            if(current-&amp;gt;val &amp;gt; -101) {
                answer.push_back(current-&amp;gt;val);
                current-&amp;gt;val = -101;
            }

            if(current-&amp;gt;left != nullptr) {
                parents.push_back(current);
                current = current-&amp;gt;left;
                parents.back()-&amp;gt;left = nullptr;
            } else if(current-&amp;gt;right != nullptr) {
                parents.push_back(current);
                current = current-&amp;gt;right;
                parents.back()-&amp;gt;right = nullptr;
            } else if (!parents.empty()) {
                current = parents.back();
                parents.pop_back();
            }
            if(parents.empty()) {
                ++count;
            }
        } while(count &amp;lt; 2);

        return answer;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-100~100의 값을 가지는 조건이어서 지나갈 경우 지나간 표시로 그 외의 값을 넣어 주고 부모들을 모아 두는 벡터에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 left부터 순회하면서 값을 저장하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/binary-tree-postorder-traversal&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/binary-tree-postorder-traversal&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;155번 문제는 후위로 담는 문제로, 거꾸로 자식 노드부터 넣도록 하면 되는데 위 코드와 유사한 점이 많다.&lt;/p&gt;
&lt;pre id=&quot;code_1729684060679&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector&amp;lt;int&amp;gt; postorderTraversal(TreeNode* root) {
        vector&amp;lt;int&amp;gt; answer;
        TreeNode* current = root;
        vector&amp;lt;TreeNode*&amp;gt; parents;
        int count = 0;
        if(root == nullptr) {
            return answer;
        }
        do {
            if(current-&amp;gt;left != nullptr) {
                parents.push_back(current);
                current = current-&amp;gt;left;
                parents.back()-&amp;gt;left = nullptr;
            } else if(current-&amp;gt;right != nullptr) {
                parents.push_back(current);
                current = current-&amp;gt;right;
                parents.back()-&amp;gt;right = nullptr;
            } else {
                if(current-&amp;gt;val &amp;gt; -101) {
                    answer.push_back(current-&amp;gt;val);
                    current-&amp;gt;val = -101;
                }
                if (!parents.empty()) {
                    current = parents.back();
                    parents.pop_back();
                }
            }
            if(parents.empty()) {
                ++count;
            }
        } while(count &amp;lt; 3);

        return answer;
    }
};&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;</description>
      <category>PS/LeetCode</category>
      <category>C++</category>
      <category>leetcode</category>
      <author>판교토끼</author>
      <guid isPermaLink="true">https://icerabbit.tistory.com/163</guid>
      <comments>https://icerabbit.tistory.com/163#entry163comment</comments>
      <pubDate>Wed, 23 Oct 2024 20:48:47 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 112. Path Sum</title>
      <link>https://icerabbit.tistory.com/162</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/path-sum/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/path-sum/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BWiTW/btsFDCkqUhO/K9ynaHn7UoP3NcyiKEHwd1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BWiTW/btsFDCkqUhO/K9ynaHn7UoP3NcyiKEHwd1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BWiTW/btsFDCkqUhO/K9ynaHn7UoP3NcyiKEHwd1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBWiTW%2FbtsFDCkqUhO%2FK9ynaHn7UoP3NcyiKEHwd1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;592&quot; height=&quot;421&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;421&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tree에서 root~leaf 까지의 합이 주어진 값과 일치하는 경우가 있는지 확인하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학부생 시절 코딩테스트를 준비할 때에는 tree 문제를 아주 많이 풀었으나, 요즘에는 잘 풀지 않아 DFS라는 개념만 기억하고 있었다. 따로 예전의 스킬을 찾아 보지는 않고, 논리에 맞게 생각하면서 분기를 태웠다. 아래가 제출한 코드인데, 분기 코드가 좀 지저분하다. 주어진 TreeNode가 부모는 갖고 있지 않아 stack에 push/pop하면서 비교하고, 한 쪽만 비거나 양 쪽 모두 비었을 경우 모두를 분기하느라 분기 조건이 길어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제 또한 요즘 C++을 사용하고 있어 문제도 C++로 풀어 보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1709816152228&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        bool result = false;
        if(root == nullptr) {
            return result;
        }

        std::stack&amp;lt;TreeNode*&amp;gt; st;
        st.push(root);
        auto iterNode = st.top();
        TreeNode* popNode = nullptr;
        int sum = tempNode-&amp;gt;val;
        int count = 0;
        while(!st.empty()) {
            iterNode = st.top();
            if (iterNode == root) {
                if ((root-&amp;gt;left == nullptr &amp;amp;&amp;amp; root-&amp;gt;right == nullptr) &amp;amp;&amp;amp; ++count == 1) {
                    result = (root-&amp;gt;val == targetSum);
                    break;
                } else if ((root-&amp;gt;left == nullptr || root-&amp;gt;right == nullptr) &amp;amp;&amp;amp; ++count == 2) {
                    break;
                } else if (++count == 3) {
                    break;
                }
            }
            if(iterNode-&amp;gt;left != nullptr &amp;amp;&amp;amp; popNode != iterNode-&amp;gt;left &amp;amp;&amp;amp; (popNode != iterNode-&amp;gt;right || popNode == nullptr || iterNode-&amp;gt;right == nullptr)) {
                sum += iterNode-&amp;gt;left-&amp;gt;val;
                st.push(iterNode-&amp;gt;left);
                continue;
            } else if(iterNode-&amp;gt;right != nullptr &amp;amp;&amp;amp; popNode != iterNode-&amp;gt;right) {
                sum += iterNode-&amp;gt;right-&amp;gt;val;
                st.push(iterNode-&amp;gt;right);
                continue;
            } else {
                if (sum == targetSum &amp;amp;&amp;amp; iterNode-&amp;gt;left == nullptr &amp;amp;&amp;amp; iterNode-&amp;gt;right == nullptr) {
                    result = true;
                    break;
                }
                popNode = st.top();
                sum -= popNode-&amp;gt;val;
                st.pop();
            }
        }

        return result;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말했듯이 우선 while문 내에서 stack에 넣고 빼기를 하도록 하고 그 후에 분기들을 붙이느라 여러 케이스들이 많아 조건문이 길고 보기 복잡해졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제풀이를 가끔씩 하던 터라 감을 잡기 위해 이번에는 따로 공부하지 않고 논리로 풀었으나, 좀 더 간결하게 짤 수 있도록 해야겠다.&lt;/p&gt;</description>
      <category>PS/LeetCode</category>
      <category>leetcode</category>
      <author>판교토끼</author>
      <guid isPermaLink="true">https://icerabbit.tistory.com/162</guid>
      <comments>https://icerabbit.tistory.com/162#entry162comment</comments>
      <pubDate>Thu, 7 Mar 2024 21:57:36 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 58. Length of Last Word</title>
      <link>https://icerabbit.tistory.com/161</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/length-of-last-word&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/length-of-last-word&lt;/a&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;근래에 계속 Python을 이용해서 풀다가, 요즘에는 현업에서 C++을 사용하고 있기에 C++로 풀어보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1709814796483&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
public:
    int lengthOfLastWord(string s) {
        std::istringstream iss(s);

        std::vector&amp;lt;std::string&amp;gt; tokens;

        std::string token;
        std::string old_token;
        while (std::getline(iss, token, ' ')) {
            if (token.empty()) {
                continue;
            }
            old_token = token;
        }


        return old_token.size();
    }
};&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/LeetCode</category>
      <category>leetcode</category>
      <author>판교토끼</author>
      <guid isPermaLink="true">https://icerabbit.tistory.com/161</guid>
      <comments>https://icerabbit.tistory.com/161#entry161comment</comments>
      <pubDate>Thu, 7 Mar 2024 21:34:38 +0900</pubDate>
    </item>
    <item>
      <title>[flutter] Unable to find bundled Java version 오류가 발생할 경우</title>
      <link>https://icerabbit.tistory.com/160</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 환경에서 flutter 설치 후에 flutter doctor를 실행했을 때 Unable to find bundled Java version 오류가 발생하는 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android Studio 버전이 올라가면서 오류가 발생하는 것으로 추정되는데 아래와 같이 해결 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 안드로이드가 설치된 경로(기본은 C:\Program Files\Android\Android Studio) 하위의 jbr 폴더 내용을 jre 폴더에 붙여넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 명령 프롬프트(cmd)를 관리자 권한으로 실행하고, 심볼릭 링크를 생성한다.&lt;br /&gt;ex) &lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;mklink /d &quot;C:\Program Files\Android\Android Studio\jre&quot; &quot;C:\Program Files\Android\Android Studio\jbr&quot;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Development</category>
      <category>Flutter</category>
      <category>플러터</category>
      <author>판교토끼</author>
      <guid isPermaLink="true">https://icerabbit.tistory.com/160</guid>
      <comments>https://icerabbit.tistory.com/160#entry160comment</comments>
      <pubDate>Wed, 27 Dec 2023 22:01:22 +0900</pubDate>
    </item>
  </channel>
</rss>