<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>코딩수첩</title>
    <link>https://codingnotes.tistory.com/</link>
    <description>  호기심 많은 서버 개발자 길민호입니다.</description>
    <language>ko</language>
    <pubDate>Fri, 26 Jun 2026 17:51:02 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>길민호(ethan.mino)</managingEditor>
    <image>
      <title>코딩수첩</title>
      <url>https://tistory1.daumcdn.net/tistory/4006013/attach/1e85559ea52f415d95ba18278ee03ff1</url>
      <link>https://codingnotes.tistory.com</link>
    </image>
    <item>
      <title>Mac OS LogStash 특정 버전 설치</title>
      <link>https://codingnotes.tistory.com/240</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;다운로드&lt;/h3&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;brew를 이용한 방법도 있지만, 제가 필요한 버전은 지원을 하지 않더라구요.&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;s&gt;Logstash&lt;/s&gt;를 설치해줍니다. &lt;a href=&quot;https://www.elastic.co/kr/downloads/logstash&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Logstash 홈페이지&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1682061908031&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;Download Logstash Free | Get Started Now&quot; data-og-description=&quot;Download Logstash or the complete Elastic Stack (formerly ELK stack) for free and start collecting, searching, and analyzing your data with Elastic in minutes.&quot; data-og-host=&quot;www.elastic.co&quot; data-og-source-url=&quot;https://www.elastic.co/kr/downloads/logstash&quot; data-og-url=&quot;https://www.elastic.co/kr/downloads/logstash&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dRdOv3/hySlAH8nTJ/x7GOUwQafc8GL84KwboMck/img.png?width=1600&amp;amp;height=837&amp;amp;face=0_0_1600_837,https://scrap.kakaocdn.net/dn/OqXdq/hySlJZlIzw/YQKK1TdkgYprf2Ehxsb061/img.png?width=1600&amp;amp;height=837&amp;amp;face=0_0_1600_837,https://scrap.kakaocdn.net/dn/8UUmL/hySlLbPa3X/YJCK6zApTt0wJqIDRYZgz0/img.png?width=1438&amp;amp;height=839&amp;amp;face=0_0_1438_839&quot;&gt;&lt;a href=&quot;https://www.elastic.co/kr/downloads/logstash&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.elastic.co/kr/downloads/logstash&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dRdOv3/hySlAH8nTJ/x7GOUwQafc8GL84KwboMck/img.png?width=1600&amp;amp;height=837&amp;amp;face=0_0_1600_837,https://scrap.kakaocdn.net/dn/OqXdq/hySlJZlIzw/YQKK1TdkgYprf2Ehxsb061/img.png?width=1600&amp;amp;height=837&amp;amp;face=0_0_1600_837,https://scrap.kakaocdn.net/dn/8UUmL/hySlLbPa3X/YJCK6zApTt0wJqIDRYZgz0/img.png?width=1438&amp;amp;height=839&amp;amp;face=0_0_1438_839');&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;Download Logstash Free | Get Started Now&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Download Logstash or the complete Elastic Stack (formerly ELK stack) for free and start collecting, searching, and analyzing your data with Elastic in minutes.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.elastic.co&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 버전을 설치하려면, &lt;a href=&quot;https://www.elastic.co/kr/downloads/logstash&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Detailed release notes&lt;/a&gt;에서 원하는 버전을 설치해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;압축 해제&lt;/h3&gt;
&lt;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;설치된 tar.gz 파일을 압축 해제 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더블 클릭 또는 아래 명령어를 실행해서 압축 해제할 수 있어요.&lt;/p&gt;
&lt;pre id=&quot;code_1682062116223&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tar -xzf ${SOURCE_DIR}/${LOGSTASH_FILE_NAME} -C ${DESTINATION_DIR}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;SOURCE_DIR&lt;/s&gt; : 다운로드 받은 Logstash 파일의 path&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;LOGSTASH_FILE_NAME&lt;/s&gt; : 다운로드 받은 Logstash 파일명&lt;/li&gt;
&lt;li&gt;&lt;s&gt;DESTINATION_DIR&lt;/s&gt; : 압축 해제한 Logstash가 위치할 path&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PATH 설정&lt;/h3&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;logstash 명령어를 사용하기 위해 path를 설정해줍니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1682063072231&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export PATH=&quot;$LOGSTASH_PATH/bin:$PATH&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음, 터미널을 재실행 하거나, source ${SHELL_CONFIG_PATH}을 통해 shell 설정을 리로드할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1682064039215&quot; class=&quot;ada&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;logstash --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로, logstash의 version을 출력해보았을 때 아래와 같이 출력되면 성공입니다!&lt;/p&gt;
&lt;pre id=&quot;code_1682064031782&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Using bundled JDK: /usr/local/logstash-x.x.x/jdk.app/Contents/Home
logstash x.x.x&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>설치</category>
      <author>길민호(ethan.mino)</author>
      <guid isPermaLink="true">https://codingnotes.tistory.com/240</guid>
      <comments>https://codingnotes.tistory.com/240#entry240comment</comments>
      <pubDate>Fri, 21 Apr 2023 17:01:14 +0900</pubDate>
    </item>
    <item>
      <title>싱글턴 패턴 (Singleton Pattern)이란?</title>
      <link>https://codingnotes.tistory.com/239</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;싱글턴 패턴이란?&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;특정 클래스의 인스턴스가 하나만 만들어지도록 해 주는 패턴&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 &lt;s&gt;싱글턴&lt;/s&gt; 패턴을 사용하면, 하나의 인스턴스만 생성되도록 할 수 있습니다. 설정 객체, Connection Pool, Thread Pool 등과 같이 하나의 인스턴스만 필요한 경우, 여러 개의 인스턴스가 생성되지 않도록 해서 자원이 불필요하게 사용되지 않도록 할 수 있는거죠.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;고전적 싱글턴 패턴&lt;/h2&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;고전적인 싱글턴 구현은 간단합니다. 생성자의 접근 제어자(Access Modifier)를 &lt;s&gt;private&lt;/s&gt;로 하여, 외부에서 접근하지 못하도록 하고, getInstance()와 같은 메서드를 통해 정해진 인스턴스를 반환하도록 하면 돼요.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Singleton {
	// 하나뿐인 인스턴스를 저장하는 static 변수
    private static Singleton uniqueInstance;
		
	// private 생성자, 클래스 내부에서만 접근 가능하다.
    private Singleton() {
    }

    public static Singleton getInstance(){
        if(Objects.isNull(uniqueInstance)){
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}&lt;/code&gt;&lt;/pre&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;s&gt;private&lt;/s&gt;, 따라서 클래스 내부에서만 인스턴스를 생성할 수 있음&lt;/li&gt;
&lt;li&gt;인스턴스를 저장하는 레퍼런스 변수와 하나뿐인 인스턴스를 반환하는 메서드는 &lt;s&gt;static&lt;/s&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;이 메서드를 호출할 때 인스턴스가 생성되도록 하여, 불필요한 자원 낭비 방지&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Test{
    @Test
    @DisplayName(&quot;싱글스레드 환경에서 싱글턴 인스턴스는 하나만 생성되어야 한다.&quot;)
    void test1(){
        // Given
        Set&amp;lt;Singleton&amp;gt; singletonSet = new HashSet&amp;lt;&amp;gt;();

        // When
        IntStream.range(0, 1000)
                .forEach((index) -&amp;gt; singletonSet.add(Singleton.getInstance()));

        // Then
        Assertions.assertEquals(1, singletonSet.size());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 테스트 코드는, 싱글스레드 환경에서 싱글턴 인스턴스를 1000번 생성했을 때, 몇 개의 인스턴스가 생성되는지 확인하는 테스트입니다. 테스트 결과는 성공으로, 예상대로 1개의 인스턴스만 생성되어요.&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;u&gt;멀티스레드 환경에서 여러 개의 인스턴스가 생성될 수 있다는 것&lt;/u&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Test{
    @Test
    @DisplayName(&quot;멀티스레드 환경에서 싱글턴 인스턴스는 하나만 생성되어야 한다.&quot;)
    void test1() throws Exception{
        // Given
        Map&amp;lt;String, Singleton&amp;gt; singletonHashMap = new ConcurrentHashMap&amp;lt;&amp;gt;();
        var executorService = Executors.newFixedThreadPool(10);

        // When
        IntStream.range(0, 3).forEach((index) -&amp;gt; executorService.submit(() -&amp;gt;{
            Singleton instance = Singleton.getInstance();
            singletonHashMap.put(instance.toString(), instance);
        }));

        executorService.awaitTermination(500, TimeUnit.MICROSECONDS);

        // Then
        Assertions.assertEquals(1, singletonHashMap.size());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 크기가 멀티스레드 환경에서 Singleton 인스턴스를 3번 생성해서 &lt;s&gt;ConcurrentHashMap&lt;/s&gt;에 삽입하는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과, &lt;u&gt;2개의 싱글턴 인스턴스가 생성&lt;/u&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;getInstance()&lt;/s&gt; &lt;u&gt;메서드에 진입하여 경합을 벌이는 과정에서 서로 다른 두 개의 인스턴스가 만들어질 수 있는거죠.&lt;/u&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;expected: &amp;lt;1&amp;gt; but was: &amp;lt;2&amp;gt;
Expected :1
Actual   :2
&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1680617492150&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  스레드의 동작에 따라 결과가 달라집니다. 하나의 인스턴스만 생성되는 경우도 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;Thread-Safe Singleton&lt;/span&gt;&lt;/h2&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드가 경합을 벌였을 때, 문제가 발생하는 메서드를 동기화하면 멀티스레딩과 관련된 문제가 해결됩니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Singleton{
    ...
    public static synchronized Singleton getInstance(){
        if(Objects.isNull(uniqueInstance)){
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 getInstance() 메서드에 &lt;s&gt;synchronized&lt;/s&gt; 키워드를 붙여주었는데요,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 키워드를 메서드에 붙여주면 이 메서드는 &lt;s&gt;임계 영역(Critical Section)&lt;/s&gt;이 되어서, 한 번에 하나의 스레드만 실행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, &lt;u&gt;동기화로 인해 성능이 저하&lt;/u&gt;되며, 심지어 인스턴스가 생성된 후부터는 동기화가 필요하지 않으므로, &lt;u&gt;불필요한 오버헤드가 증가&lt;/u&gt;합니다. 따라서 getInstance()가 병목으로 작용할 수도 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, DCL 방식에서 살펴보겠지만, &lt;u&gt;이 방식도 완전히 Thread-safe 하지는 않아요.&lt;/u&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;span data-token-index=&quot;0&quot;&gt;Eager Initialization (Early Loading)&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Singleton{
    private static final Singleton uniqueInstance = new Singleton()
    public static Singleton getInstance(){
        return uniqueInstance;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;정적변수&lt;/s&gt;의 경우 &lt;u&gt;클래스가 로딩될 때 초기화&lt;/u&gt; 되며, 정적변수가 초기화되기 전까지 어떤 스레드도 정적 변수에 접근할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;u&gt;멀티스레드 환경이라도 서로 다른 싱글턴 인스턴스가 생성되지 않습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 클래스가 로딩될 때 초기화되므로, &lt;u&gt;인스턴스가 필요하지 않은 경우에도 미리 인스턴스를 생성하기 때문에 메모리를 낭비&lt;/u&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;그렇다면, 정적변수는 Application이 실행될 때 초기화 된다는 걸까요?&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Singleton {
    private static final Singleton uniqueInstance;
    static {
        uniqueInstance = new Singleton()
        System.out.println(&quot;정적 변수 초기화&quot;);
    }

    private Singleton() {
    }

    public static Singleton getInstance(){
        return uniqueInstance;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;public class Application {
    public static void main(String[] args) {
        IntStream.range(0, 5)
                .forEach(System.out::println);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Application의 출력은 어떨까요? &amp;ldquo;정적 변수 초기화&amp;rdquo;라는 문자열이 출력될까요?&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;0
1
2
3
4
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Singleton 클래스의 &lt;s&gt;static block&lt;/s&gt;에서 싱글턴 인스턴스를 초기화하고 있음에도, &amp;ldquo;정적 변수 초기화&amp;rdquo;라는 문자열이 출력되지 않았습니다. 즉, 클래스는 인스턴스 생성, static method 호출, 변수 접근, Class.forName() 등으로 인해 클래스가 로딩되는데, 현재는 &lt;s&gt;Singleton&lt;/s&gt; 클래스에 대한 접근이 없기 때문에 클래스가 로딩되지 않는거고, 그에 따라 uniqueInstance가 초기화 되지 않는겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼, 인스턴스가 필요하지 않는 경우에도 인스턴스가 미리 생성된다는 게 무슨 말이야..?&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Singleton {
    private final static Singleton uniqueInstance;

    static {
        System.out.println(&quot;정적 변수 초기화&quot;);
        uniqueInstance = new Singleton();
    }

    private Singleton() {
    }

    public static void noOperation(){
    }

    public static Singleton getInstance(){
        return uniqueInstance;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Application {
    public static void main(String[] args) {
        Singleton.noOperation();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;정적 변수 초기화
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Singleton 클래스의 static block이 실행되었습니다. 이유가 뭘까요??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application의 main()에서 Singleton의 정적 메서드 &lt;s&gt;noOperation()&lt;/s&gt;을 호출하고 있기 때문에, Singleton 클래스가 로드되면서 uniqueInstance까지 초기화가 되는거죠. 따라서 &lt;u&gt;싱글턴 인스턴스가 사용되지 않음에도 자원을 낭비&lt;/u&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;span data-token-index=&quot;0&quot;&gt;Bill Pugh Solution (&lt;/span&gt;Initialization-on-demand holder idiom)&lt;/h2&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;s&gt;holder&lt;/s&gt;를 이용하여 싱글턴 인스턴스를 초기화하는 방법이에요.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Singleton {
    private Singleton() {
    }

    private static Singleton getIntance(){
        return SingletonHolder.INSTANCE;
    }

    public static void noOperation(){
        System.out.println(&quot;noOperation call&quot;);
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE;
        static {
            INSTANCE = new Singleton();
            System.out.println(&quot;정적 객체 초기화&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Application {
    public static void main(String[] args) {
        Singleton.noOperation();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;noOperation call
&lt;/code&gt;&lt;/pre&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;Singleton 클래스 내부의 &lt;s&gt;SingletonHolder&lt;/s&gt; (private static class)&lt;/li&gt;
&lt;li&gt;SingletonHolder의 INSTANCE 정적 변수&lt;/li&gt;
&lt;li&gt;getInstance()의 return SingletonHolder.INSTANCE&lt;/li&gt;
&lt;/ol&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;s&gt;Lazy Loading&lt;/s&gt;이 가능하다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;위 출력을 보면, Singleton의 noOperation()을 호출했음에도 싱글턴 인스턴스가 생성되지 않았어요. 오직 getInstance()를 통해서만 &lt;s&gt;SingletonHolder&lt;/s&gt; 클래스가 로드되고, 싱글턴 인스턴스가 생성됩니다.&lt;/li&gt;
&lt;li&gt;따라서, &lt;u&gt;인스턴스가 불필요하게 생성되는 것을 방지&lt;/u&gt;할 수 있어요.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;클래스가 로드될 때 싱글턴 인스턴스가 생성되므로, &lt;s&gt;Thread-safe&lt;/s&gt; 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DCL(Double-Checked Locking)을 이용한 싱글턴 패턴&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1680617895539&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;⚠️ 자바5 이전 버전에서는 DCL이 제대로 동작하지 않습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 패턴은 instance가 null인지 두 번 check 하기 때문에 &lt;s&gt;Double Checked Locking&lt;/s&gt;이라고 해요.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Singleton {
    private volatile static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance(){
        if(Objects.isNull(instance)){    // Single Checked
            synchronized (Singleton.class){
                if(Objects.isNull(instance)){    // Double Checked
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}&lt;/code&gt;&lt;/pre&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;getInstance()에서 두 번의 instance 정적 변수 null check&lt;/li&gt;
&lt;li&gt;안쪽 null check 코드의 &lt;s&gt;synchronized 블록&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;instance 정적 변수의 &lt;s&gt;volatile&lt;/s&gt; 키워드&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 두 번의 null check이 필요할까요?&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public static Singleton getInstance(){
	  if(Objects.isNull(instance)){    // Single Checked
        instance = new Singleton();
    }
    return instance;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 위 코드에서 스레드1과 스레드2가 동시에 첫 번째 null check을 수행하는 경우, 두 스레드 모두 if문을 통과하고, 각각의 스레드가 Singleton 인스턴스를 생성하는 것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 아래 코드는?&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public static Singleton getInstance(){
    synchronized(Singleton.class){
        if(Objects.isNull(instance)){    // Single Checked
            instance = new Singleton();
        }
    }
	  
    return instance;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, getInstance()를 호출하는 모든 스레드가 synchronized 블록을 실행해야하기 때문에 &lt;u&gt;성능이 저하&lt;/u&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글턴 인스턴스가 생성된 이후 또 다른 스레드가 getInstance()를 호출했을 때, synchronized 블록을 수행할 필요 없이 빠르게 instance를 반환 받도록 하기 위해 Double Check을 하는 것이죠.&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;그럼에도 getInstance() 메서드는 instance 정적 변수를 &lt;s&gt;volatile&lt;/s&gt;로 만들지 않으면 제대로 동작하지 않아요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 왜 싱글턴 인스턴스를 volatile로 선언해야할까요?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vM5s5/btr753hlHhB/r7NRubko7vMutk8J8V9p51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vM5s5/btr753hlHhB/r7NRubko7vMutk8J8V9p51/img.png&quot; data-alt=&quot;Java memory&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vM5s5/btr753hlHhB/r7NRubko7vMutk8J8V9p51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvM5s5%2Fbtr753hlHhB%2Fr7NRubko7vMutk8J8V9p51%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;600&quot; height=&quot;509&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;417&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Java memory&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼, 스레드는 &lt;s&gt;working 메모리&lt;/s&gt;를 가지고 있어요.&lt;/p&gt;
&lt;pre id=&quot;code_1680617976431&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  여기서 working memory는 각 스레드마다 독립적으로 할당되는 메모리 영역으로, Cpu Cache 메모리와는 다름.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, main 메모리와 working 메모리 간의 데이터 이동이 있기 때문에 메모리 간에 동기화가 진행되는 동안 빈틈이 생겨요. 그래서 volatile을 쓰지 않는 Double Checked Locking 방식은 아래와 같은 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;스레드 1이 instance를 생성하고 synchronized 블록을 벗어남.&lt;/li&gt;
&lt;li&gt;스레드2가 synchronized 블록을 null check을 수행하는 시점에 스레드1이 생성한 instance가 working 메모리에만 존재&lt;/li&gt;
&lt;li&gt;스레드2가 인스턴스를 생성&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;volatile&lt;/s&gt; 키워드가 선언된 변수는 캐시 메모리를 사용하지 않고, 항상 main 메모리에서 직접 읽고 쓰기 때문에 싱글턴 인스턴스에 volatile 키워드를 선언해주어야 해요. 하지만, &lt;u&gt;volatile 키워드로 선언하면 항상 메인 메모리에서 읽거나 쓰기 때문에 성능 저하가 발생할 수 있습니다.&lt;/u&gt;&lt;u&gt;&lt;/u&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Enum을 이용한 방식&lt;/h2&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;s&gt;Enum&lt;/s&gt;을 이용하면, 싱글턴 구현이 굉장히 간단합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, Java에서 모든 enum은 프로그램에서 한 번만 인스턴스 화되도록 보장하기 때문에 &lt;s&gt;Thread-safe&lt;/s&gt; 합니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;class Status{
    private final int value;

    Status(int value) {
        this.value = value;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public enum EnumSingleton {
    INSTANCE(1, 2);

    private final Status state1;
    private final Status state2;

    // enum 생성자의 기본 접근 제어자(Access Modifier)는 private
    EnumSingleton(int statusValue1, int statusValue2){
        state1 = new Status(statusValue1);
        state2 = new Status(statusValue2);
    }

    public void doSomething(){
        System.out.println(&quot;doSomething&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Application{
    public static void main(String[] args) {
        EnumSingleton instance = EnumSingleton.INSTANCE;
        instance.doSomething();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;doSomething
&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;b&gt;Pros&lt;/b&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;Thread-safe 하다.&lt;/li&gt;
&lt;li&gt;&lt;s&gt;직렬화/역직렬화&lt;/s&gt;에 대한 처리가 필요없다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cons&lt;/b&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;s&gt;lazy Loading&lt;/s&gt;이 아니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직렬화/역직렬화를 왜 처리해주어야 하나에 대해 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;s&gt;Early Loading&lt;/s&gt; 방식의 Singleton 클래스가 &lt;s&gt;Serializable&lt;/s&gt; 인터페이스를 구현하도록 하고, &lt;s&gt;serialVersionUID&lt;/s&gt;(직렬화 프로토콜의 고유 식별자)를 추가해주었습니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Singleton implements Serializable {
    private static final long serialVersionUID = 123456789L;

    private static Singleton INSTANCE = new Singleton();
    private Singleton() {
    }

    public static Singleton getInstance(){
        return INSTANCE;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Test{
	@Test
    @DisplayName(&quot;싱글턴 클래스에서 readResolve()를 정의하지 않으면, 싱글턴 인스턴스를 역직렬화할 때 새로운 인스턴스가 생성된다.&quot;)
    void test4() throws IOException, ClassNotFoundException {
        // Given
        Singleton serializedInstance = Singleton.getInstance();
        final String fileName = &quot;output.txt&quot;;

        // When
        // 직렬화
        try(var out = new ObjectOutputStream(new FileOutputStream(fileName))){
            out.writeObject(serializedInstance);
        }

        // 역직렬화
        Singleton deSerializedInstance = null;
        try(var in = new ObjectInputStream(new FileInputStream(fileName))){
            deSerializedInstance = (Singleton) in.readObject();
        }

        // Then
        Assertions.assertNotEquals(serializedInstance.hashCode(), deSerializedInstance.hashCode());
    }
}&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;s&gt;hashCode&lt;/s&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;이것을 해결하려면, Singleton 클래스에 &lt;s&gt;readResolve()&lt;/s&gt;를 추가해주면 돼요. 그럼 인스턴스를 역직렬화해도 새로운 인스턴스가 생성되지 않아요.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;public class Singleton implements Serializable {
    private static final long serialVersionUID = 123456789L;

    private static Singleton INSTANCE = new Singleton();
    private Singleton() {
    }

    public static Singleton getInstance(){
        return INSTANCE;
    }

    public Object readResolve(){
        return getInstance();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, enum은 어떨까요?&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Test{
    @Test
    @DisplayName(&quot;enum은 싱글턴 인스턴스를 역직렬화해도 새로운 인스턴스가 생성되지 않는다.&quot;)
    void test5() throws IOException, ClassNotFoundException {
        // Given
        EnumSingleton serializedInstance = EnumSingleton.INSTANCE;
        final String fileName = &quot;output.txt&quot;;

        // When
        // 직렬화
        try(var out = new ObjectOutputStream(new FileOutputStream(fileName))){
            out.writeObject(serializedInstance);
        }

        // 역직렬화
        EnumSingleton deSerializedInstance = null;
        try(var in = new ObjectInputStream(new FileInputStream(fileName))){
            deSerializedInstance = (EnumSingleton) in.readObject();
        }

        // Then
        Assertions.assertEquals(serializedInstance.hashCode(), deSerializedInstance.hashCode());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 결과는 성공으로, 역직렬화 해도 새로운 인스턴스가 생성되지 않아요.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;싱글턴 패턴의 장점&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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;한 개의 인스턴스만 생성되기 때문에, 메모리 공간을 절약할 수 있다.&lt;/li&gt;
&lt;li&gt;전역적으로 접근할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;싱글턴 패턴의 문제점&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Reflection을 통해 인스턴스 생성이 가능하다.&lt;/h4&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Test{
	@Test
    @DisplayName(&quot;Reflection을 이용하여 싱글턴 클래스의 인스턴스를 직접 생성할 수 있다.&quot;)
    void test3() throws Exception {
        Singleton instance = Singleton.getInstance();

        Constructor[] constructors = Singleton.class.getDeclaredConstructors();

        for (Constructor constructor : constructors) {
            constructor.setAccessible(true); // singleton breaker
            Assertions.assertNotEquals(instance, constructor.newInstance());
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 Reflection을 이용하여 싱글턴 클래스의 인스턴스를 직접 생성하는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 싱글턴 클래스의 접근 제어자(Access Modifier)가 private라도, &lt;u&gt;Reflection을 이용해서 Singleton 패턴을 무력화시킬 수 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인스턴스 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread-Safe와는 별개로, &lt;u&gt;클래스 로더를 어떻게 구성하고 있느냐에 따라서 싱글턴 클래스임에도 하나 이상의 오브젝트가 만들어질 수 있습니다.&lt;/u&gt; 따라서 &lt;u&gt;자바 언어를 이용한 싱글턴 패턴 기법은 서버 환경에서는 싱글톤이 꼭 보장된다고 볼 수 없습니다.&lt;/u&gt; 또한 여러 개의 &lt;u&gt;JVM에 분산되어 설치되는 경우에도 각각 독집적으로 오브젝트가 생기기 때문에 싱글턴으로서의 가치가 떨어집니다.&lt;/u&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;동기화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글턴 인스턴스의 상태가 변할 수 있는 경우, 동시에 접근하는 스레드 간 상호작용에 의해 예기치 않은 결과가 발생할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Test{
	@Test
    @DisplayName(&quot;가변 Singleton 인스턴스를 여러 스레드가 이용하는 경우, 동기화 이슈가 발생할 수 있다.&quot;)
    void test6() throws Exception{
        // Given
        var executorService = Executors.newFixedThreadPool(10);

        // When
        IntStream.range(0, 1000).forEach((index) -&amp;gt; executorService.submit(() -&amp;gt;{
            Count.getInstance().increase();
        }));
        executorService.awaitTermination(500, TimeUnit.MICROSECONDS);

        // Then
        int resultCount = Count.getInstance().getCount();
        System.out.println(resultCount);
        Assertions.assertNotEquals(1000, resultCount);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;resultCount : 522
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 멀티스레드 환경에서 Count 싱글턴 인스턴스의 값을 1000번 증가시키는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 결과 값은 522로 훨씬 낮은 값이 출력되는 것을 볼 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;u&gt;싱글턴이 멀티스레드 환경에서 사용되는 경우, 불변 객체로 만들어져야합니다.&lt;/u&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테스트가 어렵다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤 객체는 프로그램 전역에서 유일하게 하나의 객체만 존재하기 때문에 &lt;s&gt;Mock Object&lt;/s&gt;로 대체하기 어렵습니다. Mock Object는 보통 실제 객체와 같은 인터페이스를 구현하며, 테스트 시에는 Mock Object를 실제 객체 대신 사용하여 테스트를 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 싱글톤 객체는 유일하게 하나만 존재해야 하기 때문에 Mock Object로 대체할 수 없습니다. Mock Object로 대체하면 다른 객체에서 해당 싱글톤 객체를 참조하면서 예상치 못한 동작을 할 수 있기 때문입니다. 따라서, 싱글톤 객체를 사용하는 코드를 테스트하기 위해서는 다른 방법을 사용해야 합니다. 예를 들어, &lt;s&gt;의존성 주입(Dependency Injection)&lt;/s&gt; 등을 활용하여 싱글톤 객체에 대한 의존성을 주입하는 방법이 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;유연성이 떨어진다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글턴 인스턴스는 정적 필드로 선언되어 있기 때문에 런타임 시에 동적으로 변경되지 않습니다. 이로 인해 &lt;u&gt;싱글턴 클래스를 다른 클래스로 교체하거나, 여러 개의 인스턴스를 생성하는 것과 같은 일이 어렵습니다.&lt;/u&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;객체지향적이지 않다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무 객체나 자유롭게 접근하고 수정하고 공유할 수 있는 전역 상태를 갖는 것은 객체지향 프로그래밍에서는 지양되어야 할 모델이며, Java에서 Singleton은 private 생성자를 가지므로 &lt;u&gt;상속할 수 없습니다.&lt;/u&gt; 따라서 &lt;u&gt;다형성과 같은 객체지향의 특징을 적용하기 어렵습니다.&lt;/u&gt; 또한, &lt;u&gt;상속과 다형성 같은 객체지향의 특징이 적용되지 않는 스태틱 필드와 메소드를 사용해야 합니다.&lt;/u&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전역적 사용으로 인해, 프로그램 전체에 영향을 미친다.&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nqX84/btr72avxGZv/WXd3Joy7kCoy1z0DhVxTaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nqX84/btr72avxGZv/WXd3Joy7kCoy1z0DhVxTaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nqX84/btr72avxGZv/WXd3Joy7kCoy1z0DhVxTaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnqX84%2Fbtr72avxGZv%2FWXd3Joy7kCoy1z0DhVxTaK%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;700&quot; height=&quot;412&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글턴 클래스의 인스턴스는 전역 상태를 유지하기 때문에 &lt;u&gt;다른 객체와의 의존성이 높아집니다.&lt;/u&gt; &lt;u&gt;이는 코드의 유지보수를 어렵게 만들 수 있으며, 특히 테스트 코드 작성을 어렵게 만들 수 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;안티패턴으로 여겨지는가?&lt;/h2&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;/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;s&gt;Spring&lt;/s&gt;에서는 이러한 싱글턴의 단점을 보완해서 사용하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring의 &lt;s&gt;어플리케이션 컨텍스트&lt;/s&gt;는 싱글톤을 저장하고 관리하는 &lt;s&gt;싱글톤 레지스트리&lt;/s&gt;이기도 한데요, 스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈(Bean) 오브젝트를 모두 싱글톤으로 만듭니다. 하지만, 우리가 Spring에서 Service 빈을 싱글톤으로 작성했던 적이 있었나요? 없죠. 여기서 싱글턴이라는 것은 디자인 패턴의 싱글턴과 비슷한 개념이지만, 그 구현 방법이 다르기 때문이에요.&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;s&gt;싱글턴 레지스트리&lt;/s&gt;의 장점은 &lt;u&gt;스태틱 메서드와 private 생성자를 사용해야 하는 비정상적인 클래스가 아니라 평범한 자바 클래스를 싱글턴으로 활용하게 해줍니다.&lt;/u&gt; 평범한 자바 클래스라도 IoC 방식의 컨테이너를 사용해서 생성과 관계 설정, 사용 등에 대한 제어권을 컨테이너에게 넘기면 손십게 싱글턴 방식으로 만들어져 관리되게 할 수 있습니다. &lt;u&gt;오브젝트 생성에 관한 모든 권한은 IoC 기능을 제공하는 애플리케이션 컨텍스트에게 있기 때문&lt;/u&gt;입니다. 이 경우, &lt;u&gt;테스트 환경에서 자유롭게 오브젝트를 만들 수 있고, 테스트를 위한 Mock 오브젝트로 대체하는 것도 간단&lt;/u&gt;합니다. 또한, &lt;u&gt;생성자 파라미터를 이용해서 사용할 오브젝트를 넣어주게 할 수도 있습니다.&lt;/u&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;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Head First Design Pattern Chapter 5. 싱글턴 패턴&lt;/li&gt;
&lt;li&gt;토비의 3.1 VOL.1, 스프링 1.6 싱글턴 레지스트리와 오브젝트 스코프&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://yaboong.github.io/design-pattern/2018/09/28/thread-safe-singleton-patterns/&quot;&gt;디자인패턴 - 싱글톤 패턴&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html#axzz5TDnUaIpM&quot;&gt;Double Checked Locking on Singleton Class in Java - Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jvms/se6/html/Threads.doc.html&quot;&gt;Thread and Locks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://herdin.github.io/2020/12/25/about-double-check-locking&quot;&gt;Double-checked Locking Pattern (DCLP) 을 쓰지 말아야 하는 이유&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom#cite_note-4&quot;&gt;Initialization-on-demand holder idiom&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dzone.com/articles/all-about-the-singleton&quot;&gt;All About the Singleton&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.notion.so/ac505685abd54de5baeaa13820a313b2&quot;&gt;[Structure] 싱글톤 패턴과 문제점&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스터디</category>
      <author>길민호(ethan.mino)</author>
      <guid isPermaLink="true">https://codingnotes.tistory.com/239</guid>
      <comments>https://codingnotes.tistory.com/239#entry239comment</comments>
      <pubDate>Tue, 4 Apr 2023 23:10:00 +0900</pubDate>
    </item>
    <item>
      <title>데코레이터 패턴(Decorator Pattern)이란?</title>
      <link>https://codingnotes.tistory.com/238</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;s&gt;데코레이터 패턴(Decorator Pattern)&lt;/s&gt;으로 객체에 추가 요소를 동적으로 더할 수 있습니다. 데코레이터를 사용하면 서브 클래스를 만들 때보다 훨씬 유연하게 기능을 확장할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;커피 전문점&lt;/s&gt;의 &lt;s&gt;주문 시스템&lt;/s&gt;을 예로, 데코레이터 패턴을 단계 별로 설명드리겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1단계. 간단한 주문 시스템&lt;/h2&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1954&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/46Wjg/btr72XP7R5H/GqND3DCfpHA2xBxaz9r3k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/46Wjg/btr72XP7R5H/GqND3DCfpHA2xBxaz9r3k1/img.png&quot; data-alt=&quot;커피 전문점의 주문 시스템&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/46Wjg/btr72XP7R5H/GqND3DCfpHA2xBxaz9r3k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F46Wjg%2Fbtr72XP7R5H%2FGqND3DCfpHA2xBxaz9r3k1%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;700&quot; height=&quot;292&quot; data-origin-width=&quot;1954&quot; data-origin-height=&quot;816&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;커피 전문점의 주문 시스템&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;Beverage&lt;/s&gt; 추상 클래스가 있고, 매장에서 판매되는 모든 음료는 이 클래스의 서브 클래스가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, &lt;s&gt;Beverage&lt;/s&gt; 추상 클래스에는 getDescription(), getCost(), getName()이라는 &lt;s&gt;추상 메서드&lt;/s&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;s&gt;getDescription()&lt;/s&gt;은 &amp;ldquo;가장 훌륭한 다크 로스트 커피&amp;rdquo;와 같은 음료 설명을 반환하는 메서드이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;getCost()&lt;/s&gt;는 음료의 가격을 반환하는 메서드,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;getName()&lt;/s&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;s&gt;첨가물&lt;/s&gt;을 추가할 수 없습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2단계. 첨가물 추가가 가능한 주문 시스템&lt;/h2&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1749&quot; data-origin-height=&quot;1426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lMLNS/btr73PYmLxI/sdCVhrFr5kX8EkHOH3zcnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lMLNS/btr73PYmLxI/sdCVhrFr5kX8EkHOH3zcnk/img.png&quot; data-alt=&quot;첨가물을 고려한 주문 시스템&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lMLNS/btr73PYmLxI/sdCVhrFr5kX8EkHOH3zcnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlMLNS%2Fbtr73PYmLxI%2FsdCVhrFr5kX8EkHOH3zcnk%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;700&quot; height=&quot;571&quot; data-origin-width=&quot;1749&quot; data-origin-height=&quot;1426&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;첨가물을 고려한 주문 시스템&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구현할 경우, 매우 복잡한 구조가 만들어집니다.&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;s&gt;인스턴스 변수&lt;/s&gt;와 &lt;s&gt;슈퍼 클래스 상속&lt;/s&gt;을 써서 첨가물을 관리하도록 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(단, 아직 첨가물은 우유, 두유, 휘핑크림, 모카만 가능하다고 가정합니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;1568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Re5FT/btr74fWOChD/qQV11zcOZ8PxxxRubL5kk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Re5FT/btr74fWOChD/qQV11zcOZ8PxxxRubL5kk0/img.png&quot; data-alt=&quot;Beverage 클래스의 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Re5FT/btr74fWOChD/qQV11zcOZ8PxxxRubL5kk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRe5FT%2Fbtr74fWOChD%2FqQV11zcOZ8PxxxRubL5kk0%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;200&quot; height=&quot;431&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;1568&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Beverage 클래스의 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼, &lt;s&gt;Beverage&lt;/s&gt; 클래스가 이러한 구조로 변합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멤버변수로 첨가물의 첨가 여부를 나타내는 milk, soy, whip, mocha가 추가되고, has첨가물(), set첨가물() 메서드가 추가되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, &lt;s&gt;Beverage&lt;/s&gt; 클래스의 getCost() 메서드는 추상 메서드로 정의하지 않고, 구현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;s&gt;Beverage&lt;/s&gt; 클래스의 getCost()는 첨가물의 가격을 계산하고, &lt;s&gt;서브클래스&lt;/s&gt;의 getCost()에서는 &lt;s&gt;Beverage&lt;/s&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;Beverage 추상 클래스의 코드는 이렇습니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public abstract class Beverage {
    boolean milk;
    boolean soy;
    boolean mocha;
    boolean whip;

    public Beverage(){
        this.milk = false;
        this.soy = false;
        this.mocha = false;
        this.whip = false;
    }

    public abstract String getDescription();
    public abstract String getName();

    public Double getCost(){
        double condimentCost = 0;
        if(hasMilk()){
            condimentCost += 0.25;
        }
        if(hasSoy()){
            condimentCost += 0.1;
        }
        if(hasWhip()){
            condimentCost += 0.15;
        }
        if(hasMocha()){
            condimentCost += 0.05;
        }
        return condimentCost;
    }

    public boolean hasMocha(){return milk;}

    public boolean hasWhip(){return whip;}

    public boolean hasSoy(){return soy;}

    public boolean hasMilk(){return milk;}

    public void setMilk(boolean milk) {this.milk = milk;}

    public void setSoy(boolean soy) {this.soy = soy;}

    public void setMocha(boolean mocha) {this.mocha = mocha;}

    public void setWhip(boolean whip) {this.whip = whip;}

    public void 기타_메서드들(){}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구현하는 경우, 앞서 모든 경우의 클래스를 만들었을 때 보다 클래스의 수가 현저히 적습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class DarkRoast{
	public int getCost(){
		return super.getCost() + 0.33;
	}
}
&lt;/code&gt;&lt;/pre&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;u&gt;첨가물 가격이 바뀔 때마다 기존 코드를 수정&lt;/u&gt;해야합니다.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;첨가물의 종류가 많아지면 새로운 메소드를 추가해야 하고, 슈퍼 클래스의 getCost() 메서드도 고쳐야 합니다.&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;새로운 음료가 출시될 수도 있습니다. 그 중에는 특정 첨가물이 들어가면 안 되는 음료도 있을 것입니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;예를 들어, &lt;s&gt;아이스 티&lt;/s&gt;는 여전히 hasWhip()을 상속 받게 됩니다.&lt;/li&gt;
&lt;li&gt;즉, &lt;u&gt;일부 서브 클래스에 적합하지 않은 행동이 추가&lt;/u&gt;되었습니다.&lt;/li&gt;
&lt;li&gt;&lt;s&gt;Duck&lt;/s&gt; 추상 클래스에 fly()를 추가하면서 서브 클래스인 RubberDuck이 fly() 상속받은 것과 같은 문제입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;s&gt;더블 모카&lt;/s&gt;를 주문할 수 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;상속&lt;/s&gt;이 강력하기는 하나,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;상속을 사용한다고 해서 무조건 유연하거나 관리하기 쉬운 디자인이 만들어지지는 않습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브클래스를 만드는 방식으로 행동을 상속 받으면, 그 행동은 &lt;s&gt;컴파일 시점&lt;/s&gt;에 완전히 결정됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 모든 서브 클래스에서 똑같은 행동을 상속 받아야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3단계. 데코레이터 패턴을 이용한 주문 시스템&lt;/h2&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;s&gt;구성&lt;/s&gt;, &lt;s&gt;위임&lt;/s&gt;으로 &lt;u&gt;객체의 행동을 확장하면 실행 중에 동적으로 행동을 설정할 수 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, &lt;u&gt;객체를 동적으로 구성하면, 기존 코드를 고치는 대신 새로운 코드를 만들어서 기능을 추가&lt;/u&gt;할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;기존 코드는 건드리지 않기 때문에 코드 수정에 따른 버그나 의도하지 않은 부작용을 원천봉쇄&lt;/u&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;s&gt;OCP(Open-Closed Principle)&lt;/s&gt;는 정말 중요한 디자인 원칙 중 하나입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;디자인 원칙&lt;/b&gt;&lt;br /&gt;클래스는 &lt;s&gt;확장&lt;/s&gt;에는 열려 있어야 하지만, &lt;s&gt;변경&lt;/s&gt;에는 닫혀 있어야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리의 목표는 기존 코드를 건드리지 않고 확장으로 새로운 행동을 추가하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 배울 &lt;s&gt;데코레이터&lt;/s&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;u&gt;무조건 OCP를 적용한다면, 필요 이상으로 복잡하고 이해하기 힘든 코드를 만들게 되는 부작용이 발생할 수 있으니 주의&lt;/u&gt;해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;❓ 확장에는 열려 있고, 변경에는 닫혀 있다고요? 어떻게 2가지 조건을 동시에 만족할 수 있죠?&lt;/h4&gt;
&lt;pre id=&quot;code_1680615745910&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;✋ 코드를 변경하지 않아도 시스템을 확장하게 해주는 기발한 객체지향 기법은 많습니다. 
옵저버 패턴에서도, 옵저버를 새로 추가하면 subject에 코드를 추가하지 않으면서도 얼마든지 확장할 수 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;❓ 모든 부분에서 OCP를 준수하려면 어떻게 해야 하나요?&lt;/h4&gt;
&lt;pre id=&quot;code_1680615781174&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;✋ 보통 그렇게 하는 것은 불가능합니다. OCP를 준수하는 객체지향 디자인을 만드려면 적지 않은 시간과 노력이 필요합니다. 
디자인의 모든 부분을 깔끔하게 정돈할 만큼 여유가 있는 상황도 흔치 않습니다. (게다가 그렇게 할 필요가 없습니다.) 
OCP를 지키다 보면 새로운 단계의 추상화가 필요한 경우가 종종 있는데, 추상화를 하다보면 코드가 복잡해집니다. 
그래서 가장 바뀔 가능성이 높은 부분을 중점적으로 살펴보고 OCP를 적용하는 방법이 가장 좋습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;❓ 바뀌는 부분 중에서 OCP를 적용할만큼 중요한 부분을 어떻게 골라낼 수 있죠?&lt;/h4&gt;
&lt;pre id=&quot;code_1680615811072&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;✋ 객체지향 시스템 디자인 경험과 지금 건드리고 있는 분야의 지식이 많다면 쉽게 구분할 수 있습니다. 
여러 디자인을 살펴보면 바뀌는 부분 가운데 중요한 부분을 골라내는 안목이 높아집니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2단계에서는 문제점이 발견되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 단계에서는 음료를 첨가물로 &lt;s&gt;장식(decorate)&lt;/s&gt; 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 데코레이터의 구조를 먼저 살펴보면 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2390&quot; data-origin-height=&quot;1322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOavW3/btr76AeTyG9/eDSm4RbnCLozmwpCwKtq6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOavW3/btr76AeTyG9/eDSm4RbnCLozmwpCwKtq6K/img.png&quot; data-alt=&quot;데코레이터 패턴의 일반화된 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOavW3/btr76AeTyG9/eDSm4RbnCLozmwpCwKtq6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOavW3%2Fbtr76AeTyG9%2FeDSm4RbnCLozmwpCwKtq6K%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;700&quot; height=&quot;387&quot; data-origin-width=&quot;2390&quot; data-origin-height=&quot;1322&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데코레이터 패턴의 일반화된 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주문 시스템에서 &lt;s&gt;Component&lt;/s&gt;는 &lt;s&gt;Beverage&lt;/s&gt;를 의미하고, &lt;s&gt;Decorator&lt;/s&gt;는 첨가물을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;ConcreteComponentA/B&lt;/s&gt;는 DarkRoast와 같은 음료를 의미하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;ConcreateDecoratorA/B&lt;/s&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Component는 직접 쓰일 수도 있고, Decorator에 감싸여 쓰일 수도 있다.&lt;/li&gt;
&lt;li&gt;Decorator는 자신이 장식할 구성 요소(ex. DarkRoast)와 같은 &lt;s&gt;인터페이스&lt;/s&gt; 또는 &lt;s&gt;추상 클래스&lt;/s&gt;를 구현한다.&lt;/li&gt;
&lt;li&gt;Decorator는 자신이 장식할 Component 객체를 갖는다.&lt;/li&gt;
&lt;li&gt;각 ConcreteComponent는 Component를, 각 ConcreteDecorator는 Decorator를 확장한다.&lt;/li&gt;
&lt;li&gt;데코레이터가 새로운 메서드를 추가할 수도 있습니다. 하지만, 일반적으로 새로운 메소드를 추가하는 대신 Component에 있던 메서드를 별도의 작업으로 처리해서 새로운 기능을 추가합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단은 이러한 구조라는 것만 봐주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로, 주문 시스템에 Decorator 패턴을 적용한 모습을 보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2620&quot; data-origin-height=&quot;908&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ebAxT1/btr760kikcN/M9ALtsHfRwW4cTN0IFTJ51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ebAxT1/btr760kikcN/M9ALtsHfRwW4cTN0IFTJ51/img.png&quot; data-alt=&quot;주문 시스템에 데코레이터 패턴을 적용한 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ebAxT1/btr760kikcN/M9ALtsHfRwW4cTN0IFTJ51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FebAxT1%2Fbtr760kikcN%2FM9ALtsHfRwW4cTN0IFTJ51%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;700&quot; height=&quot;243&quot; data-origin-width=&quot;2620&quot; data-origin-height=&quot;908&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;주문 시스템에 데코레이터 패턴을 적용한 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;Beverage&lt;/s&gt;는 Component에 대응되고, &lt;s&gt;BeverageCondiment&lt;/s&gt;는 Decorator에 대응됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;BeverageCondiment&lt;/s&gt;는 자신이 장식할 &lt;s&gt;Beverage&lt;/s&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;s&gt;DarkRoast&lt;/s&gt; 객체를 가져온다.&lt;/li&gt;
&lt;li&gt;&lt;s&gt;Mocha&lt;/s&gt; 객체로 장식한다.&lt;/li&gt;
&lt;li&gt;&lt;s&gt;Whip&lt;/s&gt; 객체로 장식한다.&lt;/li&gt;
&lt;li&gt;&lt;s&gt;getCost()&lt;/s&gt; 메서드로 첨가물의 가격을 계산한다.&lt;/li&gt;
&lt;/ol&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;s&gt;DarkRoast&lt;/s&gt; 객체에서 시작합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lPs9D/btr725gesen/kGxrU9obr2FRyQ24DeVil1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lPs9D/btr725gesen/kGxrU9obr2FRyQ24DeVil1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lPs9D/btr725gesen/kGxrU9obr2FRyQ24DeVil1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlPs9D%2Fbtr725gesen%2FkGxrU9obr2FRyQ24DeVil1%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;140&quot; height=&quot;133&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;133&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DarkRoast는 Beverage로부터 상속받으므로, 음료의 가격을 계산하는 메서드를 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 고객이 모카를 추가했으니까 Mocha 객체를 만들고, 그 객체로 &lt;s&gt;DarkRoast&lt;/s&gt;를 감쌉니다.&lt;br /&gt;여기서 Mocha 객체는 BeverageCondiment의 서브클래스이면서, Beverage의 서브클래스입니다.&lt;br /&gt;따라서, Mocha에도 getCost() 메서드가 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ITEea/btr79gfVP1e/gPpLs9bGn4KQ0vmWkfqZqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ITEea/btr79gfVP1e/gPpLs9bGn4KQ0vmWkfqZqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ITEea/btr79gfVP1e/gPpLs9bGn4KQ0vmWkfqZqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FITEea%2Fbtr79gfVP1e%2FgPpLs9bGn4KQ0vmWkfqZqk%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;231&quot; height=&quot;202&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 고객이 휘핑크림도 추가했으니까 &lt;s&gt;Whip 데코레이터&lt;/s&gt;로 &lt;s&gt;Mocha&lt;/s&gt;를 감쌉니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Whip도 마찬가지로, cost() 메서드를 갖고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;354&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MT94d/btr753hkcPz/HmRtRqiFkI1yrAfNCu3RT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MT94d/btr753hkcPz/HmRtRqiFkI1yrAfNCu3RT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MT94d/btr753hkcPz/HmRtRqiFkI1yrAfNCu3RT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMT94d%2Fbtr753hkcPz%2FHmRtRqiFkI1yrAfNCu3RT0%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;354&quot; height=&quot;289&quot; data-origin-width=&quot;354&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;가격을 구할 때는 가장 바깥쪽에 있는 데코레이터인 &lt;s&gt;Whip&lt;/s&gt;의 getCost()를 호출하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 Whip은 그 객체가 장식하고 있는 객체에게 가격 계산을 &lt;s&gt;위임&lt;/s&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격이 구해지고 나면, 계산된 가격에 휘핑크림의 가격을 더한 다음 그 결과값을 리턴합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G7AYz/btr74usKI2n/eShbdTAayePILdS6PlLfTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G7AYz/btr74usKI2n/eShbdTAayePILdS6PlLfTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G7AYz/btr74usKI2n/eShbdTAayePILdS6PlLfTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG7AYz%2Fbtr74usKI2n%2FeShbdTAayePILdS6PlLfTk%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;700&quot; height=&quot;550&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드로 보면 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Whip implements BeverageCondiment{
	private final Beverage beverage;

	public int getCost(){
		return beverage.getCost() + .33;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Beverage beverage = new Whip(new Mocha(new DarkRoast()));
System.out.println(beverage.getCost()) // 1.29
&lt;/code&gt;&lt;/pre&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;한 객체를 여러 개의 데코레이터로 감쌀 수 있다.&lt;/li&gt;
&lt;li&gt;데코레이터는 자신이 감싸고 있는 객체와 같은 슈퍼클래스를 갖고 있기에 원래 객체(싸여있는 객체)가 들어갈 자리에 데코레이터 객체를 넣어도 상관없다.&lt;/li&gt;
&lt;li&gt;데코레이터는 자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 일 말고도 추가 작업을 수행할 수 있다.&lt;/li&gt;
&lt;li&gt;객체는 언제든지 감쌀 수 있으므로 실행 중에 필요한 데코레이터를 마음대로 적용할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;❓&amp;nbsp;CondimentDecorator에서 Beverage 클래스를 확장하고 있는데, 그럼 상속 아니야..?&lt;/h4&gt;
&lt;pre id=&quot;code_1680616102159&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;✋ 상속을 사용하고 있는 것은 맞습니다. 
하지만, 형식을 맞추기 위해 사용한 것이지, 행동을 물려받기 위해 상속을 사용한 것은 아니에요. 
어떤 구성 요소를 가지고 데코레이터를 만들 때, 새로운 행동을 추가합니다. 
슈퍼 클래스로부터 행동을 상속 받아서 얻는 것이 아니라 객체를 구성해서 새로운 행동을 얻는 거죠. 
만약 상속만 써야 했다면, 행동이 컴파일시에 정적으로 결정되어 버리지만, 
객체 구성을 이용하고 있기 때문에 실행 중에 데코레이터를 조합해서 사용할 수 있기 때문에, 
새로운 첨가물이 추가되어도 유연성을 잃지 않을 수 있어요.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;❓ 구성 요소의 형식만 상속하면 되는 거라면 Beverage 클래스를 왜 인터페이스로 만들지 않고 추상 클래스로 만든 건가요?&lt;/h4&gt;
&lt;pre id=&quot;code_1680616137402&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;✋ Beverage 클래스를 받았을 때부터 추상 클래스였기 때문이에요. 
사실 인터페이스를 쓰면 되지만, 기존 코드를 고치는 일은 될 수 있으면 피하는 게 좋으니까 추상 클래스를 써도 되는 상황이라면, 
그냥 추상 클래스만 가지고 작업을 하는 게 나을 수도 있어요.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데코레이터 패턴을 적용한 코드는 아래 깃헙 주소에서 확인하실 수 있습니다!&lt;/p&gt;
&lt;figure id=&quot;og_1680616302213&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - rlfalsgh95/design-pattern&quot; data-og-description=&quot;Contribute to rlfalsgh95/design-pattern development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/rlfalsgh95/design-pattern&quot; data-og-url=&quot;https://github.com/rlfalsgh95/design-pattern&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cwOR0L/hyR9INiGjS/iF5P2aFOMoRpEXZlErtCC1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/rlfalsgh95/design-pattern&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/rlfalsgh95/design-pattern&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cwOR0L/hyR9INiGjS/iF5P2aFOMoRpEXZlErtCC1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - rlfalsgh95/design-pattern&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to rlfalsgh95/design-pattern development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데코레이터의 적용된 예 : 자바 I/O&lt;/h2&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;s&gt;java.io&lt;/s&gt;는 데코레이터 패턴을 바탕으로 만들어졌어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일에서 데이터를 읽어오는 스트림에 기능을 더하는 데코레이터를 사용하는 객체는 보통 다음과 같은 형식으로 구성됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K3spT/btr72bBaghd/i8QTnh2eg1PEsmm1DN2l0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K3spT/btr72bBaghd/i8QTnh2eg1PEsmm1DN2l0k/img.png&quot; data-alt=&quot;java.io의 사용 예&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K3spT/btr72bBaghd/i8QTnh2eg1PEsmm1DN2l0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK3spT%2Fbtr72bBaghd%2Fi8QTnh2eg1PEsmm1DN2l0k%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;700&quot; height=&quot;639&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;java.io의 사용 예&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;FileInputStream&lt;/s&gt;을 데코레이터로 장식할 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 I/O 라이브러리는 FileInputStream, StringBufferInputStream 등 다양한 구성 요소를 제공합니다.&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;s&gt;BufferedInputStream&lt;/s&gt;은 구상 데코레이터입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FileInputStream에 입력을 미리 읽어서 더 빠르게 처리할 수 있게 해주는 버퍼링 기능을 더해 주는 역할을 합니다.&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;s&gt;ZipInputStream&lt;/s&gt;도 구상 데코레이터입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;zip 파일에서 데이터를 읽어올 때, 그 속에 들어있는 항목을 읽는 기능을 더해주죠.&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;s&gt;java.io&lt;/s&gt; 패키지의 구조는 아래와 같아요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;5320&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rZNcx/btr72o1rk4P/mqrETjC04QiqvdamnRfj0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rZNcx/btr72o1rk4P/mqrETjC04QiqvdamnRfj0k/img.png&quot; data-alt=&quot;java.io &amp;amp;amp;nbsp;패키지 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rZNcx/btr72o1rk4P/mqrETjC04QiqvdamnRfj0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrZNcx%2Fbtr72o1rk4P%2FmqrETjC04QiqvdamnRfj0k%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;700&quot; height=&quot;103&quot; data-origin-width=&quot;5320&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;java.io &amp;amp;nbsp;패키지 구조&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;InputStream만 떼어서 한 번 보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3784&quot; data-origin-height=&quot;672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTeAl7/btr75uzfWSN/vqsg6NlmBRBfKodwp1qAHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTeAl7/btr75uzfWSN/vqsg6NlmBRBfKodwp1qAHK/img.png&quot; data-alt=&quot;java.io &amp;amp;amp;nbsp;InputStream의 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTeAl7/btr75uzfWSN/vqsg6NlmBRBfKodwp1qAHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTeAl7%2Fbtr75uzfWSN%2Fvqsg6NlmBRBfKodwp1qAHK%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;700&quot; height=&quot;124&quot; data-origin-width=&quot;3784&quot; data-origin-height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;java.io &amp;amp;nbsp;InputStream의 구조&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;여기서 &lt;s&gt;InputStream&lt;/s&gt;은 &lt;s&gt;Component&lt;/s&gt; 역할을, &lt;s&gt;FileInputStream&lt;/s&gt;은 &lt;s&gt;Decorator&lt;/s&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;s&gt;FileInputStream&lt;/s&gt;과 &lt;s&gt;BufferedInputStream&lt;/s&gt;의 코드를 보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3JDp9/btr746Flaon/CAmY6P3wiUAEmXoJqnYoEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3JDp9/btr746Flaon/CAmY6P3wiUAEmXoJqnYoEK/img.png&quot; data-alt=&quot;FileInputStream의 생성자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3JDp9/btr746Flaon/CAmY6P3wiUAEmXoJqnYoEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3JDp9%2Fbtr746Flaon%2FCAmY6P3wiUAEmXoJqnYoEK%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;700&quot; height=&quot;257&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FileInputStream의 생성자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1702&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1eNlU/btr74tUTSG8/4QpNMv2SkBibc3VOXM1fR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1eNlU/btr74tUTSG8/4QpNMv2SkBibc3VOXM1fR1/img.png&quot; data-alt=&quot;BufferedInputStream의 생성자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1eNlU/btr74tUTSG8/4QpNMv2SkBibc3VOXM1fR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd1eNlU%2Fbtr74tUTSG8%2F4QpNMv2SkBibc3VOXM1fR1%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;700&quot; height=&quot;394&quot; data-origin-width=&quot;1702&quot; data-origin-height=&quot;958&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BufferedInputStream의 생성자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;FileInputStream&lt;/s&gt;은 &lt;s&gt;InputStream&lt;/s&gt;이면서, InputStream을 필드로 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;BufferedInputStream&lt;/s&gt;은 생성자 파라미터로 InputStream을 받아서 FileInputStream을 초기화 하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;BufferedInputStream&lt;/s&gt;는 필드로 갖고 있는 in의 기능에 더해서 &lt;s&gt;버퍼링&lt;/s&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;s&gt;InputStream&lt;/s&gt;은 어떻게 사용할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘 문제를 풀 때, 버퍼링 기능을 이용하여 &lt;s&gt;Scanner&lt;/s&gt;보다 더 빠르게 입력 받기 위해 아래와 같은 방식을 사용해보신 분들도 많을 거에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저도 문제를 풀 때는 몰랐지만, &lt;s&gt;데코레이터 패턴&lt;/s&gt;이라고 하더라구요.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;InputStreamReader input = new InputStreamReader(new BufferedInputStream(System.in));
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;Reader/Writer 스트림&lt;/s&gt;도 InputStream과 거의 똑같이 디자인 되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 I/O를 보면 잡다한 클래스가 많고, 복잡하다는 것을 발견할 수 있는데요, 이것이 데코레이터 패턴의 단점 중 하나입니다.&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;u&gt;입력 스트림에 있는 대문자를 전부 소문자로 바꿔주는 데코레이터&lt;/u&gt;를 만들어 볼게요.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class LowerCaseInputStream extends FilterInputStream {
    public LowerCaseInputStream(InputStream in){
        super(in);
    }

    // ! 입력 스트림으로부터 다음 byte를 읽는 메서드, 더이상 읽을 byte가 없으면 -1을 반환한다.
    @Override
    public int read() throws IOException {
        int c = in.read();
        return (c == -1 ? c : Character.toLowerCase((char)c));
    }

    // ! 이 입력 스트림에서 최대 len byte의 데이터를 byte 배열로 읽습니다.
    // ! 버퍼로 읽은 총 바이트 수 또는 스트림 끝에 도달하여 더 이상 데이터가 없는 경우 -1을 반환한다.
    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        int result = in.read(b, off, len);
        for(int i = off; i &amp;lt; off + result; i++){
            b[i] = (byte) Character.toLowerCase((char)b[i]);
        }
        return result;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;read() 메서드&lt;/s&gt;는 입력 스트림으로부터 다음 byte를 읽는 메서드에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드 안에서 필드로 갖고 있는 In의 read() 메서드를 호출한 다음,&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;아래 test.txt 파일을 읽어서 소문자로 바꾸는 코드를 작성해볼게요.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;hello. This is ethan, who announced the Decorate Pattern.&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class InputTest {
    @Test
    void test(){
        int c;
        StringBuilder stringBuilder = new StringBuilder();
        try(InputStream in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream(&quot;./src/test/resources/test.txt&quot;)))){

            while((c = in.read()) &amp;gt; 0){
                stringBuilder.append((char) c);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }

        Assertions.assertEquals(stringBuilder.toString(), &quot;hello. this is minho gil, who announced the decorate pattern.&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;FileInputStream&lt;/s&gt;는 파일에서 byte를 읽고, &lt;s&gt;BufferdInputStream&lt;/s&gt;은 버퍼링 기능을 확장하였고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 &lt;s&gt;LowerCaseInputStream&lt;/s&gt;은 소문자로 변환하는 기능을 확장하였어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국, 파일에서 읽은 byte를 소문자로 변환하여 가져올 수 있었어요!&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;h2 data-ke-size=&quot;size26&quot;&gt;더블 모카는 어떻게 만들 수 있을까?&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;class BeverageTest {
    @Test
    @DisplayName(&quot;더블 모카 다크 로스트의 가격은 1.05이다.&quot;)
    void test1(){
        Beverage beverage = new Mocha(new Mocha(new DarkRoast()));
        assertEquals(beverage.getCost(), 1.05);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장/단점?&lt;/h2&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;b&gt;Pros&lt;/b&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;데코레이터는 상속보다 유연한 방식으로 기능을 추가할 수 있습니다. 즉, 데코레이터는 객체 간의 결합도를 낮추며, 객체 지향 설계의 원칙 중 하나인 개방-폐쇄 원칙(OCP)을 따릅니다.&lt;/li&gt;
&lt;li&gt;높은 유연성을 갖고 있어, 유지보수가 용이하다. (서브 클래스를 이용하여 기능을 확장할 수 있다.)&lt;/li&gt;
&lt;li&gt;기존 클래스를 수정하지 않고도 새로운 기능을 추가할 수 있습니다. 이는 코드의 유지보수와 확장성을 높여줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;cons&lt;/b&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;데코레이터 패턴을 구현하는 과정에서, 많은 중복 코드가 발생할 수 있습니다. 이는 코드 가독성을 떨어뜨릴 수 있습니다.&lt;/li&gt;
&lt;li&gt;데코레이터 패턴이 중첩되어 사용될 경우, 코드가 복잡해질 수 있습니다. 이는 디버깅과 유지보수에 어려움을 더할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출처&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Head First Design Pattern Chapter. 3&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://neillmorgan.wordpress.com/2010/02/07/decorator-pattern-pros-and-cons/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://neillmorgan.wordpress.com/2010/02/07/decorator-pattern-pros-and-cons/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스터디</category>
      <author>길민호(ethan.mino)</author>
      <guid isPermaLink="true">https://codingnotes.tistory.com/238</guid>
      <comments>https://codingnotes.tistory.com/238#entry238comment</comments>
      <pubDate>Tue, 4 Apr 2023 22:58:17 +0900</pubDate>
    </item>
    <item>
      <title>신입 서버 개발자의 합격 후기 (Feat. Kakao 최종합격, SSAFY 8기)</title>
      <link>https://codingnotes.tistory.com/237</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;그동안의 개발자 생활을 정리하고, 하소연하는 게시글&lt;/u&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⚠️&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;개인적인 경험, 의견이므로 참고만&lt;/u&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;span style=&quot;font-size: 1.44em; letter-spacing: -1px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;  학교 생활&lt;/span&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;u&gt;학점은 1.7점대에, 학사 경고&lt;/u&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;u&gt;지도 교수님의 따끔한 조언&lt;/u&gt;이 동기가 되었고, &lt;u&gt;일단 학교 성적이라도 잘 받아보자는 생각으로 열심히 노력&lt;/u&gt;했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼마나 공부해야 좋은 성적을 받을 수 있는 지도 몰랐고, CS 지식도 전무 했던 상태라 정말 정말 정말 정말 열심히 했다.&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;그리고, &lt;span&gt;매 수업마다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;강의가 끝나면 녹음을 들으며 수업을 정리했고, 시험 기간에는 잠을 쫓기 위해 야외에서 허벅지 내려치며 공부했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국, &lt;u&gt;5학기 연속 차석/수석&lt;/u&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;2학년 때는 어느 한 동아리원이 대학원생에게 필요한 정보를 제공해주는 챗봇을 만들어줄 수 있냐는 요청을 받았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(지금은 그때 이용했던&amp;nbsp;&lt;s&gt;오픈 빌더&lt;/s&gt;와 관련된 부서에서 일하고 있다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 처음으로 서비스를 만들어보았고, &lt;u&gt;&quot;개발자란 누군가에게 편의를 제공해줄 수 있구나&quot;&lt;/u&gt;라는 생각이 들었고, &lt;u&gt;눈에 보이는 결과물을 보고 개발에 큰 매력을 느꼈다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 무엇보다 &lt;u&gt;기획부터, 개발 그리고 서비스하는 과정 자체가 즐거웠고 설렜다.&lt;/u&gt;&amp;nbsp; 그 때부터 여러 프로젝트를 진행했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4학년 때는 &lt;s&gt;AI&lt;/s&gt;에 관심이 생겼고, &lt;s&gt;멀티미디어 보안 랩실&lt;/s&gt;에 들어가서 &lt;s&gt;CNN 네트워크&lt;/s&gt;와 &lt;s&gt;PRNU 알고리즘&lt;/s&gt;을 이용하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;SMDB&lt;/s&gt;라는 &lt;u&gt;이미지 데이터 셋에 대한 검증을 수행&lt;/u&gt;하고, &lt;u&gt;논문을 작성하는 역할&lt;/u&gt;을 맡았고, &lt;s&gt;KCI&lt;/s&gt;에 &lt;u&gt;논문이 등재&lt;/u&gt;되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 군 입대 전의 학점을 복구 하기 위해 22 ~ 23학점을 신청했고, 논문/졸업 프로젝트/토이 프로젝트로 인해 취업을 준비하지 못했던 것이 아쉽다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  스펙&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;  &lt;/span&gt;교육&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;XX대학교 멀티미디어 보안랩 (학부연구생) &lt;/b&gt;(2020.12 ~ 2022.01)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;XX대학교 컴퓨터과학과 졸업, &lt;/b&gt;학점 3.94 (2014.03 ~ 2022.02)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;삼성 청년 SW 아카데미 8기 &lt;/b&gt;(2022.07.06 ~ 2022.11.25)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;nbsp;  자격증&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정보처리기사&lt;/b&gt; (2020.11.12 취득)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SQL 개발자 &lt;/b&gt;(2021.10.01 취득)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PCCP cpp level 3, 700점&lt;/b&gt; (2022.10.23 취득)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  프로젝트&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;  Good-I-Deer
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2019.09 ~ 2019.12 (약 3개월)&lt;/li&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;XX대학교 문화기술대학원생&lt;/span&gt;들이 빈번하게 찾는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;정보를 카카오톡 챗봇을 통하여 제공&lt;/span&gt;하는 서비스&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;  코로나 웹&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2020.08 ~ 2020.09 (약 1개월)&lt;/li&gt;
&lt;li&gt;안심 식당/병원의 정보와 코로나 확진자 정보를 제공하는 웹 사이트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;  전자상거래 주문 수집 자동화 및 재고 관리 사이트&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;2020.04 ~ 2020.12 (약 8개월)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;전자상거래 주문 수집 자동화 및 재고 관리 사이트&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt; ️ Rest Camping&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;2021.09 ~ 2021.12 (약 3개월)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;캠핑장 예약 플랫폼&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;❤️ 들어줄게&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;2021.03 ~ 2021.10 (약 8개월)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;사용자가 챗봇에게 지인, 연예인, 캐릭터등의 음성을 학습시킬 수 있으며, 음성을 학습한 챗봇과 속마음을 털어놓을 수 있는 AI 음성 챗봇&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;  논문&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;크라우드소싱을 통해 수집된 카메라 소스 식별 데이터셋(이미지)의 검증 및 공유&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;2021.03 ~ 2022.01 (약 11개월)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;크라우드소싱 방식을 통해 수집된 Sangmyung Image Database (SMDB)의 유효성 검증 및 데이터 공유&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;기술 스택&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 296px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 105px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 105px;&quot;&gt;Backend&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 105px;&quot;&gt;&lt;s&gt;Spring Boot&lt;/s&gt;, &lt;s&gt;Spring MVC&lt;/s&gt;,&amp;nbsp; &lt;s&gt;Express&lt;/s&gt;, &lt;br /&gt;&lt;s&gt;Spring Security&lt;/s&gt;, &lt;s&gt;JSP&lt;/s&gt;, &lt;s&gt;Node.js&lt;/s&gt;, &lt;br /&gt;&lt;s&gt;Mybatis&lt;/s&gt;, &lt;s&gt;Swagger&lt;/s&gt;, &lt;s&gt;Selenium&lt;/s&gt;, &lt;s&gt;Puppeteer&lt;/s&gt;,&amp;nbsp;&lt;br /&gt;&lt;s&gt;Mysql&lt;/s&gt;&lt;br /&gt;&lt;s&gt;JAVA&lt;/s&gt;&lt;span&gt;, &lt;/span&gt;&lt;s&gt;Javascript&lt;br /&gt;&lt;/s&gt;&lt;s&gt;Maven&lt;/s&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;Gradle&lt;/s&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 35px;&quot;&gt;Frontend&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 35px;&quot;&gt;&lt;s&gt;HTML5&lt;/s&gt;, &lt;s&gt;CSS&lt;/s&gt;, &lt;s&gt;Javascript&lt;/s&gt;&lt;br /&gt;&lt;s&gt;jQuery&lt;/s&gt;, &lt;s&gt;ES6&lt;/s&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 52px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 52px;&quot;&gt;DevOps&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 52px;&quot;&gt;&lt;s&gt;Linux&lt;/s&gt;, &lt;s&gt;Docker&lt;/s&gt;&lt;br /&gt;&lt;s&gt;NGINX&lt;/s&gt;&lt;span&gt;, &lt;/span&gt;&lt;s&gt;Tomcat&lt;/s&gt;&lt;span&gt;, &lt;/span&gt;&lt;s&gt;Apache&lt;br /&gt;&lt;/s&gt;&lt;s&gt;EC2&lt;/s&gt;, &lt;s&gt;S3&lt;/s&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Collaboration&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;s&gt;Notion&lt;/s&gt;, &lt;s&gt;Trello&lt;/s&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Configuration Management&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;s&gt;git&lt;/s&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 70px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 70px;&quot;&gt;Tools&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 70px;&quot;&gt;&lt;s&gt;XMind&lt;/s&gt;&lt;span&gt;, &lt;/span&gt;&lt;s&gt;StarUML&lt;/s&gt;&lt;br /&gt;&lt;s&gt;Postman&lt;/s&gt;, &lt;s&gt;Talend API Tester&lt;br /&gt;&lt;/s&gt;&lt;s&gt;Putty&lt;/s&gt;&lt;span&gt;, &lt;/span&gt;&lt;s&gt;Wireshark&lt;/s&gt;, &lt;s&gt;FileZila&lt;/s&gt;&lt;br /&gt;&lt;s&gt;Visual Studio Code&lt;/s&gt;, &lt;s&gt;PyCharm&lt;/s&gt;, &lt;s&gt;intellij&lt;/s&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  상반기 지원 현황&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;당근마켓 백엔드 개발 인턴&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 서류 탈락&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;티맥스 Fintech, AI, Office&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 서류 탈락&lt;/li&gt;
&lt;li&gt;&lt;s&gt;라인&lt;/s&gt; - 코딩 테스트 탈락&lt;/li&gt;
&lt;li&gt;&lt;s&gt;카카오 채용 연계 인턴&lt;/s&gt; - 코딩 테스트 탈락&lt;/li&gt;
&lt;li&gt;&lt;s&gt;엔씨소프트 인턴&lt;/s&gt; - 코딩 테스트 탈락&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;카카오 페이 신입 개발자&lt;/s&gt; - 코딩 테스트 탈락&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;라인 플러스 Ads Server Engineer&lt;/s&gt; - 코딩 테스트 탈락&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;오늘의 집&lt;/s&gt; - 서류 합격, 코딩 테스트 탈락&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;네이버 Code Your Careers 인턴십&lt;/s&gt;&amp;nbsp;- 서류/코딩 테스트 합, 1차 면접 탈락&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;SSAFY 8기&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 최종합격&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;다나와&lt;/s&gt; - 최종합격&lt;/li&gt;
&lt;li&gt;&lt;s&gt;엔젤스윙 인턴&lt;/s&gt; - 최종합격&lt;/li&gt;
&lt;li&gt;&lt;s&gt;렛시&lt;/s&gt; - 최종 합격&lt;/li&gt;
&lt;li&gt;&lt;s&gt;로민&lt;/s&gt; - 최종 과제 테스트 불참&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학부생 때는 취업 준비를 못했던 탓에 &lt;u&gt;알고리즘, 자료구조 역량이 부족&lt;/u&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;b&gt;빠킹독&lt;/b&gt;님의&amp;nbsp;&lt;a href=&quot;https://www.youtube.com/watch?v=LcOIobH7ues&amp;amp;list=PLtqbFd2VIQv4O6D6l9HcD732hdrnYb6CY&quot;&gt;실전&amp;nbsp;알고리즘 강의&lt;/a&gt;와&amp;nbsp;&lt;a href=&quot;https://github.com/encrypted-def/basic-algo-lecture/blob/master/workbook.md&quot;&gt;문제집&lt;/a&gt;으로 &lt;u&gt;알고리즘과 자료구조 공부를 시작&lt;/u&gt;했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;u&gt;자기소개서와 포트폴리오를 작성하고 다듬었다.&lt;/u&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;u&gt;포트폴리오도 없었고, 코딩 테스트 역량도 부족&lt;/u&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;u&gt;여러 스타트업에 지원하면서 부족한 점을 파악해서 채우고, 면접 역량을 키웠다. 그리고 코딩 테스트 출제 유형을 파악&lt;/u&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;u&gt;대규모의 사용자들 대상으로 서비스하고 있는 대기업을 목표&lt;/u&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;s&gt;SSAFY&lt;/s&gt;&lt;u&gt;에 지원하였고, 운이 좋게 합격&lt;/u&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;h3 data-ke-size=&quot;size23&quot;&gt; &amp;zwj;♀️ SSAFY&lt;/h3&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;u&gt;대전 지역 Java 전공 반으로 배정&lt;/u&gt;되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;SSAFY에서는&lt;span&gt;&lt;s&gt; Java&lt;/s&gt;부터 시작하여, &lt;s&gt;알고리즘&lt;/s&gt;, &lt;s&gt;HTML/CSS/Javascript&lt;/s&gt;, &lt;s&gt;Web&lt;/s&gt;, &lt;s&gt;DB&lt;/s&gt;, &lt;s&gt;Spring MVC&lt;/s&gt;, &lt;s&gt;Spring Boot&lt;/s&gt;, &lt;s&gt;Vue.js&lt;/s&gt;을 약 5개월 동안 속성으로 교육한다.&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;u&gt;매주, &lt;span&gt;매월&lt;span&gt; &lt;/span&gt;&lt;/span&gt;시험을 치르며 일정 횟수 이상 통과해야 1학기를 수료할 수 있고, 매 주제가 끝나면 해당 기술을 이용하여 간단한 프로젝트를 진행&lt;/u&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;그래서 &lt;u&gt;SSAFY 수업보다 스터디에 집중&lt;/u&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 data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;s&gt;SSAFY&lt;/s&gt;&lt;u&gt;의 장단점&lt;/u&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 data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;SSAFY의 장점&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 프레임워크, 언어, 기술 등에 대한 복기&amp;nbsp;&lt;/li&gt;
&lt;li&gt;취업지원센터의 면접, 자소서 등 취업에 대한 체계적인 도움&lt;/li&gt;
&lt;li&gt;&lt;span&gt;여러 기업의 채용 우대&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;여러 개발자들과 함께 스터디, 취업 준비&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;100만원의 지원금, 점심 지원&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;삼성 임직원 멘토링, 삼성 B형 응시 기회, 기업 탐방 등의 다양한 지원&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;SSAFY의 단점&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그래밍이 처음이라면 커리큘럼을 따라가기 매우 어렵고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;깊숙한 이해는 본인이 별도로 학습해야함&lt;/li&gt;
&lt;li&gt;취업을 준비하는데 오히려 시간을 뺏길 수 있음 (과목평가, 월말평가, 프로젝트 등 ...)&lt;/li&gt;
&lt;li&gt;짧은 개발 기간으로 인해 탄탄한 프로젝트를 진행하기 어렵고, 교육생들이 많기 때문에 프로젝트에 대한 피드백을 받기 어려움&lt;/li&gt;
&lt;li&gt;오프라인 수업으로 인한 통학 시간&lt;/li&gt;
&lt;li&gt;본가와 캠퍼스가 먼 경우, 방을 구해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;결과적으로는, &lt;u&gt;SSAFY에 입과한 것에 매우 만족&lt;/u&gt;하고 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;span&gt;SSAFY는 충분한 지원을 제공하고 있으며, 본인이 필요한 부분만 얻어간다면, 많은 도움을 받을 수 있다.&lt;/span&gt;&lt;/u&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;  알고리즘 &amp;amp; 자료구조&lt;/h3&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;SSAFY 입과 전에는 &lt;b&gt;빠킹독&lt;/b&gt;님의&amp;nbsp;&lt;a href=&quot;https://www.youtube.com/watch?v=LcOIobH7ues&amp;amp;list=PLtqbFd2VIQv4O6D6l9HcD732hdrnYb6CY&quot;&gt;실전&amp;nbsp;알고리즘 강의&lt;/a&gt;와&amp;nbsp;&lt;a href=&quot;https://github.com/encrypted-def/basic-algo-lecture/blob/master/workbook.md&quot;&gt;문제집&lt;/a&gt;&lt;span&gt;으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;u&gt;알고리즘과 자료구조 역량을 쌓았고,&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSAFY 입과 후에는 5명 ~ 7명의 &lt;u&gt;교육생들과 알고리즘/CS 스터디를 진행&lt;/u&gt;하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스터디는 일주일에 두 번 진행하였고, 매 스터디마다&amp;nbsp;&lt;u&gt;백준 문제와(&lt;a href=&quot;https://github.com/encrypted-def/basic-algo-lecture/blob/master/workbook.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;빠킹독님&amp;nbsp;문제집&lt;/a&gt;, &lt;a href=&quot;https://www.acmicpc.net/workbook/view/1152&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;삼성 A형 기출&lt;/a&gt;) &lt;a href=&quot;https://programmers.co.kr/&quot;&gt;&lt;b&gt;프로그래머스&lt;/b&gt;&lt;/a&gt;의 카카오 기출을 2 ~ 3 문제씩 풀어와서 코드 리뷰&lt;/u&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;s&gt;기업 코딩 테스트&lt;/s&gt;, &lt;s&gt;삼성 역량 테스트&lt;/s&gt;, &lt;s&gt;PCCP&lt;/s&gt;&lt;u&gt;에 응시하여, 자주 출제되는 유형과 현재 역량을 파악&lt;/u&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;u&gt;알고리즘 공부 방식은 아래 포스팅을 참고&lt;/u&gt;해주세요!&lt;/p&gt;
&lt;figure id=&quot;og_1669976534609&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;2022 코딩 테스트 후기 (라인 플러스, 오늘의 집, 카카오 인턴십 ...)&quot; data-og-description=&quot;본 게시글은 아래의 코딩 테스트를 경험하고 느낀점에 대한 게시글입니다. 주관적인 느낀점이니 참고만 해주시면 감사하겠습니다!   라인 플러스 (2022.03.26) 원티드 (2022.04.02) 오늘의 집 (2022.04.&quot; data-og-host=&quot;codingnotes.tistory.com&quot; data-og-source-url=&quot;https://codingnotes.tistory.com/164&quot; data-og-url=&quot;https://codingnotes.tistory.com/164&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bj3X5O/hyQMyw291s/8o7eHiuDPhNoPWMfSGmTo0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/blVF9A/hyQKTbSwVS/fMTLTZGUwTHepJtSKCcqgk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/jIMLS/hyQMv1ot64/AJok2RlQXpsFsTozMQ4Q0k/img.png?width=750&amp;amp;height=217&amp;amp;face=0_0_750_217&quot;&gt;&lt;a href=&quot;https://codingnotes.tistory.com/164&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://codingnotes.tistory.com/164&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bj3X5O/hyQMyw291s/8o7eHiuDPhNoPWMfSGmTo0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/blVF9A/hyQKTbSwVS/fMTLTZGUwTHepJtSKCcqgk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/jIMLS/hyQMv1ot64/AJok2RlQXpsFsTozMQ4Q0k/img.png?width=750&amp;amp;height=217&amp;amp;face=0_0_750_217');&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;2022 코딩 테스트 후기 (라인 플러스, 오늘의 집, 카카오 인턴십 ...)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;본 게시글은 아래의 코딩 테스트를 경험하고 느낀점에 대한 게시글입니다. 주관적인 느낀점이니 참고만 해주시면 감사하겠습니다!   라인 플러스 (2022.03.26) 원티드 (2022.04.02) 오늘의 집 (2022.04.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;codingnotes.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;h3 data-ke-size=&quot;size23&quot;&gt;  Computer Science&lt;/h3&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;s&gt;CS 스터디&lt;/s&gt;는,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;CS 지식을 쌓고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/u&gt;&lt;s&gt;기술 면접&lt;/s&gt;&lt;u&gt;에 대비하기 위한 스터디&lt;/u&gt;로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매주 2명의 인원이 발표 주제를 정하고, &lt;u&gt;학습한 내용을 발표&lt;/u&gt;하는 방식으로 진행하였다. (짧게는 10분, 길게는 한 시간)&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;CS 주제는 아래와 같다.&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Java&lt;/li&gt;
&lt;li&gt;컴퓨터 구조&lt;/li&gt;
&lt;li&gt;디자인 패턴 &amp;lt;- &lt;u&gt;여기까지만 진행하고 취뽀&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;네트워크&lt;/li&gt;
&lt;li&gt;Web&lt;/li&gt;
&lt;li&gt;운영체제&lt;/li&gt;
&lt;li&gt;데이터베이스&lt;/li&gt;
&lt;li&gt;프레임워크&lt;/li&gt;
&lt;li&gt;아키텍처&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정보의 신뢰성이 없다면, 팀원들 모두가 잘못된 정보를 얻어갈 수 있기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;CS 스터디의 가장 중요한 점은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/u&gt;&lt;s&gt;정보의 신뢰성&lt;/s&gt;&lt;u&gt;이라고 생각&lt;/u&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 방지하기 위해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;팀원들이 최대한 질문을 많이 하도록 분위기를 이끌었다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;질문을 반복하면서 정보의 오류를 찾아낼 수 있고, 질문에 충분히 답변하려면,&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;발표자는 해당 주제에 대해 정확하게 학습해야 하기 때문에,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/u&gt;&lt;s&gt;정보의 신뢰성&lt;/s&gt;&lt;u&gt;을 높일 수 있다.&lt;/u&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;u&gt;발표 자료를 준비하고, 다른 팀원의 학습 자료를 습득하면서 보다 더 깊숙한 CS 지식을 얻었고 기술 면접에서 좋은 성적을 낼 수 있었다.&lt;/u&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;span&gt;  하반기 지원 현황&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;신한은행 (디지털/ICT 수시채용 삼성청년 SW 아카데미 특별 전형)&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 서류 탈락&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;쿠팡 신입 공채&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 추천서, 서류 탈락&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;카카오 뱅크 채용연계형 인턴&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 서류 탈락&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;현대 오토에버&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 서류/코테 탈락&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;요기요 Rookies 5기&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 서류/코딩 테스트 탈락&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;CJ E&amp;amp;M&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 서류 합, 코딩 테스트 불참&lt;/li&gt;
&lt;li&gt;&lt;s&gt;KT DS 2022년 하반기 신입사원 공개 채용&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 서류 합, 코딩 테스트 불참&lt;/li&gt;
&lt;li&gt;&lt;s&gt;2022 넷마블 컴퍼니 신입사원 공개 채용&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 서류/코딩 테스트 합, 필기 불참&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;KT SW 개발 역량 우수자 블라인드 채용 신입&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 코딩 테스트 합, 1차 면접 탈락&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;넥토리얼 2기 채용형 인턴십&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 서류/코딩 테스트 합, 최종 면접 탈락&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;2023 카카오 Blind 신입 공채&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 최종합격&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상반기에는 지원하는 것을 회피했는데, 코딩 테스트가 어느 정도 준비가 되어,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하반기에는 &lt;u&gt;이름 있는 스타트업과 대기업에 많이 지원&lt;/u&gt;하였다. 포트폴리오는 상반기와 동일한 상태였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;여러 번 지원하다보니 자료가 많이 쌓여 자소서를 작성하는 시간이 많이 줄어들어, 닥치는 대로 지원&lt;/u&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;u&gt;2023년도 상/하반기에 취업하는 것을 목표&lt;/u&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;s&gt;KT DIGICO&lt;/s&gt;는 프레임워크와 프로젝트 DB 설계에 대한 질문에 잘 대답하지 못했고, &lt;u&gt;1차 면접에서 탈락&lt;/u&gt;하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;넥슨&lt;/s&gt;은 인턴십이기도 하고, 면접 제의온 곳들은 스킬셋이 맞지 않았고, 면접도 잘 보지 못했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 기대를 많이 하고 있진 않았고, &lt;u&gt;최종 면접에서 떨어졌다.&lt;/u&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;s&gt;카카오&lt;/s&gt;는 면접을 그럭저럭 보았고, 내심 기대하였는데 &lt;u&gt;결국, 최종 합격&lt;/u&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;u&gt;⚠️ 카카오 채용 자세한 후기는 아래 포스팅을 참조&lt;/u&gt;해주세요.&lt;/p&gt;
&lt;figure id=&quot;og_1669979866981&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;2023 Kakao Blind 신입 공채 최종 합격 후기 (프로그래밍 직무)&quot; data-og-description=&quot;길고 긴 두달 간의 채용 전형이 마무리 되었고, 드디어 학부생 때 부터 꿈에 그리던 카카오에 합격하였다. 11월 24일로 결과 발표가 예정되어 있었지만, 오전에 결과가 발표되지 않았고, 일부 지&quot; data-og-host=&quot;codingnotes.tistory.com&quot; data-og-source-url=&quot;https://codingnotes.tistory.com/235&quot; data-og-url=&quot;https://codingnotes.tistory.com/235&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/3Lz7w/hyQKS48UBK/lu4HWUFd5dKhaCDIsZxxHK/img.png?width=800&amp;amp;height=231&amp;amp;face=0_0_800_231,https://scrap.kakaocdn.net/dn/2UdFU/hyQK2Ns1NH/IY6h2u7ipNXFYJEy2Z0jfK/img.png?width=800&amp;amp;height=231&amp;amp;face=0_0_800_231&quot;&gt;&lt;a href=&quot;https://codingnotes.tistory.com/235&quot; data-source-url=&quot;https://codingnotes.tistory.com/235&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/3Lz7w/hyQKS48UBK/lu4HWUFd5dKhaCDIsZxxHK/img.png?width=800&amp;amp;height=231&amp;amp;face=0_0_800_231,https://scrap.kakaocdn.net/dn/2UdFU/hyQK2Ns1NH/IY6h2u7ipNXFYJEy2Z0jfK/img.png?width=800&amp;amp;height=231&amp;amp;face=0_0_800_231');&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;2023 Kakao Blind 신입 공채 최종 합격 후기 (프로그래밍 직무)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;길고 긴 두달 간의 채용 전형이 마무리 되었고, 드디어 학부생 때 부터 꿈에 그리던 카카오에 합격하였다. 11월 24일로 결과 발표가 예정되어 있었지만, 오전에 결과가 발표되지 않았고, 일부 지&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;codingnotes.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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;❗️취준Tip&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;본인이 가려는 회사가 어떤 유형의 회사인지 고민 (서비스 기업/제조사/SI/은행, 대기업/스타트업 &amp;hellip;) &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고민하는 과정에서 본인의 개발 가치관에 대해서도 고민할 수 있고, 회사 유형에 따라 인재상이 다르기 때문에 앞으로 준비해야할 스펙들이 조금 더 명확해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프로젝트 수 보다는 퀄리티&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개의 프로젝트를 했냐를 중요시하는 기업도 있지만, &lt;u&gt;서비스 기업의 경우 하나의 프로젝트라도 얼마나 고민을 했냐를 더 중요시&lt;/u&gt;합니다. 또한, 프로젝트 수가 적어도 붙은 분들이 꽤 있으니까 너무 부담갖지 마시고, &lt;u&gt;천천히 깊숙히 고민하며 프로젝트를 진행&lt;/u&gt;하시면 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 프로젝트 수가 많으면 오히려 답변하기 어렵기 때문에, 저는 확실하게 답변할 수 있는 2 ~ 3개 정도만 어필했습니다.&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;u&gt;&lt;b&gt;⚠️ 프로젝트 기획의도도 중요합니다!&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;포트폴리오, Git, 블로그 다듬기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;포트폴리오, Git, 블로그를 꾸준히 작성하고 다듬어 가세요. &lt;/u&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;u&gt;&lt;b&gt;⚠️ 레포마다 Readme.md도 잘 정리&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;지원은 다다익선&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 당장 부족하더라도 &lt;u&gt;기업에 꾸준히 지원하면서 코딩 테스트에 참여하고 자기소개서를 많이 쓰는 것도 중요&lt;/u&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;CS 학습&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;CS 스터디를 꼭 꼭 꼭 꼭 꼭 하세요.&lt;/u&gt; 코딩 테스트는 한 관문일 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩 테스트를 지나고 나면 &lt;s&gt;CS 테스트&lt;/s&gt;나 &lt;s&gt;기술 인터뷰&lt;/s&gt;가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통과하려면 &lt;u&gt;꾸준한 CS 스터디가 중요&lt;/u&gt;합니다. 하지만, 단 기간의 학습은 꼬리 질문에 답변하기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;여러 명이 면접 질문 리스트를 만들어서 다 같이 질문을 채워나가고, 스터디를 통해서는 좀 더 깊숙한 지식을 채워나가는 것을 추천&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개발자에 대한 깊은 고민&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인성 인터뷰를 통과하려면 협업 경험, 소통 역량도 중요하지만, 개발에 얼마나 진심이고, 얼마나 고민했냐를 보는 질문들을 하십니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소에 &lt;u&gt;내가 왜 개발을 하고 싶은지, 좋은 개발자란 무엇이고, 이를 위해 나는 어떤 노력을 하고 있는지 등 개발 철학과 가치관에 대해 깊은 고민이 필요&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;그간의 경험 정리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접 전형에서는 본인이 어떻게 학습해왔고, 어떤 경험을 했는지, 어떤 가치관을 가지고 있는 지, 내가 어떤 경쟁력을 갖고 있는지 짧은 시간 동안 보여주어야 합니다. 그 간 잘 해왔어도 이 부분에 대한 정리가 부족하면, 면접관에게 어필하기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소에 꾸준히 정리하기! &lt;u&gt;나에게 장점이 없어 보여도, 프로젝트가 너무 부실해보여도 고민하다보면, 분명 분명 어필할 점이 있습니다!&lt;/u&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;u&gt;&lt;b&gt;⚠️ 어찌보면 프로젝트보다도 더 중요! 이미 당신은 준비되어 있을 지 모릅니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;코딩 테스트라도 봐야지&quot;하고 붙은 분들이 꽤 있었습니다.&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;멀리 가고 싶다면 함께 가라&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혼자 취업을 준비하기에는 너무 많은 양입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;최대한 여러 명이 정보를 공유하고, 자료를 쌓아나가 효율적으로 준비&lt;/u&gt;하기!&lt;/p&gt;</description>
      <category>후기</category>
      <author>길민호(ethan.mino)</author>
      <guid isPermaLink="true">https://codingnotes.tistory.com/237</guid>
      <comments>https://codingnotes.tistory.com/237#entry237comment</comments>
      <pubDate>Thu, 1 Dec 2022 18:57:55 +0900</pubDate>
    </item>
    <item>
      <title>22.11.15 SSAFY 스터디 CS 발표 - 디자인 패턴 (전략 패턴)</title>
      <link>https://codingnotes.tistory.com/236</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;❓&amp;nbsp;전략 패턴 (Strategy)&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;객체들이 할 수 있는 행위 각각에 대해 전략 클래스를 생성하고, 유사한 행위들을 캡슐화 하는 인터페이스를 정의하여, 객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정하지 않고 전략을 바꿔주기만 함으로써 행위를 유연하게 확장하는 방법을 말합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전략 패턴도 마찬가지로 &lt;u&gt;단계별로 설명&lt;/u&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;1단계 (상속을 이용한 Simple Duck)&lt;/h3&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yvajq/btrSBnoftrV/LvRhakm9CTCMYoaHpnH1IK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yvajq/btrSBnoftrV/LvRhakm9CTCMYoaHpnH1IK/img.png&quot; data-alt=&quot;오리시뮬레이션 게임의 클래스 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yvajq/btrSBnoftrV/LvRhakm9CTCMYoaHpnH1IK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyvajq%2FbtrSBnoftrV%2FLvRhakm9CTCMYoaHpnH1IK%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;398&quot; height=&quot;249&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오리시뮬레이션 게임의 클래스 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;quck()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 꽥꽥거리는 소리를 내는 메서드&lt;/li&gt;
&lt;li&gt;&lt;s&gt;swim()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 헤엄치는 메서드&lt;/li&gt;
&lt;li&gt;&lt;s&gt;display()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 오리의 고유한 모양을 화면에 보여주는 메서드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;Duck&lt;/s&gt;이라는&lt;span&gt;&amp;nbsp;&lt;/span&gt;추상 클래스가 있고, &lt;u&gt;여러 유형의 오리가 Duck 클래스로부터 상속&lt;/u&gt;을 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;모든 오리가 꽥꽥 소리를 낼 수 있고, 헤엄을 칠 수 있으므로&lt;/u&gt;, &lt;s&gt;quack()&lt;/s&gt;과 &lt;s&gt;swim()&lt;/s&gt;은 &lt;u&gt;슈퍼 클래스에 작성&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, &lt;u&gt;오리별로 모양이 다르므로, 오리를 화면에 보여주는&lt;/u&gt; &lt;s&gt;display() 메서드&lt;/s&gt;는 &lt;u&gt;추상 메서드로 선언&lt;/u&gt;하여, &lt;u&gt;서브 클래스에서 반드시 구현하도록 하였습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;374&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NMda7/btrSxPTCB0R/jC7GP4ZktWdLKg9gSkJyI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NMda7/btrSxPTCB0R/jC7GP4ZktWdLKg9gSkJyI1/img.png&quot; data-alt=&quot;fly() 기능을 추가한 클래스 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NMda7/btrSxPTCB0R/jC7GP4ZktWdLKg9gSkJyI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNMda7%2FbtrSxPTCB0R%2FjC7GP4ZktWdLKg9gSkJyI1%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;374&quot; height=&quot;250&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;374&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;fly() 기능을 추가한 클래스 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;u&gt;오리들이 날 수 있도록 하기 위해서&lt;/u&gt; Duck 클래스에 &lt;s&gt;fly() 메서드&lt;/s&gt;&lt;u&gt;를 추가&lt;/u&gt;하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, &lt;u&gt;모든 서브 클래스에서 fly()를 상속&lt;/u&gt; 받습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnENjI/btrSCv6665k/hq1ZdZUKrcAQKeKLpOH8ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnENjI/btrSCv6665k/hq1ZdZUKrcAQKeKLpOH8ak/img.png&quot; data-alt=&quot;고무오리(Rubber Duck)을 추가한 클래스 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnENjI/btrSCv6665k/hq1ZdZUKrcAQKeKLpOH8ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnENjI%2FbtrSCv6665k%2Fhq1ZdZUKrcAQKeKLpOH8ak%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;552&quot; height=&quot;278&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;고무오리(Rubber Duck)을 추가한 클래스 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, Duck이라는 슈퍼 클래스에 fly() 메서드를 추가하면서, &lt;u&gt;날아다니면 안 되는 오리(고무오리, RubberDuck)에게도 날아다니는 기능이 추가되었습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;즉, 일부 서브 클래스에 적합하지 않은 행동이 추가 되었습니다.&lt;/u&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-filename=&quot;Untitled.png&quot; data-origin-width=&quot;166&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHwVUd/btrSxFwFxJr/onNqIPZuuisMBNiAgEsd21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHwVUd/btrSxFwFxJr/onNqIPZuuisMBNiAgEsd21/img.png&quot; data-alt=&quot;RubberDuck 클래스 내부&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHwVUd/btrSxFwFxJr/onNqIPZuuisMBNiAgEsd21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHwVUd%2FbtrSxFwFxJr%2FonNqIPZuuisMBNiAgEsd21%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;166&quot; height=&quot;130&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;166&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RubberDuck 클래스 내부&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서, &lt;s&gt;RubberDuck&lt;/s&gt;&lt;u&gt;의 fly() 메서드가 호출되었을 때, 아무것도 하지 않도록 오버라이드&lt;/u&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;하지만, 임원진이 앞으로 6개월마다 제품을 업데이트하기로 결정했습니다.&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;u&gt;상속을 계속 활용한다면, 규격이 바뀔 때마다 프로그램에 추가했던 Duck 클래스의 메서드들을 일일이 살펴보고 상황에 따라 오버라이드&lt;/u&gt; 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(예를 들면, 나무로 된 가짜 오리는 날 수도, 소리를 낼 수도 없기 때문에 quack()과 fly() 메서드가 아무것도 하지 않도록 오버라이딩 해주어야 합니다.)&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;2단계 (행동 인터페이스)&lt;/h3&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;u&gt;상속이 옳은 방법이 아니라는 사실을 깨달았습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;u&gt;fly()를 Duck 슈퍼 클래스에서 빼고&lt;/u&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;Flyable 인터페이스&lt;/s&gt;를 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 &lt;u&gt;날 수 있는 오리만 Flyable 인터페이스를 구현해서 날아다니는 행동, 즉, fly() 메서드를 넣을 수 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;s&gt;quack()&lt;/s&gt;도 마찬가지)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SCJDT/btrSySh19dv/9hqcY1ZFRRFzLJreXCbS3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SCJDT/btrSySh19dv/9hqcY1ZFRRFzLJreXCbS3k/img.png&quot; data-alt=&quot;행동 인터페이스를 추가한 클래스 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SCJDT/btrSySh19dv/9hqcY1ZFRRFzLJreXCbS3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSCJDT%2FbtrSySh19dv%2F9hqcY1ZFRRFzLJreXCbS3k%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;585&quot; height=&quot;320&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;행동 인터페이스를 추가한 클래스 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, &lt;u&gt;이러한 설계의 경우, 각 날아다니는 방식의 코드를 재사용할 수 없다는 문제&lt;/u&gt;가 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;ADuck 클래스&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;BDuck 클래스&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;CDuck 클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;날아다니는 방식&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;(가)&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;(나)&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;(나)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 경우, &lt;u&gt;Flyable 인터페이스의 fly() 메서드가&lt;/u&gt; &lt;s&gt;디폴트 메서드&lt;/s&gt;이고, &lt;s&gt;(나)&lt;/s&gt;&lt;u&gt; 방식으로 작성되어 있다고 하더라도,&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;(가)&lt;/s&gt;&lt;u&gt; 방식으로 날아다니는 오리 클래스 &lt;/u&gt;&lt;s&gt;DDuck 클래스&lt;/s&gt;&lt;u&gt;가 새로 추가 된다면&lt;/u&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;(가)&lt;/s&gt;&lt;u&gt; 방식으로 날아다니는 fly() 메서드는 &lt;/u&gt;&lt;s&gt;ADuck 클래스&lt;/s&gt;&lt;u&gt;와 &lt;/u&gt;&lt;s&gt;DDuck 클래스&lt;/s&gt;&lt;u&gt;에서 중복&lt;/u&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, ADuck 클래스와 DDuck 클래스의 fly() 메서드는 재사용할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;u&gt;코드 관리에 커다란 문제가 발생&lt;/u&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;s&gt;(가)&lt;/s&gt; &lt;u&gt;방식이 수정되는 경우&lt;/u&gt;, &lt;s&gt;(가)&lt;/s&gt;&lt;u&gt; 방식으로 날아다니는 오리 클래스들의 fly() 메서드를 일일이 모두 수정해주어야 하고, 그 과정에서 새로운 버그가 생길 가능성도 있습니다.&lt;/u&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;3단계 (행동 클래스 집합)&lt;/h3&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;3단계는 아래와 같은 디자인 원칙을 적용한 방식입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분과 분리한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 디자인 원칙 가운데 첫 번째 원칙으로, &lt;u&gt;모든 디자인 패턴의 기반을 이루는 원칙&lt;/u&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;u&gt;달라지는 부분을 찾아서 나머지 코드에 영향을 주지 않도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;캡슐화한다면, 코드를 변경하는 과정에서 달라지지 않는 부분에는 영향을 미치지 않으면서, 의도치 않게 발생하는 일을 줄이고, 시스템의 유연성을 향상&lt;/u&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;s&gt;fly()&lt;/s&gt;와 &lt;s&gt;quack()&lt;/s&gt;는 &lt;u&gt;오리의 종류에 따라 달라지지만&lt;/u&gt;, &lt;u&gt;나머지 부분(display(), swim()) 은 자주 달라지지 않거나 바뀌지 않습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;따라서, Duck 클래스는 그대로 두는 것이 좋습니다.&lt;/u&gt;&lt;u&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;fly()&lt;/s&gt;와 &lt;s&gt;quack()&lt;/s&gt;&lt;u&gt;을 Duck 클래스로부터 분리하려면 2개의 메서드를 Duck 클래스에서 모두 끄집어내고, 각 행동을 나타낼 클래스 집합을 새로 만들어야 합니다.&lt;/u&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;u&gt;나는 행동과 꽥꽥거리는 행동을 구현하는 클래스 집합은 어떻게 디자인&lt;/u&gt;해야 할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;Duck의 인스턴스에 행동을 할당할 수 있도록 해서, 최대한 유연하게 만드는 것이 좋습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, &lt;s&gt;MallardDuck&lt;/s&gt; &lt;u&gt;인스턴스를 특정 형식의 나는 행동으로 초기화 하거나, 오리의 행동을 동적으로 바꿀 수 있도록 디자인&lt;/u&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;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현보다는 인터페이스에 맞춰서 프로그래밍한다.&lt;/p&gt;
&lt;/blockquote&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;s&gt;SOLID 원칙&lt;/s&gt; 중&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;의존관계 역전 원칙&lt;/s&gt;을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &amp;ldquo;&lt;u&gt;구현클래스가 아닌, 인터페이스에 의존하도록 해야 한다.&amp;rdquo;라는 걸 의미&lt;/u&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;u&gt;핵심은 실제 실행 시에 쓰이는 객체가 코드에 고정되지 않도록 상위 형식에 맞춰 프로그래밍하여 다형성을 활용해야 한다&lt;/u&gt;는 점에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;인터페이스 즉, 역할에 의존하게 되면 유연하게 구현체를 바꿀수 있게 됩니다.&lt;/u&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;⚠️ 구현 의존 VS 인터페이스 의존으로 이동!&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;u&gt;각 행동은 &lt;/u&gt;&lt;s&gt;인터페이스&lt;/s&gt;&lt;u&gt;(ex. FlyBehavior, QuackBehavior)로 표현하도록 수정&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, &lt;u&gt;이제 나는 행동과 꽥꽤거리는 행동은 Duck 클래스에서 구현하는 것이 아니라, 특정 행동만을 목적으로 하는 클래스 집합에서 구현&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;284&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bweyly/btrSz7sxFyg/mgMPXwmIFW3xi5lujLhGqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bweyly/btrSz7sxFyg/mgMPXwmIFW3xi5lujLhGqk/img.png&quot; data-alt=&quot;두 번째 디자인 원칙을 고려한 fly 행동 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bweyly/btrSz7sxFyg/mgMPXwmIFW3xi5lujLhGqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbweyly%2FbtrSz7sxFyg%2FmgMPXwmIFW3xi5lujLhGqk%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;284&quot; height=&quot;267&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;284&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;두 번째 디자인 원칙을 고려한 fly 행동 설계&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;새로운 디자인을 사용하면, &lt;u&gt;나는 행동과 꽥꽥거리는 행동은 특정 행동 인터페이스를 구현한 별도의 클래스 안에 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;따라서, Duck 클래스에서는 그 행동을 구체적으로 구현할 필요가 없습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDbQHv/btrSCGtWeIx/5wISUPVqdxzSNy6rLF7Kdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDbQHv/btrSCGtWeIx/5wISUPVqdxzSNy6rLF7Kdk/img.png&quot; data-alt=&quot;두 번째 디자인 원칙을 고려한 fly, quack 행동 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDbQHv/btrSCGtWeIx/5wISUPVqdxzSNy6rLF7Kdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDbQHv%2FbtrSCGtWeIx%2F5wISUPVqdxzSNy6rLF7Kdk%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;649&quot; height=&quot;255&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;두 번째 디자인 원칙을 고려한 fly, quack 행동 설계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설계에서는 &lt;s&gt;FlyBehavior&lt;/s&gt;와 &lt;s&gt;QuackBehavior&lt;/s&gt;라는 2개의 &lt;u&gt;인터페이스를 사용&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;그리고 구체적으로 핼동을 구현하는 클래스들이 있습니다.&lt;/u&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;또한, FlyBehavior과 QuackBehavior 인터페이스에는 &lt;u&gt;구현 클래스에서 반드시 구현하도록 특정 행동을 나타내는 메서드가 선언&lt;/u&gt;되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;각 구현 클래스는 특정 행동에 맞춰서 이 메서드를 구현&lt;/u&gt;하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;이런 식으로 디자인하면, 다른 형식의 객체에서도 나는 행동과 꽥꽥거리는 행동을 재사용&lt;/u&gt;할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, &lt;u&gt;기존의 행동 클래스를 수정하거나 날아다니는 행동을 사용하는 Duck 클래스를 전혀 건드리지 않고도 새로운 행동을 추가&lt;/u&gt;할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;즉, 상속의 부담을 떨쳐 버리고도, 재사용의 장점을 누릴 수 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;❓ 매번 바뀔 수 있는 부분을 찾아낸 후, 바뀌는 것과 바뀌지 않는 것을 분리해서 캡슐화하는 식으로 작업해야 하나요?&lt;/h4&gt;
&lt;pre id=&quot;code_1669872284702&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;✋ 언제나 그렇게 해야 하는 것은 아닙니다. 
애플리케이션을 디자인 하는 과정에서 바뀔 수 있는 부분을 예측하고 대처해서 유연한 코드를 만들 수도 있습니다. 
여기에서 설명하는 원칙과 패턴은 개발 라이프사이클 어느 단계에서든지 적용할 수 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;❓ 행동만 나타내는 클래스를 만든다는 게 이상하게 느껴지네요, 클래스는 원래 어떤 대상을 나타내는 것 아닌가요? 클래스에는 상태와 행동이 모두 들어있어야 하지 않나요?&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1669872319508&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;✋ 객체지향 시스템에서는 질문한 내용이 맞습니다. 
클래스는 일반적으로 상태(인스턴스 변수)와 메서드를 모두 가지고 있습니다. 
그런데, 이 경우에는 클래스가 '행동'을 가지고 있습니다. 
하지만, 행동에도 여전히 상태와 메서드가 들어 있을 수 있습니다. 
fly 행동에 속성(ex. 1분당 날개를 펄럭이는 횟수, 최고 높이, 속도)을 나타내는 인스턴스 변수를 넣을 수도 있으니까요.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4단계 (동적으로 행동 지정하기)&lt;/h3&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;u&gt;나는 행동과 꽥꽥거리는 행동을 Duck 클래스 또는 서브 클래스에서 정의한 메서드를 써서 구현하지 않고, 다른 클래스에 위임한다는 것&lt;/u&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;u&gt;동적으로 오리의 행동을 지정해주기 위해, Duck 클래스에 &lt;/u&gt;&lt;s&gt;flyBehavior&lt;/s&gt;&lt;u&gt;과 &lt;/u&gt;&lt;s&gt;quackBehavior&lt;/s&gt;&lt;u&gt;라는 인터페이스 타입의 멤버 변수를 추가&lt;/u&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;u&gt;각 오리 객체는 실행 시 이 행동 인터페이스 타입의 레퍼런스 변수에 특정 행동 객체(FlyWithWings 등)를 설정&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 나는 행동과 꽥꽥거리는 행동은 FlyBehavior와 QuackBehavior 인터페이스로 옮겨놨으므로, &lt;u&gt;Duck 클래스와 모든 서브 클래스에서 fly()와 quack() 메서드를 제거하고, Duck 클래스에 performFly()와 performQuack()을 추가&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;171&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1XgVG/btrSyp1wfcy/zPPubMtv0zc6utQNYcEAk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1XgVG/btrSyp1wfcy/zPPubMtv0zc6utQNYcEAk1/img.png&quot; data-alt=&quot;Duck 클래스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1XgVG/btrSyp1wfcy/zPPubMtv0zc6utQNYcEAk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1XgVG%2FbtrSyp1wfcy%2FzPPubMtv0zc6utQNYcEAk1%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;171&quot; height=&quot;164&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;171&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Duck 클래스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼, 이러한 형태의 Duck 클래스가 만들어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼, &lt;u&gt;flyBehavior 레퍼런스 변수에는 언제 어떤 객체가 들어갈까요?&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, &lt;u&gt;Duck 클래스에 행동 객체를 파라미터로 전달하는 &lt;/u&gt;&lt;s&gt;setter() 메서드&lt;/s&gt;&lt;u&gt;를 정의한 후,&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;실행 시 동적으로 오리의 행동을 지정하는 방식&lt;/u&gt;을 생각해볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;interface QuackBehavior {
	public void quack();
}

interface FlyBehavior {
	public void fly();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Quack implements QuackBehavior {
	public void quack() {
		System.out.println(&quot;Quack&quot;);
	}
}

class MuteQuack implements QuackBehavior {
	public void quack() {
		System.out.println(&quot;&amp;lt;&amp;lt; Silence &amp;gt;&amp;gt;&quot;);
	}
}

class FlyRocketPowered implements FlyBehavior {
	public void fly() {
		System.out.println(&quot;I'm flying with a rocket&quot;);
	}
}

class FlyNoWay implements FlyBehavior {
	public void fly() {
		System.out.println(&quot;I can't fly&quot;);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;abstract class Duck {
	FlyBehavior flyBehavior;
	QuackBehavior quackBehavior;

	public Duck() {}

	abstract void display();

	public void setFlyBehavior(FlyBehavior fb) {
		flyBehavior = fb;
	}

	public void setQuackBehavior(QuackBehavior qb) {
		quackBehavior = qb;
	}
	
	public void performFly() {
		flyBehavior.fly();
	}

	public void performQuack() {
		quackBehavior.quack();
	}

	public void swim() {
		System.out.println(&quot;All ducks float, even decoys!&quot;);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class MallardDuck extends Duck {

	public MallardDuck() {}

	public void display() {
		System.out.println(&quot;I'm a real Mallard duck&quot;);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class MiniDuckSimulator1 {
 
    public static void main(String[] args) {
            Duck mallard = new MallardDuck();
            mallard.setFlyBehavior(new FlyRocketPowered());
            mallard.setQuackBehavior(new MuteQuack());
            mallard.performQuack();
            mallard.performFly();

            System.out.println();

            // setter() 메서드를 이용하여 날아다니는 행동, 소리를 내는 행동을 교체
            mallard.setFlyBehavior(new FlyNoWay());
            mallard.setQuackBehavior(new Quack());
            mallard.performQuack();
            mallard.performFly();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;&amp;lt;&amp;lt; Silence &amp;gt;&amp;gt;
I'm flying with a rocket

Quack
I can't fly
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;performFly()&lt;/s&gt;와 &lt;s&gt;performQuack()&lt;/s&gt; 메서드에서는 Duck 클래스, 즉 슈퍼 클래스로부터 상속받은 &lt;u&gt;flyBehavior와 quackBehavior에 의해 참조되는 객체의 &lt;/u&gt;&lt;s&gt;fly()&lt;/s&gt;&lt;u&gt;와 &lt;/u&gt;&lt;s&gt;quack()&lt;/s&gt; &lt;u&gt;메서드를 호출하여 행동을 위임&lt;/u&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;u&gt;생성자에서 초기화하는 방식&lt;/u&gt;을 생각해볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;abstract class Duck {
    private FlyBehavior flyBehavior;
	private QuackBehavior quackBehavior;

    public Duck() {}
    public Duck(QuackBehavior quackBehavior, FlyBehavior flyBehavior){
        this.quackBehavior = quackBehavior;
        this.flyBehavior = flyBehavior;
    }
    
    abstract void display();

    public void performFly() {
        flyBehavior.fly();
    }

    public void performQuack() {
        quackBehavior.quack();
    }

    public void swim() {
        System.out.println(&quot;All ducks float, even decoys!&quot;);
    }
}

class MallardDuck extends Duck{
    public MallardDuck(QuackBehavior quackBehavior, FlyBehavior flyBehavior){
        super(quackBehavior, flyBehavior);
    }

    @Override
    void display() {

    }
}

class MiniDuckSimulator1 {
    public static void main(String[] args) {
        QuackBehavior muteQuack = new MuteQuack();
        FlyBehavior flyRocketPowerd = new FlyRocketPowered();

        Duck mallard = new MallardDuck(muteQuack, flyRocketPowerd);
        mallard.performQuack();
        mallard.performFly();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;setter()&lt;/s&gt; 메서드 방식의 경우, &lt;u&gt;실행 시에 언제나 오리의 행동을 바꿀 수 있지만, setter() 메서드가 아니라 생성자에서 오리의 행동을 지정 해준다면, 오리 객체의 행동을 실행 시에 결정하고 난 이후 임의로 오리의 행동을 수정할 수 없도록 할 수 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;u&gt;단, 슈퍼 클래스의 오리 행동 객체를 &lt;/u&gt;&lt;s&gt;private&lt;/s&gt;&lt;u&gt;로 설정&lt;/u&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;2922&quot; data-origin-height=&quot;1108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4SrJ7/btrSAxrcid3/WNI38ZvNfqkDFmJp9ekcJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4SrJ7/btrSAxrcid3/WNI38ZvNfqkDFmJp9ekcJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4SrJ7/btrSAxrcid3/WNI38ZvNfqkDFmJp9ekcJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4SrJ7%2FbtrSAxrcid3%2FWNI38ZvNfqkDFmJp9ekcJ0%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;700&quot; height=&quot;265&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;2922&quot; data-origin-height=&quot;1108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마지막 단계까지 마쳤을 때, 프로젝트는 이러한 모습의 구조가 됩니다.&lt;/span&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;구현 의존 VS 인터페이스 의존&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daTwNE/btrSyi2qkLk/Bh0QXOCjM5XXZ4K9ETWxk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daTwNE/btrSyi2qkLk/Bh0QXOCjM5XXZ4K9ETWxk0/img.png&quot; data-alt=&quot;Animal, Dog, Cat 클래스 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daTwNE/btrSyi2qkLk/Bh0QXOCjM5XXZ4K9ETWxk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaTwNE%2FbtrSyi2qkLk%2FBh0QXOCjM5XXZ4K9ETWxk0%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;258&quot; height=&quot;360&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Animal, Dog, Cat 클래스 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;Animal&lt;/s&gt;이라는 &lt;s&gt;추상 클래스&lt;/s&gt;가 있고, &lt;s&gt;Dog&lt;/s&gt;와 &lt;s&gt;Cat&lt;/s&gt;이라는 서브 클래스가 있다고 가정해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현에 의존하여 프로그래밍한다면 다음과 같이 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;Dog dog = new Dog();
d.bark();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이러한 방식의 경우, &lt;u&gt;Animal의 구현체를 유연하게 바꿀 수 없다는 단점이 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;Animal animal = new Dog();
animal.makeSound();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 Dog라는 구현체가 아닌 &lt;u&gt;Animal 인터페이스에 의존한 예시&lt;/u&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, &lt;u&gt;Animal 추상 클래스의 구현체를 갈아낄 수 있다는 장점&lt;/u&gt;이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, &lt;u&gt;이 경우에도 구현체를 갈아끼려면 코드의 수정이 필요&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, &lt;u&gt;아래와 같은 코드가 더 바람직한 방법&lt;/u&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;Animal animal = getAnimal();
animal.makeSound();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;구현체를 넣어주는 코드를 직접 작성해주는 대신, 실행 시에 대입하는 방식을 통해서, 기존 코드를 수정하지 않아도 구현체를 바꿔줄 수 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전략 패턴의 장단점&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. &lt;u&gt;전략 사용자(context)의 코드 변경 없이 새로운 전략을 추가&lt;/u&gt; 할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669872556704&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;따라서 &quot;확장에는 열려있으나 변경에는 닫혀있어야 한다&quot;를 의미하는 Open/Close Prinipal을 준수할 수 있습니다. 
또한, if - else 분기를 제거할 수 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.&amp;nbsp;&lt;b&gt;&lt;u&gt;런타임에 전략을 변경&lt;/u&gt;시킬 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. &lt;u&gt;Strategy 객체와 Composition클래스 객체 사이에 의사소통 오버헤드가 발생&lt;/u&gt;할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669872587675&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;서브클래스에서 구현할 알고리즘의 복잡함과는 상관없이 모든 ConcreteStrategy 클래스는 Strategy 인터페이스를 공유한다. 
따라서 어떤 ConcreteStrategy 클래스는 이 인터페이스를 통해 들어온 모든 매개변수를 다 사용하지 않는데도 전달받아야 할 때가 생긴다. 
즉, 사용되지도 않을 매개변수를 Composition 객체가 생성하고 초기화하는 경우가 발생할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.&amp;nbsp;&lt;b&gt;&lt;u&gt;객체 수가 증가&lt;/u&gt;한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669872604352&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Strategy들로 생성하는 객체 수가 증가한다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참조&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Head First Design Pattern Chapter.01 디자인 패턴 소개와 전략 패턴&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.notion.so/f8da6f35f2ae4f218dcfa1abec918afa&quot;&gt;객체지향 설계 5원칙 SOLID (이상현)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devnest.tistory.com/3&quot;&gt;디자인패턴 - 전략 패턴(Strategy Pattern) in Javascript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;b&gt;&lt;a href=&quot;https://velog.io/@kyle/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%A0%84%EB%9E%B5%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80&quot;&gt;디자인 패턴 : 전략패턴이란?&lt;/a&gt;&lt;/b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>스터디</category>
      <author>길민호(ethan.mino)</author>
      <guid isPermaLink="true">https://codingnotes.tistory.com/236</guid>
      <comments>https://codingnotes.tistory.com/236#entry236comment</comments>
      <pubDate>Thu, 1 Dec 2022 14:07:25 +0900</pubDate>
    </item>
    <item>
      <title>2023 카카오 블라인드 신입 공채 프로그래밍 직무 최종 합격 후기 (1, 2차 코딩 테스트, 1, 2차 면접)</title>
      <link>https://codingnotes.tistory.com/235</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⚠️&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;u&gt;자세한 취업 과정은 아래 포스트를 참고&lt;/u&gt;해주세요!&lt;/p&gt;
&lt;figure id=&quot;og_1670474442242&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;서버 개발자 신입의 취뽀 후기 (Feat. Kakao 최종합격, SSAFY 8기 후기)&quot; data-og-description=&quot;그동안의 개발자 생활을 정리하고, 하소연하는 게시글입니다. ⚠️ 개인적인 경험, 의견이므로 참고만 하세요!   학교 생활 군 입대 전, 나는 프로그래밍에 큰 흥미를 느끼지 못했고, 학점은 1.&quot; data-og-host=&quot;codingnotes.tistory.com&quot; data-og-source-url=&quot;https://codingnotes.tistory.com/237&quot; data-og-url=&quot;https://codingnotes.tistory.com/237&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eLB3S/hyQPdHnBHs/tHkkS0rLeZFRDKEK2N9gF1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/Zs2wy/hyQO44KLc0/f3ZMGQJElET7eLkek1FFd0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/ehUa5f/hyQPaw7odG/1tOnNlyjBoWKFxwaoJknb0/img.png?width=750&amp;amp;height=217&amp;amp;face=0_0_750_217&quot;&gt;&lt;a href=&quot;https://codingnotes.tistory.com/237&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://codingnotes.tistory.com/237&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eLB3S/hyQPdHnBHs/tHkkS0rLeZFRDKEK2N9gF1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/Zs2wy/hyQO44KLc0/f3ZMGQJElET7eLkek1FFd0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/ehUa5f/hyQPaw7odG/1tOnNlyjBoWKFxwaoJknb0/img.png?width=750&amp;amp;height=217&amp;amp;face=0_0_750_217');&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;서버 개발자 신입의 취뽀 후기 (Feat. Kakao 최종합격, SSAFY 8기 후기)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;그동안의 개발자 생활을 정리하고, 하소연하는 게시글입니다. ⚠️ 개인적인 경험, 의견이므로 참고만 하세요!   학교 생활 군 입대 전, 나는 프로그래밍에 큰 흥미를 느끼지 못했고, 학점은 1.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;codingnotes.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;1017&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCkSqZ/btrV55xkR46/RwSxojnxGJ9HBgGILp5ay1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCkSqZ/btrV55xkR46/RwSxojnxGJ9HBgGILp5ay1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCkSqZ/btrV55xkR46/RwSxojnxGJ9HBgGILp5ay1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCkSqZ%2FbtrV55xkR46%2FRwSxojnxGJ9HBgGILp5ay1%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;500&quot; height=&quot;769&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;1017&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길고 긴 두달 간의 채용 전형이 마무리 되었고, &lt;span&gt;드디어 학부생 때 부터&amp;nbsp;꿈에 그리던&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;카카오&lt;/s&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;11월 24일로 결과 발표가 예정되어 있었지만, 오전에 결과가 발표되지 않았고,&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;15시에 합격 결과가 발표되었고, 보자마자 SSAFY 강의실을 뛰쳐나와 가족들과 지인들에게 합격 소식을 알렸다.&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;s&gt;Server&lt;/s&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;u&gt;앞으로, 최고의 개발자들 옆에서 도태되지 않고, 성장하기 위해 그리고 더 많은 사용자들에게 편의를 제공하는 서비스를 개발하기 위해 끊임없이 노력할 것이다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1차 코딩 테스트&lt;/h4&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;1차 코딩 테스트는 &lt;u&gt;블라인드 채용 기출 문제보다 약간 쉬운 수준으로 출제&lt;/u&gt;되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;문제는 4문제를 풀었고&lt;/u&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;(직무 :&amp;nbsp;&lt;s&gt;프로그래밍&lt;/s&gt;, 풀이 언어 : &lt;s&gt;C++&lt;/s&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;s&gt;1차 코딩 테스트&lt;/s&gt; : 9월 24일, &lt;s&gt;결과 발표&lt;/s&gt; : 10월 04일 19:30)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2차 코딩 테스트&lt;/h4&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;s&gt;API&lt;/s&gt;를 사용하여 문제를 풀이해야하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API를 호출하고, 데이터를 가져오는 과정을 &lt;s&gt;Java&lt;/s&gt;로 하면,&amp;nbsp;시간이 더 걸릴 것 같아서 &lt;s&gt;Python&lt;/s&gt;을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 전에 Python을 다뤄본 적이 있어서, 1차 코딩 테스트 발표 후,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;간단히 Python 문법을 복기하고, 프로그래머스의 2차 코딩테스트 기출 문제를 풀어보았던게 많이 도움&lt;/u&gt;되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;(2차 코딩 테스트 전, 미리 API를 호출하는 모듈을 만들어놓았다)&lt;/u&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;첫 솔루션을 테스트 했을 때는 전체 24등을 기록했고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후, 최종 제출 한 시간 전까지 약 98등을 기록했지만, 이후 100등 이하로 떨어졌다.&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;u&gt;1차 코딩 테스트보다 난이도가 낮았고, 구현만 제대로 한다면, 점수를 얻어낼 수 있었다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;600점대인데, 2차 코딩 테스트를 통과&lt;/u&gt;하신 분이 있는 걸 보면, &lt;u&gt;합격 컷이 너무 높지는 않은 것 같다.&lt;/u&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;s&gt;895점&lt;/s&gt;, 풀이 언어 : &lt;s&gt;Python&lt;/s&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;s&gt;2차 코딩 테스트&lt;/s&gt; : 10월 8일, &lt;s&gt;결과 발표&lt;/s&gt; : 10월 17일 19:45)&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;u&gt;2차 코딩 테스트 깃헙 레포 주소&lt;/u&gt;입니다.&lt;/p&gt;
&lt;figure id=&quot;og_1674469565175&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - rlfalsgh95/2023_KAKAO_ASSIGNMENT_TEST: 2023 카카오 과제 테스트&quot; data-og-description=&quot;2023 카카오 과제 테스트. Contribute to rlfalsgh95/2023_KAKAO_ASSIGNMENT_TEST development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/rlfalsgh95/2023_KAKAO_ASSIGNMENT_TEST&quot; data-og-url=&quot;https://github.com/rlfalsgh95/2023_KAKAO_ASSIGNMENT_TEST&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jKkgQ/hyRm70MmGD/D4XhmoCkFOBDcOKgKNY3Hk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/rlfalsgh95/2023_KAKAO_ASSIGNMENT_TEST&quot; data-source-url=&quot;https://github.com/rlfalsgh95/2023_KAKAO_ASSIGNMENT_TEST&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jKkgQ/hyRm70MmGD/D4XhmoCkFOBDcOKgKNY3Hk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - rlfalsgh95/2023_KAKAO_ASSIGNMENT_TEST: 2023 카카오 과제 테스트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2023 카카오 과제 테스트. Contribute to rlfalsgh95/2023_KAKAO_ASSIGNMENT_TEST development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1차 인터뷰&lt;/h4&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;나는 1차 인터뷰 첫 날에 일정이 잡혔다. (10월 31일)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신입 공채 단톡방에 면접 스터디를 모집하는 분들이 있으셨고, 그 중 &lt;u&gt;SSAFY 교육생이 모집하는 스터디가 있어서 참여&lt;/u&gt;하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;자소서와 2차 코딩 테스트에 대한 간단한 솔류션을 공유하고, 그에 대한 예상 질문을 서로 작성&lt;/u&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;1차 인터뷰는 화상으로 진행되었고, &lt;u&gt;생각보다 기본적인 CS 질문을 하셨다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 간 해왔던 CS 스터디와 면접 스터디의 내용이 많이 나와서 나름 잘 답변하였다.&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;s&gt;지원서 제출&lt;/s&gt; : 10월 21일 ~ 10월 26일)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;s&gt;1차 인터뷰&lt;/s&gt; : 10월 31일 ~ 11월 03일, &lt;s&gt;결과 발표&lt;/s&gt; : 11월 9일 11:00)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2차 인터뷰&lt;/h4&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;2차 인터뷰도 인터뷰 첫 날에 일정이 잡혔다. (11월 14일)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;1차 인터뷰를 붙을 지 모르고 준비가 전혀 안된 상태에 첫 날에 일정이 잡혀서 멘붕이 왔었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;부랴 부랴 SSAFY 취업지원센터에 도움을 요청하여 방향을 잡았다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기술적 인성 면접이었기 때문에 기술 면접을 준비하지는 않았고, &lt;u&gt;인성 면접만 준비&lt;/u&gt;하였다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;u&gt;개발 가치관, 협업 경험, 갈등 해결 경험 등을 정리&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;2차 인터뷰는 편안한 분위기로 진행되었고, 긴장을 풀고 잘 답변하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;span&gt;(생각보다 정리했던 내용은 나오지 않았고, 예상하기 어려운 질문을 해주셨다&lt;/span&gt;&lt;/u&gt;&lt;u&gt;&lt;span&gt;)&lt;/span&gt;&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;인성 면접에서는 대부분의 인원이 합격할 거라 생각했지만, 오픈채팅방을 보니 &lt;u&gt;생각보다 많은 인원들이 탈락&lt;/u&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;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;&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;span&gt;(&lt;s&gt;2차 인터뷰&lt;/s&gt; : 11월 14일 ~ 11월 17일,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;결과 발표&lt;/s&gt; : 11월 24일 15:00)&lt;/p&gt;</description>
      <category>후기</category>
      <author>길민호(ethan.mino)</author>
      <guid isPermaLink="true">https://codingnotes.tistory.com/235</guid>
      <comments>https://codingnotes.tistory.com/235#entry235comment</comments>
      <pubDate>Thu, 1 Dec 2022 00:59:00 +0900</pubDate>
    </item>
    <item>
      <title>2022.11.09일 서버 개발자 취준생의 성장 일지 ✍ </title>
      <link>https://codingnotes.tistory.com/233</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rJ68U/btrQOl7OHpG/polwG6VlbUraXPBfUfKWAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rJ68U/btrQOl7OHpG/polwG6VlbUraXPBfUfKWAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rJ68U/btrQOl7OHpG/polwG6VlbUraXPBfUfKWAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrJ68U%2FbtrQOl7OHpG%2FpolwG6VlbUraXPBfUfKWAK%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;617&quot; height=&quot;222&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;s&gt;카카오&lt;/s&gt; 1차 인터뷰에 합격&lt;/u&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;SSAFY에서 했던 알고리즘/CS 스터디가 많이 도움된 것 같다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ &lt;s&gt;KT SW 역량 우수자 전형&lt;/s&gt;은 탈락했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Framework와 내 프로젝트에 대한 분석이 부족했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ &lt;s&gt;넥토리얼 2기&lt;/s&gt; 면접 결과는 21일 공개된다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마케팅 개발팀은 면접 시간이 짧았지만, 실무적인 부분까지 질문 하셨고, 잘 답변하지 못했기 때문에 탈락을 예상하고 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브 웹실 면접에서는 나름 기본적인 CS와 인성 질문에 대해 답변했기 때문에 결과를 기다리는 중이다.&lt;/p&gt;</description>
      <category>성장일지</category>
      <author>길민호(ethan.mino)</author>
      <guid isPermaLink="true">https://codingnotes.tistory.com/233</guid>
      <comments>https://codingnotes.tistory.com/233#entry233comment</comments>
      <pubDate>Wed, 9 Nov 2022 13:13:14 +0900</pubDate>
    </item>
    <item>
      <title>22.11.8 SSAFY 스터디 CS 발표 - 디자인 패턴 (템플릿 메서드 패턴)</title>
      <link>https://codingnotes.tistory.com/232</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;❓&lt;/span&gt; 템플릿 메서드 패턴이란?&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;템플릿 메서드 패턴은 알고리즘의 골격을 정의합니다. 템플릿 메서드를 사용하면, 알고리즘의 일부 단계를 서브 클래스에서 구현할 수 있으며, 알고리즘의 구조는 그대로 유지하면서 알고리즘의 특정 단계를 서브 클래스에서 재정의할 수도 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;템플릿 메소드&lt;/s&gt;는&amp;nbsp;&lt;u&gt;알고리즘의 템플릿(틀)&lt;/u&gt;로, &lt;u&gt;일련의 단계로 알고리즘을 정의한 메서드&lt;/u&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿 메서드는 &lt;u&gt;여러 단계 가운데 하나 이상의 단계가 &lt;/u&gt;&lt;s&gt;추상 메서드&lt;/s&gt;&lt;u&gt;로 정의&lt;/u&gt;되며, &lt;u&gt;추상 메서드는 서브 클래스에서 구현&lt;/u&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면, &lt;u&gt;서브 클래스가 일부분의 구현을 처리하게 하면서도 알고리즘의 구조는 바꾸지 않아도 됩니다.&lt;/u&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;❗️예 1 (커피와 홍차)&lt;/h3&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;u&gt;커피와 홍차는 매우 비슷한 방법으로 만들어집니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;prepareRecipe() 메서드&lt;/s&gt;는 &lt;u&gt;알고리즘의 절차를 정의한 메서드&lt;/u&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, &lt;u&gt;각 메소드는 알고리즘의 각 단계를 구현&lt;/u&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;s&gt;Coffee 클래스&lt;/s&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Coffee{
	void prepareRecipe(){
		boilWater();// 물을 끓인다.
		brewCoffeeGrinds(); // 끓는 물에 커피를 우려낸다.
		pourInCup(); // 커피를 컵에 따른다.
		addSugarAndMilk(); // 설탕과 우유를 추가한다.
	}
	
	public void boilWater() {
		System.out.println(&quot;물 끓이는 중&quot;);
	}
	
	public void brewCoffeeGrinds() {
		System.out.println(&quot;필터로 커피를 우려내는 중&quot;);
	}
	
	public void pourInCup() {
		System.out.println(&quot;컵에 따르는 중&quot;);
	}
	
	public void addSugarAndMilk() {
		System.out.println(&quot;설탕과 우유를 추가하는 중&quot;);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 홍차를 우려내는 &lt;s&gt;Tea 클래스&lt;/s&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tea 클래스는 두 번째 단계와 네 번째 단계가 조금 다르지만, &lt;u&gt;기본적으로 Coffee 클래스와 유사&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;steepTeaBag()&lt;/s&gt;, &lt;s&gt;addLemon()&lt;/s&gt;은 &lt;u&gt;홍차 전용 메서드&lt;/u&gt;이지만, &lt;s&gt;boilWater()&lt;/s&gt;와 &lt;s&gt;pourInCup()&lt;/s&gt;은 &lt;u&gt;Coffee 클래스와 동일&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;즉, 중복된 코드가 발생&lt;/u&gt;하였습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Tea{
	void prepareRecipe(){
		boilWater(); // 물을 끓인다.
		steepTeaBag(); // 끓는 물에 찻잎을 우려낸다.
		pourInCup(); // 홍차를 컵에 따른다.
		addLemon(); // 레몬을 추가한다.
	}
	
	public void boilWater() {
		System.out.println(&quot;물 끓이는 중&quot;);
	}
	
	public void steepTeaBag() { // 홍차 전용 메서드
		System.out.println(&quot;찻잎을 우려내는 중&quot;);
	}
	
	public void pourInCup() { 
		System.out.println(&quot;컵에 따르는 중&quot;);
	}
	
	public void addLemon() { // 홍차 전용 메서드
		System.out.println(&quot;레몬을 추가하는 중&quot;);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 &lt;u&gt;어떻게 하면 중복되는 코드를 줄일 수 있을까요?&lt;/u&gt; 단계 별로 설계를 수정해보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_Untitled.png&quot; data-origin-width=&quot;395&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ci8zwH/btrSyqMNkg7/NmnumatUMUFbSCp5ZayVVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ci8zwH/btrSyqMNkg7/NmnumatUMUFbSCp5ZayVVK/img.png&quot; data-alt=&quot;Coffee와 Tea의 수정된 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ci8zwH/btrSyqMNkg7/NmnumatUMUFbSCp5ZayVVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fci8zwH%2FbtrSyqMNkg7%2FNmnumatUMUFbSCp5ZayVVK%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;395&quot; height=&quot;258&quot; data-filename=&quot;edited_Untitled.png&quot; data-origin-width=&quot;395&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Coffee와 Tea의 수정된 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, &lt;u&gt;두 클래스의 공통된 부분을 추상화해서 &lt;/u&gt;&lt;s&gt;베이스 클래스&lt;/s&gt;&lt;u&gt;로 만드는 방법&lt;/u&gt;을 생각해볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 커피와 홍차 &lt;u&gt;모두 카페인 음료&lt;/u&gt;이기 때문에 &lt;s&gt;CaffeineBeverage&lt;/s&gt;를 &lt;u&gt;베이스 클래스로 두었고, 서브 클래스에서 메서드를 구현할 수 있도록 &lt;/u&gt;&lt;s&gt;추상 클래스&lt;/s&gt;&lt;u&gt;로 두었습니다.&lt;/u&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;s&gt;prepareRecipe()&lt;/s&gt;는 &lt;u&gt;서브 클래스마다 다르기 때문에 &lt;/u&gt;&lt;s&gt;추상 메서드&lt;/s&gt;&lt;u&gt;로 선언&lt;/u&gt;하였고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;boilWater()&lt;/s&gt;과 &lt;s&gt;pourInCup()&lt;/s&gt;&lt;u&gt;은 두 클래스에서 공통으로 사용되므로, CaffeineBeverage 클래스에 정의&lt;/u&gt;하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;서브 클래스는 prepareRecipe()를 오버라이드해서 구현&lt;/u&gt;하고, brewCoffeeGrinds()와 같이 &lt;u&gt;해당 클래스에만 존재하는 메서드는 서브 클래스에 그대로 두었습니다.&lt;/u&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;u&gt;현재 구조에서는 커피와 홍차의 제조 과정이 비슷함에도 서브 클래스에서 구현해야 된다는 단점이 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 커피와 홍차에 모두 적용시킬 수 있는 알고리즘입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;물을 끓인다.&lt;/li&gt;
&lt;li&gt;뜨거운 물을 사용해서 커피 또는 찻잎을 우려낸다.&lt;/li&gt;
&lt;li&gt;만들어진 음료를 컵에 따른다.&lt;/li&gt;
&lt;li&gt;각 음료에 맞는 첨가물을 추가한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 알고리즘은 커피와 홍차에 모두 적용시킬 수 있기 때문에, prepareRecipe(), 즉 &lt;u&gt;카페인 음료의 제조 절차를 베이스 클래스에 추상화&lt;/u&gt;할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 설계에서, &lt;s&gt;Coffee 클래스&lt;/s&gt;는 brewCoffeeGrinds()와 addSugarAndMilk() 메서드를 쓰고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;Tea 클래스&lt;/s&gt;는 steepTeaBag()과 addLemon()을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, &lt;u&gt;brewCoffeeGrinds()와 steepTeaBag()을 &lt;/u&gt;&lt;s&gt;brew() 메서드&lt;/s&gt;&lt;u&gt;로 일반화&lt;/u&gt;하고, &lt;u&gt;addSugarAndMilk()와 addLemon()은 &lt;/u&gt;&lt;s&gt;addCondiments()&lt;/s&gt;&lt;u&gt; 즉, 첨가물을 넣는 함수로 일반화&lt;/u&gt; 해주겠습니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public abstract class CaffeineBeverage{
	final void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		addCondiments();
	}
	
	abstract void brew();
	abstract void addCondiments();

	void boilWater() {
		System.out.println(&quot;물 끓이는 중&quot;);
	}
	
	void pourInCup() {
		System.out.println(&quot;컵에 따르는 중&quot;);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Tea extends CaffeineBeverage{
	@Override
	void brew() {
		System.out.println(&quot;찻잎을 우려내는 중&quot;);
	}

	@Override
	void addCondiments() {
		System.out.println(&quot;레몬을 추가하는 중&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Coffee extends CaffeineBeverage{
	@Override
	void brew() {
		System.out.println(&quot;필터로 커피를 우려내는 중&quot;);
	}

	@Override
	void addCondiments() {
		System.out.println(&quot;설탕과 우유를 추가하는 중&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;서브 클래스에 공통되는 메서드를 베이스 클래스에 정의&lt;/u&gt;해주었고, &lt;u&gt;서브 클래스마다 다르게 구현하는 brew()와 addCondiments() 메서드는 &lt;/u&gt;&lt;s&gt;추상 메서드&lt;/s&gt;&lt;u&gt;로 선언&lt;/u&gt;하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;서브 클래스는 CoffeinBeverage를 상속 받아서 추상 메서드를 구현&lt;/u&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;(CoffeinBeverage 클래스의 prepareRecipe() 메서드와 같은 &lt;s&gt;Concrete&amp;nbsp;메서드&lt;/s&gt;&lt;u&gt;는 &lt;/u&gt;&lt;s&gt;final&lt;/s&gt;&lt;u&gt;로 선언하여, 서브 클래스에서 재정의하지 못하도록 할 수도 있습니다&lt;/u&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;prepareRecipe()는 서브 클래스가 임의로 오버라이딩 할 수 없도록, final로 선언&lt;/u&gt;하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;prepareRecipe()&lt;/s&gt;가 &lt;s&gt;템플릿 메서드&lt;/s&gt;입니다. 즉, &lt;u&gt;어떤 알고리즘의 템플릿 역할&lt;/u&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;⚠️ Concrete 메서드란 &lt;s&gt;Default Method&lt;/s&gt;&lt;u&gt;,&lt;/u&gt; &lt;s&gt;Abstract Method&lt;/s&gt;,&amp;nbsp;&lt;s&gt;interface private Method&lt;/s&gt;&lt;u&gt;&amp;nbsp;외의 메서드&lt;/u&gt;를 의미&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;❗️예 2 (Hook 메서드)&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;후크는 추상 클래스에서 선언되지만, 기본적인 내용만 구현되어 있거나, 아무 코드도 들어있지 않은 메서드입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;추상 클래스에 후크 메서드가 있으면, 서브 클래스는 그 메서드를 선택적으로 오버라이딩 할 수 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;오버라이드하지 않으면, 추상 클래스에서 기본으로 제공한 코드가 실행&lt;/u&gt;됩니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public abstract class CaffeineBeverageWithHook {
 
	void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		if (customerWantsCondiments()) {
			addCondiments();
		}
	}
 
	abstract void brew();
 
	abstract void addCondiments();
 
	void boilWater() {
		System.out.println(&quot;Boiling water&quot;);
	}
 
	void pourInCup() {
		System.out.println(&quot;Pouring into cup&quot;);
	}
 
	boolean customerWantsCondiments() {
		return true;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class CoffeeWithHook extends CaffeineBeverageWithHook {
	@Override
	public void brew() {
		System.out.println(&quot;Dripping Coffee through filter&quot;);
	}
 
	@Override
	public void addCondiments() {
		System.out.println(&quot;Adding Sugar and Milk&quot;);
	}
 
	// 첨가물의 추가 여부를 반환하는 함수
	@Override
	public boolean customerWantsCondiments() {

		String answer = getUserInput();

		if (answer.toLowerCase().startsWith(&quot;y&quot;)) {
			return true;
		} else {
			return false;
		}
	}
 
	private String getUserInput() {// 유저의 키보드로부터 입력받는 함수
		String answer = null;
		
		System.out.print(&quot;Would you like milk and sugar with your coffee (y/n)? &quot;);
		
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));'

		try {
			answer = in.readLine();
		} catch (IOException ioe) {
			System.err.println(&quot;IO error trying to read your answer&quot;);
		}

		if (answer == null) {
			return &quot;no&quot;;
		}
		return answer;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CaffeineBeverageWithHook 클래스, 즉, 추상클래스의 &lt;s&gt;customerWantsCondiments()&lt;/s&gt;&lt;u&gt;의 구현부를 보면 단순히 true를 반환&lt;/u&gt;하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;서브 클래스는 이 메서드를 선택적으로 오버라이딩&lt;/u&gt; 하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 CoffeeWithHook 클래스는 추상 클래스의 &lt;s&gt;Hook 메서드&lt;/s&gt;를 오버라이딩 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, &lt;u&gt;커피를 제조할 때 사용자에게 입력을 받아서 첨가물의 추가 여부를 결정&lt;/u&gt;하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;반면, 서브 클래스가 이 메서드를 구현하지 않는다면, 카페인 음료를 제조할 때, 반드시 첨가물을 추가&lt;/u&gt;합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;❓ 템플릿을 만들 때 추상 메서드를 써야될 때와 후크를 써야할 때를 어떻게 구분할 수 있나요?&lt;/h4&gt;
&lt;pre id=&quot;code_1669868366377&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;✋ 서브 클래스가 알고리즘의 특정 단계를 필수로 제공해야 한다면 추상 메서드를 써야 합니다. 
알고리즘의 특정 단계가 선택적으로 적용된다면, 후크를 쓰면 됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;❓ 추상 메서드가 너무 많아지면 서브 클래스에서 일일이 추상 메서드를 구현해야 하니까 별로 좋지 않을 것 같아요.&lt;/h4&gt;
&lt;pre id=&quot;code_1669868399688&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;✋ 알고리즘 단계를 너무 잘게 쪼개지 않는 것도 한 가지 방법입니다. 
하지만, 알고리즘을 큼직한 몇 가지 단계로만 나눠 놓으면 유연성이 떨어진다는 단점도 있습니다. 
또한, 모든 단계가 필수가 아니기 때문에, 필수가 아닌 부분을 후크로 구현하면, 
그 추상 클래스의 서브 클래스를 만들 때 부담이 줄어듭니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; 실 사용 예시 (Arrays.sort())&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;private static void mergeSort(Object[] src, Object[] dest,
	                            int low, int high, int off) {
		// 많은 코드
		
		for (int i=low; i&amp;lt;high; i++){
				for (int j=i; j&amp;gt;low &amp;amp;&amp;amp; 
						((Comparable) dest[j-1]).compareTo(dest[j])&amp;gt;0; j--)
		        swap(dest, j, j-1);
		}
		
		// 많은 코드
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;Arrays&lt;/s&gt;의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;mergeSort()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수를 보면 이런식으로 작성되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;mergeSort() 안에서는 compareTo()와 swap()이라는 method를 사용&lt;/u&gt;합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;compareTo() - Comparable 인터페이스의 메서드&lt;/li&gt;
&lt;li&gt;swap() - Arrays 클래스에 의미 정의되어 있는 Concreate 메서드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 mergeSort()도 &lt;s&gt;템플릿 메서드&lt;/s&gt;라고 할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;❓ Arrays는 추상 클래스도 아닐 뿐더러, Arrays의 서브 클래스를 만들지 않았는데 템플릿 메서드 패턴인가?&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_Untitled.png&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bki9uf/btrSCsbxiUL/RHim1N22hSr7TB0TzwGZhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bki9uf/btrSCsbxiUL/RHim1N22hSr7TB0TzwGZhK/img.png&quot; data-alt=&quot;Array를 상속받을 수 있을 때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bki9uf/btrSCsbxiUL/RHim1N22hSr7TB0TzwGZhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbki9uf%2FbtrSCsbxiUL%2FRHim1N22hSr7TB0TzwGZhK%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;366&quot; height=&quot;249&quot; data-filename=&quot;edited_Untitled.png&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Array를 상속받을 수 있을 때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;✋ 실전에서 패턴을 적용는 방법이 책에 나와있는 방법과 완전히 같을 수는 없습니다. 
주어진 상황과 구현상 제약조건에 맞게 고쳐서 적용해야합니다.
Arrays의 mergeSort() 메서드를 디자인한 사람도 몇 가지 제약조건이 있었습니다. 
자바에서는 배열의 서브 클래스를 만들 수 없지만, 
모든 타입의 배열에 대해 정렬 기능을 사용할 수 있도록 만들어야 했습니다. 
그래서 정적 메소드를 정의한 다음, 대소를 비교하는 부분은 정렬될 객체에서 구현하도록 만든겁니다. 
만약 Array가 상속 가능했다면 위와 같은 방식으로 구현됐을 것입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;❓&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;추상 클래스 말고, 인터페이스의 디폴트 메소드로 템플릿 메소드 패턴을 구현할 수 있지 않나요?&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1669868738989&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;✋ 인터페이스의 경우, 디폴트 메서드는 final로 정의할 수 없어 재정의가 가능하기 때문에, 
추상 클래스에서 알고리즘을 독점할 수 없습니다. 
또한, 인터페이스의 모든 추상 메소드는 public이므로 
템플릿 메서드 내부에서만 호출되어야 할 메서드들이 의도치 않은 사용처에서 호출될 위험이 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  템플릿 메서드 패턴의 장점&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 180px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;시시한 Tea와 Coffee 클래스&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;템플릿 메서드로 새로 만든 CoffeinBeverage 클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;Coffee와 Tea 클래스가 각각 작업을 처리하고, 두 클래스에서 각자 알고리즘을 수행&lt;/td&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;CaffeineBeverage 클래스에서 작업을 처리하고, 알고리즘을 독점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Coffee와 Tea 클래스에 중복된 코드가 존재&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;CaffeineBeverage 클래스 덕분에 공통 메서드를 서브 클래스에서 재사용할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;알고리즘이 바뀌면, 서브 클래스를 일일이 고쳐주어야 함&lt;/td&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;알고리즘이 한 군데 모여 있으므로, 한 부분만 고치면 됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;클래스 구조상 새로운 음료를 추가하려면, 꽤 많은 일을 수행해야 하며, 중복 코드가 또 발생함&lt;/td&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;다른 음료도 쉽게 추가할 수 있는 프레임워크를 제공, 음료를 추가할 때 몇 가지 메서드만 더 만들면 됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;알고리즘 지식과 구현 방법이 여러 클래스에 분산되어 있음&lt;/td&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;CaffeineBeverage 클래스에 알고리즘 지식이 집중되어 있으며, 일부 구현만 서브 클래스에 의존. 즉, 알고리즘과 구체적인 구현을 분리할 수 있음.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참조&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Head First Design Pattern Chapter 8, 템플릿 메소드 패턴&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;b&gt;&lt;a href=&quot;https://joeylee.tistory.com/21&quot;&gt;[Design Pattern] 템플릿메소드(Template Method)란?&lt;/a&gt;&lt;/b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://gmlwjd9405.github.io/2018/07/13/template-method-pattern.html&quot;&gt;[Design Pattern] 템플릿 메서드 패턴이란&lt;/a&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://stackoverflow.com/questions/59951096/how-does-the-jls-specify-the-terms-abstract-method-concrete-method-and-def&quot;&gt;How does the JLS specify the terms &quot;abstract method&quot;, &quot;concrete method&quot; and &quot;default method&quot;?&lt;/a&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.4.3.1&quot;&gt;JLS 8.4.3.1, &lt;/a&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-9.4&quot;&gt;JLS 9.4&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>스터디</category>
      <author>길민호(ethan.mino)</author>
      <guid isPermaLink="true">https://codingnotes.tistory.com/232</guid>
      <comments>https://codingnotes.tistory.com/232#entry232comment</comments>
      <pubDate>Mon, 7 Nov 2022 16:48:15 +0900</pubDate>
    </item>
    <item>
      <title>백트래킹(Backtracking)이란?</title>
      <link>https://codingnotes.tistory.com/231</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;백트래킹이란?&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 현재 상태에서 가능한 모든 후보군을 따라 들어가며 탐색하다가, 가능성이 없다고 판단되면, 되돌아가서 다시 해를 찾아가는 기법
2. 조합, 순열, 부분집합도 가지치기가 없는 백트래킹이라고 할 수 있습니다.
3. 백트래킹을 이용하여, 완전탐색을 수행할 수 있고, 가지치기를 통해 최적화가 가능합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백트래킹은 크게 두 부분으로 구분할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;void back(int depth, int m){
    if(depth == m){
        // 1. 선택한 원소로 기반으로 결과를 계산하는 부분
    }else{
        // 2. 원소를 선택하는 부분
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;꿀팁&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀 함수는 함수를 명확히 정의하는 것이 좋습니다.&lt;br /&gt;함수를 명확하게 정의하면, 나를 이용하여 나를 정의하는 것이 명확해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 함수는 m - cnt개를 선택하는 모든 경우의 수를 탐색하는 함수로 정의할 수 있습니다.&lt;br /&gt;back 함수의 전체적인 틀은 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. m개를 모두 선택한 경우
    1-1. 선택한 원소들을 기반으로 결과를 계산
2. m개를 선택하지 않은 경우
    2-1. 원소를 하나 선택
    2-2. 남은 원소를 선택하는 역할을 back(cnt + 1, m) 호출에 위임 
    2-3. 즉, m - (cnt + 1)개를 선택하는 모든 경우의 수를 탐색하는 함수에게 남은 원소의 선택을 위임&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 구현은 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;/*
    * @param m 선택해야 할 원소의 총 개수
    * m은 반드시 backtracking 함수의 인자로 전달될 필요는 없습니다, static 변수로 두어도 됩니다
    * @param cnt 현재까지 선택한 원소의 개수
    * cnt는 main 함수 호출 시 0또는 1로 시작할 수 있습니다. 
    * 다만, 주로 원소가 배열로 주어지기 때문에, 저는 배열의 인덱스와 맞추기 위해 0을 사용합니다.
*/

void back(int cnt, int m){
    if(cnt == m){
        // 1. 선택한 원소로 기반으로 결과를 계산하는 부분
    }else{
        // 2. 원소를 선택하는 부분
        for(int cur = XXX; cur &amp;lt; XXX ; cur++){
            // 원소를 선택
            back(cnt + 1, m); // cnt + 1부터 총 m개를 선택하는 함수를 호출
            // 원소의 선택을 해제
        }
    }
}

public static void main(String[] args) {
    back(0, 10); // (10 - 0)개를 선택하는 모든 경우의 수를 탐색
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원소 선택&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원소를 선택하는 부분은 두 가지 유형으로 구분될 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;depth가 기준이 되는 유형
원소가 기준이 되는 유형&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;depth가 기준이 되는 유형&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;depth란 단계라고 생각하시면 됩니다.&lt;br /&gt;depth가 기준이 되는 유형은 현재 단계에서 원소를 선택하고, 다음 단계에게 나머지 역할을 위임시켜 주면 됩니다.&lt;br /&gt;순열/중복 순열/조합이 이 유형에 해당됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;순열&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순열은 순서에 따라 결과가 달라질 수 있는 문제에 적용시킬 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;static boolean [] isSelected = new boolean[10];
static int [] selected = new int[10];

static void back(int depth, int m){
    if(depth == m){
        // 1. 선택한 원소로 기반으로 결과를 계산하는 부분
    }else{
        // 2. 원소를 선택하는 부분
        for(int curIdx = 0; curIdx &amp;lt; n; curIdx++){  // curIdx는 n개의 원소 중에서 선택할 원소의 idx
            if(!isSelected[curIdx]){ // curIdx번째 원소가 선택되지 않았다면
                isSelected[curIdx] = true; // curIdx번째 원소를 선택 처리
                selected[depth] = cur;  // depth 단계에서 선택한 값 저장
                back(idx + 1, maxNum); 
                isSelected[curIdx] = false;  // curIdx번째 원소를 선택 해제 처리
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;중복 순열&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복 순열은 순열과 같지만, 같은 원소를 중복 선택 가능한 경우 문제에 적용시킬 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;static void back(int depth){
    if(depth == n){
        // 1. 선택한 원소로 기반으로 결과를 계산하는 부분
    }else{
        for(int cur = 0; cur &amp;lt; n; cur++){
            // 2. 원소를 선택하는 부분
            selected[depth] = cur;  // depth 단계에서 선택한 값 저장
            back(depth + 1, maxNum); 
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;조합&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조합은 순서는 결과에 영향을 주지 않고, 어떤 원소를 선택했냐가 중요한 경우 문제에 적용시킬 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;static int [] selected = new int[10];
static void back(int depth, int pre){
    if(depth == m){
        // 1. 선택한 원소로 기반으로 결과를 계산하는 부분
    }else{
        for(int cur = pre + 1; cur &amp;lt; n; cur++){ // * cur의 시작점이 pre + 1
            // 2. 원소를 선택하는 부분
            selected[depth] = cur;  // depth 단계에서 선택한 값 저장
            back(depth + 1, cur); 
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원소가 기준이 되는 유형&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 현재 원소를 몇개 선택할 것이냐입니다.&lt;br /&gt;부분 집합, 중복 조합이 이 유형에 해당됩니다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;static int [] selectCnt = new int[10];
/*
    * @param maxNum idx 원소의 최대 선택 가능 개수
    * 부분집합의 경우 maxNum은 1이 됩니다.
*/
static void back(int idx, int maxNum, int n){
    if(idx == n){
        // 1. 선택한 원소로 기반으로 결과를 계산하는 부분
    }else{
        // 2. 원소를 선택하는 부분
        for(int num = 0; num &amp;lt; maxNum; num++){  // 해당 원소를 선택하지 않을 수 있기 때문에 0부터 시작
            selectCnt[idx] = num;   // idx 번째 원소를 num개 선택
            back(idx + 1, maxNum); 
        }
    }
}

public static void main(String[] args) {
    back(0, 10, 20); // 20개의 원소 중에서 0번째 원소부터 각각의 원소를 최대 10개 선택
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과 계산&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 계산은 크게 두 가지 유형으로 구분할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;1. 첫 번째 유형
    - 반드시 현재 경우의 수를 완성한 다음, 해당 경우의 수에 대한 결과를 계산해야하는 경우
    - ex. 캐슬 디펜스 

2. 두 번째 유형
    - 경우의 수를 완성해가면서, 결과를 계산하며 depth를 진행시킬 수 있는 경우
    - ex. 해밀턴 순환 회로, 배열 돌리기&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;캐슬 디펜스의 경우, 3명의 궁수를 모두 격자판에 배치시킨 후,&lt;br /&gt;해당 배치에 대해 제거 가능한 적의 수를 계산해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 결과를 누적시키며 백트래킹을 진행할 수 없습니다.&lt;br /&gt;이 경우 주어진 n개 중에서 m개를 모두 선택했을 때, 결과를 계산하면 됩니다.&lt;br /&gt;이 유형의 경우 아래와 같은 코드 작성이 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;static void backtracking(int depth, int m){
    if(depth == m){    
        // depth가 0부터 시작했다면, depth == m일 때 0 ~ m - 1까지 m개의 원소를 선택한 것이므로, 
        // 이 때 결과를 계산하면 됩니다.

        game(); // 선택한 원소를 기반으로, 결과를 계산
    }else{  // 아직 m개를 선택하지 않은 경우
        // 선택 가능한 원소들 중에서 하나를 선택
        backtracking(depth + 1, m);
    }
}

public static void main(String[] args) {
    back(0, 3);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 유형은 원소를 선택하면서 결과를 계산할 수 있기 때문에, 원소를 모두 선택한 다음 결과를 계산하지 않고, 원소를 하나씩 선택할 때마다, 현재까지의 결과를 계산하는 것입니다.&lt;br /&gt;이 경우 계산 중복을 피할 수 있기 때문에 시간을 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 유형은 다시 두 가지 유형으로 구분할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;m개를 반드시 선택해야 하는 경우
최대 m개를 선택할 수 있는 경우&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;m개를 반드시 선택해야 하는 경우&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;static int minDist = Integer.MAX_VALUE;

/*
    * @param preDist 시작 노드부터 이전 노드까지의 거리 
*/

static void backtracking(int depth, int m, int preDist){
    if(depth == m){    
        // depth가 0부터 시작했다면, depth == m일 때 0 ~ m - 1까지 m개의 원소를 선택한 것이므로, 
        // 이 때 결과를 계산하면 됩니다.

        minDist = Math.min(minDist, preDist); // * 핵심
    }else{  // 아직 m개를 선택하지 않은 경우
        // 선택 가능한 원소들 중에서 하나를 선택

        for(int cur = XXX; cur &amp;lt; XXX ; cur++){
            int curDist = 이전 노드와 현재 노드간의 거리
            backtracking(depth + 1, m, preDist + curDist);
        }
    }
}

public static void main(String[] args) {
    back(0, 3);
    System.out.println(minDist);    // 최종 출력 변수를 출력
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;최대 m개를 선택할 수 있는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, 최대 m개를 선택할 수 있을 뿐, 반드시 m개를 선택해야 하는 것은 아닙니다.&lt;br /&gt;따라서, 아래와 같이 함수 작성이 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;static void backtracking(int depth, int m, int preDist){
    // 반드시 m개를 선택해야 하는 것은 아니므로, 원소를 선택할 때마다 최종 출력 변수의 값을 업데이트함
    minDist = Math.min(minDist, preDist); // ** 핵심

    if(depth &amp;lt; m){  // 아직 m개를 선택하지 않은 경우
        // 선택 가능한 원소들 중에서 하나를 선택

        for(int cur = XXX; cur &amp;lt; XXX ; cur++){
            int curDist = 이전 노드와 현재 노드간의 거리
            backtracking(depth + 1, m, preDist + curDist);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;백트래킹 최적화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백트래킹에서 최적화는 일명 가지치기라고 할 수 있습니다.&lt;br /&gt;대표적으로 최소 값을 구할 때, 이미 앞서 계산된 최소 값보다 현재 계산된 값이 더 클 때 더이상 진행하지 않는 방법이 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;static int minDist = Integer.MAX_VALUE;

static void backtracking(int depth, int m, int preDist){
    if(depth == m){    
        // depth가 0부터 시작했다면, depth == m일 때 0 ~ m - 1까지 m개의 원소를 선택한 것이므로, 
        // 이 때 결과를 계산하면 됩니다.

        minDist = Math.min(minDist, curDist);
    }else if(preDist &amp;lt; minDist){ // ** 핵심 
        // 아직 m개를 선택하지 않은 경우
        // 선택 가능한 원소들 중에서 하나를 선택

        for(int cur = XXX; cur &amp;lt; XXX ; cur++){
            int curDist = 이전 노드와 현재 노드간의 거리

            // ** 아래와 같은 방식도 가능
            if(preDist + curDist &amp;lt; minDist)
                backtracking(depth + 1, m, preDist + curDist);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풀이 과정&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 모든 경우의 수에 대해 시도해보아야 결과를 알 수 있겠다라는 생각이 들면 일단 백트래킹을 후보로 둠
2. n의 크기를 살펴봄
    2-1. 백트래킹의 경우 보통 순서를 고려해야 한다면 11이하, 고려하지 않아도 된다면 25이하의 n이 주어짐
2-2. n이 그 이상이지만, 완탐이 아닌 이상 절대 알 수 없을 것 같다면, 가지치기를 고려
    2-3. n이 50 이상인 경우, 백트래킹일 가능성이 낮음 
2-4. 특히, n이 100,000이상인 경우, DP/그리디/이분탐색/그래프를 고려
3. 백트래킹을 할 수 있다면, 위의 백트래킹 유형 중 어떤 유형인지 파악
4. 유형 파악이 된 이후에는, 가지치기를 통해 최적화가 가능한지 파악
5. 솔루션 작성 이후에는, 어떠한 케이스가 있을 수 있는지, 정답이 잘 나오는지 파악하기 위해 간단한 테스트케이스부터 복잡한 테스트 케이스까지 직접 만들어보고 입력해서 솔루션을 검증&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚠ 주의할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 출력 변수의 값을 업데이트 할 수 있는 경우의 수가 존재하지 않는 경우,&lt;br /&gt;Integer.MAX_VALUE 또는 Integer.MIN_VALUE와 같은 쓰레기 값이 출력될 수 있습니다.&lt;br /&gt;따라서, 최종 출력 변수의 값을 업데이트 할 수 있는 경우의 수가 존재하는지, 이러한 경우, 출력해야 될 값을 문제에서 제시하였는지 등을 확인해야합니다.&lt;/p&gt;</description>
      <category>Algorithm/Backtracking</category>
      <author>길민호(ethan.mino)</author>
      <guid isPermaLink="true">https://codingnotes.tistory.com/231</guid>
      <comments>https://codingnotes.tistory.com/231#entry231comment</comments>
      <pubDate>Wed, 2 Nov 2022 00:54:02 +0900</pubDate>
    </item>
    <item>
      <title>22.10.11 SSAFY 스터디 CS 발표 - 컴퓨터 구조, 기억장치</title>
      <link>https://codingnotes.tistory.com/229</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;기억장치&lt;/h2&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;s&gt;레지스터&lt;/s&gt;, 중앙처리장치와 직접 버스로 연결되는 &lt;s&gt;주기억장치&lt;/s&gt;, 그리고 입출력 장치로 연결되는 &lt;s&gt;보조기억장치&lt;/s&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;s&gt;주기억장치(Main memory)&lt;/s&gt;은 &lt;u&gt;주로 전원이 켜져 있을 때 실행하는 프로그램과 데이터를 저장&lt;/u&gt;하고, &lt;u&gt;보조기억장치(Auxiliary storage)는 영구적으로 프로그램과 데이터를 저장&lt;/u&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;s&gt;주기억장치&lt;/s&gt;는 &lt;u&gt;중앙처리장치와 &lt;/u&gt;&lt;s&gt;온라인&lt;/s&gt;&lt;u&gt;으로 연결&lt;/u&gt;되고, &lt;s&gt;보조기억장치&lt;/s&gt;는 &lt;s&gt;오프라인&lt;/s&gt;&lt;u&gt;으로 연결&lt;/u&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;온라인 연결&lt;/s&gt;은 &lt;u&gt;중앙처리장치가 필요할 때 항상 사용할 수 있다&lt;/u&gt;는 의미이고, &lt;s&gt;오프라인&lt;/s&gt;은 &lt;u&gt;별도의 연결 과정을 거친 후 사용할 수 있다&lt;/u&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;s&gt;레지스터&lt;/s&gt;는 &lt;u&gt;주기억장치의 내용을 필요에 따라 임시로 중앙처리장치 안에 저장하는 용도&lt;/u&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;주기억장치(Main memory)&lt;/h2&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;s&gt;ROM(Read Only Memory)&lt;/s&gt;과 &lt;s&gt;RAM(Random Access Memory)&lt;/s&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;s&gt;ROM&lt;/s&gt;은 &lt;u&gt;그 안에 저장된 데이터를 읽기만 가능&lt;/u&gt;하고, &lt;u&gt;지우거나 다른 값으로 갱신할 수 없는 &lt;/u&gt;&lt;s&gt;기억 소자&lt;/s&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;전원이 제거되더라도, ROM에 저장된 데이터를 그대로 유지&lt;/u&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전원이 인가될 때, 처음으로 실행하는 프로그램인 &lt;s&gt;부트로더&lt;/s&gt;를 저장하는 용도로 ROM을 사용합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;s&gt;부트로더&lt;/s&gt;는 &lt;u&gt;시스템 스택을 설정, 서비스 루틴을 기억 장치에 적재, 인터럽트 벡터 설정 등의 역할을 수행&lt;/u&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;s&gt;RAM&lt;/s&gt;에 &lt;u&gt;저장된 데이터를 읽고 쓰기가 가능&lt;/u&gt;하며, &lt;u&gt;전원이 제거되면 RAM의 내용이 모두 지워집니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터는 &lt;u&gt;보조 기억장치에 있는 프로그램을 RAM으로 복사한 후, RAM에서 실행&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;휘발성&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;비휘발성&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;삭제 가능&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;삭제 불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;RAM&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;ROM, 보조기억장치&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;RAM, 보조기억장치&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;ROM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;보조기억장치&lt;/h2&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;s&gt;보조기억장치&lt;/s&gt;는 &lt;u&gt;프로그램과 데이터를 영구적으로 보관할 목적으로 사용&lt;/u&gt;되는 입출력 장치입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중앙처리장치는 &lt;u&gt;보조기억장치에 저장되어있는 데이터를 주기억장치로 옮겨놓고 사용&lt;/u&gt;합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류로는 &lt;s&gt;플로피 디스크&lt;/s&gt;, &lt;s&gt;하드 디스크&lt;/s&gt;, &lt;s&gt;SSD&lt;/s&gt;, &lt;s&gt;마그네틱 테이프&lt;/s&gt;, &lt;s&gt;CD-ROM&lt;/s&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;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;입출력 장치를 연결하는 방식으로 보조기억장치를 엑세스&lt;/u&gt;하며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;한 번 연결하여 적은 양의 데이터를 송수신하는 것은 전체적인 성능이 낮아지는 요인&lt;/u&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;u&gt;컴퓨터는 주기억장치에 많은 양의 데이터를 모아 두었다가 한 번에 보조기억장치로 전송&lt;/u&gt;하고, &lt;u&gt;보조기억장치에서 많은 양의 데이터를 읽어 주기억장치로 저장해두고 필요한 데이터를 가져옵니다&lt;/u&gt;.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(⚠️ &lt;u&gt;컴퓨터와 보조기억장치 간에 데이터를 전송하는 단위&lt;/u&gt;를 &lt;s&gt;블록(block)&lt;/s&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;u&gt;블록의 크기는 보조기억장치의 용량 증가와 함께&lt;/u&gt; 1K 바이트, 2K 바이트, 4K 바이트로 &lt;u&gt;점점 증가하는 추세&lt;/u&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;/h2&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;s&gt;순차 엑세스&lt;/s&gt;, &lt;s&gt;직접 엑세스&lt;/s&gt;, &lt;s&gt;임의 엑세스&lt;/s&gt;, &lt;s&gt;연관 엑세스&lt;/s&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;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순차 엑세스(Sequential access) :
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일렬로 저장된 데이터를 &lt;u&gt;처음부터 차례대로 엑세스&lt;/u&gt;하는 방식&lt;/li&gt;
&lt;li&gt;필요한 데이터가 저장된 위치에 따라 엑세스 시간이 달라짐. (ex. 자기 테이프)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;직접 엑세스(Direct access) :
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;기억장치의 임의 위치로&lt;/u&gt; 디스크 헤드를 이동시켜 엑세스하는 방식&lt;/li&gt;
&lt;li&gt;디스크 헤드가 이동하는 시간에따라 엑세스 시간이 달라짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;임의 엑세스(Random access)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;주소에 의하여 데이터를 엑세스&lt;/u&gt;하는 방식&lt;/li&gt;
&lt;li&gt;&lt;u&gt;기억장치의 임의 위치를 엑세스하는 시간이 같음&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;&lt;u&gt;각 기억장치의 위치마다 고유의 주소가 할당&lt;/u&gt;되어 있으며, &lt;s&gt;RAM&lt;/s&gt;도 임의 엑세스 방식을 사용&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기억장치 계층&lt;/h2&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;u&gt;컴퓨터의 기억장치는 용량이 크고, 속도가 빠르고, 가격이 저렴할수록 좋습니다.&amp;nbsp;&lt;/u&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;u&gt;기억장치를 빠르게 엑세스 하면서도, 많은 용량을 사용하기 위해서 기억장치를 계층적으로 구성&lt;/u&gt;하여 운영하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;윗 계층일수록 중앙처리장치의 사용 빈도가 증가하고, 엑세스 시간이 짧고, 용량이 적고, 비트당 가격이 비쌉니다.&lt;/u&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;u&gt;컴퓨터는 속도가 느리고, 용량이 큰 기억장치의 내용 중에서 중앙처리장치가 자주 사용하는 데이터를 속도가 빠른 기억장치로 옮겨 놓고 사용함으로써, 전체적인 기억장치 엑세스 속도를 개선&lt;/u&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;728&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAWLqg/btrOkROeSgI/XoUVwmjYNK4f0xyKKyXOpk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAWLqg/btrOkROeSgI/XoUVwmjYNK4f0xyKKyXOpk/img.jpg&quot; data-alt=&quot;기억장치 계층 구조 (출처 : 코딩 팩토리, https://coding-factory.tistory.com/354)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAWLqg/btrOkROeSgI/XoUVwmjYNK4f0xyKKyXOpk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAWLqg%2FbtrOkROeSgI%2FXoUVwmjYNK4f0xyKKyXOpk%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;728&quot; height=&quot;300&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기억장치 계층 구조 (출처 : 코딩 팩토리, https://coding-factory.tistory.com/354)&lt;/figcaption&gt;
&lt;/figure&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;/h2&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;s&gt;디스크 캐시&lt;/s&gt;를 사용하기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;디스크 캐시는 주기억장치 혹은 보조기억장치에 존재&lt;/u&gt;할 수 있으며, &lt;u&gt;일반적으로 두 개의 장치 모두 디스크 캐시를 포함&lt;/u&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;u&gt;응용 프로그램이 데이터를 디스크의 파일로 전송하는 과정&lt;/u&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;운영체제는 파일에 기록할 데이터를 주기억장치에 마련되어 있는 디스크 캐시로 저장하고, 응용 프로그램에게 데이터 전송이 완료되었음을 알림&lt;/li&gt;
&lt;li&gt;운영체제는 한 개의 블록에 대한 데이터가 디스크 캐시에 모일 때까지 기다렸다가, 블록이 채워지면, 적절한 시간에 보조기억장치로 한 개의 블록을 전송, 이때 보조기억장치는 자신의 디스크 캐시에 데이터를 저장하고, 운영체제에게 디스크 기록이 완료되었다고 보고&lt;/li&gt;
&lt;li&gt;디스크는 컴퓨터 시스템과 독립적으로 동작하는 입출력 장치이므로, 디스크 캐시의 데이터를 안전한 시간에 디스크로 기록&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;디스크 캐시의 운영은 전적으로 &lt;/u&gt;&lt;s&gt;운영체제&lt;/s&gt;&lt;u&gt;가 관리&lt;/u&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;/h2&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;프로그램 내장형 컴퓨터&lt;/s&gt;&lt;u&gt;는 프로그램과 데이터를 주기억장치에 저장해두고, 명령어를 하나씩 중앙처리장치로 인출하여 실행&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 구조는 &lt;s&gt;폰 노이만&lt;/s&gt;이 처음으로 제안하였으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주기억장치를 프로그램 기억장치와 데이터 기억장치로 분리하여, 명령어와 데이터를 동시에 엑세스하여, 기억장치 엑세스 속도를 개선한&amp;nbsp;&lt;s&gt;하버드 구조&lt;/s&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;u&gt;주기억장치의 엑세스 속도가 중앙처리장치보다 4 ~ 10배 느리다&lt;/u&gt;는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;중앙처리장치는 주기억장치에서 명령어나 데이터를 인출하기 위해서 긴 시간 동안 대기&lt;/u&gt;해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제점을 해결하기 위해 &lt;s&gt;캐시 기억장치&lt;/s&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;u&gt;중앙처리장치와 주기억장치 사이에 존재&lt;/u&gt;하는 &lt;s&gt;캐시 기억장치&lt;/s&gt;는 &lt;u&gt;용량은 적지만, 엑세스 속도는 레지스터의 속도와 같거나 조금 느립니다.&lt;/u&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;중앙처리장치가 주기억장치를 엑세스하려고 기억장치 주소를 생성하&lt;/u&gt;면, &lt;s&gt;캐시 제어기&lt;/s&gt;는 &lt;u&gt;해당 주소를 포함하는 여러 개 또는 수십 개의 데이터를 한 번에 캐시 기억장치로 옮겨 놓고, 그곳에서 데이터를 엑세스하도록 만듭니다.&lt;/u&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;/h2&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;u&gt;프로그램을 수행할 때 기억장치의 특정 부분을 집중적으로 참조하는 경향&lt;/u&gt;이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 특성을 &lt;s&gt;참조의 지역성&lt;/s&gt;이라고 하며, 이것이 &lt;u&gt;캐시 도입으로 기억장치의 엑세스 속도를 개선할 수 있는 이유&lt;/u&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;s&gt;공간적 참조의 지역성&lt;/s&gt;과 &lt;s&gt;시간적 참조의 지역성&lt;/s&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;인접한 영역의 명령어 또는 데이터가 다음 번에도 계속 참조될 가능성이 큰 것을 의미&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;아래의 경우 hit할 가능성이 높음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 실행 중인 명령어의 다음 명령어를 수행&lt;/li&gt;
&lt;li&gt;루프를 실행&lt;/li&gt;
&lt;li&gt;함수 호출 (함수 안에서는 지역 변수를 많이 사용하는데, 지역 변수는 스택과 같은 일정한 영역에 집중적으로 배치됨)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;시간적 참조의 지역성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;한번 사용된 프로그램이 일정한 시간 내에 다시 참조될 가능성이 크다는 의미&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;프로그램 코드는 한 번 사용한 데이터를 여러 번 사용하는 경향이 있고, 함수에서 복귀하면, 원래 수행하던 코드 영역을 다시 수행할 가능성이 높음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, &lt;u&gt;참조의 지역성을 고려할 때, 주기억장치에서 캐시 기억장치로 옮겨온 데이터를 중앙처리장치가 여러번 사용할 가능성이 큽니다.&lt;/u&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;s&gt;퀵소트&lt;/s&gt;는 시간 복잡도 면에서 빠른 것도 있지만, &lt;u&gt;배열 안에서 자리 바꿈으로 처리가 되기 때문에 적중률이  높아서 속도가 빠른 이유도 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;또한, 해시 충돌 회피 방법으로, &lt;/u&gt;&lt;s&gt;Open Addressing&lt;/s&gt; 방식에서 &lt;u&gt;n칸 옆의 슬롯을 검사&lt;/u&gt;하는 &lt;s&gt;LInear probing&lt;/s&gt;의 경우 &lt;u&gt;인접한 영역을 확인하기 때문에 적중률이 높습니다&lt;/u&gt;. 다만, &lt;u&gt;데이터가 밀집 저장되는 &lt;/u&gt;&lt;s&gt;클러스터링&lt;/s&gt;&lt;u&gt; 문제가 발생할 수 있습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌 발생 시, &lt;u&gt;이동할 칸의 수를 새로운 해시 함수로 계산&lt;/u&gt;하는 &lt;s&gt;Double Hashing&lt;/s&gt; 방식은 &lt;u&gt;클러스터링을 효과적으로 회피&lt;/u&gt;할 수 있지만, &lt;u&gt;적중률이 매우 낮습니다.&lt;/u&gt;&amp;nbsp;&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-filename=&quot;blob&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;569&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1VnWc/btrOlQnJafq/UMmRGeFXgDn4YCCCY7kYa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1VnWc/btrOlQnJafq/UMmRGeFXgDn4YCCCY7kYa1/img.png&quot; data-alt=&quot;캐시의 동작&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1VnWc/btrOlQnJafq/UMmRGeFXgDn4YCCCY7kYa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1VnWc%2FbtrOlQnJafq%2FUMmRGeFXgDn4YCCCY7kYa1%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;500&quot; height=&quot;279&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;569&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;캐시의 동작&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터 시스템은 아래와 같은 방식으로 캐시를 활용합니다.&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;중앙처리 장치가 요청한 데이터가 캐시 안에 존재한다면, 중앙처리장치는 캐시 안에서 데이터를 가져온다.&lt;/li&gt;
&lt;li&gt;만일 데이터가 캐시 안에 존재하지 않는다면, 주기억장치에서 중앙처리장치가 원하는 데이터를 포함한 데이터 블록을 캐시로 복사한 후, 캐시에서 해당 단어를 중앙처리장치로 전송한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;중앙처리장치가 요청한 데이터가 캐시에 들어 있는 것&lt;/u&gt;을 &lt;s&gt;&quot;적중하였다(hit)&quot;&lt;/s&gt;라고 말하고, &lt;u&gt;존재하지 않는 것&lt;/u&gt;을 &lt;s&gt;&quot;실패하였다(miss)&quot;&lt;/s&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;s&gt;적중률&lt;/s&gt;은 &lt;u&gt;중앙처리장치의 기억장치에 대한 전체 요청 수에 대하여 적중한 수를 퍼센트로 표현한 척도&lt;/u&gt;로, &lt;u&gt;실제로 95 ~ 99%로 측정&lt;/u&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적중률에 의하여 캐시를 도입함으로써 기억장치 엑세스 속도가 얼마나 개선되었는지 계산할 수 있으며, &lt;u&gt;적중률은 응용 프로그램의 지역성에 의하여 결정&lt;/u&gt;됩니다. &lt;s&gt;데이터베이스&lt;/s&gt; 같은 경우 캐시 적중률이 낮으며, &lt;s&gt;신호처리 프로그램&lt;/s&gt;의 경우 &lt;u&gt;캐시 적중률이 높은 것으로 알려져 있습니다.&lt;/u&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;주기억장치와 캐시 간에는 여러 개의 단어로 구성된 블록 단위로 데이터를 전송&lt;/u&gt;하며, 일반적으로 &lt;u&gt;주기억장치의 데이터 전송 단위&lt;/u&gt;를 &lt;s&gt;블록block)&lt;/s&gt;이라고 부르고, &lt;u&gt;캐시의 전송 단위&lt;/u&gt;를 &lt;s&gt;라인(line)&lt;/s&gt; 또는 &lt;s&gt;슬롯(slot)&lt;/s&gt;이라고 부릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주기억장치의 &lt;u&gt;블록 크기와 캐시의 라인 크기를 서로 다르게 설정할 수 있으나, 일반적으로 같게 설정&lt;/u&gt;합니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;계층적 캐시&lt;/h2&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;u&gt;마이크로프로세서 칩 내부에 캐시를 포함&lt;/u&gt;함으로써, &lt;u&gt;두 레벨의 캐시를 사용&lt;/u&gt;하기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(⚠️ 마이크로프로세서는 중앙처리장치의 기능을 하나의 칩 안에 내장한 소자, 작은 칩안에 중앙처리장치를 구현했다는 의미로 Micro 사용)&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-filename=&quot;blob&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;343&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5JlVS/btrOmsGFkPh/HCOssId6t6AwVoeuYpnCh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5JlVS/btrOmsGFkPh/HCOssId6t6AwVoeuYpnCh1/img.png&quot; data-alt=&quot;계층적 캐시 구성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5JlVS/btrOmsGFkPh/HCOssId6t6AwVoeuYpnCh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5JlVS%2FbtrOmsGFkPh%2FHCOssId6t6AwVoeuYpnCh1%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;500&quot; height=&quot;119&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;343&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;계층적 캐시 구성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;칩 내부에 포함된 캐시&lt;/u&gt;를 &lt;s&gt;온 칩(on-chip) 캐시&lt;/s&gt; 또는 &lt;s&gt;레벨1 캐시&lt;/s&gt;라고 부르고, &lt;u&gt;칩 외부의 캐시&lt;/u&gt;를 &lt;s&gt;오프 칩(off-chip) 캐시&lt;/s&gt; 또는 &lt;s&gt;레벨 2 캐시&lt;/s&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;u&gt;레벨 2 캐시는 엑세스 속도 면에서 레벨 1 캐시보다 느리고, 주기억장치보다 빠르며, 용량 면에서는 레벨1 캐시보다 많습니다.&lt;/u&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;/h2&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;u&gt;캐시 실패(miss)가 발생할 때, 주기억장치에서 새로 가져온 블록은 기존 캐시에 저장된 라인 중 하나와 교체&lt;/u&gt;되어야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;여러 개의 캐시 라인 중에서 어느 것을 선택하는 지 결정하는 방법&lt;/u&gt;을 &lt;s&gt;교체 정책&lt;/s&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;u&gt;교체 정책을 선택할 때의 기준은 적중률을 높게 유지하는 것&lt;/u&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 캐시의 목적이 기억장치의 엑세스 속도를 빠르게 하기 위함이므로, &lt;u&gt;캐시 교체 정책은 하드웨어로 구현&lt;/u&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;u&gt;가장 이상적인 교체 정책은 교체 대상인 캐시 라인 중에서 향후 가장 오랫동안 사용하지 않은 라인을 선택하는 것&lt;/u&gt;이지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;가장 오랫동안 사용하지 않을지는 미래의 일이기 때문에 현실적으로 구현이 불가능&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LRU(Least Recently Used)&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;현재부터 과거로 볼때, 가장 오래 전에 사용하였던 라인을 선택하여 교체&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;한동안 사용하지 않았기 때문에 앞으로도 사용할 가능성이 적다는 추정에 근거한 방법&lt;/li&gt;
&lt;li&gt;&lt;u&gt;우수한 적중률&lt;/u&gt;을 유지&lt;/li&gt;
&lt;li&gt;캐시 라인마다 사용한 시기를 기록하는 &lt;s&gt;USE 카운터&lt;/s&gt;를 둔다.&lt;/li&gt;
&lt;li&gt;캐시 라인을 엑세스할 때마다, 해당 라인의 USE 카운터를 0으로 만들고, 다른 라인의 USE 카운터를 증가&lt;/li&gt;
&lt;li&gt;대상 라인 중에서 USE 카운터의 값이 가장 큰 라인을 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;FIFO(First-In-First-Out)&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;캐시 안에 가장 오랫동안 존재했던 라인을 교체&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;가장 오래된 라인이더라도 자주 사용될 가능성이 있으므로, &lt;u&gt;적중률 면에서 높은 효율을 보이지 않는 것으로 알려짐&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;캐시 라인에 새 블록이 들어올 때, USE 카운터를 0으로 만들고, 다른 라인의 USE 카운터를 증가&lt;/li&gt;
&lt;li&gt;교체할 때, 대상 라인 중에서 USE 카운터 값이 가장 큰 라인을 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;LFU (Least Frequency Used)&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;사용 횟수가 가장 적은 라인을 교체&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;u&gt;최근 캐시에 들어온 라인이 교체 대상으로 선택될 가능성이 있다는 단점이 있음&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;새로운 블록이 캐시에 들어올 때마다 USE 카운터를 0으로 만들고, 사용될 때마다 USE 카운터를 증가시킴으로써 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Random
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;임의로 교체할 라인을 선택&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;제법 효율이 좋음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;/h2&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;기억장치에 대한 쓰기는 궁극적으로 주기억장치에 데이터가 기록되어야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;쓰기 정책은 캐시와 주기억장치에 대한 쓰기 동작을 처리하는 방법을 규정&lt;/u&gt;하며, &lt;s&gt;write-through&lt;/s&gt;, &lt;s&gt;write-back&lt;/s&gt; 정책이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Write-through
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;쓰기 정책이 수행될 때마다 캐시 라인의 해당 단어와 주기억장치의 단어를 모두 수정하는 방법&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;주기억장치는 항상 유효한 데이터를 보관하게 됨&lt;/li&gt;
&lt;li&gt;항상 캐시와 주기억장치의 내용을 같게 유지할 수 있으며, 캐시 라인을 교체할 때 주기억장치의 블록을 선택된 캐시 라인으로 복사하면 된다는 장점이 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;하지만, 주기억장치를 엑세스하는 횟수가 증가하여 전체적인 개선 속도가 느려지는 단점이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Write-back
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;쓰기 동작이&amp;nbsp; 수행될 때, 일단 캐시 라인의 단어만을 수정하고, 주기억장치의 단어를 수정하지 않음&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;대신 캐시 라인이 변경될 때마다, 해당 라인이 수정되었다고 표시&lt;/li&gt;
&lt;li&gt;이 정보를 &lt;s&gt;dirty 비트&lt;/s&gt;라고 한다.&lt;/li&gt;
&lt;li&gt;나중에 dirty 비트가 세트되어 있는 캐시 라인을 교체할 때, 해당 라인 전체를 주기억장치 블록으로 복사한 다음, 새로운 블록을 가져온다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Write-through 정책보다는 기억장치 엑세스 속도가 빠르다는 것이 장점이지만, 주기억장치와 캐시의 내용이 일치하지 않는 순간이 존재한다는 것이 주요 단점이다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;/h2&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;s&gt;프로세스(Process)&lt;/s&gt;는 &lt;u&gt;실행중인 프로그램&lt;/u&gt;을 의미하며, &lt;s&gt;프로세서(Processor)&lt;/s&gt;는 &lt;u&gt;현재는 CPU와 혼용되어 사용&lt;/u&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;코어(Core)는 프로세서의 주요 구성 요소입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;프로그램의 명령을 읽고 수행하는 독립적인 처리 장치&lt;/u&gt;로, &lt;u&gt;프로세서가 하는 일을 분담하여 처리&lt;/u&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;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;프로세서&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;Core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;컴퓨터 내의 전자 회로&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;프로세서&amp;nbsp;내의 전자 회로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;컴퓨터에 여러 개의 프로세서가 있을 수 있다.&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;프로세서에 여러 개의 코어(Core)가 있을 수 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;일관성 유지&lt;/h2&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;u&gt;입출력장치도 &lt;s&gt;DMA 방식&lt;/s&gt;으로 기억장치에 엑세스할 수 있으며&lt;/u&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;한 개의 컴퓨터에 여러 개의 프로세스를 탑재한 &lt;/u&gt;&lt;s&gt;멀티 프로세서&lt;/s&gt;&lt;u&gt; 시스템인 경우, 프로세서마다 독립적으로 기억장치에 엑세스&lt;/u&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;&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;1440&quot; data-origin-height=&quot;761&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GPAjd/btrOlJCveZI/PuoIBCJuzV2tlrSgSEsVk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GPAjd/btrOlJCveZI/PuoIBCJuzV2tlrSgSEsVk0/img.png&quot; data-alt=&quot;멀티프로세스 시스템 환경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GPAjd/btrOlJCveZI/PuoIBCJuzV2tlrSgSEsVk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGPAjd%2FbtrOlJCveZI%2FPuoIBCJuzV2tlrSgSEsVk0%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;500&quot; height=&quot;264&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;761&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;멀티프로세스 시스템 환경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주기억장치의 한 개의 블록이 여러 개의 캐시에 복사될 수 있다. 만일 프로세서 중 하나가 데이터를 자신의 캐시에 기록하면 같은 블록을 복사해 갖고 있는 프로세서의 캐시 라인은 틀린 값을 갖게 된다.&lt;/li&gt;
&lt;li&gt;만일 입출력 장치가 주기억장치에 데이터를 직접 기록하는 경우, 해당 데이터가 포함된 블록을 복사하고 있는 캐시 라인은 틀린 값을 갖게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;캐시와 주기억장치의 내용이 일치하지 않는 상태&lt;/u&gt;를 &lt;s&gt;무효 상태(Invalid state)&lt;/s&gt;라고 하고, &lt;u&gt;캐시와 주기억장치의 내용이 같도록 유지하는 문제&lt;/u&gt;를 &lt;s&gt;일관성 유지(coherence)&lt;/s&gt;라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시 불가능 영역 지정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 개의 프로세서들이 동시에 사용하는 주기억장치의 데이터 영역을 캐시로 들어가지 않도록 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;버스 감시 :
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;각 프로세서의 캐시 제어기는 시스템 버스에서 데이터 쓰기가 발생하는 것을 감시&lt;/u&gt;.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;만일 자신의 캐시가 새로 갱신되는 기억장치 블록을 저장하고 있다면, 해당 라인을 무효화&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;Write-through&lt;/s&gt; &lt;u&gt;정책을 채택하는 경우, 이 방법을 주로 사용&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;동시 갱신
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;주기억장치의 데이터를 수정할 때 , 모든 캐시의 내용을 모두 수정&lt;/u&gt;&lt;u&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;질문&lt;u&gt;&lt;/u&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 베이스의 캐시 적중률이 낮은 이유는?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;출처&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 눈에 보이는 컴퓨터 구조, 9장 기억장치(생능출판사, 전중남)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m.blog.naver.com/sjc02183/221844611260&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;프로세서(Processor), 코어(Core), 쓰레드(Thread)&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m.blog.naver.com/cjsksk3113/222254273021&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CPU, 프로세서, 코어는 어떤 연관성과 차이점이 있을까?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스터디</category>
      <author>길민호(ethan.mino)</author>
      <guid isPermaLink="true">https://codingnotes.tistory.com/229</guid>
      <comments>https://codingnotes.tistory.com/229#entry229comment</comments>
      <pubDate>Tue, 11 Oct 2022 14:44:08 +0900</pubDate>
    </item>
  </channel>
</rss>