<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Soyeon Park 님의 블로그</title>
    <link>https://parkso-yeon.tistory.com/</link>
    <description>Soyeon Park 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Mon, 29 Jun 2026 10:35:25 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>chief.park</managingEditor>
    <item>
      <title>GoF Design Pattern</title>
      <link>https://parkso-yeon.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;디자인 패턴은 아키텍처 스타일보다 낮은 수준에서 구체화를 위해서 적용된다. 요구사항을 고려하여 아키텍처를 확장한 후 아키텍처의 컴포넌트들을 구현하는 단계에서 적용된다. 컴포넌트 안의 클래스의 역할과 동작이 결정된 후 발생하는 설계 이슈에 대하여 해법이 되는 것이 디자인패턴이다. 최상위 설계(아키텍처 스타일) &amp;rarr; 중위 설계(디자인 패턴) &amp;rarr; 하위 설계(알고리즘과 자료구조)의 단계이다.&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;디자인 패턴은 캡슐화, 응집, 추상화와 같은 설계 원리와 객체지향 SOLID 원칙을 잘 지킨 모범 사례&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;&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;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. 싱글톤 패턴&lt;/b&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;b&gt;클래스 생성에 대한 통제&lt;/b&gt;를 하는 것이다. 단일 객체를 만들어야 할 때 필요로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 클래스 인스턴스만 원하고 모든 클라이언트가 단일 동일 인스턴스를 공유하려면 싱글톤 패턴을 적용한다. 예를 들어, DB 커넥션을 위한 인터페이스가 있다. 클라이언트마다 열어주는게 아니라 &lt;b&gt;DB 커넥션 객체를 하나만 두고, 계속 재사용하겠다는 의미이다. &lt;/b&gt;DB 입장에서 커넥션을 여러개 관리하는 것이 낭비이다. - 자원 절약하자&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;b&gt;클래스 자체를 정적(static)변수로 둔다.&lt;/b&gt;&lt;br /&gt;자바에서 static이 붙은 변수는 객체마다 생기는 것이 아니라, 클래스 자체에서 딱 1개만 생성된다. 외부에서 new로 객체를 만들 수 없으니, 자기 클래스 내부에서 객체를 만들고 static으로 두는 것이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클래스의 생성자는 private으로 선언한다.&lt;/b&gt;&lt;br /&gt;클래스 생성자가 public이면, 어디서든 new Class()로 객체를 찍어낼 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유일하게 객체를 접근하는 정적 메서드를 둔다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;976&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Aw3N2/dJMcagyqywo/lhQwRck86YtORDprK5K6Vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Aw3N2/dJMcagyqywo/lhQwRck86YtORDprK5K6Vk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Aw3N2/dJMcagyqywo/lhQwRck86YtORDprK5K6Vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAw3N2%2FdJMcagyqywo%2FlhQwRck86YtORDprK5K6Vk%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;550&quot; height=&quot;369&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;976&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1606&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OOOPp/dJMcaiXh2K5/RLHlovr5iRiHZHvK5j91K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OOOPp/dJMcaiXh2K5/RLHlovr5iRiHZHvK5j91K1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OOOPp/dJMcaiXh2K5/RLHlovr5iRiHZHvK5j91K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOOOPp%2FdJMcaiXh2K5%2FRLHlovr5iRiHZHvK5j91K1%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;550&quot; height=&quot;232&quot; data-origin-width=&quot;1606&quot; data-origin-height=&quot;676&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;&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;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. 반복자(Iterator) 패턴&lt;/b&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1599&quot; data-origin-height=&quot;1070&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5aeDd/dJMcadaxGsd/x98X7F53NRpKogKNDhdb4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5aeDd/dJMcadaxGsd/x98X7F53NRpKogKNDhdb4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5aeDd/dJMcadaxGsd/x98X7F53NRpKogKNDhdb4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5aeDd%2FdJMcadaxGsd%2Fx98X7F53NRpKogKNDhdb4K%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;340&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1599&quot; data-origin-height=&quot;1070&quot;/&gt;&lt;/span&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;b&gt;&amp;lt;&amp;lt;interface&amp;gt;&amp;gt; Aggregate&lt;/b&gt;에서 Iterator 객체를 만들어서 반환한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ConcreteAggregate&lt;/b&gt;는 Aggregate 인터페이스를 구현한 클래스이다. List, LinkedList 등 다양한 구체적인 집합이 된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;lt;&amp;lt;interface&amp;gt;&amp;gt; Iterator&lt;/b&gt;는 데이터를 순서대로 보기 위해서 필요한 메서드를 정의해두었다. 클라이언트는 getFirst(), getNext()...로 접근한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ConcreteIterator&lt;/b&gt;는 Iterator 인터페이스를 구현한 클래스이다. array면 인덱스를 움직이고, linkedlist면 노드 포인터를 움직이는 식으로 내부 데이터 구조에 맞게 탐색을 수행한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Client는 &amp;lt;&amp;lt;interface&amp;gt;&amp;gt;에만 접근을 한다.&lt;/b&gt; 내부가 어떻게 동작하는지는 관심이 없다. 그저 getFirst()하면 첫번째 요소가 나오면 되고, getNext()를 하면 다음 요소가 나오면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. 어댑터 패턴&lt;/b&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;b&gt;어댑터는 서비스가 제공하는 인터페이스를 클라이언트가 기대하는 인터페이스로 변환을 해준다.&lt;/b&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;1617&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cj8xs8/dJMcahYm0jE/01FM7eKZ3ChwZUEp5D6KbK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cj8xs8/dJMcahYm0jE/01FM7eKZ3ChwZUEp5D6KbK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cj8xs8/dJMcahYm0jE/01FM7eKZ3ChwZUEp5D6KbK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcj8xs8%2FdJMcahYm0jE%2F01FM7eKZ3ChwZUEp5D6KbK%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;500&quot; height=&quot;271&quot; data-origin-width=&quot;1617&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 interfaceClientExpects 인터페이스를 기대하고 있다. Client는 자신이 기대하는 인터페이스에서 제공하는 methodClientExpects()만 사용하고자 한다. 서비스는 interfaceAvailable 인터페이스를 제공하고 있다. ServiceAvailable은 해당 인터페이스를 구현하고 있다. 내부적으로 methodAvailable() 메서드를 구현한다. 서로 다른 규격(메서드)을 가지고 있다. 그래서 중간에 Adaptor를 둔다. interfaceClientExpects를 상속받는다. methodClientExpects()를 구현하게 될텐데, 그 안에 서비스에서 제공하고 있는 methodAvailable() 메서드가 동작하게 된다. Client쪽은 수정하지 않고 확장할 수 있는 OCP 원칙을 잘지켰다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1381&quot; data-origin-height=&quot;867&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpz5zR/dJMcagFdXfx/sWctjqPgelSEc81poT9bt1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpz5zR/dJMcagFdXfx/sWctjqPgelSEc81poT9bt1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpz5zR/dJMcagFdXfx/sWctjqPgelSEc81poT9bt1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpz5zR%2FdJMcagFdXfx%2FsWctjqPgelSEc81poT9bt1%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;450&quot; height=&quot;283&quot; data-origin-width=&quot;1381&quot; data-origin-height=&quot;867&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 변환하는 case가 다양할 때 어댑터만 갈아끼우면 된다. 아래 코드를 보면 clinet는 자신한테 들어오는 데이터가 무엇인지에 따라서, 메서드를 달리 호출하지 않는다. 그저 convert()만 호출한다. Xml xml = adapter.convert(), Csv csv = adaptor.convert() 이런식으로 말이다. 클라이언트가 원하는 인터페이스는 동이라고, 어댑터가 그 안에서 ServiceAvailable 메서드인 converToXML()과 이어주는 역할을 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1776414344197&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface IDataAdapter {
    Xml convert(Json json);
}

// client가 바로 이걸 호출하게 하지 않는다.
class Json {
    public Json(){}
    Xml convertToXML(){
        // Logic to convert the data into XML
    }
}

// client는 convert 메서드만 알고 싶다.
class JsonToXmlAdapter implements IDataAdapter {
    private Json json;
    public JsonToXmlAdapter(Json json) {
        this.json = json;
    }
    
    public Xml convert(Json json) {
        // Logic to convert json to Xml
        this.json.convertToXML() // ServiceAvailable 메서드와 연결
    }
}

class Main {
    public static void main(String[] args) {
        Json json = new Json(&quot;some json data&quot;);
        Csv csv = new Csv(&quot;some csv data&quot;);
        Xml xml = new Xml(&quot;some xml data&quot;);
        Bson bson = new Bson(&quot;some bson data&quot;);
        
        // Convert from Json to Xml
        IDataAdapter adapter = new JsonToXmlAdapter(json);
        Xml xml = adapter.convert();
        
        // Convert Json to csv
        adapter = new JsontoCsvAdapter(json);
        Csv csv = adapter.convert();
        
        // Convert Csv to bson
        adapter = new CsvToBsonAdapter(csv);
        Bson bson = adapter.convert();
        
        // Call the calculate tax API using the XML data
        Decimal tax = caculateTax(xml)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4. 데코레이터 패턴&lt;/b&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;b&gt;수정&lt;/b&gt;할 수 있다. 하지만 이는 OCP(Open Close Principle)에 위배된다. 그러면 상속에 의해서 동작을 확장해본다면? 정적으로 결정해놓기 때문에, 런타임에 확장이 불가능하고 무엇보다도 기능의 조합 수 만큼의 서브클래스가 필요해진다. 그래서 나온 것이 데코레이터 패턴이다. 동작을 동적으로 만들어서(데코레이트해서) 보여준다.&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;(1) component 클래스(장식대상)&lt;/b&gt;와 확장 기능이 담긴 &lt;b&gt;(2) 데코레이터&lt;/b&gt;이다. 상속된 구체적인 component1 클래스는 decorator가 가진 기능으로 확장되거나 장식될 기본 기능을 가진다. 핵심 로직을 가진 클래스고 이 클래스를 decorator들로 장식한다고 생각하자. 데코레이터 객체에는 Component1 또는 다른 데코레이터 객체의 참조가 포함되어 확장된다. 서브클래스를 수많큼 만들 필요가 없고, 데코레이터 체인을 사용하여 장식 요소를 래핑할 수 있게 된다. (b)를 보면, Decorator1 객체가 Component1 객체를 래핑하고, Decorator2 객체가 래핑되는 예시이다. 데코레이터 객체가 &lt;b&gt;재귀 합성(순서가 중요)&lt;/b&gt;으로 component 객체를 래핑하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1227&quot; data-origin-height=&quot;567&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qVPdp/dJMcafGiGRU/SMgKOdeOpjF5kbWS8b10i1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qVPdp/dJMcafGiGRU/SMgKOdeOpjF5kbWS8b10i1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qVPdp/dJMcafGiGRU/SMgKOdeOpjF5kbWS8b10i1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqVPdp%2FdJMcafGiGRU%2FSMgKOdeOpjF5kbWS8b10i1%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;550&quot; height=&quot;254&quot; data-origin-width=&quot;1227&quot; data-origin-height=&quot;567&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1521&quot; data-origin-height=&quot;824&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZWsVj/dJMcadBCDyh/PWnnoJMOkEi5BrgXRoKfyK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZWsVj/dJMcadBCDyh/PWnnoJMOkEi5BrgXRoKfyK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZWsVj/dJMcadBCDyh/PWnnoJMOkEi5BrgXRoKfyK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZWsVj%2FdJMcadBCDyh%2FPWnnoJMOkEi5BrgXRoKfyK%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;500&quot; height=&quot;271&quot; data-origin-width=&quot;1521&quot; data-origin-height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;1012&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf6HyP/dJMcaiJJQFj/eNyO45RWnRThd5JYj1U91k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf6HyP/dJMcaiJJQFj/eNyO45RWnRThd5JYj1U91k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf6HyP/dJMcaiJJQFj/eNyO45RWnRThd5JYj1U91k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf6HyP%2FdJMcaiJJQFj%2FeNyO45RWnRThd5JYj1U91k%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;500&quot; height=&quot;324&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;1012&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1776418964514&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Component
public interface Notifier {
    public void send(String message);
}

// Decorator
public class BaseDecorator implements Notifier {
    private Notifier notifier;
    public void send(String message) {
        // 기본 메세지 로직
    }
}

public class SMSDecorator extends BaseDecorator {
    public SMSDecorator(Notifier notifier) {
        super(notifier)
        public void send (String message) {
            message += &quot;SMS Format message&quot;;
            super.send(message);
        }
    }
}

BaseDecorator base = new SMSDecorator(new FBDecorator(new BaseDecorator()));
base.send(message);
// SMS -&amp;gt; FB -&amp;gt; 최종 전송&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 SMS에게 &quot;hello&quot;라고 보낸다. SMSDecorator에서 &quot;hello&quot;+&quot;SMS Format message&quot;로 전처리 하고, super.send(&quot;hello SMS Format message&quot;) 호출한다. FBDecorator는 &quot;hello SMS Format message&quot;+&quot;FB Format message&quot;로 전처리하고 super.send(&quot;hello SMS Format message FB Format message&quot;) 호출한다. 그러면 이제 마지막 BaseDecorator가 send()를 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;5. 팩토리 메서드 패턴&lt;/b&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;b&gt;새로운 객체를 생성&lt;/b&gt;할 때 사용된다. 클라이언트에서 사용할 클래스의 객체를 생성하는 책임을 분리하여 새로운 변화가 있을 때 기존 코드는 수정할 필요 없이 확장할 수 있게 돕는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 생성하기 위한 팩토리 메서드(createProduct)를 포함하는 추상클래스(AbstractCreator)를 정의한다. Client는 AbstractCreator와 AbstractProduct만 보고 있다. 실제로 객체가 만들어지는 방법과 그 구체적인 객체는 Client가 모른다. 팩토리 메서드에 따라서 아래에서 구현되고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1501&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KwcGI/dJMcafGiSXw/5HYl67KUJvXfQ4aLJYO800/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KwcGI/dJMcafGiSXw/5HYl67KUJvXfQ4aLJYO800/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KwcGI/dJMcafGiSXw/5HYl67KUJvXfQ4aLJYO800/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKwcGI%2FdJMcafGiSXw%2F5HYl67KUJvXfQ4aLJYO800%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;550&quot; height=&quot;218&quot; data-origin-width=&quot;1501&quot; data-origin-height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1577&quot; data-origin-height=&quot;953&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQrkVd/dJMcagZtUaj/NnCzfCCKgyQtDrEWr5kim0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQrkVd/dJMcagZtUaj/NnCzfCCKgyQtDrEWr5kim0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQrkVd/dJMcagZtUaj/NnCzfCCKgyQtDrEWr5kim0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQrkVd%2FdJMcagZtUaj%2FNnCzfCCKgyQtDrEWr5kim0%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;550&quot; height=&quot;332&quot; data-origin-width=&quot;1577&quot; data-origin-height=&quot;953&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;&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;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;6. 추상 팩토리 패턴&lt;/b&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;b&gt;팩토리 메서드를 모아둔 패밀리&lt;/b&gt;로 작성하는 것이다. 따라서 이 또한 객체를 사용할 클라이언트에서 구체적인 객체 생성을 지정하는 책임을 분리하기 위해 추상 인터페이스를 이용하여 관련 객체 패밀리를 생성한다. 클래스 외부로 객체를 만드는 책임을 이동시키는 방식으로 객체 생성에 융통성을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 AbstractProduct와 AbstractFactory에만 접근하고, 나머지 구체적인 부분에는 관여하지&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;916&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FN7MB/dJMcahxl65Z/HDSSEQNZYZ8bKAAKkl1F40/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FN7MB/dJMcahxl65Z/HDSSEQNZYZ8bKAAKkl1F40/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FN7MB/dJMcahxl65Z/HDSSEQNZYZ8bKAAKkl1F40/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFN7MB%2FdJMcahxl65Z%2FHDSSEQNZYZ8bKAAKkl1F40%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;550&quot; height=&quot;492&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;820&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1439&quot; data-origin-height=&quot;966&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RQTBz/dJMcacQkAH1/xkRZOjUKT7owJDTh0zBSFK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RQTBz/dJMcacQkAH1/xkRZOjUKT7owJDTh0zBSFK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RQTBz/dJMcacQkAH1/xkRZOjUKT7owJDTh0zBSFK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRQTBz%2FdJMcacQkAH1%2FxkRZOjUKT7owJDTh0zBSFK%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;550&quot; height=&quot;369&quot; data-origin-width=&quot;1439&quot; data-origin-height=&quot;966&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1776440377100&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Demo {
    /**
     * Application picks the factory type and creates it in run time (usually at
     * initialization stage), depending on the configuration or environment 
     * variables.
     */
    private static Application configureApplication() {
        Application app;
        GUIFactory factory;
        String osName = System.getProperty(&quot;os.name&quot;).toLowerCase();
        if (os.name.contains(&quot;mac&quot;)) {
            factory = new MacOSFactory();
        } else {
            factory = new WindowsFactory();
        }
        app = new Application(factory);
        return app;
    }
    
    public static void main(String[] args) {
        Application app = configureApplication();
        app.print();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;os에 따라서 동적으로 GUI를 구현하는 코드이다. MacOSFactory()와 WindowsFactory()를 사용하고 있다. 팩토리 패턴을 사용하지 않았다면, mac/windows에서 어떻게 구현하고 있는지를 여기에 다 들어갔을 것이다. 불필요하게 fat해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;7. 상태 패턴&lt;/b&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;상태에 따라 객체의 동작을 변경해야 하는 경우 사용한다. 객체에 상태 변수가 있고 if-else로 상태에 따라 다른 동작을 수행하도록 하는 방법을 생각할 수 있을 텐데, 이는 상태가 프로그램 구조에 잘 드러나지 않고 &lt;b&gt;상태 변경과 추가에 대하여 영향을 많이 받는 단점&lt;/b&gt;이 있다. 그래서 상태 패턴은 맥락과 상태를 별도로 구현하여 결합도를 낮춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Document가 상태를 직접 결정하는게 아니다. State 클래스를 포함하고 있고, 상태는 State에서 결정된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;901&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cs6nCQ/dJMcacQkBhM/g574xOk1uwKMk7Rz1yCnDk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cs6nCQ/dJMcacQkBhM/g574xOk1uwKMk7Rz1yCnDk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cs6nCQ/dJMcacQkBhM/g574xOk1uwKMk7Rz1yCnDk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcs6nCQ%2FdJMcacQkBhM%2Fg574xOk1uwKMk7Rz1yCnDk%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;550&quot; height=&quot;358&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;901&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;아래 코드는 안좋은 예시이다. Document 클래스 안에 전부 다 들어있다. 만약 새로운 state를 추가하고 싶다면...publish 메서드를 다시 작성해야한다. Document 클래스를 수정하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;1190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mRd8k/dJMcahD5f6y/7JKQxaywKStkv4ta00wic1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mRd8k/dJMcahD5f6y/7JKQxaywKStkv4ta00wic1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mRd8k/dJMcahD5f6y/7JKQxaywKStkv4ta00wic1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmRd8k%2FdJMcahD5f6y%2F7JKQxaywKStkv4ta00wic1%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;550&quot; height=&quot;453&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;1190&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;이렇게 바꿨더니 if-else문이 사라졌다. 상태 패턴을 잘 적용한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;1066&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zT6QD/dJMcahc2aZz/vBeLaAuEKnOHGLYHHZfxK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zT6QD/dJMcahc2aZz/vBeLaAuEKnOHGLYHHZfxK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zT6QD/dJMcahc2aZz/vBeLaAuEKnOHGLYHHZfxK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzT6QD%2FdJMcahc2aZz%2FvBeLaAuEKnOHGLYHHZfxK1%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;550&quot; height=&quot;411&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;1066&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tTSoH/dJMcagLZp6O/KNNToyaYZkKp2dGVKdu6V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tTSoH/dJMcagLZp6O/KNNToyaYZkKp2dGVKdu6V0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tTSoH/dJMcagLZp6O/KNNToyaYZkKp2dGVKdu6V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtTSoH%2FdJMcagLZp6O%2FKNNToyaYZkKp2dGVKdu6V0%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;550&quot; height=&quot;277&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1776443340685&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface PlayerState {
    void play();
    void pause();
    void stop();
}

// 각 state에 맞춰서 play, pause, stop을 구현
class PlayingState implements PlayerState {
    @Override
    public void play() {
        System.out.println(&quot;Already playing&quot;);
    }
    @Override
    public void pause() {
        System.out.println(&quot;Pausing music&quot;);
        // Pause playback logic
    }
    @Override
    public void stop() {
        System.out.println(&quot;Stopping music&quot;);
        // Stop playback logic
    }
}

class PausedState implements PlayerState {
    @Override
    public void play() {
        System.out.println(&quot;Resuming playback&quot;);
        //...
    }
    @Override
    public void pause() {
        System.out.println(&quot;Already paused&quot;);
    }
    @Override
    public void stop() {
        System.out.println(&quot;Stopping music&quot;);
    }
}

class StoppedState implements PlayerState {
    @Override
    public void play() {
        System.out.println(&quot;Starting playback&quot;);
    }
    @Override
    public void pause() {
        System.out.println(&quot;Can't pause when stopped&quot;);
    }
    @Override
    public void stop() {
        System.out.println(&quot;Already stopped&quot;);
    }
}

class MusicPlayer {
    private PlayerState currentState;
    
    public MusicPlayer() {
        this.currentState = new StoppedState();
    }
    
    public void play() {
        currentState.play();
    }
    public void pause() {
        currentState.pause();
    }
    public void stop() {
        currentState.stop();
    }
    
    public void setState(PlayerState newState) {
        this.currentState = newState;
    }
}

public class Client {
    public static void main(String[] args) {
        MusicPlayer player = new MusicPlayer();
        player.setState(new PlayingState());
        
        // Play music
        player.play();
        //Pause music
        player.pause();
        //Stop music
        player.stop();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;8. 전략 패턴&lt;/b&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전략 패턴의 장점은 OCP 적용 가능, 전략별 알고리즘 분리, 객체 알고리즘 동적 취사 선택 가능하다는 것이다. 단점은 전략이 소수일 때 과한 구조이며, &lt;b&gt;클라이언트가 어떤 전략이 적절한지를 알고 있어야&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게만 들으면 전략 패턴이 상태 패턴과 비슷해보인다. 클래스에서 상태를 분리한다, 전략을 분리한다... 그러나 아래 그림을 보자. 상태 패턴은 상태 간의 전환이 이루어져 의존 관계가 있다. render를 한 후 publish를 해야하는 것처럼 상태 간의 의존 관계가 있다. 반면, 전략 패턴은 전략 간의 전환이나 의존이 없다. 그저 취사선택할 수 있는 다른 전략들일 뿐이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1581&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dj37oI/dJMcaakF97e/TZB4nyZRCkEKk5QqKsk7Dk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dj37oI/dJMcaakF97e/TZB4nyZRCkEKk5QqKsk7Dk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dj37oI/dJMcaakF97e/TZB4nyZRCkEKk5QqKsk7Dk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdj37oI%2FdJMcaakF97e%2FTZB4nyZRCkEKk5QqKsk7Dk%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;550&quot; height=&quot;258&quot; data-origin-width=&quot;1581&quot; data-origin-height=&quot;742&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;&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;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;9. 옵서버 패턴&lt;/b&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;데이터를 보관하고 있는 Subject가 그 데이터를 이용하는 Observers와 효과적으로 통신하면서도 어떻게 하면 느슨하게 결합할 수 있는가라는 문제를 다룬다. 옵서버가 계속 데이터의 변경을 체크하는 것이 아니라 데이터(Subject)가 변경될 때마다 관찰하는 옵서버들에게 통지하면 느슨한 결합이 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Subject 클래스: 옵서버 목록을 유지, 변경을 고지&lt;/li&gt;
&lt;li&gt;Observer 클래스: 변경을 통지 받고 접근을 요청&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;935&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ECghs/dJMcaju8mmE/IXR3LiIJzMeggtvkSoreBk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ECghs/dJMcaju8mmE/IXR3LiIJzMeggtvkSoreBk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ECghs/dJMcaju8mmE/IXR3LiIJzMeggtvkSoreBk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FECghs%2FdJMcaju8mmE%2FIXR3LiIJzMeggtvkSoreBk%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;550&quot; height=&quot;335&quot; data-origin-width=&quot;935&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>소프트웨어공학</category>
      <author>chief.park</author>
      <guid isPermaLink="true">https://parkso-yeon.tistory.com/10</guid>
      <comments>https://parkso-yeon.tistory.com/10#entry10comment</comments>
      <pubDate>Sat, 18 Apr 2026 01:50:21 +0900</pubDate>
    </item>
    <item>
      <title>SOLID - 코드로 이해하기</title>
      <link>https://parkso-yeon.tistory.com/9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;1. 단일책임원칙 Single Responsibility Principle&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Invoice 클래스는 계산서인데, 세금 계산(calculateTax), 계산서 출력(printInvoice), 데이터베이스 저장(saveToDatabase)를 왜 하냐...&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다 분리해서 따로 두어야 응집력이 올라간다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;1020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FcgA3/dJMcac3Nwsp/AHHuDDGfj2sdzXi5St6fD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FcgA3/dJMcac3Nwsp/AHHuDDGfj2sdzXi5St6fD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FcgA3/dJMcac3Nwsp/AHHuDDGfj2sdzXi5St6fD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFcgA3%2FdJMcac3Nwsp%2FAHHuDDGfj2sdzXi5St6fD1%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;550&quot; height=&quot;427&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;그래서 아래와 같이 4개의 클래스로 분리한다. 각 클래스는 자신의 책임만을 다한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;988&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bktwXX/dJMcabcKuuZ/3rkGZftUgPAM0i7G44Ymkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bktwXX/dJMcabcKuuZ/3rkGZftUgPAM0i7G44Ymkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bktwXX/dJMcabcKuuZ/3rkGZftUgPAM0i7G44Ymkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbktwXX%2FdJMcabcKuuZ%2F3rkGZftUgPAM0i7G44Ymkk%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;550&quot; height=&quot;409&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;988&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvJBjd/dJMcagFbmyv/kwahKWqF6GFe9OjO218iFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvJBjd/dJMcagFbmyv/kwahKWqF6GFe9OjO218iFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvJBjd/dJMcagFbmyv/kwahKWqF6GFe9OjO218iFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvJBjd%2FdJMcagFbmyv%2FkwahKWqF6GFe9OjO218iFK%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;550&quot; height=&quot;341&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;2. 개방 폐쇄 원칙 Open Close Principle&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;클라이언트가 접근하는 부분에 대해서 수정에는 닫혀 있고 확장에는 열려 있어야 한다. 인터페이스 분리가 되어야 하는데, 아래의 코드는 Sorter 클래스에 어떤 소팅 알고리즘을 쓸 것인지에 대한 구현까지 다 들어가 있다. 확장하려면, 다 뜯어 고쳐야 한다. 문제다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L5pIP/dJMcabcKuHz/xkoBEtvW0Uz9HOX9gAKa40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L5pIP/dJMcabcKuHz/xkoBEtvW0Uz9HOX9gAKa40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L5pIP/dJMcabcKuHz/xkoBEtvW0Uz9HOX9gAKa40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL5pIP%2FdJMcabcKuHz%2FxkoBEtvW0Uz9HOX9gAKa40%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;550&quot; height=&quot;309&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;760&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;이렇게 SortStrategy라는 인터페이스를 두고, BubbleSort, QuickSort, MergeSort는 SortStrategy를 implement 하면 된다. 그러면 SortStrategy를 구현하는 소팅 알고리즘이 변경되거나 늘어나도, &lt;b&gt;Sorter는 바뀌지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;932&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D2SZ6/dJMcabRm0h6/MmxpNWgVWKw0i5EX8ms4PK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D2SZ6/dJMcabRm0h6/MmxpNWgVWKw0i5EX8ms4PK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D2SZ6/dJMcabRm0h6/MmxpNWgVWKw0i5EX8ms4PK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD2SZ6%2FdJMcabRm0h6%2FMmxpNWgVWKw0i5EX8ms4PK%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;550&quot; height=&quot;447&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;932&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;1108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/njO9k/dJMcai3YRwE/jXpnu75Asp46WtfYiz7TNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/njO9k/dJMcai3YRwE/jXpnu75Asp46WtfYiz7TNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/njO9k/dJMcai3YRwE/jXpnu75Asp46WtfYiz7TNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnjO9k%2FdJMcai3YRwE%2FjXpnu75Asp46WtfYiz7TNK%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;550&quot; height=&quot;480&quot; data-origin-width=&quot;1270&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;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;3. 리스코프 대체 원칙 Liskov Substitution Principle&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;하위 클래스가 상위 클래스를 대체할 수 있어야 한다. 상위 클래스를 상속 받아서 구현하는데, 상위 클래스에 있는 메서드를 구현하지 못해서 서 불필요한 Exception을 날리는 것을 방지하자.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;Bird 클래스 내부에 fly()메서드를 둬서, 날지 못하는 Ostrich는 불필요한 예외를 날리게 된다. fly 행동은 인터페이스로 빼고, Bird에는 새의 공통 속성만 두자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;1154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UXT3U/dJMcaflXal6/OsdbfAefN8PQTASAKgRKWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UXT3U/dJMcaflXal6/OsdbfAefN8PQTASAKgRKWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UXT3U/dJMcaflXal6/OsdbfAefN8PQTASAKgRKWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUXT3U%2FdJMcaflXal6%2FOsdbfAefN8PQTASAKgRKWk%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;550&quot; height=&quot;440&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;1154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;Bird 인터페이스를 상속 받은 FlyingBird 인터페이스에만 fly()메서드가 있다. 날 수 있는 새는 FlyingBird를 구현하면 되고, 날지 못하는 새는 Bird를 구현하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;1148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YH7oi/dJMcaf7iBPQ/3pkGxW31cEMxJU8ZKX1TU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YH7oi/dJMcaf7iBPQ/3pkGxW31cEMxJU8ZKX1TU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YH7oi/dJMcaf7iBPQ/3pkGxW31cEMxJU8ZKX1TU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYH7oi%2FdJMcaf7iBPQ%2F3pkGxW31cEMxJU8ZKX1TU1%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;550&quot; height=&quot;490&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;1148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cu3NEK/dJMcabqkO8m/vyzwYO6oD7E9UT9h4bdDm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cu3NEK/dJMcabqkO8m/vyzwYO6oD7E9UT9h4bdDm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cu3NEK/dJMcabqkO8m/vyzwYO6oD7E9UT9h4bdDm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcu3NEK%2FdJMcabqkO8m%2FvyzwYO6oD7E9UT9h4bdDm0%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;550&quot; height=&quot;211&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;4. 인터페이스 분리 원칙 Interface Segregation Principle&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;비만 인터페이스를 방지하자. 인터페이스에 필요 없는게 들어가면, 쓸데 없이 exception을 날려야 한다.&lt;br /&gt;Worker 인터페이스 work()와 eat()이 있다. Robot은 먹지 못하므로 eat()을 구현할 수 없다. exception을 날린다. 이렇게 하지 말고, work(), eat()을 따로따로 인터페이스로 두자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;1244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drhihp/dJMcabX9FW8/AwLfVbIkFvXK68nOORfARK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drhihp/dJMcabX9FW8/AwLfVbIkFvXK68nOORfARK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drhihp/dJMcabX9FW8/AwLfVbIkFvXK68nOORfARK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdrhihp%2FdJMcabX9FW8%2FAwLfVbIkFvXK68nOORfARK%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;550&quot; height=&quot;488&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;1244&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;아래와 같이 Workable, Eatable 인터페이스를 나눴다. 이제 HumanWorker 클래스와 RobotWorker 클래스는 자기한테 필요한 인터페이스만 구현하면 되는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;1144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vz3Mn/dJMb99MLvHL/IkU9iIr1NwROzCPde1eJMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vz3Mn/dJMb99MLvHL/IkU9iIr1NwROzCPde1eJMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vz3Mn/dJMb99MLvHL/IkU9iIr1NwROzCPde1eJMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVz3Mn%2FdJMb99MLvHL%2FIkU9iIr1NwROzCPde1eJMk%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;550&quot; height=&quot;586&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;1144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;5. 의존 관계역전 원리 &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;Dependency Inversion Principle&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #0a0a0a;&quot;&gt;고수준 모듈은 저수준 모듈에 의존하면 안된다. 둘다 추상(인터페이스)에 의존해야한다. 그래서 그 사이에 인터페이스를 하나 두는 전략이다. Computer 클래스가 Keyboard 클래스를 직접 생성하고 있다. 결합이 강하다... 왜냐하면 키보드가 바뀐다면 컴퓨터도 바뀌어야 하기 때문이다. 둘다 추상 클래스에 의존하도록 해주려면 어떻게 해야할까?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CkKGC/dJMcafTM8P0/UzF5woU4qyHI9bkuM0hST1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CkKGC/dJMcafTM8P0/UzF5woU4qyHI9bkuM0hST1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CkKGC/dJMcafTM8P0/UzF5woU4qyHI9bkuM0hST1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCkKGC%2FdJMcafTM8P0%2FUzF5woU4qyHI9bkuM0hST1%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;550&quot; height=&quot;399&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;아래와 같이 인터페이스에 의존하도록 했다. Computer는 바뀌지 않아도 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CVwvl/dJMcagFbn3K/JVKl2kD2TlE5UnBngwbMY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CVwvl/dJMcagFbn3K/JVKl2kD2TlE5UnBngwbMY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CVwvl/dJMcagFbn3K/JVKl2kD2TlE5UnBngwbMY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCVwvl%2FdJMcagFbn3K%2FJVKl2kD2TlE5UnBngwbMY0%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;550&quot; height=&quot;470&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RgYys/dJMcaiQsZbI/Eed7IHQKwRtFZLQM4he2oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RgYys/dJMcaiQsZbI/Eed7IHQKwRtFZLQM4he2oK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RgYys/dJMcaiQsZbI/Eed7IHQKwRtFZLQM4he2oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRgYys%2FdJMcaiQsZbI%2FEed7IHQKwRtFZLQM4he2oK%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;550&quot; height=&quot;541&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;1158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>소프트웨어공학</category>
      <author>chief.park</author>
      <guid isPermaLink="true">https://parkso-yeon.tistory.com/9</guid>
      <comments>https://parkso-yeon.tistory.com/9#entry9comment</comments>
      <pubDate>Tue, 14 Apr 2026 23:24:13 +0900</pubDate>
    </item>
    <item>
      <title>객체지향 설계 원리</title>
      <link>https://parkso-yeon.tistory.com/8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향 언어가 도입되면서 전통적 설계 원리인 &lt;b&gt;추상화&lt;/b&gt;라는 개념은 &lt;b&gt;인터페이스와 구현의 분리&lt;/b&gt;로 확장되었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;인터페이스는 공개된 메서드의 프로토타입(추상)만을 정의해 놓은 것이다. 대부분의 클래스는 메서드의 구현을 포함하는데 여기에서 &lt;b&gt;공개된 메서드를 인터페이스로 따로 정의&lt;/b&gt;하고 이를 &lt;b&gt;구현 상속한 것&lt;/b&gt;으로 관계를 맺는다.&lt;b&gt; 객체지향 다형성&lt;/b&gt;을 이용하다면, 인터페이스로 상속된 여러 구현을 가질 수 있다. &lt;b&gt;하나의 추상 개념&lt;/b&gt;을 필요에 따라 &lt;b&gt;여러 버전으로 구체화&lt;/b&gt;하는 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;컴포넌트의 공개 인터페이스(사용자가 알아야 하는 부분)는 컴포넌트가 어떻게 구현되는지와 분리하자는 것이다. 인터페스와 분리된 구현은 쉽게 변경할 수 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;캡슐화&lt;/b&gt; 원리는 구현 세부 사항을 숨기도록(정보은닉) 한다. 인터페이스 분리 원리는 &lt;b&gt;추상화&lt;/b&gt;를 지시한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;1. 단일 책임 원리 Single Responsibility Principle&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;클래스의 역할과 책임을 단순화&lt;/b&gt;해야 한다. 클래스를 변경해야하는 이유가 오직 하나 뿐이어야 한다. 관련 없는 책임은 다른 클래스로 분리하자.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;2. 개방 폐쇄의 원리 Open Close Principle&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;OCP는 소프트웨어 개체가 &lt;b&gt;확장을 위해서는 열려있어야 하지만, 수정을 위해서는 닫혀야 한다는 것이다. 다형성&lt;/b&gt;에 대해서 생각해보자. 상속을 이용하여 클래스가 정의되어 있을 때, 다형성이 적용되어 서로 대체할 수 있는 인터페이스 &quot;구현&quot;이 될 수 있다. 클래스 자체를 수정하지 않고 확장할 수 있다. Client에서 접근하는 쪽은 수정이 닫혀있고, 내부 구현 모듈이 확장된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;3. 리스코프 교체의 원리 Liskov Substitution Principle&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;상위 클래스A&lt;/b&gt;를 상속 받은 &lt;b&gt;하위 클래스B&lt;/b&gt;는 프로그램 동작을 방해하지 않고 A를 B로 대체할 수 있어야 한다. 상위 클래스에 하위 클래스를 넣어도 문제가 없어야 한다는 뜻이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;fly() 메서드를 포함한 Bird() 상위 클래스를 가정하자. 이를 상속 받아서 구현하는 &lt;b&gt;참새() 클래스&lt;/b&gt;와 &lt;b&gt;Ostrich() 클래스&lt;/b&gt;가 있다. 참새 클래스는 fly()를 구현할 수 있다. 그러나 타조는 날지 못하므로 fly()를 구현하지 않고, Exception을 날린다. Ostrich 클래스는 자신의 부모 클래스를 대체하지 못하므로, LSP에 위배된다. 구현하지 못하는 메서드에 대해서 다 예외를 줄 것인가? 인터페이스 구현으로 해결할 수 있다. flyable을 &amp;lt;&amp;lt;interface&amp;gt;&amp;gt;로 두자. fly() 를 그냥 상속받는게 아니라 interface로 두고, Bird()에서는 모든 새가 가지고 있는 공통 속성만을 상속받자는 것이다. 할 필요 없는 Exception을 방지하자.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Bird { 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void layEggs() { ... } // 모든 새의 진짜 공통점
}

interface Flyable { 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void fly(); // '나는 능력'은 인터페이스로 분리
}

class Sparrow extends Bird implements Flyable {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void fly() { System.out.println(&quot;참새는 난다.&quot;); }
}

class Ostrich extends Bird {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 억지로 fly()를 구현할 필요도 없고, 에러를 던질 필요도 없다.
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;Rectangle 클래스는 width와 height를 가지고 있다. getArea()를 w*h로 구한다. Square 클래스는 Rectangle 클래스를 상속받는다. 그러나 getArea가 다르게 동작한다. 어떻게 해결할 수 있을까? 아래처럼 할 수 있을 것 같다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;interface Shape {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int getArea();
}

class Rectangle implements Shape {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private int width, height;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void setWidth(int w) { this.width = w; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void setHeight(int h) { this.height = h; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public int getArea() { return width * height; }
}

class Square implements Shape {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private int side;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void setSide(int s) { this.side = s; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public int getArea() { return side * side; }
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;4. 인터페이스 분리의 원리 Interface Segregation Principle&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;ISP는 클라이언트가 사용하지 않는 인터페이스를 강제로 구현해서는 안된다는 것이다. 사용하지 않는 메서드가 있다면 fat interface다. 다수의 작은 인터페이스를 만들어 필요한 것만을 사용하도록 해야한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;Shape 인터페이스에는 면적 계산하는 area()와 부피 계산하는 volume() 메서드가 있다. Cylinder, Square, Circle 클래스는 Shape 인터페이스를 구현한다. 그런데, Square와 Circle은 2D라서 volume()이 필요 없다. 그러면 fat interface가 되는 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;Shape 인터페이스에는 area()만 두고, 3D Shape 인터페이스를 따로 둬서 Shape를 확장하자. 위에서, flyable 인터페이스를 따로 뺀 것과 동일하다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;5. 의존 관계 역전의 원리 Dependency Inversion Principle&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;높은 수준의 모듈은 변화가 많은 하위 모듈에 쉽게 영향을 받으면 안된다. 하위 모듈에 의존되지 않고 추상적인 부분에 의존되어야 한다. &lt;b&gt;상위클래스는 하위클래스에 끌려다니면 안된다는 것이다.&lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;날씨 데이터를 가져오는 WeatherService 상위 클래스가 있다. 낮은 수준의 OpenWeatherMapAPI를 직접 의존한다면 이 모듈이 바뀔 때마다 문제가 생긴다. 따라서 중간에 interface를 하나 둬서, 영향을 받지 않도록 끊어준다. &lt;b&gt;즉, 높은 수준의 모듈이 영향을 받지 않으려면 높은 수준의 모듈과 낮은 수준의 모듈을 서로 분리하는 추상화를 도입하여야 한다. &lt;/b&gt;높은 수준의 모듈이 낮은 수준의 추상화된 인터페이스에 의존하게 해야한다.&lt;/p&gt;</description>
      <category>소프트웨어공학</category>
      <author>chief.park</author>
      <guid isPermaLink="true">https://parkso-yeon.tistory.com/8</guid>
      <comments>https://parkso-yeon.tistory.com/8#entry8comment</comments>
      <pubDate>Tue, 14 Apr 2026 20:45:57 +0900</pubDate>
    </item>
    <item>
      <title>결합도와 응집도 (Coupling and Cohension)</title>
      <link>https://parkso-yeon.tistory.com/7</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;결합도(Coupling)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;1. 자료(Data) 결합도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;기본 자료형 데이터&lt;/b&gt;만 주고 받는 것이다.&lt;br /&gt;&lt;u&gt;주차요금청구서 모듈&lt;/u&gt;은 필요한 &quot;주차시간&quot;만 넘겨주고, &lt;u&gt;주차요금계산기 모듈&lt;/u&gt;이 &quot;주차요금&quot;을 계산해서 전달해준다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPPZ7O/dJMcadn5qK2/iT1K1rkU6Scamcfz8fwDo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPPZ7O/dJMcadn5qK2/iT1K1rkU6Scamcfz8fwDo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPPZ7O/dJMcadn5qK2/iT1K1rkU6Scamcfz8fwDo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPPZ7O%2FdJMcadn5qK2%2FiT1K1rkU6Scamcfz8fwDo1%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;550&quot; height=&quot;383&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;934&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;2. 스탬프(Stamp) 결합도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;배열이나 객체 같은&lt;b&gt; 자료구조를 참조&lt;/b&gt;하는 형태이다.&lt;br /&gt;&lt;u&gt;주차요금청구서 모듈&lt;/u&gt;은 주차시간(time)만 넘겨주면 되는데, &quot;이용기록(record)&quot; 자료구조를 넘긴다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQ9NT0/dJMb99TxXQn/wJpIjdPaCgJeIX6iUxzGq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQ9NT0/dJMb99TxXQn/wJpIjdPaCgJeIX6iUxzGq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQ9NT0/dJMb99TxXQn/wJpIjdPaCgJeIX6iUxzGq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQ9NT0%2FdJMb99TxXQn%2FwJpIjdPaCgJeIX6iUxzGq1%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;550&quot; height=&quot;461&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;1070&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y4TZR/dJMcaju4U7x/I9dNqEaNgS5Q54DvyaOMz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y4TZR/dJMcaju4U7x/I9dNqEaNgS5Q54DvyaOMz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y4TZR/dJMcaju4U7x/I9dNqEaNgS5Q54DvyaOMz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY4TZR%2FdJMcaju4U7x%2FI9dNqEaNgS5Q54DvyaOMz0%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;550&quot; height=&quot;441&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;1070&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Record를 계속 넘겨주고 있다. 결합력을 낮추기 위해서 Record 자료구조로 넘기기보다는 각자의 멤버변수로 만드는게 좋겠다.&lt;br /&gt;만약에 Record 자료구조에서 useTime이 useMin으로 수정이 발생했다고 하자. 그러면 Bill, Fee도 수정되어야 한다. 다른 모듈에 의해서 내 모듈이 고쳐져야 한다면 결합도가 높은 것이다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;3. 제어(Control) 결합도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;한 모듈이 다른 모듈 내부의 &lt;b&gt;논리적인 흐름을 제어하는 제어요소를 전달&lt;/b&gt;한다. 상위 모듈이 하위 모듈의 상세한 처리 절차를 통제하는 것이다. 이는 캡슐화(Encapsulation)의 원칙에 위배된다. 왜냐하면, 상위 모듈이 하위 모듈의 내부 구현을 알고 있는 것이기 때문이다. 캡슐화가 잘 되어있다면, &quot;무엇&quot;을 하는지만 외부에 공개될 뿐 안에서 &quot;어떻게&quot; 실현되느냐는 알릴 필요가 없다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;u&gt;주차요금청구서 모듈&lt;/u&gt;은 &quot;주차시간 + 회원 여부&quot;를 넘긴다. 주차요금계산기 모듈은 받은 &quot;회원 여부&quot;에 따라서 내부 논리 흐름을 결정하고 있다. 할인 여부를 주차요금청구서에서 결정하고, Fee 모듈은 계산만 하도록 해야지, 왜 회원 여부에 따른 판단을 계산 모듈에서 하고있는가? 로 이해하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;1066&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YiO1b/dJMcajof3Wv/ZAkl5BjyJeK4dACuRVZ6Yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YiO1b/dJMcajof3Wv/ZAkl5BjyJeK4dACuRVZ6Yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YiO1b/dJMcajof3Wv/ZAkl5BjyJeK4dACuRVZ6Yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYiO1b%2FdJMcajof3Wv%2FZAkl5BjyJeK4dACuRVZ6Yk%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;550&quot; height=&quot;408&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;1066&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;아래에 로깅에 대한 예시를 하나 더 보자. 현재 DataProcessor 클래스에서 조건에 따라서 로깅을 어떻게 보여줄건지를 결정하고 있다. 외부에서 DataProcessor를 호출할 때, 로그를 찍을지 말지 결정하는 isVerbose 제어 요소를 전달해야하므로 결합도가 높아졌다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSuTb8/dJMcagSGYcI/IbO6fNVGtunkv2rCJJwgJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSuTb8/dJMcagSGYcI/IbO6fNVGtunkv2rCJJwgJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSuTb8/dJMcagSGYcI/IbO6fNVGtunkv2rCJJwgJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSuTb8%2FdJMcagSGYcI%2FIbO6fNVGtunkv2rCJJwgJ0%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;550&quot; height=&quot;458&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataProcessor는 데이터를 조작하는 로직만 다루자. 로깅에 대한 것은 다른 클래스로 빼는 것이 좋겠다. 책임을 분리하도록 수정하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;932&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMyxlY/dJMcabDQxQo/Z7j6SB5s4eu6FiiFmZdfx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMyxlY/dJMcabDQxQo/Z7j6SB5s4eu6FiiFmZdfx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMyxlY/dJMcabDQxQo/Z7j6SB5s4eu6FiiFmZdfx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMyxlY%2FdJMcabDQxQo%2FZ7j6SB5s4eu6FiiFmZdfx0%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;550&quot; height=&quot;402&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;932&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1642&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baJmff/dJMcaju454x/iwgUefMKWihQKuUKnDAVyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baJmff/dJMcaju454x/iwgUefMKWihQKuUKnDAVyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baJmff/dJMcaju454x/iwgUefMKWihQKuUKnDAVyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaJmff%2FdJMcaju454x%2FiwgUefMKWihQKuUKnDAVyk%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;550&quot; height=&quot;133&quot; data-origin-width=&quot;1642&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;4. 외부(External) 결합도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;외부 결합도는 내가 할 수 있는 일임에도 불구하고 외부 모듈을 따로 두는 것이다. 이는 외부 API, 외부 시스템을 활용하는 것과는 차이가 좀 있다. 모든게 내 제어범위 안에 있을 때를 말하는거다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTwvNO/dJMcacbF9IE/hKKhuZQr7kpCOz447HNoq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTwvNO/dJMcacbF9IE/hKKhuZQr7kpCOz447HNoq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTwvNO/dJMcacbF9IE/hKKhuZQr7kpCOz447HNoq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTwvNO%2FdJMcacbF9IE%2FhKKhuZQr7kpCOz447HNoq1%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;550&quot; height=&quot;358&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;850&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3oGPs/dJMcaaLH1tY/b8KdcPKdoKZCtzBm2h1BCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3oGPs/dJMcaaLH1tY/b8KdcPKdoKZCtzBm2h1BCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3oGPs/dJMcaaLH1tY/b8KdcPKdoKZCtzBm2h1BCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3oGPs%2FdJMcaaLH1tY%2Fb8KdcPKdoKZCtzBm2h1BCk%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;550&quot; height=&quot;272&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;850&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AddFee라고 할증 관련하여 외부 모듈을 두었다. 그나마 좀 개선할 수 있는 부분을 바꿔본다면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;1068&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lhwRp/dJMcahqxhQX/QfvTYnbY9aSdznpR906abk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lhwRp/dJMcahqxhQX/QfvTYnbY9aSdznpR906abk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lhwRp/dJMcahqxhQX/QfvTYnbY9aSdznpR906abk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlhwRp%2FdJMcahqxhQX%2FQfvTYnbY9aSdznpR906abk%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;550&quot; height=&quot;485&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;1068&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKqdxh/dJMcaiiEIoX/zXiyWjo0AIdGqS9vHlrYpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKqdxh/dJMcaiiEIoX/zXiyWjo0AIdGqS9vHlrYpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKqdxh/dJMcaiiEIoX/zXiyWjo0AIdGqS9vHlrYpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKqdxh%2FdJMcaiiEIoX%2FzXiyWjo0AIdGqS9vHlrYpk%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;550&quot; height=&quot;427&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데...이것도 사실 별로다. 자료 결합으로 가는 것이 가장 좋다. AddFee와 같은 쓸데없는 외부 모듈 참조 자체를 없애자.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;5. 공통(Common) 결합도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;공통결합하면 &lt;b&gt;전역변수&lt;/b&gt;를&amp;nbsp;떠올리면 된다. 전역변수는 되도록 사용하지 말자. 누가 언제 어떻게 접근했는지 추적이 어렵기 때문이다. 클래스 내 멤버변수를 get, set으로 접근하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1606&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtu6Ng/dJMcafTMOSn/anN0DIA4djFfec66fmsP9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtu6Ng/dJMcafTMOSn/anN0DIA4djFfec66fmsP9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtu6Ng/dJMcafTMOSn/anN0DIA4djFfec66fmsP9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdtu6Ng%2FdJMcafTMOSn%2FanN0DIA4djFfec66fmsP9K%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;550&quot; height=&quot;305&quot; data-origin-width=&quot;1606&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;6. 내용(Content) 결합도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;한 모듈이 다른 모듈의 내부 기능과 내부 데이터를 직접 참조한다. 다른 모듈의 내부 기능, 데이터를 그대로 가져와 사용하고 수정한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;1116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qxWNr/dJMcajhwTq7/5UNKCuzMC4Z5OJsRaIq9FK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qxWNr/dJMcajhwTq7/5UNKCuzMC4Z5OJsRaIq9FK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qxWNr/dJMcajhwTq7/5UNKCuzMC4Z5OJsRaIq9FK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqxWNr%2FdJMcajhwTq7%2F5UNKCuzMC4Z5OJsRaIq9FK%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;550&quot; height=&quot;433&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;1116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name과 age는 User의 멤버변수로 두는 것이 자연스럽다. 지금은 public으로 외부 접근을 다 허용하고 있으므로, 다른 사람이 내 나이를 바꿀 수가 있다. 캡슐화 원칙을 위배한다. 객체를 외부에서 바꾸려(제어)하는 것을 막아야 한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;응집도(Cohension)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;결합도는 모듈 간의 관계를 본 것이라면, 응집도는 한 모듈 내를 본다. 얼만큼 뭉쳐야 하는가이다. 한 클래스가 기능에 집중하기 위한 모든 정보와 역할을 가진다. 하나의 모듈을 하나의 기능을 가진다면 응집도가 높다. 하나의 모듈에 여러 기능을 가진다면 응집도가 낮다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;응집도가 높은 모듈&lt;/b&gt;: 하나의 모듈 안에 함수와 데이터가 &lt;b&gt;하나의 기능을 구현하기 위해 필요한 것들만&lt;/b&gt;을 배치한다.&lt;br /&gt;&lt;b&gt;응집도가 낮은 모듈&lt;/b&gt;: 서로 관련 없는 함수와 데이터가 공존하고 &lt;b&gt;서로 다른 목적을 추구하는 것이 하나의 모듈에&lt;/b&gt; 들어있다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;예를 들어, 주문을 처리하는 클래스인데 회원 정보를 업데이트하는 메서드가 있다면, 많은 책임을 하나의 모듈에 넣어 응집도가 낮아진 것이다. 단일책임원칙을 위반했다. 응집도가 높으면 수정 후 다른 모듈에게 영향을 주지 않아서 유지보수에 유리하다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. 기능적(functional) 응집도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;모든 기능이 단일 목적을 가진다. 모듈 내부의 요소들이 모듈에 대하여 정의된 하나의 기능에 모두 기여하고 밀접하게 관련되어 모였다. 응집도가 높아서 좋다. Stack 클래스 안에 push, pop 가지고 있는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1336&quot; data-origin-height=&quot;978&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baqLvk/dJMcadn5PRx/6HLoEH96uxiaxolK3wkUa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baqLvk/dJMcadn5PRx/6HLoEH96uxiaxolK3wkUa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baqLvk/dJMcadn5PRx/6HLoEH96uxiaxolK3wkUa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaqLvk%2FdJMcadn5PRx%2F6HLoEH96uxiaxolK3wkUa0%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;550&quot; height=&quot;403&quot; data-origin-width=&quot;1336&quot; data-origin-height=&quot;978&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. 순차적(Sequential) 응집도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;모듈 내에서 한 구문의 &lt;b&gt;출력값&lt;/b&gt;이 다음 구문의 &lt;b&gt;입력 데이터&lt;/b&gt;로 사용되는 경우이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bX3hj2/dJMcaiiETKX/C1LUy7sGbvT4ZMpK4OCnck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bX3hj2/dJMcaiiETKX/C1LUy7sGbvT4ZMpK4OCnck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bX3hj2/dJMcaiiETKX/C1LUy7sGbvT4ZMpK4OCnck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX3hj2%2FdJMcaiiETKX%2FC1LUy7sGbvT4ZMpK4OCnck%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;550&quot; height=&quot;151&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numberGrade를 computeLetter의 입력값으로 쓰고 있다. 이렇게 하기보다, num은 num대로 뽑고, letter는 num을 기본 datatype으로 넘겨 받는 방법이 좋겠다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. 교환적(Communication) 응집도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;공통된 파라미터&lt;/b&gt;가 메서드 호출에 사용되는 경우이다. 순차적 응집도와 다르게, 처리 순서가 중요하지 않다. 의존관계도 없다. 단지 동일한 입력과 출력을 사용한다는 이유로 모아져 있는 것이다. 좋지 않다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;534&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nXBMm/dJMcagFbe6m/tLdGlJex2OKSvqLPO1F7j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nXBMm/dJMcagFbe6m/tLdGlJex2OKSvqLPO1F7j1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nXBMm/dJMcagFbe6m/tLdGlJex2OKSvqLPO1F7j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnXBMm%2FdJMcagFbe6m%2FtLdGlJex2OKSvqLPO1F7j1%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;550&quot; height=&quot;212&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;534&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;transform_matrix와 inverse_matrix는 서로 다른 기능이다. aMatrix라는 동일 매개변수를 받고 있어서 하나의 클래스로 모였다. 그래서 그들 간의 순서는 중요하지 않다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4. 절차적(Procedural) 응집도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;하나의 클래스에서 다수의 기능을 순차적으로 수행한다. 여러개의 메소드를 호출하는데, 서로 관련된건 아니다. 그러니 의존 관계도 없다. 그냥 순서대로만 될 뿐이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHVwuT/dJMcafzuCnx/plaHbItLlC1M5SAX9kqWZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHVwuT/dJMcafzuCnx/plaHbItLlC1M5SAX9kqWZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHVwuT/dJMcafzuCnx/plaHbItLlC1M5SAX9kqWZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHVwuT%2FdJMcafzuCnx%2FplaHbItLlC1M5SAX9kqWZ0%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;550&quot; height=&quot;224&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본문 쓰기 &amp;rarr; 인사말 쓰기 &amp;rarr; 보내기. 이 3개는 각각 다른 기능을 하고 있지만 편지를 쓰기 위해 순서대로 이루어져야해서 묶여있다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;5. 시간적(Temporal) 응집도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;각 기능 요소들이 순서에 상관없이 특정 시점에 수행되어서 모여있다. 연관이 되어있지 않을 수 있다. 예를 들면 변수의 초기화, Exception error 같은게 있다. 시스템 로딩 시 변수가 다 초기화된다. 하지만 변수끼리 서로 관련이 없는데 모여있게 된다. 예외처리도 마찬가지이다. Exception 안에 다 몰아 넣고 e로 퉁치는 것은 안좋다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JozSc/dJMcabw3HuY/B7iBKd73caHUMIbHQR0p6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JozSc/dJMcabw3HuY/B7iBKd73caHUMIbHQR0p6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JozSc/dJMcabw3HuY/B7iBKd73caHUMIbHQR0p6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJozSc%2FdJMcabw3HuY%2FB7iBKd73caHUMIbHQR0p6K%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;550&quot; height=&quot;241&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blXMJO/dJMcacCLIOC/mSReIbztJHmXNxgaTsJbV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blXMJO/dJMcacCLIOC/mSReIbztJHmXNxgaTsJbV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blXMJO/dJMcacCLIOC/mSReIbztJHmXNxgaTsJbV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblXMJO%2FdJMcacCLIOC%2FmSReIbztJHmXNxgaTsJbV0%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;550&quot; height=&quot;434&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;934&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 초기화를 위해서 모였지만, no_student, no_department, university_name 변수들은 서로 관련이 없다. 애플리케이션 켜졌을 때, 설정파일 읽기, 로깅시스템 초기화, DB 연결, 캐시 초기화를 같은 시간에 하지만 서로 관련은 없다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beewrj/dJMcabX9xG4/H4eHkOvekkWpYHzSyuJwP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beewrj/dJMcabX9xG4/H4eHkOvekkWpYHzSyuJwP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beewrj/dJMcabX9xG4/H4eHkOvekkWpYHzSyuJwP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbeewrj%2FdJMcabX9xG4%2FH4eHkOvekkWpYHzSyuJwP0%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;550&quot; height=&quot;287&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;readFile, parseContent, saveToDatabase는 파일 처리하는 같은 시간에 동작하지만, 서로 관련이 없다. 그리고 지금 예외를 e 하나로 잡고 있는데, 서로 다른 것들을 모두 e로 잡으면 안된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BJEoJ/dJMcafTM09L/nl0YF3ZpbCJwpGr5WAvDrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BJEoJ/dJMcafTM09L/nl0YF3ZpbCJwpGr5WAvDrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BJEoJ/dJMcafTM09L/nl0YF3ZpbCJwpGr5WAvDrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBJEoJ%2FdJMcafTM09L%2Fnl0YF3ZpbCJwpGr5WAvDrk%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;550&quot; height=&quot;353&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하는게 좋겠다. 하나의 Exception안에는 걔랑 관련된 것만 나오도록 설계한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;6. 논리적(Logical) 응집도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;논리적으로 유사하고 성격이 비슷한 기능이 모인 것이다. 카테고리가 비슷해서 그냥 묶은 것이기 때문에, 조건문이 많아지고 복잡도가 상승한다. 안에 들어있는 하나의 기능이 바뀌면 내부 로직을 뜯어 고쳐야 한다. 수정이 잦아지므로, 개방-폐쇄 원칙에 위배된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSRG2g/dJMcaiwaFcK/7oozgJy6KYOioDOve8Y8U1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSRG2g/dJMcaiwaFcK/7oozgJy6KYOioDOve8Y8U1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSRG2g/dJMcaiwaFcK/7oozgJy6KYOioDOve8Y8U1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSRG2g%2FdJMcaiwaFcK%2F7oozgJy6KYOioDOve8Y8U1%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;550&quot; height=&quot;326&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방정식을 푼다는 큰 카테고리에 묶여 있을 뿐 그 안에서는 서로 관련이 없다. 왜 복잡하게 이 안에서 조건문으로 결정하고 있냐는거다. 내가 어떤 조건에서 무엇을 해야할지는 동적으로 Equation 인터페이스를 구현하는 각각의 Equation solving 객체들에서 결정을 해야한다. &lt;b&gt;전략 패턴&lt;/b&gt;을 생각하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCJk51/dJMcaarpIHy/rBa29xPpTG5UAQk2zrcv71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCJk51/dJMcaarpIHy/rBa29xPpTG5UAQk2zrcv71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCJk51/dJMcaarpIHy/rBa29xPpTG5UAQk2zrcv71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCJk51%2FdJMcaarpIHy%2FrBa29xPpTG5UAQk2zrcv71%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;550&quot; height=&quot;246&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 height와 width를 같이 결정하는 모듈을 둬서, 조건문 처리로 하느냐...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0Yfcj/dJMb99MLoED/kVUHxKJ542DhVXCFKm1BXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0Yfcj/dJMb99MLoED/kVUHxKJ542DhVXCFKm1BXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0Yfcj/dJMb99MLoED/kVUHxKJ542DhVXCFKm1BXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0Yfcj%2FdJMb99MLoED%2FkVUHxKJ542DhVXCFKm1BXk%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;550&quot; height=&quot;320&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 예시이다. hadleMessage는 각 객체별로 handleMessage interface 구현해서 각자가 결정해야한다. 아래와 같이 구현하자. sender = new EmailSender() 처럼 동형적으로 결정하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;946&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEoHkJ/dJMcaadSIFt/sGZhqClPCKUE1aqKlWuUek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEoHkJ/dJMcaadSIFt/sGZhqClPCKUE1aqKlWuUek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEoHkJ/dJMcaadSIFt/sGZhqClPCKUE1aqKlWuUek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEoHkJ%2FdJMcaadSIFt%2FsGZhqClPCKUE1aqKlWuUek%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;550&quot; height=&quot;395&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;946&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A2tZ9/dJMcahYmacj/tIuIU6q5OaY0AqjXVwjd30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A2tZ9/dJMcahYmacj/tIuIU6q5OaY0AqjXVwjd30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A2tZ9/dJMcahYmacj/tIuIU6q5OaY0AqjXVwjd30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA2tZ9%2FdJMcahYmacj%2FtIuIU6q5OaY0AqjXVwjd30%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;550&quot; height=&quot;229&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;7. 우연적(Concidental) 응집도&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;최악이다. 유연성이 전혀 없다. 문법만 객체지향을 따랐을 뿐, 아무 상관 없는 애들이 모여 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btfDI1/dJMcajoguOX/eJJbwgcRjSmjdBk0334bH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btfDI1/dJMcajoguOX/eJJbwgcRjSmjdBk0334bH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btfDI1/dJMcajoguOX/eJJbwgcRjSmjdBk0334bH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtfDI1%2FdJMcajoguOX%2FeJJbwgcRjSmjdBk0334bH0%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;550&quot; height=&quot;315&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>소프트웨어공학</category>
      <author>chief.park</author>
      <guid isPermaLink="true">https://parkso-yeon.tistory.com/7</guid>
      <comments>https://parkso-yeon.tistory.com/7#entry7comment</comments>
      <pubDate>Tue, 14 Apr 2026 19:05:49 +0900</pubDate>
    </item>
    <item>
      <title>전통적인 설계 원리</title>
      <link>https://parkso-yeon.tistory.com/6</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1. 추상화&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;추상화는 컴포넌트 구현에 대한 자세한 사항을 고려하지 않고, 추상적인 수준으로 컴포넌트를 다루는 도구&lt;/b&gt;이다. 컴포넌트는 외부에 서비스를 제공하는데, 이때 내부가 어떻게 동작하는지 상세한 사항에 구애 받으면 안된다.&amp;nbsp;&lt;b&gt;단지 외부에 보이는 동작을 나타내면 된다.&lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;즉, 추상화란 특정한 목적에 관련된 정보에 집중하고, 나머지 정보는 무시하는 관점이다. 예를 들어, 자동차에 추상화를 적용해보자. 엑셀을 밟으면 속도가 올라가고, 브레이크를 밝으면 멈추는 객체로 추상화할 수 있다. 내부적으로 엑셀과 브레이크가 어떤 구조를 가지고 어떻게 구현되는지는 관심이 없다. &quot;자동차의 동작&quot;이라는 특정한 목적에 집중한 것이다. 추상화는 복잡한 것을 줄이고, 시스템을 효율적으로 다룰 수 있게 해준다. 외부에 보이는 동작을 잘 나타내는 추상화가 중요하다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;조금 더 개발 관점에서 바라보면, 추상화는 컴포넌트의 인터페이스는 해당 구현과 독립적이어야 한다는 뜻이다. &lt;/b&gt;자동차의 인터페이스는 페달, 기여변속 스틱 등이 있다. 이들이 전기차로 구현되건, 내연기관차로 구현되건 인터페이스와는 독립적인 관계이다.&lt;/p&gt;&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*컴포넌트: 명백한 역할을 가지고 있으며 독립적으로 존재할 수 있는 시스템의 부분. 같은 기능을 가진 다른 컴포넌트로 대체 가능&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2. 캡슐화&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;캡슐화는 추상화된 대상이 제공하는 서비스를 쉽게 접근하기 위한 개념이다. 특정한 목적만 노출시키고 어떻게 제공하는지는 숨긴다. 자세한 내부 구현(데이터 저장, 처리, 기능 구현 등)을 외부에 드러내지 않고 캡슐 안에 숨기는 것이다. 정보은닉이라고 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;정보은닉이 잘되면 모듈의 숨겨진 부분에 대한 변경사항이 모듈 외부의 내용에 영향을 미치지 않게 된다. 따라서&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;정보은닉을 통하여 더 높은 수준의 추상화를 유지할 수 있다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;소프트웨어 변경 요구에 탄력적으로 대처할 수 있는 것이다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3. 모듈화&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;모듈화는 문제를 소프트웨어 구성요소가 될 만한 수준을 분할하는 과정이다. 복잡한 시스템을 세분화된 구성요소로 정의하면 더 잘 이해할 수 있다. 소프트웨어를 패키지 또는 클래스로 나누는 것으로 볼 수 있다. 너무 많은 모듈로 분할한다면 응집력이 낮아지고, 서로 상호작용이 더 복잡해질 수 있다. 재사용성을 극대화하면서, 의존성을 최소화할 수 있는 균형을 찾는 것이 중요하다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3.1. 추상화 - 캡슐화 - 모듈화의 관계&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;추상화는 시스템의 핵심 특성에 초점을 두어 하나의 큰 시스템을 분할하는 원리이다. 캡슐화는 분할된 핵심 정보만을 노출시켜 내부 변경에 대한 영향을 최소화로 한다. 추상화와 캡슐화 원리에 의하여 큰 시스템이 잘 모듈화된 시스템으로 완성된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;모듈을 추상화한 인터페이스와 실제 구현 모듈은 별개로 생각한다. 인터페이스는 컴포넌트에 대한 사용자의 뷰이다. 구현은 개발자의 뷰이다. 사용자는 이 컴포넌트를 사용할 때 캡슐화된 모듈의 내부가 어떻게 생겼는지는 관심없고, 개발자는 사용자에게 알리지 않고 구현을 수정할 수 있게 되는 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;4. 결합&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&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;b&gt;내용 결합&lt;/b&gt;: 한 모듈이 다른 모듈의 내용을 직접 찹조하는 경우&lt;br&gt;ex. 모듈P가 모듈Q의 문장을 조작 / P가 Q의 로컬 데이터 값 참조 / P가 Q 내부로 분기하는 경우&lt;/li&gt;&lt;li&gt;&lt;b&gt;공통 결합&lt;/b&gt;: 한 모듈이 다른 모듈이 읽은 전역 변수 값을 쓰거나 변경하는 경우&lt;br&gt;모듈이 매개변수 대신에 &quot;전역 변수&quot;를 이용하여 데이터를 교환하는 경우를 공통 결합이라고 한다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;제어 결합&lt;/b&gt;: 한 모듈이 다른 모듈의 제어흐름 경로를 결정하는 경우&lt;br&gt;모듈A가 모듈B에게 매개변수를 넘겨주는데, 그 값이 모듈B 내부의 if-else, switch 문을 작동시키는 플래그로 쓰인다는 뜻이다.&lt;/li&gt;&lt;/ul&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;print(milesTraveled, displayMetricValues)
...
public void print(int miles, bool displayMetric) {
	if (displayMetric) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;	System.out.println(...);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else {...}
}&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;b&gt;스탬프 결합&lt;/b&gt;: 복합 데이터 구조의 일부만 사용하는 모듈에 복합 데이터 구조를 전달하는 경우&lt;br&gt;3개의 필드가 있는 레코드를 처음 2개 필드만 필요한 모듈에 매개변수로 전달하는 경우이다.&lt;br&gt;ex. 편의점 알바가 나이를 물었을 때 → 신분증을 보여준다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;데이터 결합&lt;/b&gt;: 모듈들이 주고받는 매개변수가 간단한 타입이거나 레코드 안의 필드라도 단순 타입인 경우&lt;br&gt;ex. 편의점 알바가 나이를 물었을 때 → 나이만 알려준다.&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;5. 응집&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&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;b&gt;우연적 응집&lt;/b&gt;: 단위 안의 요소들이 의미적으로 아무런 관계가 없다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;논리적 응집&lt;/b&gt;: 본질적으로 다르더라도 같은 범주의 기능을 수행하므로 논리적으로 분류되기 때문에 그룹으로 묶인 경우&lt;br&gt;ex. 마우스/키보드 입력 처리 루틴을 한 모듈 안에 넣었다면 &quot;입력&quot;이라는 논리로 분류한 것이다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;순차적 응집&lt;/b&gt;: 모듈 내부 요소가 프로그램이 실행되는 특정한 시간에 처리되므로 한 그룹 안에 모여 있는 경우&lt;br&gt;ex. 열린 파일 닫고 → 오류 로그 생성하고 → 사용자에게 통보하는 예외 처리 함수. 기능이 다 다른데, 예외 처리하는 시점에 발생한다는 이유로 모여있는 것이다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;절차적 응집&lt;/b&gt;: 모듈 안에서 수행되는 연산이 프로그램에서 수행되는 순서와 관련이 있는 경우&lt;br&gt;ex. 키보드 입력 → 확인하고 전역 변수에 답을 저장하다.&lt;/li&gt;&lt;/ul&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;operationA(){
	// 각각 다른 기능인데, 하나의 모듈 안에 모여있음
	readData(data,filename1);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;processAData(data);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;storeData(data,filename2);
}&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;b&gt;교환적 응집&lt;/b&gt;: 모듈 내부 요소들이 동일한 데이터를 조작하기 때문에 모아진 경우&lt;/li&gt;&lt;li&gt;&lt;b&gt;기능적 응집&lt;/b&gt;: 모듈에 대하여 정의된 하나의 기능에 모두 기여하고 밀접하게 관련되어 모아진 경우&lt;/li&gt;&lt;li&gt;&lt;b&gt;정보적 응집&lt;/b&gt;: 데이터 구조 하나를 중앙에 두고, 그 데이터를 조작하는 여러 개의 독립적인 기능(메서드)들을 하나의 모아둔 경우&lt;br&gt;객체지향에서는 자연스럽게 정보적 응집을 갖도록 설계한다. 각 객체는 객체 안에 정의된 데이터를 조작하며, 객체의 각 멤버함수는 하나의 고유한 동작, 오퍼레이션, 함수를 수행한다.&lt;/li&gt;&lt;/ul&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Airplane {
	private double speed, altitude;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void takeoff() {...}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void fly() {...}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void land() {...}
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>소프트웨어공학</category>
      <author>chief.park</author>
      <guid isPermaLink="true">https://parkso-yeon.tistory.com/6</guid>
      <comments>https://parkso-yeon.tistory.com/6#entry6comment</comments>
      <pubDate>Mon, 13 Apr 2026 21:10:49 +0900</pubDate>
    </item>
    <item>
      <title>RAG ; Retrieval-Augmented Generation</title>
      <link>https://parkso-yeon.tistory.com/5</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;1. RAG란?&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;RAG는 Retrieval Augmented Generation의 약자로, 검색 증강 생성으로 해석할 수 있다.&lt;br&gt;대형언어모델(LLM)이 새로운 정보를 검색하고 통합할 수 있도록 하는 기술이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;RAG를 사용하면, LLM이 지정된 문서를 참조하며 기존 데이터의 정보를 보완할 수 있게 된다.&lt;br&gt;이를 통해서 &lt;b&gt;훈련 데이터에서 사용할 수 없는 도메인의 정보&lt;/b&gt;나 &lt;b&gt;실시간으로 업데이트되는 정보&lt;/b&gt;를 사용할 수 있게 된다.&lt;br&gt;따라서, 할루시네이션을 방지하는 효과가 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;예를 들어, LLM 기반 챗봇이 &lt;b&gt;회사 내부 데이터에 접근&lt;/b&gt;하여 응답을 생성하게 되는 것이다. 일반적인 LLM은 회사 내부의 데이터를 알 수 없기 때문에 맞춤형 서비스는 제공할 수 없는 반면, RAG를 사용하면 가능해진다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*할루시네이션 : 생성형 AI가 사실이 아닌 정보를 마치 사실인 것처럼 답변하는 현상&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;2. RAG 동작 과정&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vAT1g/dJMcabJyF0k/au04oHkFyJr7VUJg0UwuQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vAT1g/dJMcabJyF0k/au04oHkFyJr7VUJg0UwuQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vAT1g/dJMcabJyF0k/au04oHkFyJr7VUJg0UwuQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvAT1g%2FdJMcabJyF0k%2Fau04oHkFyJr7VUJg0UwuQk%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;442&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;임베딩 ; Embedding&lt;/b&gt;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;참조할 데이터를 임베딩한다.&lt;br&gt;데이터를 벡터 공간의 수치로 표현하는 것이다.&lt;br&gt;이 임베딩 값을 벡터 데이터베이스에 저장하고, 문서 검색에 사용한다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;1) Reference documents : LLM에게 가르쳐주고 싶은 문서 준비&lt;br&gt;2) LLM embedding : 참조 문서들을 벡터로 변환한다.&lt;br&gt;3) Vector database : 벡터로 변환된 문서들을 벡터디비에 저장한다. LLM은 벡터디비에 접근하여 검색을 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;검색 ; Retrieve&lt;/b&gt;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;사용자의 쿼리가 주어지면, 질문도 문서와 같은 방식으로 임베딩을 한다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;1) User query&lt;br&gt;2) LLM embedding : 사용자의 질문 또한 문서와 동일한 방식의 벡터로 변환하여 Query vector로 만든다.&lt;br&gt;3) Find relevant documents by comparing embeddings : 사용자의 질문인 Query vector와 가장 비슷한 벡터를 벡터디비에서 찾는다. (유사도 검색)&lt;br&gt;4) Retrieve context from relevant documents : 가장 관련성 높은 내용 context chunks를 뽑아온다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;생성 ; Augmented&lt;/b&gt;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;사용자의 질문과 context chunks를 합쳐서 증강된 프롬포트를 만든다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;1) Augmented query : 사용자의 질문 + context chunks&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;ex. &quot;RAG가 뭐야?&quot; → (증강) → &quot;참고 자료(&amp;lt;chunk1&amp;gt;, &amp;lt;chunk 2&amp;gt;..)를 바탕으로 RAG가 뭔지 대답해줘&quot;&lt;/span&gt;&lt;br&gt;2) LLM generation : LLM이 위에서 증강된 질문을 읽고, 참고 자료 내용을 바탕으로 답변을 생성한다.&lt;br&gt;3) Response : 사용자에게 응답을 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;3. Vector Database 작동 원리&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;1)&lt;/b&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;b&gt;Word Embeddings&lt;/b&gt;&amp;nbsp;: 표에서 미리 정의된 숫자 임베딩을 보고, Data의 단어를 벡터로 바꾼다.&lt;br&gt;&lt;b&gt;2) Text Embeddings &amp;amp; Encoding&lt;/b&gt;&amp;nbsp;: 단어들을 인코더 신경망에 통과시킨다. 이 과정에서 단어의 문맥적 의미가 계산된다.&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;행렬곱&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;→ 음수제거(ReLU) → 평균(Mean Pooling)&lt;/span&gt;&lt;br&gt;&lt;b&gt;3) Projection&lt;/b&gt;&amp;nbsp;: 만들어진 벡터의 차원을 축소하여, 저장공간을 아끼고 검색 속도를 높이기 위해 데이터를 한번더 정제하여 최종 벡터디비에 저장한다.&lt;br&gt;&lt;b&gt;4) Retrieval(검색)&lt;/b&gt;&amp;nbsp;: 사용자의 쿼리도 벡터로 변환되었다. 질문 벡터와 저장된 벡터들을 내적한다. 값이 클수록 의미가 비슷하다는 뜻이다.&lt;br&gt;&lt;b&gt;5) Augmented(증강)&lt;/b&gt;&amp;nbsp;: 결과가 증강되었다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1624&quot; data-origin-height=&quot;1544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/egjPU7/dJMcaacNE1v/YniyMHdkXAUsRmlwNyPxYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/egjPU7/dJMcaacNE1v/YniyMHdkXAUsRmlwNyPxYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/egjPU7/dJMcaacNE1v/YniyMHdkXAUsRmlwNyPxYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FegjPU7%2FdJMcaacNE1v%2FYniyMHdkXAUsRmlwNyPxYk%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;1624&quot; height=&quot;1544&quot; data-origin-width=&quot;1624&quot; data-origin-height=&quot;1544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;4. LLM-based agents&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Non-agentic vs. Agentic workflow&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;Non-agentic workflow&lt;/b&gt;&lt;br&gt;zero-shot 방식을 사용한다. 답변을 시작해서 한번에 끝내는 방식이다.&lt;br&gt;빠르지만 복잡한 문제에서 실수가 나올 수 있으며, 수정과정이 없다.&lt;br&gt;우리가 보통 지피티에게 한번 질문하고 답을 얻는 방식이다.&lt;br&gt;ex. LLM workflow, RAG&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;Agentic workflow&lt;/b&gt;&lt;br&gt;답변을 한번에 끝내는 것이 아니라, 생각을 하고, 수정하는 과정을 반복한다.&lt;br&gt;결과물의 품질을 계속 높이는 과정이다.&lt;br&gt;ex. AI Agent, Agentic AI&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oAjCW/dJMcafFbyzE/rvLDY0KzUx7j71ESPEc0f1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oAjCW/dJMcafFbyzE/rvLDY0KzUx7j71ESPEc0f1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oAjCW/dJMcafFbyzE/rvLDY0KzUx7j71ESPEc0f1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoAjCW%2FdJMcafFbyzE%2FrvLDY0KzUx7j71ESPEc0f1%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;1184&quot; height=&quot;822&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;5. LangChain, LangGraph, LangSmith&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;LangChain&lt;/b&gt; : LLM을 가지고 앱을 쉽게 만들 수 있게 도와주는 도구 모음&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;b&gt;LangGraph&lt;/b&gt; : 랭체인 블록들을 가지고, 순환 구조나 복잡한 흐름을 만들 수 있게 해주는 상위 도구&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Agent를 위해 사용된다.&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangSmith&lt;/b&gt; : 랭체인과 랭그래프로 만든 AI의 작동, 에러, 토큰 사용량 등을 감시하고 기록하는 도구&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <author>chief.park</author>
      <guid isPermaLink="true">https://parkso-yeon.tistory.com/5</guid>
      <comments>https://parkso-yeon.tistory.com/5#entry5comment</comments>
      <pubDate>Sat, 20 Dec 2025 20:48:43 +0900</pubDate>
    </item>
    <item>
      <title>검색 엔진의 발전 과정</title>
      <link>https://parkso-yeon.tistory.com/4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;0. 개요&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;검색 기술이 어떤 식으로 변화해왔는지에 대한 전반적인 흐름을 알아본다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Google과 같은 검색 엔진을 생각해보자. &quot;DB Vector&quot;라는 검색어를 입력했다. 이는 &quot;DB Vector&quot;라는 쿼리를 검색 엔진에 날렸다는 것과 동일한 표현이다. Google에는 수천만개의 페이지가 있는데, 여기서 &quot;DB Vector&quot;와 관련있는 페이지를 어떻게 뽑아낼 수 있을까? &quot;DB&quot;, &quot;Vector&quot;가 등장하는 페이지를 모두 다 가져올 것인가? 일차적으로 드는 생각은, &quot;DB&quot;와 &quot;Vector&quot;라는 단어가 많이 있는 것을 먼저 가져올 것 같다. 각 단어가 등장하는 비율이 다를 때는 또 어떤 페이지를 뽑아야할까? 모든 페이지를 가져오는 것이 아니라, 최대한 사용자가 원하는 것을 가져오는 것이 중요하다. 검색 엔진이 어떻게 발전해왔고, 현재는 어떤 기술이 사용되고 있는지 벡터디비의 간단한 개념까지 알아보고자 한다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;1. 디렉토리 기반 검색&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;Yahoo 검색 엔진을 사용해보았는가? &lt;s&gt;사실 나는 사용해본 적이 없다.&lt;/s&gt;&lt;br&gt;Yahoo는 모든 정보를 폴더 단위로 구성한다. 토픽별로 폴더를 구성하고 있는 것이다. 사람들이 토픽을 직접 모아 정리하고 있다고 한다. 예를 들어, 스포츠에 관한 정보를 알고 싶다고 하면, 나는 스포츠 페이지로 들어가서 정보를 찾아야 한다.&lt;br&gt;이는 &quot;검색&quot;이라기보다는 내가 필요한 정보의 폴더를 &quot;직접 찾아들어가는&quot; 구조이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;2. 희소 벡터 기반 (= 전통 검색 엔진)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;Google을 생각해보자. 구글은 유명한 검색 엔진이다. Yahoo처럼 직접 정보를 찾아 들어갈 필요가 없이, 키워드 검색을 하면 관련된 페이지를 정렬해서 보여준다. 개요에서 언급했듯이, 구글은 어떻게 수천만개의 페이지에서 사용자가 검색한 키워드와 관련있는 페이지를 적절하게 찾아서 제공하는 것일까?&lt;br&gt;&amp;nbsp;&lt;br&gt;아이디어는 다음과 같다.&lt;br&gt;&quot;DB Vector&quot;라고 검색을 했다고 하자. (= &quot;DB Vector&quot; 쿼리 날린 것)&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;방금 날린&amp;nbsp;&lt;/span&gt;&lt;u&gt;쿼리문&lt;/u&gt;&lt;span style=&quot;color: #333333;&quot;&gt;에 대해서&amp;nbsp;&quot;DB&quot;, &quot;Vector&quot;의 빈도수를 매겨본다.&amp;nbsp;→&amp;nbsp;&lt;/span&gt;&lt;b&gt;쿼리 벡터&lt;/b&gt;&lt;br&gt;모든 &lt;u&gt;문서(page)&lt;/u&gt;에 대해서도 &quot;DB&quot;, &quot;Vector&quot;의 빈도수를 매겨본다. → &lt;b&gt;문서 벡터&lt;/b&gt;&lt;br&gt;이렇게 빈도수로 생성된 쿼리 벡터와 문서 벡터 사이의 유사도를 계산하여, 가장 유사한 문서를 반환한다.&lt;br&gt;이 벡터를 좌표평면에 찍는다면, 방향과 거리의 차이의 관점에서 유사도를 계산할 수 있게 된다. 이 예시에서는 DB, Vector 2개의 키워드라서 2차원 벡터가 될 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;유사도를 가장 쉬운 유클리디안 거리로 계산했다고 하자. 이때 발생할 수 있는 문제가 있다.&lt;br&gt;만약 두 벡터가 같은 방향을 가리키고 있는데도 불구하고 화살표 길이의 차이 때문에 유클리디안 거리가 크게 나왔다면, 이는 올바른 유사도가 계산된 것이 아니다. 내용이 비슷하여 같은 방향을 가리키지만, 빈도수의 차이로 거리가 크게 나오게 된다.이를 해결하기 위해서는 코사인 유사도를 사용하면 된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이렇게 Google 초기 검색 엔진의 원리를 한문장으로 요약하자면, Bow내에서 내가 원하는 것을 찾는 방법으로&amp;nbsp;&lt;b&gt;빈도수 벡터의 코사인 유사도 값을 기반으로 한 검색 기법&lt;/b&gt;이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;3. PageRank = 전통 검색 엔진의 품질 개선&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;희소 벡터 기반의 전통적인 검색 엔진으로는 관련된 문서가 너무 많이 매칭된다. 가장 관련성이 높은 문서를 앞쪽에 배치해야 사용자의 만족도가 올라간다. 따라서, &quot;품질&quot;을 반영한 문서 랭킹이 필요해졌고, PageRank라는 링크 기반 신뢰도 점수를 만들어 해결했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;링크의 개념은 투표(Vote)로 이해하면 편하다. 어떤 페이지가 다른 페이지에 의해 많이 링크 된다면 그 페이지는 중요하다고 판단되어 랭크가 올라간다. 예를 들어, A페이지가 100개의 페이지로부터 링크되면, A의 중요도는 높고, PageRank는 올라간다. B페이지는 2개의 페이지로부터 링크가 되었다면, B의 중요도는 낮고, PageRank는 낮아진다. 사람들이 클릭하는 것 또한 링크를 거는 효과를 낸다.&lt;br&gt;&amp;nbsp;&lt;br&gt;따라서 이 PageRank 방법을 사용하게 한다면 인위적으로 품질 관리를 할 필요 없이, 사람들이 많이 클릭하는 페이지가 자연스럽게 순위가 올라가면서 품질 관리가 되는 것이다. 최적화 작업을 사용자들이 알아서 해준다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;4. 대규모 문서 분산 처리 시스템&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;이제 Google의 웹 페이지가 너무 커지기 시작했다. 초기 검색 엔진으로 검색을 진행한다면, 수억~수십억 개의 웹 페이지를 모두 크롤링해서 각 페이지의 단어를 세고 역색인을 만들고 PageRank를 계산해야 한다. 이는 너무나 많은 계산을 요구하여 서버 한대로는 불가능하다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그렇다면 여러 서버가 분산적으로 처리하면 어떨까?&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;4.1 MapReduce&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;MapReduce는 대규모 데이터를 여러 서버에 나눠 병렬로 처리하고 결과를 다시 합치는 분산 처리 모델이다. 간단한 예시가 있다.&lt;br&gt;1) Map 단계 : 문제를 잘게 나눠서 여러 서버가 나눠 처리한다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;서버 1 → 문서 1~100만 처리 → (DB, 120,000)
서버 2 → 문서 100만~200만 → (DB, 98,314)
서버 1000 → 문서 9억~10억 → (DB, 124,082)&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;2) Reduce 단계 : 나눠 처리된 결과를 다시 하나로 합친다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;(DB, 120000 + 98314 + ... + 124082)&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;4.2 Hadoop = MapReduce를 오픈소스로 구현한 시스템&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;MapReduce는 구글의 내부 기술로, 소스코드를 공개하지 않았다. 따라서 Hadoop은 MapReduce를 오픈소스로 재현한 프로젝트이다.&lt;br&gt;구글의 Google File System은 Hadoop Distributed File System으로, MapReduce는 Hadoop MapReduce 엔진으로 구현했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;4.3 Spark = Hadoop MapReduce 보다 100배 빠른 최신 분산 처리 시스템&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;Spark는 메모리 기반 처리로 속도 향상을 도모하였다. 지금은 대부분 Spark를 사용하고 다고 보면 된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;5. Word2vec = Dense Vector 등장&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;지금까지는 알아본 방식은 모두 희소 벡터(BoW, TF-IDF)를 기반으로 하였다. 희소 벡터는 단어의 빈도수로만 문서를 표현하는 방식이다. 이러한 방식은 크게 2가지 문제점을 가진다. 첫째는 단어의 임베딩이 매우 sparse하다는 점이다. 두번째는 단어 사이의 의미 관계를 담지 못한다는 점이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그래서 word2vec이 등장하였다. 단어의 &lt;b&gt;의미&lt;/b&gt;를 벡터로 표현하고자 하는 시도이다. word2vec은 단어를 밀집 벡터로 만들고, 주변 단어(문맥)으로부터 의미를 학습할 수 있다. 예를 들어, &quot;보르도&quot;라는 단어 주변에 &quot;위치, 프랑스, 도시&quot; 라는 단어가 자주 나온다면 보르도는 &quot;도시&quot;이다. 반면, &quot;향, 품종, 와인&quot;이 많이 나온다면 보르도는 &quot;와인&quot;이다.&lt;br&gt;skip-gram 방식은 중심 단어를 통해서 주변 단어를 예측하는 것이다. NN은 보르도에 가장 어울리는 주변 단어 분포를 만들어내도록 학습되며, 그 결과 보르도의 의미를 포함하는 latent vector가 생성된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;6. Attention → Transformer → LLM → RAG&lt;br&gt;&amp;nbsp;&lt;br&gt;word2vec는 주변 단어의 의미를 고려한다. 그럼에도 Word2vec의 한계는 존재한다.&lt;br&gt;&quot;보르도 날씨가 좋네&quot; 라는 문장이 있다. 여기서 &quot;보르도&quot;와 &quot;날씨&quot;는 단지 문장에서 같이 등장했을 뿐, 둘이 의미적으로 강하게 연결되어 있지는 않다. Word2vec은 단어 간의 관계가 어떤지는 파악하지 못한다는 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;6.1 Attention&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;Attention Mechanism을 도입하자. 상관성을 attention이라고 한다. &quot;보르도는 프랑스 남서부에 있는 도시이다.&quot; 이 문장에서 &quot;보르도&quot;의 진짜 중요한 단어는 &quot;프랑스, 남서부, 도시&quot;이지 &quot;날씨&quot;같은 단어가 아니다. Attention은 이러한 단어 간의 관계(연관성)을 학습한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;6.2 Transformer&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;Transformer는 Attention을 기반으로 한 아키텍처이다.&lt;br&gt;word2vec보다 훨씬 발전된 구조로, 문장 전체를 한번에 보고, 모든 단어 쌍의 관계를 계산하고, 문맥을 더 정확하게 이해한다.&lt;br&gt;LLM이 모두 Transformer 기반인 이유가 여기에 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;6.3 RAG&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;RAG(Retrieval-Augmented Generation)의 정의를 먼저 알아보자.&lt;br&gt;1) Retrieval = 검색, 찾아오기, 불러오기&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;외부 데이터베이스(벡터DB)에서 필요한 문서를 가져온다.&lt;/span&gt;&lt;br&gt;2) Augmented = 증강된, 강화된&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;검색해서 가져온 정보를 LLM 답변 생성에 더한다.&lt;/span&gt;&lt;br&gt;3) Generation = 생성&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;LLM이 답변을 만든다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;RAG는 &lt;b&gt;모델 바깥에서 작동&lt;/b&gt;하는 구조이다.&lt;br&gt;벡터디비에서 쿼리와 유사한 문서를 검색한 후, 검색된 문서를 LLM에 넣어 답변을 생성한다. 즉, LLM을 크게 바꾸지 않고 벡터DB로 외부 지식을 실시간으로 공급하는 시스템이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;7. 추가적인 내용&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;7.1 DBSCAN&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;Density-Based Spatial Clustering of Applications with Noise&lt;br&gt;DBSCAN은 밀도 기반 군집화 알고리즘이다. 데이터가 촘촘히 모여있는 밀도 높은 부분을 자동으로 찾아서 클러스터링하는 방법이다. 클러스터 개수를 미리 지정할 필요가 없으며, 밀도가 낮은 곳(노이즈, outlier)은 자동으로 버린다.&lt;br&gt;임베딩 벡터를 만들면 비슷한 문장 벡터끼리 군집화가 가능하며, 서로 떨어진 문장들은 noise로 거를 수가 있게 된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;7.2 양자화(Quantization)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;벡터가 길어지면 양자화를 해야 한다. 여기서 양자화는 벡터의 각 숫자를 더 적은 비트로 압축하는 기술이다. 벡터 차원이 크면 메모리와 검색 속도에 비용이 많이 들기 때문에, 벡터를 압축해야 할 필요가 있다.&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <author>chief.park</author>
      <guid isPermaLink="true">https://parkso-yeon.tistory.com/4</guid>
      <comments>https://parkso-yeon.tistory.com/4#entry4comment</comments>
      <pubDate>Tue, 25 Nov 2025 20:57:33 +0900</pubDate>
    </item>
    <item>
      <title>토픽 모델링 알고리즘 LDA (Latent Dirichlet Allocation)</title>
      <link>https://parkso-yeon.tistory.com/3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0. 토픽 모델링이란?&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;글의 토픽, 즉 주제를 찾아내는 것이다.&lt;br&gt;조금 더 구체적으로 말하자면, 문서 집합 속에 &lt;b&gt;내재된 주제&lt;/b&gt;를 &lt;b&gt;통계적으로 추론&lt;/b&gt;하는 기법이다. 우리가 일반적으로 글을 읽을 때도 느끼듯, 주제는 문서에 직접적으로 드러나지 않는다. 이 방법 또한 마찬가지로 &lt;b&gt;글에 내재된 주제&lt;/b&gt;를 찾아내는 것에 의미가 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;따라서, 토픽 모델링에서의 &lt;b&gt;토픽(topic)은 당어들의 동시 출현 패턴으로 표현되는 잠재적 의미 집합(latent semantic group)&lt;/b&gt;을 의미한다. Topic #1 이 {&quot;에너지&quot;, &quot;탄소&quot;, &quot;기후&quot;, &quot;정책&quot;} 등의 단어의 묶음이라면 이는 기후 정책으로 해석을 할 수 있다는 뜻이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVhr9z/dJMcaiaA3eR/xtjEZ53zk4XZi09hhamYD1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVhr9z/dJMcaiaA3eR/xtjEZ53zk4XZi09hhamYD1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVhr9z/dJMcaiaA3eR/xtjEZ53zk4XZi09hhamYD1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVhr9z%2FdJMcaiaA3eR%2FxtjEZ53zk4XZi09hhamYD1%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;500&quot; height=&quot;281&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;이 그림에서 documents와 words는 관측가능하며, topics는 잠재되어 있다. 앞서 말했듯이, &lt;b&gt;토픽 모델링은 관측 가능한 단어와 문서로부터 잠재적인 topic을 찾아내는 방법론&lt;/b&gt;이다. 그리고, 각각의 &lt;b&gt;토픽은 단어들의 출현 패턴(확률 분포)로 표현&lt;/b&gt;된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1. LDA (Latent Dirichlet Allocation)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;LDA는 토픽 모델링의 대표적인 알고리즘&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이다. 확률을 기반으로 하는 토픽 모델링 기법이다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Latent Dirichlet Allocation 각 단어의 의미를 알아두면, 도움이 된다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Latent&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;는 잠재적인 이라는 뜻으로, topic이 문서 내에 잠재하고 있음을 말한다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Dirichlet&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;은 Dirichelt 분포라는 것이 있는데, 이를 과정 중에 사용한다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Allocation&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;은 할당한다는 뜻이며, 문서의 각 단어가 어떤 topic에서 왔는지를 할당하여 주제를 추론하게 된다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RdcbL/dJMcahpdZMJ/r8vIWQimvKtVC9FOOrQIp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RdcbL/dJMcahpdZMJ/r8vIWQimvKtVC9FOOrQIp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RdcbL/dJMcahpdZMJ/r8vIWQimvKtVC9FOOrQIp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRdcbL%2FdJMcahpdZMJ%2Fr8vIWQimvKtVC9FOOrQIp0%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;1280&quot; height=&quot;468&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;이 그림의 LDA의 핵심이다.&lt;br&gt;먼저 각 기호가 무엇을 의미하는지를 알아본다.&lt;br&gt;&amp;nbsp;&lt;br&gt;먼저 alpha와 beta는 하이퍼파라미터(사용자가 미리 정해주는 값)이다.&lt;br&gt;토픽 수 K도 미리 정해주기 때문에 하이퍼파라미터이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;D : 전체 document 수&lt;br&gt;N : document 내 단어 수 (vocabulary)&lt;br&gt;K : topic 수&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;theta_d는 문서에서 각 토픽이 차지하는 비중&lt;/b&gt;을 뜻한다. theta_1 = (0.1, 0.2, 0.7) 이라고 하면 문서1에 topic #1에 해당하는 단어가 0.1, topic #2가 0.2, topic #3이 0.7을 차지하고 있다는 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;pi_k는 토픽별로 단어의 분포&lt;/b&gt;를 나타낸 것이다. 토픽 모델링에서 &lt;u&gt;&lt;b&gt;토픽 = 단어들의 확률 분포&lt;/b&gt;&lt;/u&gt; 라는 것이 핵심이다.&lt;br&gt;해당 토픽에 들어있는 단어들(vocabulary)이 어떤 확률로 있는지 그 분포를 의미한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;w_d,n은 실제로 문서에서 관찰되는 단어&lt;/b&gt;를 말한다. w_1,1은 1번 문서의 첫 번째 단어이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;z_d,n은 그 w_d,n이 어떤 topic에서 온 것인지&lt;/b&gt;를 나타낸다. z_d,n = (0, 1, 0) 이라면, 이 단어는 topic #2에서 온 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2. 토픽과 단어, 토픽과 문서의 관계&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;목표 : 여러 개의 문서가 있는 corpus가 주어졌을 때, LDA는 다음 내용을 추정한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #a6bc00;&quot;&gt;&lt;b&gt;1. Topic 별 단어의 분포&lt;/b&gt;&lt;/span&gt;&lt;br&gt;- Topic A : 사과 50%, 먹어요 50%, 귀여운 0%, 강아지 0%&lt;br&gt;- Topic B : 사과 0%, 먹어요 0%, 귀여운 40%, 강아지 60%&lt;br&gt;&amp;nbsp;&lt;br&gt;여기서 vocabulary는 4개의 단어로 구성되어 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;여기서 Topic A를 sampling 하면 (사과, 먹어요)가 나온다. Topic B를 sampling 하면 (귀여운, 강아지)가 나온다. 그리고 이 sampling을 아주 많이 하면 큰 수의 법칙에 의해서, Topic을 구성하는 단어의 분포와 일치하게 될 것이다. Topic B를 샘플링한 결과로 귀여운 4, 강아지 6으로 구성될 것이라는 뜻이다. 그리고 이렇게 sampling한 것을 문서로 보는 것이 토픽 모델링의 관점이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #a6bc00;&quot;&gt;&lt;b&gt;2. 문서별 topic 분포&lt;/b&gt;&lt;/span&gt;&lt;br&gt;- 문서 #1 : topic A 80%, topic B 20%&lt;br&gt;- 문서 #2 : topic A 100%&lt;br&gt;&amp;nbsp;&lt;br&gt;문서를 구성하고 있는 단어들이 어떤 토픽에서 나왔는지에 의해서 표현을 하고 있다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2689&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bW9TvM/dJMcah3Pwzg/1bTbVuspP3kQhoKNkkc56k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bW9TvM/dJMcah3Pwzg/1bTbVuspP3kQhoKNkkc56k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bW9TvM/dJMcah3Pwzg/1bTbVuspP3kQhoKNkkc56k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbW9TvM%2FdJMcah3Pwzg%2F1bTbVuspP3kQhoKNkkc56k%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;260&quot; data-origin-width=&quot;2689&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Topic은 단어들의 확률 분포로 이루어져 있다.&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3921&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/egUR0H/dJMcah3Pwzd/4c1Afhond5JsU1yCsNFju0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/egUR0H/dJMcah3Pwzd/4c1Afhond5JsU1yCsNFju0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/egUR0H/dJMcah3Pwzd/4c1Afhond5JsU1yCsNFju0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FegUR0H%2FdJMcah3Pwzd%2F4c1Afhond5JsU1yCsNFju0%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;179&quot; data-origin-width=&quot;3921&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서는 구성하고 있는 단어들의 토픽에 의해 표현이 가능하다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3. LDA에서 필요한 확률 분포&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3.1 다항 분포 (Multinomial Distribution)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;다항 분포는 하나의 시행(trial)에서 여러 개의 결과 중 각 결과가 특정 확률로 발생하는 경우에 대한 확률 분포이다.&lt;br&gt;여러 범주(category) 중 하나가 선택되는 실험을 n번 반복했을 때, 각 범주가 몇 번씩 선택되는지를 나타낸다.&lt;br&gt;&amp;nbsp;&lt;br&gt;주사위를 던졌을 때 1부터 6까지 각 숫자가 나오는 것은 다항 분포이다. 주사위를 던지는 한 시행에서 가능한 결과의 개수가 6개이며, 각 결과 i(i=1~6)의 발생 확률은 1/6씩으로 합은 1이된다. &lt;span style=&quot;color: #333333;&quot;&gt;(1/6,&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1/6,&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1/6,&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1/6,&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1/6,&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;1/6) 이런 확률 벡터가 나온다.&lt;br&gt;&amp;nbsp;&lt;br&gt;기억해야할 점은 &lt;b&gt;다항 분포에서 샘플링을 하면, 각 범주가 가진 확률에 따라 결과가 나온다는 것이다.&lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;토픽 모델링에서 &lt;u&gt;&lt;b&gt;토픽 = 단어들의 확률 분포&lt;/b&gt;&lt;/u&gt;라고 했는데, 이 확률 분포가 다항 분포임을 알 수 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3.2 디리클레 분포 (Dirichlet Distribution)&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;디리클레 분포는 연속 확률 분포의 하나, k차원의 실수 벡터 중 벡터의 요소가 양수이며 모든 요소를 더한 값이 1인 경우에 대해 확률값이 정의되는 분포이다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어렵다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;토픽 모델링에서는 디리클레 분포의 정확한 개념을 이해하지 못해도 좋다. 디리클레는 확률 벡터를 생성하는 분포이다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;중요한 것은 &lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;u&gt;&lt;b&gt;디리클레 분포에서 샘플링을 하면 다항 분포의 확률 분포가 나온다&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;는 것이다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;Dirichlet sampling을 했을 때 나오는 결과는&lt;br&gt;(0.1, 0.2, 0.7)&lt;br&gt;(0.3, 0.1, 0.6)&lt;br&gt;(0.5, 0.2, 0.3) 이런 식이다. 각 숫자를 다 더하면 항상 1이 된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이걸 보면 무엇이 떠오르는가? 바로 위에서 본 다항 분포의 확률 분포이다.&lt;br&gt;&lt;b&gt;토픽 모델링에서 디리클레 분포는 이렇게 다항 분포의 확률 분포를 만들기 위해서만 사용된다.&lt;/b&gt;&lt;br&gt;이제 디리클레에서 샘플링한 결과는 어디서 사용되는지를 알아볼 수 있게 되었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3.3 문서별 토픽 비중과 토픽별 단어 발생 확률 분포&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;3.2에서 알아본 디리클레 분포의 샘플링 결과는 &lt;u&gt;&lt;b&gt;문서별 토픽 비중&lt;/b&gt;&lt;/u&gt;과 &lt;u&gt;&lt;b&gt;토픽별 단어 발생 확률 분포&lt;/b&gt;&lt;/u&gt;의 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;초기값&lt;/b&gt;&lt;/span&gt;을 설정하는데 사용된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;4. LDA의 기본 가정 : 문서 생성 과정&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RdcbL/dJMcahpdZMJ/r8vIWQimvKtVC9FOOrQIp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RdcbL/dJMcahpdZMJ/r8vIWQimvKtVC9FOOrQIp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RdcbL/dJMcahpdZMJ/r8vIWQimvKtVC9FOOrQIp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRdcbL%2FdJMcahpdZMJ%2Fr8vIWQimvKtVC9FOOrQIp0%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;1280&quot; height=&quot;468&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #a6bc00;&quot;&gt;&lt;b&gt;1) pi_k : k번째 topic에서 각 단어가 발생할 확률 vector&lt;/b&gt;&lt;/span&gt;&lt;br&gt;- pi_k ~ Dir(beta)&lt;br&gt;- 디리클레 분포에서 샘플링된 vector&lt;br&gt;- 이때, beta는 hyperparameter&lt;br&gt;&amp;nbsp;&lt;br&gt;k번째 topic을 구성하는 단어 확률 분포의 초기값을 디리클레 분포에서 얻는다.&lt;br&gt;beta = (1,2,3,4,5,6)으로 주어졌다고 가정하면,&lt;br&gt;6개의 단어를 가진 topic의 단어 확률 분포가 나온다.&lt;br&gt;beta의 vector 차원만큼 단어를 가진 확률 분포가 나오게 되는 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #a6bc00;&quot;&gt;&lt;b&gt;2) theta_d : d번째 문서의 topic 비중 vector&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- theta_d ~ Dir(alpha)&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 디리클레 분포에서 샘플링된 vector&lt;/span&gt;&lt;br&gt;- 이때, alpha는 hyperparameter&lt;br&gt;&amp;nbsp;&lt;br&gt;d번째 문서를 구성하는 topic 구성 확률 분포의 초기값을 디리클레 분포에서 얻는다.&lt;br&gt;alpha = (2,3,4)로 주어졌다고 가정하면,&lt;br&gt;3개의 topic 구성 확률 분포가 나온다.&lt;br&gt;alpha의 vector차원은 문서의 개수를 나타낸다.&lt;br&gt;&amp;nbsp;&lt;br&gt;마지막으로 정리하겠다. Dir(alpha), Dir(beta)를 통해서 theta_d와 pi_k의 초기값을 세팅하는 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #a6bc00;&quot;&gt;&lt;b&gt;3) z_d,n : d번째 문서, n번째 단어를 topic에 할당&lt;/b&gt;&lt;/span&gt;&lt;br&gt;- z_d,n ~ Multi(theta_d)&lt;br&gt;- &lt;b&gt;다항 분포에서 sampling된 vector&lt;/b&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1704&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XaZqq/dJMcag4Xrfz/qfAcsUy6K2Gry4JwGLLei1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XaZqq/dJMcag4Xrfz/qfAcsUy6K2Gry4JwGLLei1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XaZqq/dJMcag4Xrfz/qfAcsUy6K2Gry4JwGLLei1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXaZqq%2FdJMcag4Xrfz%2FqfAcsUy6K2Gry4JwGLLei1%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;1704&quot; height=&quot;459&quot; data-origin-width=&quot;1704&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;z_1,1 = (1,0,0) : 첫번째 문서의 첫번째 단어는 topic 1 때문에 발생했다. = topic 1에서 왔다.&lt;br&gt;그렇다면 z_1,1은 어떻게 나왔을까? 이는 Multi((0.6,0.1,0.3))을 샘플링한 결과이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #a6bc00;&quot;&gt;&lt;b&gt;4) w_d,n : 문서에 등장하는 단어를 할당하는 역할&lt;/b&gt;&lt;/span&gt;&lt;br&gt;- z_d,n 과 pi_k에 동시에 영향을 받는다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dx1BVg/dJMcahJy2PU/cpXqEizrz8qPdFwjYxAYE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dx1BVg/dJMcahJy2PU/cpXqEizrz8qPdFwjYxAYE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dx1BVg/dJMcahJy2PU/cpXqEizrz8qPdFwjYxAYE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdx1BVg%2FdJMcahJy2PU%2FcpXqEizrz8qPdFwjYxAYE1%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;1838&quot; height=&quot;476&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;w_1,2 = &quot;Jordan&quot; 임을 관측해서 알고 있다.&lt;br&gt;좀 전에&amp;nbsp;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;Multi(theta_1)에서 샘플링된 결과로, &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;z_1,2 = (1,0,0)을 Topic1에서 왔다고 할당해두었다.&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;그리고 Topic1에서 각 단어 발생 확률 벡터인 pi_1 = (0.3,0.2,0.2,0.0,0.2,0.1)에서 샘플링된 결과로, w_d,n이 나왔다고 보는 것이다.&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;u&gt;&lt;b&gt;LDA 문서 생성 과정 정리해보자.&lt;/b&gt;&lt;/u&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;1. 각 Topic 별로 단어 발생 분포 vector 생성&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;pi_k ~ Dir(beta)&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;2. 각 문서별로 topic 비중 분포 vector 생성&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;theta_d ~ Dir(alpha)&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;3. 각 문서내 단어를 topic에 할당하는 vector 생성&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;z_d,n ~ Multi(theta_d)&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;4. 각 문서내 단어를 topic에 할당한 정보 + topic별 단어 발생 분포 vector를 활용하여 →&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;w_d,n ~ Multi(pi_(z_d,n))&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;5. LDA 학습과정&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1857&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oEv9U/dJMcabP7cFE/5Yb7wY9IUgRwf2aVmdzZa0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oEv9U/dJMcabP7cFE/5Yb7wY9IUgRwf2aVmdzZa0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oEv9U/dJMcabP7cFE/5Yb7wY9IUgRwf2aVmdzZa0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoEv9U%2FdJMcabP7cFE%2F5Yb7wY9IUgRwf2aVmdzZa0%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;1857&quot; height=&quot;470&quot; data-origin-width=&quot;1857&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LDA의 학습은&lt;br&gt;주어진 문서들과 각 문서들의 단어들로부터, 다음 파라미터들을 학습해야 한다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;theta_d&lt;/li&gt;&lt;li&gt;pi_k&lt;/li&gt;&lt;li&gt;z_d,n&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 3개의 파라미터는 위의 식이 최대화가 되는 방향으로 학습되어야 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;Step1. Topic의 개수(K) 결정한다.&lt;br&gt;Step2. 문서 d에 포함된 각 단어 w를 무작위로 K개의 토픽 중 하나에 할당한다.&lt;br&gt;Step3. 문서 d에 포함된 각 단어 w에 대해 X*Y의 확률로 토픽 k로 재할당한다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;&lt;li&gt;X : theta_d = p(topic k | document d) = 문서 d에서 토픽 k가 차지하는 비율&lt;/li&gt;&lt;li&gt;Y : pi_k = p(word w | topic k) = topic k에 할당된 단어들 가운데 단어 w가 차지하는 비율&lt;/li&gt;&lt;li&gt;X*Y = 문서 d에서 topic k가 word w를 생성한 비율&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;Step4. 안정화된 최종 topic 할당한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;예시를 통해서 topic을 재할당해보자.&lt;br&gt;예시 : 문서 3개 / topic 2개&lt;br&gt;미션 : Doc3의 4번째 단어인 fish에 대해서 topic을 재할당해보기&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b74ZlR/dJMcaaRc14j/MYUfsmkCzGTWvI6KZqi13K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b74ZlR/dJMcaaRc14j/MYUfsmkCzGTWvI6KZqi13K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b74ZlR/dJMcaaRc14j/MYUfsmkCzGTWvI6KZqi13K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb74ZlR%2FdJMcaaRc14j%2FMYUfsmkCzGTWvI6KZqi13K%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;500&quot; height=&quot;311&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리의 목적은 fish에 대해서 토픽별로 X*Y을 계산하고 큰 값을 가지는 topic으로 재할당하는 것이다.&lt;br&gt;&lt;u&gt;&lt;b&gt;1) fish의 topic이 A인 경우&lt;/b&gt;&lt;/u&gt;&lt;br&gt;X = 3/6&lt;br&gt;Y = 0/7&lt;br&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #202122;&quot;&gt;∴ &lt;/span&gt;&lt;/span&gt;X*Y = 0&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;u&gt;&lt;b&gt;2) fish의 topic이 B인 경우&lt;/b&gt;&lt;/u&gt;&lt;br&gt;X = 3/6&lt;br&gt;Y = 2/9&lt;br&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #202122;&quot;&gt;∴ &lt;/span&gt;&lt;/span&gt;X*Y = 1/9&lt;br&gt;&amp;nbsp;&lt;br&gt;X*Y의 값이 topic B가 더 크므로, Doc3의 4번쨰 단어 fish는 topic B로 할당될 확률이 높다.&lt;br&gt;재할당하면, 문서 내 토픽 비율과 토픽별 단어 분포에 변화가 생긴다.&lt;br&gt;이를 바탕으로 분포와 비율이 수렴할 때까지 학습 과정을 반복한다.&lt;/p&gt;</description>
      <category>텍스트마이닝</category>
      <author>chief.park</author>
      <guid isPermaLink="true">https://parkso-yeon.tistory.com/3</guid>
      <comments>https://parkso-yeon.tistory.com/3#entry3comment</comments>
      <pubDate>Wed, 12 Nov 2025 20:56:02 +0900</pubDate>
    </item>
    <item>
      <title>함수 종속성</title>
      <link>https://parkso-yeon.tistory.com/2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0. 개요&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;데이터베이스 정규화는 함수 종속성을 제거하는 방향으로 진행된다.&lt;br&gt;따라서, 정규화 전에 함수 종속성에 대해서 먼저 알아보자.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1. 함수 종속성이란?&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;함수 종속성(Functional Dependency)은 FD로 줄여서 말하기도 한다.&lt;br&gt;디비에서 속성들 간의 종속 관계를 말한다. 테이블 필드들의 종속 관계를 알아야 데이터 중복을 줄이고, 테이블을 논리적으로 만들 수 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;속성 A의 값에 따라 속성 B의 값이 유일하게 결정될 때, 속성 B는 A에 함수 종속적이라고 한다. A → B로 표현하며 A가 B를 결정한다는 의미를 가진다. 이때 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;A를 결정자&lt;/b&gt;&lt;/span&gt;, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;B를 종속자&lt;/b&gt;&lt;/span&gt;라고 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2. 완전 함수 종속 (Full Functional Dependency)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;기본키 전체&lt;/b&gt;가 다른 속성을 결정할 때 발생하는 종속성이다.&lt;br&gt;예를 들면, {학번, 과목코드}라는 기본키가 {성적}을 결정하는 관계이다. 학번으로만 또는 과목코드만으로는 성적을 결정할 수 없다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3. 부분 함수 종속 (Partial Functional Dependency)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;기본키의 일부 속성&lt;/b&gt;만으로 다른 속성이 결정할 때 발생하는 종속성이다. 다시 말해, &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;종속자가 기본키가 아닌 다른 속성에 종속되거나&lt;/b&gt;&lt;/span&gt;, &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;기본키를 구성하는 여러 속성들의 부분집합 중 일부분에만 종속&lt;/b&gt;&lt;/span&gt;되어 있다는 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;예시로 알아보자.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KEhqV/dJMcahCK3LH/796vj16VC1WKFZacd8QwZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KEhqV/dJMcahCK3LH/796vj16VC1WKFZacd8QwZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KEhqV/dJMcahCK3LH/796vj16VC1WKFZacd8QwZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKEhqV%2FdJMcahCK3LH%2F796vj16VC1WKFZacd8QwZK%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;174&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;이 테이블에서 알 수 있는 점은 이렇다.&lt;br&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;candidate key : {sid, cid}&lt;/li&gt;&lt;li&gt;non-prime attribute : sname, addr, major, title, iname,iloc, grade&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;종속 관계 :&lt;br&gt;{sid → sname, addr, major}&lt;br&gt;{cid → title, iname, iloc}&lt;br&gt;{sid, cid → grade}&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;sname, addr, major는 기본키(sid, cid)의 일부분인 sid에만 종속&lt;/b&gt;되어 있다. 마찬가지로 &lt;b&gt;title, iname, iloc은 cid에 대해서만 결정&lt;/b&gt;된다. 따라서 부분 함수 종속성이 있다. 앞서 말했던 것처럼 정규화는 함수 종속성을 제거하는 방향으로 진행되며, 이런 부분 함수 종속성을 없애는 정규화는 제2정규화(2NF)이다. &lt;b&gt;함수 종속성을 제거하기 위해서 테이블을 따로 빼낸다.&lt;/b&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1134&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o1a22/dJMb99Luznn/ATj2HKr2cijM8AkOqJblrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o1a22/dJMb99Luznn/ATj2HKr2cijM8AkOqJblrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o1a22/dJMb99Luznn/ATj2HKr2cijM8AkOqJblrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo1a22%2FdJMb99Luznn%2FATj2HKr2cijM8AkOqJblrk%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;295&quot; data-origin-width=&quot;1134&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;4. 이행 함수 종속 (Transitive Functional Dependecy)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;Transitive의 뜻은 전이적이다. 테이블에서 x, y, z 라는 3개의 속성이 있을 때, &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;x → y, y → z 라는 종속 관계가 있을 경우, x → z가 성립&lt;/b&gt;&lt;/span&gt;되면 이행 함수 종속이 있다고 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;위의 예시를 다시 생각해보자. 부분 함수 종속이 제거된 상태에서, 이행 함수 종속이 어디 있을까? iname → iloc 에 주목해보자.&lt;br&gt;(cid → iname), (iname → iloc) 일 때, cid → iloc이 성립한다. 이행 함수 종속이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;5. 다치 종속 (Multivalued Dependency)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;한 속성이 다른 속성 집합에 독립적으로 여러 값을 가질 때를 말한다. A라는 속성에 대해서 여러개의 B가 가능할 때이다. 그리고 그 B에 해당하는 값들은 서로 독립적이어야 한다. 기호로는 A →→ B 로 표현한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이해하기 쉬운 예제가 있다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6FIja/dJMcagKCi1U/aItYZDMTp0UK8la2iZTxlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6FIja/dJMcagKCi1U/aItYZDMTp0UK8la2iZTxlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6FIja/dJMcagKCi1U/aItYZDMTp0UK8la2iZTxlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6FIja%2FdJMcagKCi1U%2FaItYZDMTp0UK8la2iZTxlk%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;229&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;여기서 (restaurant →→ pizza variety), (restaurant →→ delivery area) 로 다치 종속이 발생한다.&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;restaurant&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;→→ pizza variety 의 의미는 restaurant 하나에 대해서 pizza 종류가 여러개라는 뜻이다. 1:N으로 이해해도 좋다.&lt;br&gt;restaurant(A)에 대해서 pizza variety(B)와 delivery area(B)가 각각 여러개의 값을 가지고 있고, 피자 종류와 배달지역은 서로 독립적이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;6. 조인 종속 (Join Dependency)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;조인 종속은 다치 종속의 일반화된 형태이다.&lt;br&gt;하나의 테이블을 여러개의 테이블로 무손실 분해를 했다가 다시 결합할 수 있다면 이를 조인 종속이라고 한다. 조금 더 정확하게 말하자면, 릴레이션 R이 여러개의 부분 테이블로 나누어져 있고, 이 테이블을 natural join을 통해 다시 원래 R로 정확히 복원할 수 있을 때 성립한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;이제 함수 종속성을 제거하는 정규화를 배울 준비가 되었다.&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <author>chief.park</author>
      <guid isPermaLink="true">https://parkso-yeon.tistory.com/2</guid>
      <comments>https://parkso-yeon.tistory.com/2#entry2comment</comments>
      <pubDate>Wed, 12 Nov 2025 14:12:54 +0900</pubDate>
    </item>
    <item>
      <title>데이터베이스 정규화</title>
      <link>https://parkso-yeon.tistory.com/1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0. 정규화는 왜 필요한가?&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;결론부터 말하자면 중복을 제거하기 위해서이다. 즉, 비일관성으로 인한 오류를 방지하기 위해서이다.&lt;br&gt;정규화를 하지 않았을 경우에 테이블이 일관적으로 관리되는 것이 어렵다.&lt;br&gt;&amp;nbsp;&lt;br&gt;참고로, 가용성을 중요하게 생각하는 NoSQL DB는 오히려 중복을 장려한다.&lt;br&gt;하지만 RDBMS의 경우에는 중복을 제거해야한다. 따라서 정규화가 필요하다.&lt;br&gt;&amp;nbsp;&lt;br&gt;정규화를 말하기 위해서는 함수 종속성이라는 개념이 필수적이다. 함수 종속성에 대해서는 앞서 따로 글을 썼다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1. 정규화하지 않으면 발생하는 문제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H1ghF/dJMcahbJwrL/3iN1kPrpqOxTljVd7BKTzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H1ghF/dJMcahbJwrL/3iN1kPrpqOxTljVd7BKTzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H1ghF/dJMcahbJwrL/3iN1kPrpqOxTljVd7BKTzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH1ghF%2FdJMcahbJwrL%2F3iN1kPrpqOxTljVd7BKTzk%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;650&quot; height=&quot;258&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;현재 데이터베이스에 이 테이블 하나만 존재한다고 가정해보자.&lt;br&gt;정규화가 되지 않은 상태이다. 아래 3가지 상황에서 각각 어떤 문제가 발생하는지 보면서 정규화의 필요성을 명확하게 알게된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1.1 insert 시 문제&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;위와 같이 주문 내역 테이블 하나만 존재한다.&lt;br&gt;B11 이라는 책을 추가하고 싶다. 그런데, 이 책을 산 사람이 아직 없다. 그렇다면 책의 정보를 넣을 수 없게 되거나, 다른 attribute(name, age 등)의 값을 모두 null로 해서 행을 삽입해야 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;새로운 고객이 가입했다. 그런데, 아직 주문을 한 적이 없다. 고객 정보를 저장하기 위해서는 고객 정보와 관련된&amp;nbsp; id, name, age 필드만 채우고, 나머지는 null 값으로 삽입을 해야 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이 상황들을 모두 억지로 insert를 발생시켜야 한다. 문제가 된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1.2 update 시 문제&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;B1 책의 가격이 올랐다. 책에 대한 정보가 update된 상황이다. 그런데, 테이블이 하나밖에 없으니 B1 책을 가지고 있는 모든 행의 price를 모두 update 해야한다. 만약 주문 정보가 엮여 있지 않은, 책 테이블이 있다면 B1 하나 행만 update 해주면 되는데 정규화가 되어있지 않으니 여러 행을 다 건들여야 하는 비효율성이 발생한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1.3 delete 시 문제&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;B2 책의 정보를 삭제해야 한다. 그런데, 지금 테이블이 주문 내역 테이블이기 때문에, B2 책을 지우기 위해서는 해당 row를 delete 해야 한다. 책의 정보만을 삭제하고 싶었는데, 주문 정보(고객 정보까지)가 지워지게 된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;반대 상황도 동일하다. 어떤 고객의 정보를 지우려고 했는데, 책의 정보까지 지워지는 불상사가 발생한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;u&gt;&lt;b&gt;결론적으로&lt;/b&gt;&lt;/u&gt;&lt;br&gt;정규화가 되지 않은 테이블에서 insert / update / delete 할 때 많은 문제가 발생한다.&lt;br&gt;그 이유는 테이블이 하나로 합쳐져 있기 때문이며, 중복 정보가 생기기 때문이다.&lt;br&gt;따라서, 정규화를 해야하는 이유는 중복 정보를 제거하기 위함이다.&lt;br&gt;지금부터는 이 단일 테이블을 어떻게 적절하게 나눌지 정규화에 대한 구체적인 내용을 다뤄보자.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2. 제 1정규화 (1NF)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;u&gt;&lt;b&gt;1NF는 레코드가 atomic 해야 한다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IeGOg/dJMcadNTLJg/Pjp32DtdijFPDXZ9bDma7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IeGOg/dJMcadNTLJg/Pjp32DtdijFPDXZ9bDma7k/img.png&quot; data-alt=&quot;레코드가 atomic 하지 않다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IeGOg/dJMcadNTLJg/Pjp32DtdijFPDXZ9bDma7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIeGOg%2FdJMcadNTLJg%2FPjp32DtdijFPDXZ9bDma7k%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;195&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;레코드가 atomic 하지 않다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;하나의 row에 2개 또는 3개의 정보가 들어가 있다. 이것은 atomic하지 않다.&lt;br&gt;하나의 row에는 하나의 정보만 들어가야 한다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PqYBV/dJMcaaRecYb/eoTUrOEKQmI7VBbmCcfVD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PqYBV/dJMcaaRecYb/eoTUrOEKQmI7VBbmCcfVD0/img.png&quot; data-alt=&quot;1NF 만족한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PqYBV/dJMcaaRecYb/eoTUrOEKQmI7VBbmCcfVD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPqYBV%2FdJMcaaRecYb%2FeoTUrOEKQmI7VBbmCcfVD0%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;1118&quot; height=&quot;492&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1NF 만족한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;총 5개의 row 각각에 데이터가 하나씩 들어가도록 하였다. 이제 1NF를 만족한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3. 제 2정규화 (2NF)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;u&gt;&lt;b&gt;2NF는 부분함수종속성을 제거해야 한다.&lt;/b&gt;&lt;/u&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;2NF의 조건은&lt;br&gt;1) 1NF을 만족한 상태에서&lt;br&gt;2) 기본키에 대해서 부분함수종속성이 없다.&lt;br&gt;&amp;nbsp;&lt;br&gt;부분함수종속성이란&lt;br&gt;기본키가 여러 속성으로 구성되어 있을 때, 종속자가 기본키의 일부분에만 종속되어있는 것을 말한다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGzwQz/dJMcadmPmfi/QlZ88pF9qw0tSfZFHNTTl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGzwQz/dJMcadmPmfi/QlZ88pF9qw0tSfZFHNTTl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGzwQz/dJMcadmPmfi/QlZ88pF9qw0tSfZFHNTTl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGzwQz%2FdJMcadmPmfi%2FQlZ88pF9qw0tSfZFHNTTl0%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;1104&quot; height=&quot;384&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;여기서 기본키는 (sid, cid)의 조합이다.&lt;br&gt;속성 중에 (sid, cid) 조합으로 결정되는 것이 아닌, sid로만 결정되거나, cid로만 결정되는 것이 있는지 보자.&lt;br&gt;sname, addr, major는 sid로만 결정된다.&lt;br&gt;title, iname, iloc은 cid로만 결정된다.&lt;br&gt;grade만 (sid, cid)의 조합으로 결정되고 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;2NF를 만족하기 위해서는 이러한 부분함수종속성을 제거해야 한다.&lt;br&gt;sid, sname, addr, major → table1&lt;br&gt;cid, title, iname, iloc → table2&lt;br&gt;sid, cid, grad → table3&lt;br&gt;이렇게 나누면 2NF 만족이다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1583&quot; data-origin-height=&quot;918&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ypih5/dJMcachcLHW/PXfGn4VtRVG4iF9UOUzBu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ypih5/dJMcachcLHW/PXfGn4VtRVG4iF9UOUzBu0/img.png&quot; data-alt=&quot;2NF 만족한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ypih5/dJMcachcLHW/PXfGn4VtRVG4iF9UOUzBu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYpih5%2FdJMcachcLHW%2FPXfGn4VtRVG4iF9UOUzBu0%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;1583&quot; height=&quot;918&quot; data-origin-width=&quot;1583&quot; data-origin-height=&quot;918&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2NF 만족한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;참고로, sid, cid로 하나씩 기본키가 잡혀있는 테이블에 대해서는 부분함수종속성을 논할 수 없다.. 부분함수종속성은 기본키가 두개 이상인 경우에 부분적으로 종속이 발생한다는 개념이기 때문이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;4. 제 3정규화 (3NF)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;u&gt;&lt;b&gt;3NF는 이행함수종속성을 제거해야 한다.&lt;/b&gt;&lt;/u&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;3NF의 조건은&lt;br&gt;1) 2NF를 만족한 상태에서&lt;br&gt;2) 이행함수종속성이 없다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이행함수종속성이란&lt;br&gt;transitive(전이적)인 행이 있는지인데,&lt;br&gt;x(기본키), y, z 라는 3개의 속성에 대해서 x(기본키) → y, y → z 라는 종속 관계가 있을 경우 x → z가 성립되면 이행함수종속이 있다고 한다.&lt;br&gt;&lt;u&gt;기본키가 아닌 속성이 다른 기본키가 아닌 속성을 결정하고 있는 경우이다.&lt;/u&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1583&quot; data-origin-height=&quot;918&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ypih5/dJMcachcLHW/PXfGn4VtRVG4iF9UOUzBu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ypih5/dJMcachcLHW/PXfGn4VtRVG4iF9UOUzBu0/img.png&quot; data-alt=&quot;2NF 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ypih5/dJMcachcLHW/PXfGn4VtRVG4iF9UOUzBu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYpih5%2FdJMcachcLHW%2FPXfGn4VtRVG4iF9UOUzBu0%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;1583&quot; height=&quot;918&quot; data-origin-width=&quot;1583&quot; data-origin-height=&quot;918&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2NF 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;2NF를 만족하고 있는 이 테이블에 대해서 이행함수종속을 찾아보자.&lt;br&gt;cid가 iname을 결정했는데, iname이 iloc을 결정하고 있다. 그리고 이때 cid가 iloc을 결정하는게 성립한다.&lt;br&gt;이행함수종속이며, 분리해줘야 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;여기서 헷갈렸던 부분은 sname이 addr를 결정하고, sname이 major를 결정하는 것도 이행함수종속이 아닌가라고 생각했다.&lt;br&gt;sid → sname, sname → addr, sid → addr로 보였기 때문이다. 하지만 데이터의 모양만 보고 판단하는 것이 아니라 도메인 규칙을 파악해야한다. sname → addr 일까? sname → major 일까? 아니다. A라는 이름을 가진 사람이 모두 반포에만 사는 것이 아니며, A라는 이름을 가진 사람은 무조검 컴공이 아니기 때문이다. 그러므로 함수 종속이 인정되지 않는다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;이제 3NF를 만족하도록 테이블을 나눠보겠다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;1004&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfVK3N/dJMcaiIrNoz/LeJ87ideHnwSAby8ChxpC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfVK3N/dJMcaiIrNoz/LeJ87ideHnwSAby8ChxpC0/img.png&quot; data-alt=&quot;3NF 만족한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfVK3N/dJMcaiIrNoz/LeJ87ideHnwSAby8ChxpC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfVK3N%2FdJMcaiIrNoz%2FLeJ87ideHnwSAby8ChxpC0%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;1362&quot; height=&quot;1004&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;1004&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;3NF 만족한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;만약, 새로운 레코드가 들어왔는데, ME102에 대해서 iname이 I2이면, cid → iname이 만족하지 않으므로, transitive 하지 않다.&lt;br&gt;이런 경우, iname → iloc은 만족하고 있으므로 iloc을 수업 장소가 아닌 교수 연구실로 해석할 수 있게 된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;5. 제 3.5정규화 (BCNF)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;u&gt;&lt;b&gt;BCNF는 모든 결정자가 candidate key(후보키)가 되어야 한다.&lt;/b&gt;&lt;/u&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;BCNF는 Boyce-Codd Normal Form&lt;br&gt;3NF의 추가적인 버전이기 때문에 3.5NF로 불리며, 3NF보다 더 엄격하다.&lt;br&gt;3NF를 만족하는 테이블에 대해서 모든 결정자가 후보키가 되어야 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;BCNF의 조건은&lt;br&gt;1) 3NF를 만족한 상태에서&lt;br&gt;2) 모든 결정자가 후보키여야 한다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kY04K/dJMcaiVYzRU/41nyF85odkfvcL0fHjwxb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kY04K/dJMcaiVYzRU/41nyF85odkfvcL0fHjwxb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kY04K/dJMcaiVYzRU/41nyF85odkfvcL0fHjwxb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkY04K%2FdJMcaiVYzRU%2F41nyF85odkfvcL0fHjwxb1%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;1070&quot; height=&quot;306&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;함수종속성을 먼저 찾아보겠다.&lt;br&gt;(sid, major) → advisor&lt;br&gt;advisor → major&lt;br&gt;&amp;nbsp;&lt;br&gt;이때 두번째 관계에서 advisor가 후보키가 아니다. 그러므로, BCNF를 만족하지 못한다.&lt;br&gt;테이블을 분리해보자.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpRlQj/dJMcafkHsSh/eoKbW1nO4PO0UkBOZy4xl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpRlQj/dJMcafkHsSh/eoKbW1nO4PO0UkBOZy4xl0/img.png&quot; data-alt=&quot;BCNF 만족한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpRlQj/dJMcafkHsSh/eoKbW1nO4PO0UkBOZy4xl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpRlQj%2FdJMcafkHsSh%2FeoKbW1nO4PO0UkBOZy4xl0%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;1548&quot; height=&quot;364&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BCNF 만족한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;6. 제 4정규화 (4NF)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;u&gt;&lt;b&gt;4NF는 다중값종속(다치종속)을 제거해야 한다.&lt;/b&gt;&lt;/u&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;4NF의 조건은&lt;br&gt;1) BCNF를 만족한 상태에서&lt;br&gt;2) 다치종속을 제거해야 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;다치종속이란&lt;br&gt;A가 B를 결정할 때, B속성이 여러개의 값을 가질 수 있는 경우를 말한다. 그리고 B에 해당하는 값들은 서로 독립적이어야 한다.&lt;br&gt;기호로는 &lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;A →→ B 로 표현한다.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;예시를 보자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPGgjr/dJMcaacCE9L/0NBk80vg5l4UhuRh7LSeIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPGgjr/dJMcaacCE9L/0NBk80vg5l4UhuRh7LSeIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPGgjr/dJMcaacCE9L/0NBk80vg5l4UhuRh7LSeIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPGgjr%2FdJMcaacCE9L%2F0NBk80vg5l4UhuRh7LSeIk%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;1070&quot; height=&quot;356&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;course →→ instructor&lt;br&gt;course →→ textbook 인데, instructor와 textbook은 독립적이다. 다치종속이 발생했다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1633&quot; data-origin-height=&quot;713&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chWj9P/dJMb99Y5oS3/J4o6JTA9FA5BLmdRTcTVVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chWj9P/dJMb99Y5oS3/J4o6JTA9FA5BLmdRTcTVVK/img.png&quot; data-alt=&quot;4NF 만족한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chWj9P/dJMb99Y5oS3/J4o6JTA9FA5BLmdRTcTVVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchWj9P%2FdJMb99Y5oS3%2FJ4o6JTA9FA5BLmdRTcTVVK%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;1633&quot; height=&quot;713&quot; data-origin-width=&quot;1633&quot; data-origin-height=&quot;713&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;4NF 만족한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;7. 제 5정규화 (5NF)&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;u&gt;&lt;b&gt;5NF는 조인종속을 제거해야 한다.&lt;/b&gt;&lt;/u&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;중복을 제거하기 위해 분해할 수 있는만큼 전부 분해하는 것이라고 생각하면 된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;5NF의 조건은&lt;br&gt;1) 4NF를 만족한 상태에서&lt;br&gt;2) 조인종속이 없어야 한다.&lt;br&gt;3) 조인연산을 했을 때 손실이 없어야 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;조인종속이란&lt;br&gt;여러 테이블을 조인할 때, 그 조인이 필수적이지 않거나 불필요한 중복 데이터를 만들 수 있는 경우에 발생한다.&lt;br&gt;모든 테이블이 최소한의 정보를 가질 수 있도록 분리하는데,&lt;br&gt;다시 조인을 했을 때 원래 데이터로 복원을 할 수 있어야 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이때, 4NF에서 5NF로 바꿨을 때 4NF가 깨진다면 4NF에서 멈춘다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;u&gt;&lt;b&gt;오늘 배운 것을 정리해보면&lt;/b&gt;&lt;/u&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;1NF : 각 레코드가 atomic 해야 한다.&lt;br&gt;2NF : 부분함수종속성을 제거한다.&lt;br&gt;3NF : 이행함수종속성을 제거한다.&lt;br&gt;BCNF : 결정자는 모두 후보키여야 한다.&lt;br&gt;4NF : 다치종속을 제거한다.&lt;br&gt;5NF : 조인종속을 제거한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;Reference&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;/ul&gt;</description>
      <category>데이터베이스</category>
      <author>chief.park</author>
      <guid isPermaLink="true">https://parkso-yeon.tistory.com/1</guid>
      <comments>https://parkso-yeon.tistory.com/1#entry1comment</comments>
      <pubDate>Wed, 12 Nov 2025 13:09:07 +0900</pubDate>
    </item>
  </channel>
</rss>