<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>erikanes's Macchiato</title>
    <link>https://erikanes.tistory.com/</link>
    <description>게임 개발자를 꿈꾸는 공간</description>
    <language>ko</language>
    <pubDate>Fri, 31 Jul 2026 12:28:51 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>에리카네스</managingEditor>
    <image>
      <title>erikanes's Macchiato</title>
      <url>https://t1.daumcdn.net/cfile/tistory/21761A3955934EE415</url>
      <link>https://erikanes.tistory.com</link>
    </image>
    <item>
      <title>default 키워드에 의한 can't delete incomplete type 에러</title>
      <link>https://erikanes.tistory.com/564</link>
      <description>&lt;pre id=&quot;code_1716885048560&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Session.h
class SessionImpl;

class Session
{
public:
    Session() = default;
    ~Session() = default;
    
private:
    std::unique_ptr&amp;lt;SessionImpl&amp;gt; m_pImpl;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 전방선언한 클래스의 객체를 삭제할 때에는 생성자와 소멸자에 default를 붙여주면 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 정확하게는 스마트 포인터 사용 또는 헤더파일에서 원시 포인터를 delete를 하는 경우이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716885247673&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Session
{
...
    ~Session() { m_pUserManager.reset(); }
};&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;그래서 불완전한 타입을 삭제할 수 없다는 오류가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716885417600&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Session.h
class SessionImpl;

class Session
{
public:
    Session();
    ~Session();
};


// Session.cpp
#include &quot;SessionImpl.h&quot;

Session::Session() {}
Session::~Session() {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결법은 간단하다. 기본 생성자와 소멸자를 사용하더라도 default 키워드 대신 cpp에 명시적으로 정의해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 &lt;b&gt;&quot;생성자와 소멸자 둘 다&quot;&lt;/b&gt; default 키워드를 사용하면 안된다.&lt;/p&gt;</description>
      <category>C++/기타</category>
      <author>에리카네스</author>
      <guid isPermaLink="true">https://erikanes.tistory.com/564</guid>
      <comments>https://erikanes.tistory.com/564#entry564comment</comments>
      <pubDate>Tue, 28 May 2024 17:43:32 +0900</pubDate>
    </item>
    <item>
      <title>TCP/IP 소켓 프로그래밍 주요 함수</title>
      <link>https://erikanes.tistory.com/562</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서버 함수&lt;/b&gt;&lt;/h3&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; border: #cbcbcb 1px dashed; padding: 10px;&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;bind&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1715671690925&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int bind(
    SOCKET s, // 클라이언트 접속 수용을 목저긍로 만든 소켓
    const struct sockaddr* name, // 지역 IP 주소와 지역 포트 번호가 담긴 구조체의 주소
    int namelen // 소켓 주소 구조체 변수의 길이
);
// 반환값 : 성공(0), 실패(SOCKET_ERROR)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;서버의 지역 IP 주소와 지역 포트 번호를 결정하는 역할을 한다.&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;&lt;b&gt;listen&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1715671690926&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int listen(
    SOCKET s, // bind 함수에 의해 지역 IP 주소와 지역 포트 번호가 설정된 상태
    int backlog // 접속 가능한 클라이언트의 개수. 프로토콜의 지원 가능한 최대값은 SOMAXCONN
);
// 반환값 : 성공(0), 실패(SOCKET_ERROR)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;소켓과 결합된 TCP 포트 상태를 LISTENING으로 바꾸는 역할을 한다.&lt;/p&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;&lt;b&gt;accept&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1715671690927&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;SOCKET accept(
    SOCKET s, // 클라이언트 접속을 수용할 목적으로 만든 소켓
    struct sockaddr* addr, // 소켓 주소 구조체 변수의 주소
    int* addrlen
);
// 반환값 : 성공(new socket), 실패(INVALID_SOCKET)&lt;/code&gt;&lt;/pre&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;접속한 클라이언트의 IP 주소와 포트 번호를 알려준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;접속한 클라이언트가 없을 경우 서버를 대기 상태로 만든다.&lt;/p&gt;
&lt;/div&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;클라이언트 함수&lt;/b&gt;&lt;/h3&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; border: #cbcbcb 1px dashed; padding: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;connect&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1715671794062&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int connect(
    SOCKET s, // 서버와 통신하기 위해 만든 소켓
    const struct sockaddr* name,
    int namelen, // 구조체의 크기
);
// 반환값 : 성공(0), 실패(SOCKET_ERROR)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 서버에 접속하여 TCP 프로토콜 수준의 연결이 이루어지도록 한다.&lt;/p&gt;
&lt;/div&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;데이터 전송 함수&lt;/b&gt;&lt;/h3&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; border: #cbcbcb 1px dashed; padding: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;send&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1715672222154&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int send(
    SOCKET s, // 통신할 대상과 연결된 소켓
    const char* buf, // 보낼 데이터를 담고 있는 어플리케이션 버퍼의 주소
    int len, // 보낼 데이터의 크기
    int flags // 대부분 0 사용. MSG_DONTROUTE, MSG_OOB를 사용하는 경우도 드물게 있다
);
// 반환값 : 성공(보낸 바이트 수), 실패(SOCKET_ERROR)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션 데이터를 송신 버퍼에 복사함으로써 하부 프로토콜(TCP/IP)에 의해 데이터가 전송되도록 한다.&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;블로킹 소켓이라면 len과 같은 값, 넌블로킹 소켓이라면 송신 버퍼의 여유 공간만큼 데이터를 복사하기 때문에 복사한 데이터 바이트 수를 리턴한다. (최소1, 최대 len)&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;recv&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1715672445103&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int recv(
    SOCKET s, // 통신할 대상과 연결된 소켓
    char* buf, // 받은 데이터를 저장할 어플리케이션 버퍼의 주소
    int len, // 수신 버퍼로부터 복사할 최대 데이터 크기
    int flags // 대부분 0 사용. MSG_PEEK, MSG_OOB를 드물게 사용한다
              // 복사하고 나면 해당 데이터를 수신 버퍼에서 삭제하지만 MSG_PEEK 사용 시 삭제하지 않는다
);
// 반환값 : 성공(받은 바이트 수 또는 0), 실패(SOCKET_ERROR)&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;수신 버퍼에 데이터가 도달한 경우 복사한 바이트 수를 리턴하고 접속 종료 요청이라면 0을 리턴한다.&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;recv 사용 시 주의할점은 len보다 작은 데이터가 복사될 수 있기 때문에 받을 데이터의 크기를 미리 알고 있다면 전부 받을 때까지 여러 번 호출해야 한다.&lt;/p&gt;
&lt;/div&gt;</description>
      <category>C++/Socket</category>
      <category>tcp/ip 소켓 프로그래밍</category>
      <author>에리카네스</author>
      <guid isPermaLink="true">https://erikanes.tistory.com/562</guid>
      <comments>https://erikanes.tistory.com/562#entry562comment</comments>
      <pubDate>Tue, 14 May 2024 16:46:32 +0900</pubDate>
    </item>
    <item>
      <title>IOCP의 기본 구조 및 동작 원리</title>
      <link>https://erikanes.tistory.com/561</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IOCP(Input/Output Completion Port)&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;IOCP는 다수의 클라이언트의 요청을 효율적으로 처리하기 위한 방법들 중 하나이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 스레드를 미리 만들어두고 소켓에서의 IO가 완료되었을 때 잠들어있는 스레드를 깨워서 작업을 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최적화 할 수 있는 스레드의 수는 프로세서의 2배 정도가 적당하다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IOCP는 스레드를 관리하기 위해 1개의 스레드 큐와 2개의 스레드 리스트, 1개의 작업 큐를 사용하고 각 스레드는 3개의 큐중 하나의 큐에만 존재할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; border: #c1c1c1 1px solid; padding: 10px;&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IOCompletionQueue&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;FIFO Queue. 입출력이 완료된 작업들이 들어간다. 스레드들은 이 큐에서 작업을 꺼내서 수행한다.&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;&lt;b&gt;WatingThreadQueue&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LIFO Queue. 작업 대기중인 스레드들이 들어있다. 만약 입출력이 완료되었다면 이 큐에서 스레드를 하나 꺼내서 사용한다.&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;&lt;b&gt;ReleaseThreadList&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현재 작업을 수행하고 있는 스레드들이 들어간다. 만약 실행중인 스레드가 일시정지해야 한다면 해당 스레드를 PausedThreadList로 보내고 WatingThreadQueue에서 새로운 스레드를 하나 꺼내온다. 이것 때문에 프로세서의 갯수보다 많은 스레드를 미리 만들어 놓는 것이다.&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;&lt;b&gt;PausedThreadList&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어떠한 원인으로 인해 일시정지된 스레드들이 들어간다. 일시정지가 풀리더라도 ReleaseThreadList가 꽉 차있다면 바로 ReleaseThreadList로 보내지 않고 대기한다.&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;IOCP를 사용하려면 먼저 IOCP를 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1715580788288&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HANDLE WINAPI CreateIoCompletionPort(
    _In_        HANDLE fileHandle, // IOCP와 연결할 핸들. 최초 생성시 INVALID_HANDLE_VALUE를 넘긴다
    _In_opt_    HANDLE ExistingCompletionPort, // IOCP 핸들. 최초 생성시 NULL
    _In_        ULONG_PTR CompletionKey, // IO 완료 시 넘어갈 값. 사용자가 넘기고 싶은 값을 넘김
    _In_        DWORD NumberOfConcurrentThreads) // 한 번에 동작할 수 있는 최대 스레드 개수. 0을 넘기면 프로세서 숫자로 자동 지정됨
    
HANDLE hPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); // CP 최초 생성
HANDLE port = CreateIoCompletionPort(socket, hPort, (ULONG_PTR)session, 0); // 디바이스-CP 연결&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;CreateIoCompletionPort 함수는 2가지 일을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. CP 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 디바이스와 CP 연결&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IOCompletionQueue&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 완료시 말고도 유저가 직접 PostQueuedCompletionStatus 함수를 호출해서 정보를 직접 큐에 집어넣을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1715583461602&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BOOL WINAPI PostQueuedCompletionStatus(
    _In_        HANDLE CompletionPort,
    _In_        DWORD dwNumberofByteTransferred,
    _In_        ULONG_PTR dwCompletionKey,
    _in_opt_    LPOVERLAPPED lpOverlapped)&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;전달한 정보들은 입출력이 완료되면 CompletionQueue에 저장되어 스레드가 자신을 처리해주기를 기다리게 된다.&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;b&gt;WatingThreadQueue&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GetQueuedCompletionStatus 함수로 스레드를 대기 상태로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReleaseThreadList에 공간이 있고, 완료된 입출력이 있는 경우에만 대기 상태가 해제된다.&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;b&gt;ReleaseThreadList&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 장치 또는 PostQueuedCompletionStatus로 보낸 정보를 GetQueuedCompletionStatus 함수를 통해서 받아온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1715584081606&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BOOL WINAPI GetQueuedCompletionStatus(
    _In_    HANDLE CompletionPort,
    _Out_   LPDWORD lpNumberofBytes,
    _Out_   PULONG_PTR lpCompletionKey,
    _Out_   LPOVERLAPPED *lpOverlapped,
    _In_    DWORD dwMilliseconds)&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;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PausedThreadList&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GetQueuedCompletionStatus를 호출하지 않더라도 스레드가 Blocking 상태에 빠지면 IOCP가 알아서 PauseThreadList에 집어넣는다.&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;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;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; border: #c1c1c1 3px double; padding: 10px;&quot;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.slideshare.net/namhyeonuk90/iocp&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.slideshare.net/namhyeonuk90/iocp&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1715580880076&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Iocp 기본 구조 이해&quot; data-og-description=&quot;Iocp 기본 구조 이해 - Download as a PDF or view online for free&quot; data-og-host=&quot;www.slideshare.net&quot; data-og-source-url=&quot;https://www.slideshare.net/namhyeonuk90/iocp&quot; data-og-url=&quot;https://www.slideshare.net/namhyeonuk90/iocp&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b6WzCJ/hyV56PnYV1/A7UZJoERTKZrC4eFJqUOn0/img.jpg?width=640&amp;amp;height=480&amp;amp;face=0_0_640_480,https://scrap.kakaocdn.net/dn/bencsM/hyV2yGK5xg/AP3YWHszRUT4aOdqXkVZOk/img.jpg?width=640&amp;amp;height=480&amp;amp;face=0_0_640_480&quot;&gt;&lt;a href=&quot;https://www.slideshare.net/namhyeonuk90/iocp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.slideshare.net/namhyeonuk90/iocp&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b6WzCJ/hyV56PnYV1/A7UZJoERTKZrC4eFJqUOn0/img.jpg?width=640&amp;amp;height=480&amp;amp;face=0_0_640_480,https://scrap.kakaocdn.net/dn/bencsM/hyV2yGK5xg/AP3YWHszRUT4aOdqXkVZOk/img.jpg?width=640&amp;amp;height=480&amp;amp;face=0_0_640_480');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Iocp 기본 구조 이해&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Iocp 기본 구조 이해 - Download as a PDF or view online for free&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.slideshare.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;a href=&quot;https://eastroot1590.tistory.com/entry/IOCP%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://eastroot1590.tistory.com/entry/IOCP%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1715584424886&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;IOCP의 기본 동작 원리&quot; data-og-description=&quot;socket 개발자들은 오랜시간 다수의 클라이언트의 요청을 효율적으로 처리하기 위한 방법들을 연구해왔다. 그 결과 수많은 해결책들이 개발되었고, 아직도 더 효율적인 방법을 찾으려 노력중인 &quot; data-og-host=&quot;eastroot1590.tistory.com&quot; data-og-source-url=&quot;https://eastroot1590.tistory.com/entry/IOCP%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; data-og-url=&quot;https://eastroot1590.tistory.com/entry/IOCP%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ldUyX/hyV2yNzItY/4ZMMPzwafexNIBNLqK22Fk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cK6vK9/hyV55Xe6Fp/vvvmBWMh0Y47NCGulkX5a0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://eastroot1590.tistory.com/entry/IOCP%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://eastroot1590.tistory.com/entry/IOCP%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ldUyX/hyV2yNzItY/4ZMMPzwafexNIBNLqK22Fk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cK6vK9/hyV55Xe6Fp/vvvmBWMh0Y47NCGulkX5a0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;IOCP의 기본 동작 원리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;socket 개발자들은 오랜시간 다수의 클라이언트의 요청을 효율적으로 처리하기 위한 방법들을 연구해왔다. 그 결과 수많은 해결책들이 개발되었고, 아직도 더 효율적인 방법을 찾으려 노력중인&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;eastroot1590.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;a href=&quot;https://ozt88.tistory.com/23&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ozt88.tistory.com/23&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1715584432820&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;windows IOCP 기초&quot; data-og-description=&quot;IOCP 개념 싱글 쓰레드로는 부족해 이전 게시물까지 이야기 했던 통지모델들은 싱글쓰레드 멀티플렉싱을 위한 확장처럼 보였다. 하지만 싱글쓰레드 스마트폰도 보기 힘든 2015년 현재, 굳이 하이&quot; data-og-host=&quot;ozt88.tistory.com&quot; data-og-source-url=&quot;https://ozt88.tistory.com/23&quot; data-og-url=&quot;https://ozt88.tistory.com/23&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bwaP7e/hyV56IB4Vu/KORzN6yWdD7OiupEj2rWD1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cmUqnl/hyV2DafnnD/jhCeMup408xHIatDgdJH9k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://ozt88.tistory.com/23&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ozt88.tistory.com/23&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bwaP7e/hyV56IB4Vu/KORzN6yWdD7OiupEj2rWD1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cmUqnl/hyV2DafnnD/jhCeMup408xHIatDgdJH9k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;windows IOCP 기초&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;IOCP 개념 싱글 쓰레드로는 부족해 이전 게시물까지 이야기 했던 통지모델들은 싱글쓰레드 멀티플렉싱을 위한 확장처럼 보였다. 하지만 싱글쓰레드 스마트폰도 보기 힘든 2015년 현재, 굳이 하이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ozt88.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;/div&gt;</description>
      <category>C++/IOCP</category>
      <category>iocp</category>
      <author>에리카네스</author>
      <guid isPermaLink="true">https://erikanes.tistory.com/561</guid>
      <comments>https://erikanes.tistory.com/561#entry561comment</comments>
      <pubDate>Mon, 13 May 2024 16:14:03 +0900</pubDate>
    </item>
    <item>
      <title>[DirectX 11] Tower of Fantasy 모작</title>
      <link>https://erikanes.tistory.com/560</link>
      <description>&lt;p&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/HTYqXzAWxKA?si=6RAazsYLMVzSduhd&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&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;Tower of Fantasy&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제작 기간 : 23. 11. 01 ~ 24. 01. 04&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 인원 : 5인&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;주요 구현 내역&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;/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;/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;</description>
      <category>포트폴리오</category>
      <author>에리카네스</author>
      <guid isPermaLink="true">https://erikanes.tistory.com/560</guid>
      <comments>https://erikanes.tistory.com/560#entry560comment</comments>
      <pubDate>Mon, 29 Apr 2024 15:55:50 +0900</pubDate>
    </item>
    <item>
      <title>[DirectX 11] Furi 모작</title>
      <link>https://erikanes.tistory.com/559</link>
      <description>&lt;p&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/B31p4tJ335I?si=LWdEPhjqYpaWhiQm&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&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;Furi&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제작 기간 : 23. 07. 31 ~ 23. 10. 25&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 인원 : 1인&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;- fbx 바이너리 포맷 변환&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 오브젝트 풀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Material&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 인스턴싱&lt;/p&gt;</description>
      <category>포트폴리오</category>
      <author>에리카네스</author>
      <guid isPermaLink="true">https://erikanes.tistory.com/559</guid>
      <comments>https://erikanes.tistory.com/559#entry559comment</comments>
      <pubDate>Mon, 29 Apr 2024 15:51:35 +0900</pubDate>
    </item>
    <item>
      <title>[DirectX 9] Delver 모작</title>
      <link>https://erikanes.tistory.com/558</link>
      <description>&lt;p&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/KXR-AcEWCc0?si=pvpzOy1r5ZiMhXpa&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&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;Delver&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제작 기간 : 23. 06. 04 ~ 23. 07. 05&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 인원 : 4인&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;주요 구현 내역&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ImGui를 이용한 맵툴 제작&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;/p&gt;</description>
      <category>포트폴리오</category>
      <author>에리카네스</author>
      <guid isPermaLink="true">https://erikanes.tistory.com/558</guid>
      <comments>https://erikanes.tistory.com/558#entry558comment</comments>
      <pubDate>Mon, 29 Apr 2024 15:44:21 +0900</pubDate>
    </item>
    <item>
      <title>[WinAPI] Cave Story 모작</title>
      <link>https://erikanes.tistory.com/557</link>
      <description>&lt;p&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/ffobRa2nT1M?si=MuKHwLt4MKbTf9kl&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&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;Cave Story&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제작 기간 : 23. 03. 28 ~ 23. 04. 13&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 인원 : 1인&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;- json을 이용한 리소스 관리&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;</description>
      <category>포트폴리오</category>
      <author>에리카네스</author>
      <guid isPermaLink="true">https://erikanes.tistory.com/557</guid>
      <comments>https://erikanes.tistory.com/557#entry557comment</comments>
      <pubDate>Mon, 29 Apr 2024 15:38:22 +0900</pubDate>
    </item>
    <item>
      <title>객체 지향 프로그래밍의 설계 원칙</title>
      <link>https://erikanes.tistory.com/554</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;SOLID. OOP를 구글링 하면 OOP의 특징과 함께 나오는 항상 나오는 약어이다.&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;객체 지향 프로그래밍의 5가지 설계 원칙&lt;/b&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;b&gt;단일 책임 원칙 (Single Responsibility Principle, SRP)&lt;/b&gt;&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;책임은 변화에 대한 것이다.&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;SRP를 지키지 않으면 연쇄작용이 발생할 가능성이 높아지기 때문에 유지보수가 어려워진다.&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;b&gt;개방-폐쇄 원칙 (Open-Closed Principle, OCP)&lt;/b&gt;&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;개방 폐쇄 원칙은 유연함에 대한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상화와 다형성을 이용해서 OCP를 달성할 수 있고 상속을 통해서도 달성할 수 있다. (오버라이딩)&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;OCP가 깨진 코드에는 다운 캐스팅, 비슷한 if-else 블록이 존재하는 특징이 있다.&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;b&gt;리스코프 치환 원칙 (Liskov Substitution Principle, LSP)&lt;/b&gt;&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;리스코프 치환 원칙은 계약과 확장에 대한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다형성을 이용해서 LSP를 달성할 수 있다.&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;상속 관계처럼 보이지만 실제로는 상속 관계가 아닌 경우 LSP의 위반이 발생한다. 예를 들어 사각형과 정사각형 클래스의 경우가 될 수 있다.&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;LSP를 어기면 OCP도 어길 가능성도 자연스레 높아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709023755603&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int calculateDiscountAmount(Item* item)
{
    if (nullptr != dynamic_cast&amp;lt;SpecialItem*&amp;gt;(item))
        return 0;
        
    return item-&amp;gt;getPrice() * discountRate;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드처럼 다운 캐스팅으로 타입을 비교하여 예외를 처리하는 경우가 일반적인 LSP 위반 사례이다. 또한 기능을 확장하며 예외를 처리하는 과정에서 변경에는 닫혀 있어야 하는데 코드가 변경되었기 때문에 OCP도 위반된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709023957504&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Item
{
public:
    virtual bool isDiscountAvailable() { return true; }    
};

class SpecialItem : public Item
{
public:
    virtual bool isDiscountAvailable() override { return false; }
};

int calculateDiscountAmount(Item* item)
{
    if (!item-&amp;gt;isDiscountAvailable())
        return 0;
        
    return item-&amp;gt;getPrice() * discountRate;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 추후 기능이 확장된다 하더라도 다운캐스팅 및 코드의 변경 없이도 의도한 대로 동작할 수 있어야 LSP 및 OCP를 지킬 수 있다.&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;b&gt;인터페이스 분리 원칙 (Interpace Segregation Principle, ISP)&lt;/b&gt;&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;&lt;b&gt;=&amp;nbsp;인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C/C++같이 컴파일과 링크를 직접 해주는 언어를 사용할 때 장점이 잘 드러난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 ArticleListUI, ArticleWriteUI, ArticleDeleteUI가 ArticleService 클래스의 헤더를 참조하고 있을 때,&amp;nbsp; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ArticleService&amp;nbsp;&lt;/span&gt;클래스에서 읽기와 관련된 메소드가 변경된다고 하면 재컴파일 후에 목적 파일이 재생성 된다. 여기서 읽기 기능만 변경되었음에도 불구하고 전혀 상관없는 ListUI, WriteUI도 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ArticleService&lt;span&gt; &lt;/span&gt;&lt;/span&gt;클래스의 헤더 파일을 참조하고 있기 때문에 다시 컴파일되며 목적 파일이 재생성 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ArticleService&lt;span&gt; &lt;/span&gt;&lt;/span&gt;클래스를 각 기능으로 나누어서 분리하면 사용하지 않는 인터페이스에 변경이 발생하더라도 영향을 받지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1035&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SZ9tx/btsFm4H5wc9/GQrCDzwuyfFookN7HQNLtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SZ9tx/btsFm4H5wc9/GQrCDzwuyfFookN7HQNLtk/img.png&quot; data-alt=&quot;UML로 그리면 위와 같다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SZ9tx/btsFm4H5wc9/GQrCDzwuyfFookN7HQNLtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSZ9tx%2FbtsFm4H5wc9%2FGQrCDzwuyfFookN7HQNLtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1035&quot; height=&quot;250&quot; data-origin-width=&quot;1035&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UML로 그리면 위와 같다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직관적으로 보이듯이 단일 책임 원칙과도 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;의존 역전 원칙 (Dependency Inversion Principle, DIP)&lt;/b&gt;&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;추상화를 이용해서 DIP를 달성할 수 있다.&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;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; border: #c1c1c1 1px dashed; padding: 10px;&quot;&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;- 바이트 데이터를 읽어와 암호화 하고 결과 바이트 데이터를 쓴다.&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;◾ 저수준 모듈&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;- AES 알고리즘으로 암호화한다.&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;고수준 모듈은 의미 있는 단일 기능을 제공하는 모듈이라고 정의할 수 있고 저수준 모듈은 고수준 모듈을 구현하기 위해 필요한 하위 기능의 실제 구현으로 정의할 수 있다.&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;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; border: #c1c1c1 1px dashed; padding: 10px;&quot;&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;상위 수준에서의 쿠폰 정책은 한 번 안정화되면 쉽게 변하지 않지만 쿠폰은 상황에 따라 다양한 종류가 추가될 수 있다. 여기서 쿠폰을 이용한 가격 계산 모듈이 개별적인 쿠폰 구현에 의존하게 되면 새로운 쿠폰 구현이 추가되거나 변경될 때마다 가격 계산 모듈이 변경되는 상황이 발생된다.&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;즉, DIP는 LSP와 OCP를 따르는 설계를 만들어 주는 기반이 된다.&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;추가로 DIP는 런타임에서의 의존이 아닌 소스 코드의 의존을 역전시킴으로써 변경의 유연함을 확보할 수 있도록 만들어주는 원칙이다.&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;b&gt;정리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SOLID 원칙을 한 마디로 정의하자면 &lt;b&gt;변화에 유연하게 대처할 수 있는 설계 원칙&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRP와 ISP는 객체가 커지지 않도록 막아준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OCP는 변화되는 부분을 추상화하고 다형성을 이용함으로써 기능 확장을 하면서 동시에 기존 코드를 수정하지 않도록 만들어준다. 여기서 변화되는 부분을 추상화할 수 있도록 도와주는 것이 DIP이고 다형성을 도와주는 것이 LSP이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 사용자 입장에서의 기능 사용을 중시하기 때문에 사용자 관점에서의 설계를 지향한다.&lt;/p&gt;</description>
      <category>도서/개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴</category>
      <category>객체 지향과 디자인 패턴</category>
      <author>에리카네스</author>
      <guid isPermaLink="true">https://erikanes.tistory.com/554</guid>
      <comments>https://erikanes.tistory.com/554#entry554comment</comments>
      <pubDate>Tue, 27 Feb 2024 18:55:07 +0900</pubDate>
    </item>
    <item>
      <title>객체 지향 프로그래밍의 특징</title>
      <link>https://erikanes.tistory.com/553</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;절차 지향과 객체 지향의 차이&lt;/b&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;절차 지향 프로그램은 다수의 프로시저들이 데이터를 공유하는 방식으로 만들어지기 때문에 자연스럽게 데이터를 중심으로 구현하게 된다. 그러다보니 프로그램의 규모가 커졌을 때, 프로시저간에 서로 공유되는 데이터의 타입이 변경되거나 데이터를 서로 다른 의미로 사용하고 있다면 코드의 유지 보수가 매우 어려워지기 시작한다.&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;(캡슐화)&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;b&gt;객체가 어떤 데이터를 가지고 있는지&lt;/b&gt;가 아니라 &lt;b&gt;객체가 어떤 메세지를 주고 받는가&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;객체 지향 프로그래밍의 4가지 특징&lt;/b&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;b&gt;캡슐화&lt;/b&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;&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;그래서 캡슐화를 위한 2가지 규칙을 숙지하면 캡슐화된 코드를 작성하는 데 도움이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; border: #c1c1c1 1px solid; padding: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;◾ &lt;b&gt;Tell, Don't Ask.&lt;/b&gt; 데이터를 요구하지 말고 기능을 처리해 달라고 요청한다.&lt;/p&gt;
&lt;pre id=&quot;code_1708509492876&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (REGULAR == acc.getMembership()) { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 데이터를 받아서 처리하면 동일한 코드가 많아질 수록 기능변경시 문제가 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1708509710262&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (acc.hasRegularPermission()) { ... }&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;◾ &lt;b&gt;Demeter's Law.&lt;/b&gt; &lt;span style=&quot;background-color: #eeeeee; color: #333333; text-align: start;&quot;&gt;해당 객체의 메소드만 호출해야 한다.&lt;/span&gt; 해당 객체가 어떤 데이터를 가지고 있는지 몰라야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(메서드에서 생성한 객체, 파라미터로 받은 객체, 멤버로 참조하는 객체)&lt;/p&gt;
&lt;pre id=&quot;code_1708509943657&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;acc.getExpData().isAfter(now);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 코드는 호출하는 객체의 내부 데이터 구조를 알기때문에 작성할 수 있는 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1708510080301&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;acc.isExpired();&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;디미터 법칙을 어기는 전형적인 증상은 연속된 get 메서드를 호출하거나 임시 변수의 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;참고로 컨테이너와 같은 자료 구조의 경우에는 적용할 필요가 없다.&lt;/p&gt;
&lt;/div&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;b&gt;상속&lt;/b&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;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; border: #c1c1c1 1px solid; padding: 10px;&quot;&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;상위 클래스를 변경하면 의존하는 하위 클래스들도 영향을 받기 때문에 시간이 지날수록 하나의 거대한 단일 구조처럼 만들어지는 결과가 발생할 수 있다.&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;◾ 클래스의 불필요한 증가가 발생한다&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;&amp;nbsp;&lt;/p&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;예를 들어 STL 컨테이너를 상속 받아서 구현하는 경우에 필요하지 않은 메소드들까지 모두 상속될 뿐만 아니라 외부에서 잘못 사용할 여지가 생긴다.&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;상속 대신 컴포지션으로 구현하면 단점을 많이 상쇄시킬 수 있다.&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;재사용이라는 관점이 아니라 기능의 확장이라는 관점에서 사용하는 것이 좋다. 그리고 명확한 IS-A 관계일 때 적용하는 것이 옳다. 즉, 명확한 IS-A 관계에서 기능을 점진적으로 확장시켜 나가고 싶을 때 사용하면 된다.&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;b&gt;다형성&lt;/b&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;구현 상속은 보통 상위 클래스에 정의된 기능을 재사용하기 위한 목적으로 사용되고 인터페이스 상속은 기능의 구현을 강제하기 위한 목적으로 사용된다.&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;b&gt;추상화&lt;/b&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구 사항이 바뀔 때 변화하는 부분을 추상화시키면 향후 유연하게 대처할 수 있는 가능성이 높아진다.&lt;/p&gt;</description>
      <category>도서/개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴</category>
      <category>객체 지향과 디자인 패턴</category>
      <author>에리카네스</author>
      <guid isPermaLink="true">https://erikanes.tistory.com/553</guid>
      <comments>https://erikanes.tistory.com/553#entry553comment</comments>
      <pubDate>Wed, 21 Feb 2024 20:04:42 +0900</pubDate>
    </item>
    <item>
      <title>최장 공통 부분 수열 (LCS)</title>
      <link>https://erikanes.tistory.com/551</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Longest Common Subsequence. 두 수열이 주어졌을 때, 모두의 부분 수열이 되는것 중에 가장 긴 것을 찾는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최장 공통 문자열의 개념에서 조금 확장시키면 어렵지 않게 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점화식을 사용하는 DP의 일종이다.&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;두 문자열을 한글자씩 비교한다.&lt;/li&gt;
&lt;li&gt;두 문자가 다르면 LCS[i][j]에 0을 표시한다.&lt;/li&gt;
&lt;li&gt;두 문자가 같으면 LCS[i-1][j-1]에 1을 더한다.&lt;/li&gt;
&lt;li&gt;위의 과정을 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684567126918&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// i, j는 1부터
if (str1[i-1] == str2[j-1])
    LCS[i][j] = LCS[i-1][j-1] + 1;
else
    LCS[i][j] = 0;&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;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;pre id=&quot;code_1684567234122&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (str1[i-1] == str2[j-1])
    LCS[i][j] = LCS[i-1][j-1] + 1;
else
    LCS[i][j] = max(LCS[i][j-1], LCS[i-1][j]);&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;LCS[i][j-1], LCS[i-1][j]가 현재 문자를 비교하는 과정의 이전 과정인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길이만 구하는 경우라면 LCS[str1.size()][str2.size()]로 구할 수 있고 실제 문자열이 필요한 것이라면 LCS배열의 가장 끝에서부터 0을 만날때까지 역순으로 추적한 뒤에 문자열을 뒤집으면 된다.&lt;/p&gt;</description>
      <category>알고리즘/공부</category>
      <author>에리카네스</author>
      <guid isPermaLink="true">https://erikanes.tistory.com/551</guid>
      <comments>https://erikanes.tistory.com/551#entry551comment</comments>
      <pubDate>Sat, 20 May 2023 16:43:50 +0900</pubDate>
    </item>
  </channel>
</rss>