<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>천천히 가는 것을 걱정하지 말고 서있는 것을 걱정하라.</title>
    <link>https://soobarkbar.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 12 May 2026 22:57:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>기내식은수박바</managingEditor>
    <image>
      <title>천천히 가는 것을 걱정하지 말고 서있는 것을 걱정하라.</title>
      <url>https://tistory1.daumcdn.net/tistory/3076242/attach/d9ec458535e6467b967018217189ee98</url>
      <link>https://soobarkbar.tistory.com</link>
    </image>
    <item>
      <title>인덱스 풀스캔(Index Full Scan) 훑어보기</title>
      <link>https://soobarkbar.tistory.com/258</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;실행 계획을 보다 보면 &lt;code&gt;range&lt;/code&gt;, &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;ALL&lt;/code&gt; 같은 접근 방식과 함께&lt;br /&gt;DBMS에 따라 &lt;b&gt;인덱스 풀스캔&lt;/b&gt;에 가까운 동작이 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름만 보면 비효율적으로 느껴질 수 있지만, 실제로는 상황에 따라 꽤 합리적인 선택이 된다.&lt;br /&gt;중요한 건 &lt;b&gt;&amp;ldquo;인덱스를 일부만 읽는 스캔이 아니라, 인덱스 전체를 읽는 스캔&amp;rdquo;&lt;/b&gt; 이라는 점이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 인덱스 풀스캔이 무엇이며, 어떻게 동작하는가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 풀스캔은 말 그대로 &lt;b&gt;인덱스의 처음부터 끝까지 전체를 읽는 방식&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 인덱스는 특정 값을 빠르게 찾기 위해 사용한다고 배운다.&lt;br /&gt;예를 들어 &lt;code&gt;where id = 10&lt;/code&gt; 같은 조건에서는 인덱스의 일부만 읽으면 된다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 왜 인덱스 전체를 읽는가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 건을 저장하는 데이터 크기가 더 작아서 같은 양을 읽을 때 필요한 블록 수가 더 적을 수 있다. 용량을 많이 차지하는 데이터 타입 (varchar(1000), blob, ...) 이 있다면 하나의 블록에 들어갈 수 있는 로우 수가 적어지기 때문에 테이블의 폭이 더 넓을 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;B-Tree 인덱스는 키 순서대로 정렬되어 있다&lt;/li&gt;
&lt;li&gt;필요한 컬럼이 인덱스 안에 있으면 테이블까지 안 가도 되는 경우가 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &amp;ldquo;전체를 읽더라도 테이블보다 인덱스가 비용이 더 싸다&amp;rdquo;라고 판단되면 인덱스 풀스캔이 나올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 동작 방식&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인덱스 루트/브랜치 블록을 따라 첫 리프 블록으로 이동한다&lt;/li&gt;
&lt;li&gt;리프 블록을 처음부터 끝까지 읽는다&lt;/li&gt;
&lt;li&gt;필요한 컬럼이 인덱스에 다 있으면 결과를 바로 반환한다&lt;/li&gt;
&lt;li&gt;필요한 컬럼이 인덱스에 없으면 테이블을 추가 조회한다&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE employees (
    emp_id INT,
    dept_id INT,
    emp_name VARCHAR(100),
    hire_date DATE,
    salary INT
);

CREATE INDEX idx_emp_hire_date ON employees(hire_date);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;SELECT hire_date
FROM employees
ORDER BY hire_date;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 쿼리는 hire_date만 필요하고, 정렬도 hire_date 기준이다.&lt;br /&gt;이 경우 테이블을 전부 읽고 정렬하는 것보다, idx_emp_hire_date 인덱스를 처음부터 끝까지 읽는 편이 더 유리할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 인덱스 풀스캔이 유리한 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 풀스캔은 &amp;ldquo;풀스캔&amp;rdquo;이라는 이름 때문에 무조건 나쁜 방식처럼 보이지만, 실제로는 꽤 유용한 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 필요한 컬럼이 인덱스에 다 들어 있는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 가장 유리하다.&lt;br /&gt;인덱스만 읽고 결과를 만들 수 있으니 테이블 접근을 줄일 수 있다. MySQL에서는 이런 경우 Using index가 붙는 covering index 형태가 될 수 있고, Oracle에서도 인덱스만으로 결과를 만들 수 있을 때 빠른 경로를 선택할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;CREATE INDEX idx_emp_dept_salary ON employees(dept_id, salary);

SELECT dept_id, salary
FROM employees
ORDER BY dept_id, salary;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회 컬럼도 인덱스에 있고, 정렬 순서도 인덱스 순서와 맞는다.&lt;br /&gt;이런 경우 인덱스를 전체로 읽는 방식이 꽤 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 결과를 이미 정렬된 상태로 가져오고 싶은 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B-Tree 인덱스는 키 순서대로 저장된다.&lt;br /&gt;그래서 ORDER BY가 인덱스 순서와 맞아떨어지면 별도의 정렬 작업을 줄일 수 있다. MySQL은 인덱스를 이용해 정렬을 피할 수 있고, Oracle의 일반적인 index full scan도 인덱스 키 순서를 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;SELECT hire_date
FROM employees
ORDER BY hire_date;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 전체를 읽고 sort 하는 것보다, hire_date 인덱스를 순서대로 읽는 편이 더 나을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 테이블보다 인덱스가 훨씬 가벼운 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블은 컬럼이 많고 행 크기가 큰 반면, 인덱스는 상대적으로 얇다.&lt;br /&gt;그래서 &amp;ldquo;어차피 많이 읽어야 한다면 차라리 인덱스를 읽자&amp;rdquo;라는 판단이 가능하다. MySQL 문서도 covering index의 경우 인덱스만 스캔하는 것이 보통 테이블 전체를 읽는 것보다 빠를 수 있다고 설명한다.&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;employees 테이블에 큰 TEXT, JSON, BLOB 컬럼이 많다고 가정하자.&lt;/p&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;SELECT dept_id
FROM employees
ORDER BY dept_id;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 dept_id 인덱스만 훑는 편이 테이블 전체를 읽는 것보다 부담이 적을 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 인덱스 풀스캔이 불리한 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 인덱스 풀스캔이 비효율적인 경우도 분명하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 대부분 행에 대해 다시 테이블 접근이 필요한 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스에는 키 값만 있고 실제 필요한 컬럼은 테이블에 있는 경우가 많다.&lt;br /&gt;이때 인덱스를 전체로 읽은 뒤, 각 행마다 다시 테이블을 찾아가면 랜덤 I/O가 많이 생길 수 있다.&lt;br /&gt;이 경우는 차라리 테이블 풀스캔이 더 나을 수 있다. Oracle도 다른 접근 경로보다 비용이 높으면 full table scan을 선택한다.&lt;/p&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;SELECT *
FROM employees
ORDER BY hire_date;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hire_date 인덱스는 정렬에는 도움이 되지만, SELECT * 이므로 결국 대부분 행을 다시 테이블에서 읽어야 한다.&lt;br /&gt;데이터 양이 많으면 비효율적일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 조건으로 일부만 읽어도 되는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회 대상이 일부 구간에 불과하다면 인덱스 전체를 읽는 것보다 레인지 스캔이 더 낫다.&lt;br /&gt;MySQL 문서도 range access는 인덱스 값의 일부 구간만 읽는 방식이라고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT emp_id, hire_date
FROM employees
WHERE hire_date BETWEEN '2025-01-01' AND '2025-01-31';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우는 전체 인덱스를 다 볼 필요가 없다.&lt;br /&gt;조건에 맞는 구간만 읽는 것이 훨씬 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 인덱스 정렬 순서가 쿼리와 맞지 않는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스를 전체로 읽더라도 원하는 정렬이나 필터링 조건과 맞지 않으면 이점이 줄어든다.&lt;/p&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;CREATE INDEX idx_emp_dept_id ON employees(dept_id);

SELECT salary
FROM employees
ORDER BY hire_date;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 인덱스는 dept_id 기준인데, 쿼리는 hire_date 기준으로 정렬한다.&lt;br /&gt;이런 경우 idx_emp_dept_id를 풀스캔해도 정렬 이점을 얻기 어렵다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. MySQL과 Oracle에서 인덱스 풀스캔이 어떻게 다르게 동작하는지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 &amp;ldquo;인덱스를 많이 읽는다&amp;rdquo;는 점은 비슷하지만, 실행 계획에서의 의미와 세부 동작은 다르게 봐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) Oracle은 INDEX FULL SCAN과 INDEX FAST FULL SCAN을 구분한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle은 인덱스 전체를 읽는 방식도 두 가지로 나눠서 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;INDEX FULL SCAN&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;인덱스를 키 순서대로 읽는다&lt;/li&gt;
&lt;li&gt;ORDER BY를 만족시키는 데 유리하다&lt;/li&gt;
&lt;li&gt;보통 단일 블록 I/O 성격으로 이해한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;INDEX FAST FULL SCAN&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;인덱스 전체를 정렬 순서와 무관하게 빠르게 읽는다&lt;/li&gt;
&lt;li&gt;멀티블록 I/O, 병렬 처리 활용이 가능하다&lt;/li&gt;
&lt;li&gt;물리적으로 디스크에 저장된 순서대로 블록을 읽기 때문에 결과가 인덱스 순서로 나오지 않을 수 있다&lt;/li&gt;
&lt;li&gt;인덱스에 필요한 컬럼이 모두 있을 때 full table scan의 대안이 될 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) MySQL은 보통 type=index와 Using index를 같이 해석해야 한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 실행 계획에서는 Oracle처럼 INDEX FULL SCAN, INDEX FAST FULL SCAN이라는 이름으로 세분화해서 보여주지 않는다.&lt;br /&gt;대신 EXPLAIN 결과에서 type=index가 나오면 인덱스를 처음부터 끝까지 읽는 스캔에 가깝게 해석하는 경우가 많다.&lt;br /&gt;그리고 Extra에 Using index가 있으면 인덱스만으로 결과를 처리하는 covering index 가능성이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) Oracle은 &amp;ldquo;정렬 보장 여부&amp;rdquo;까지 구분해서 보고, MySQL은 실행 계획 컬럼 조합으로 해석하는 편이다&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;Oracle&lt;/th&gt;
&lt;th&gt;MySQL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;표현 방식&lt;/td&gt;
&lt;td&gt;INDEX FULL SCAN, INDEX FAST FULL SCAN 등으로 구분&lt;/td&gt;
&lt;td&gt;보통 type=index, Extra=Using index 등 조합으로 해석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정렬 순서 활용&lt;/td&gt;
&lt;td&gt;INDEX FULL SCAN은 키 순서 활용 가능&lt;/td&gt;
&lt;td&gt;인덱스 순서를 활용할 수 있지만 Oracle처럼 명시적으로 scan 이름이 나뉘진 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인덱스만 읽는 빠른 전체 스캔&lt;/td&gt;
&lt;td&gt;INDEX FAST FULL SCAN 존재&lt;/td&gt;
&lt;td&gt;covering index scan 형태로 해석&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 풀스캔은 단순히 &amp;ldquo;인덱스를 썼으니 좋은 것&amp;rdquo;, 또는 &amp;ldquo;풀스캔이니 무조건 나쁜 것&amp;rdquo;으로 보면 안 된다.&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;정렬 이점이나 covering index가 있으면 유리할 수 있다&lt;/li&gt;
&lt;li&gt;반대로 테이블 재접근이 많아지면 비효율적일 수 있다&lt;/li&gt;
&lt;li&gt;Oracle은 INDEX FULL SCAN과 INDEX FAST FULL SCAN을 구분해서 봐야 하고,&lt;br /&gt;MySQL은 type=index, Using index 등을 조합해서 해석해야 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 계획을 볼 때는 &amp;ldquo;인덱스를 탔는가&amp;rdquo;만 보지 말고, 인덱스를 어떻게 탔는가까지 같이 봐야 한다.&lt;/p&gt;</description>
      <category>Database</category>
      <author>기내식은수박바</author>
      <guid isPermaLink="true">https://soobarkbar.tistory.com/258</guid>
      <comments>https://soobarkbar.tistory.com/258#entry258comment</comments>
      <pubDate>Sat, 18 Apr 2026 16:57:51 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka] MacOS에서 카프카 매니저 설치</title>
      <link>https://soobarkbar.tistory.com/256</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. github에서 tar.gz 파일 다운로드 (&lt;a href=&quot;https://github.com/yahoo/CMAK&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/yahoo/CMAK&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2312&quot; data-origin-height=&quot;1114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MZKBf/btsGrW9BOEb/zHJGyiMHUMJgQNSevDX750/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MZKBf/btsGrW9BOEb/zHJGyiMHUMJgQNSevDX750/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MZKBf/btsGrW9BOEb/zHJGyiMHUMJgQNSevDX750/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMZKBf%2FbtsGrW9BOEb%2FzHJGyiMHUMJgQNSevDX750%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;2312&quot; height=&quot;1114&quot; data-origin-width=&quot;2312&quot; data-origin-height=&quot;1114&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;size18&quot;&gt;2. 압축 해제 후 설치한 폴더 내에서 ./sbt clean dist 명령어 실행&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;Cannot use JVMCI compiler: No JVMCI compiler found&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;명령어 실행 시 위 에러가 발생한 경우, &lt;a href=&quot;https://github.com/yahoo/CMAK/issues/927&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt; 참고&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;size18&quot;&gt;3. target/universal 경로에 생성된 .zip 파일을 원하는 위치에 압축 해제&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;4. conf/application.conf 파일 수정 (로컬에 주키퍼를 띄웠기 때문에 아래와 같이 수정)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nqPxL/btsGpKXqiXR/uNZJ1NEpLhVKhSsL3cRH70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nqPxL/btsGpKXqiXR/uNZJ1NEpLhVKhSsL3cRH70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nqPxL/btsGpKXqiXR/uNZJ1NEpLhVKhSsL3cRH70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnqPxL%2FbtsGpKXqiXR%2FuNZJ1NEpLhVKhSsL3cRH70%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;113&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;5.  ./bin/cmak 명령어 실행 후, localhost:9000 접속&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3340&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AkO6m/btsGprqkxz5/5wl7mrfkvQC1Mwcp7hH4Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AkO6m/btsGprqkxz5/5wl7mrfkvQC1Mwcp7hH4Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AkO6m/btsGprqkxz5/5wl7mrfkvQC1Mwcp7hH4Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAkO6m%2FbtsGprqkxz5%2F5wl7mrfkvQC1Mwcp7hH4Bk%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;3340&quot; height=&quot;604&quot; data-origin-width=&quot;3340&quot; data-origin-height=&quot;604&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;!&amp;nbsp;@7jn3njc8f&amp;nbsp;-&amp;nbsp;Internal&amp;nbsp;server&amp;nbsp;error,&amp;nbsp;for&amp;nbsp;(GET)&amp;nbsp;[/assets/dataTables/javascripts/dataTables.bootstrap4.js]&lt;/li&gt;
&lt;li&gt;카프카 매니저 접속 시에 JS, CSS를 불러오지 못하는 위 에러가 발생하는 경우, &lt;a href=&quot;https://github.com/yahoo/CMAK/issues/844&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt; 참고&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Kafka</category>
      <author>기내식은수박바</author>
      <guid isPermaLink="true">https://soobarkbar.tistory.com/256</guid>
      <comments>https://soobarkbar.tistory.com/256#entry256comment</comments>
      <pubDate>Sat, 6 Apr 2024 14:54:32 +0900</pubDate>
    </item>
    <item>
      <title>기수 정렬 (Radix Sort)</title>
      <link>https://soobarkbar.tistory.com/156</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;기수 정렬 (Radix Sort) ?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기수 정렬은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;각 자리 위치를 하나씩 증가시키면서&lt;/b&gt;&lt;/span&gt; &lt;b&gt;숫자들을 정렬&lt;/b&gt;하는 방법이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안정 정렬&lt;/b&gt;에 속하며, &lt;a href=&quot;https://soobarkbar.tistory.com/101&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;카운팅 정렬&lt;/a&gt;과 마찬가지로 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;값의 비교연산 없이&lt;/span&gt; 정렬&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;기수 정렬의 단점은 다음과 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제자리 정렬이 아니기 때문에 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;추가적인 메모리가 필요&lt;/b&gt;&lt;/span&gt;하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;동일한 길이를 가진&lt;/span&gt; 숫자나 문자열&lt;/b&gt;이여야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정렬하는 숫자 자릿수 \(k\) 에 따라 \(O(kn)\) 의 시간 복잡도를 가진다.&lt;/li&gt;
&lt;li&gt;기수 정렬 과정은 다음과 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1의 자리 숫자를 비교해서 오름차순 또는 내림차순으로 정렬한 뒤, 큐 버킷에 삽입한 뒤 순서대로 뽑아 정렬한다.&lt;/li&gt;
&lt;li&gt;그 다음, 10의 자리 숫자를 비교해서 동일하게 수행한다.&lt;/li&gt;
&lt;li&gt;가장 큰 숫자의 자릿 수만큼 반복해서 수행한다.&lt;/li&gt;
&lt;/ul&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;1057&quot; data-origin-height=&quot;419&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cF5fiB/btqCgSllX0Z/kECRdI1FQquPfMjBbkoZEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cF5fiB/btqCgSllX0Z/kECRdI1FQquPfMjBbkoZEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cF5fiB/btqCgSllX0Z/kECRdI1FQquPfMjBbkoZEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcF5fiB%2FbtqCgSllX0Z%2FkECRdI1FQquPfMjBbkoZEK%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;1057&quot; height=&quot;419&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;419&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;h3 data-ke-size=&quot;size23&quot;&gt;Code&lt;/h3&gt;
&lt;pre id=&quot;code_1696321370732&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void radixSort(int[] arr, int maxSize) {
    int jarisu = 1, size = 0;
    int len = arr.length;
    int[] output = new int[len];

    while (size &amp;lt; maxSize) {
        int[] bucket = new int[10];

        for (int i = 0; i &amp;lt; len; ++i) {
            ++bucket[arr[i] / jarisu % 10];
        }

        for (int i = 1; i &amp;lt; 10; ++i) {
            bucket[i] += bucket[i - 1];
        }

        for (int i = len - 1; i &amp;gt;= 0; --i) {
            int idx = arr[i] / jarisu % 10;

            output[bucket[idx] - 1] = arr[i];
            --bucket[idx];
        }

        for (int i = 0; i &amp;lt; len; ++i) {
            arr[i] = output[i];
        }

        jarisu *= 10;
        ++size;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #f0f0f0; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결과&lt;/h3&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #f0f0f0; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #272727; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #4f4f4f;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #aaa; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #f0f0f0; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;기수&amp;nbsp;정렬&amp;nbsp;1&amp;nbsp;단계:&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;[82,&amp;nbsp;43,&amp;nbsp;3,&amp;nbsp;15,&amp;nbsp;35,&amp;nbsp;27,&amp;nbsp;7,&amp;nbsp;38,&amp;nbsp;18,&amp;nbsp;9]&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;기수&amp;nbsp;정렬&amp;nbsp;2&amp;nbsp;단계:&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;[3,&amp;nbsp;7,&amp;nbsp;9,&amp;nbsp;15,&amp;nbsp;18,&amp;nbsp;27,&amp;nbsp;35,&amp;nbsp;38,&amp;nbsp;43,&amp;nbsp;82]&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0 2px 4px 0;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #4f4f4f; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://lktprogrammer.tistory.com/48&quot;&gt;https://lktprogrammer.tistory.com/48&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://namu.wiki/w/정렬%20알고리즘#s-2.2.2&quot;&gt;https://namu.wiki/w/정렬%20알고리즘#s-2.2.2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm/Sort</category>
      <author>기내식은수박바</author>
      <guid isPermaLink="true">https://soobarkbar.tistory.com/156</guid>
      <comments>https://soobarkbar.tistory.com/156#entry156comment</comments>
      <pubDate>Tue, 3 Oct 2023 17:23:20 +0900</pubDate>
    </item>
    <item>
      <title>Forward / Reverse Proxy</title>
      <link>https://soobarkbar.tistory.com/248</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Proxy&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가&amp;nbsp;프록시&amp;nbsp;서버를&amp;nbsp;통해서&amp;nbsp;다른&amp;nbsp;네트워크&amp;nbsp;서비스에&amp;nbsp;간접적으로&amp;nbsp;접속할&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;해주는&amp;nbsp;컴퓨터&amp;nbsp;시스템&amp;nbsp;또는&amp;nbsp;응용&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;프록시 (Proxy) : 서버와 클라이언트 사이에서 대리로 통신을 수행하는 것&amp;nbsp;&lt;/li&gt;
&lt;li&gt;프록시 서버 (Proxy Server) : 중계 기능을 수행하는 서버&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;1. Forward Proxy&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가&amp;nbsp;인터넷에&amp;nbsp;접근하는&amp;nbsp;것이&amp;nbsp;아니라&amp;nbsp;프록시&amp;nbsp;서버가&amp;nbsp;요청을&amp;nbsp;받고&amp;nbsp;인터넷에&amp;nbsp;연결하여&amp;nbsp;결과를&amp;nbsp;클라이언트에&amp;nbsp;전달&amp;nbsp;(Forward)&amp;nbsp;해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;장점&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;보안 : 프록시 서버에서 In / Out Bound 패킷에 대한 보안 정책 (Content Filtering 등) 을 적용할 수 있다.&amp;nbsp; &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;성능 : 프록시 서버 내부에 캐시를 유지하며 한 번 통신한 외부 서버의 이미지, 파일 등을 저장할 수 있다. 캐시에 데이터가 있으면 프록시 서버가 데이터를 바로 제공할 수 있어 빠른 통신을 지원한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkfkRx/btrA0XzX0Or/3MGQUCeH1LIiZaJ2G4KrZ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkfkRx/btrA0XzX0Or/3MGQUCeH1LIiZaJ2G4KrZ0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkfkRx/btrA0XzX0Or/3MGQUCeH1LIiZaJ2G4KrZ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkfkRx%2FbtrA0XzX0Or%2F3MGQUCeH1LIiZaJ2G4KrZ0%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;780&quot; height=&quot;283&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;283&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;2. Reverse Proxy&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가&amp;nbsp;인터넷에&amp;nbsp;데이터를&amp;nbsp;요청하면&amp;nbsp;리버스&amp;nbsp;프록시가&amp;nbsp;이&amp;nbsp;요청을&amp;nbsp;받아&amp;nbsp;내부&amp;nbsp;서버에서&amp;nbsp;데이터를&amp;nbsp;받은&amp;nbsp;후&amp;nbsp;클라이언트에&amp;nbsp;전달한다.&lt;br /&gt;&lt;br /&gt;클라이언트는&amp;nbsp;내부&amp;nbsp;서버에&amp;nbsp;대한&amp;nbsp;정보를&amp;nbsp;알&amp;nbsp;필요&amp;nbsp;없이&amp;nbsp;리버스&amp;nbsp;프록싱만&amp;nbsp;요청하면&amp;nbsp;된다.&lt;br /&gt;&lt;br /&gt;내부&amp;nbsp;서버&amp;nbsp;(WAS)&amp;nbsp;에&amp;nbsp;직접&amp;nbsp;접근할&amp;nbsp;경우,&amp;nbsp;DB에&amp;nbsp;접근이&amp;nbsp;가능하기&amp;nbsp;때문에&amp;nbsp;중간에&amp;nbsp;리버스&amp;nbsp;프록시를&amp;nbsp;두고&amp;nbsp;클라이언트와&amp;nbsp;내부&amp;nbsp;서버&amp;nbsp;사이의&amp;nbsp;통신을&amp;nbsp;담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;장점&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;보안 : 모든 접속은 리버스 프록시 서버에게 들어오고 요청에 매핑되는 내부 서버에 요청 정보를 넘겨주기 때문에 외부 사용자는 실제 내부망에 있는 서버의 존재를 모른다.&lt;/li&gt;
&lt;li&gt;로드밸런싱 : 프록시 서버가 내부 서버의 정보를 알고 있기 때문에 로드 밸런싱을 통해 부하 여부에 따라 요청을 분배할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;287&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZDP0i/btrA33Gzlfe/79jDfwoNKKzYWL7qkR63Hk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZDP0i/btrA33Gzlfe/79jDfwoNKKzYWL7qkR63Hk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZDP0i/btrA33Gzlfe/79jDfwoNKKzYWL7qkR63Hk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZDP0i%2FbtrA33Gzlfe%2F79jDfwoNKKzYWL7qkR63Hk%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;810&quot; height=&quot;287&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;287&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;차이점&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트가 요청하는 End Point&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;Forward&amp;nbsp;Proxy&amp;nbsp;:&amp;nbsp;&lt;b&gt;실제 서버 도메인&lt;/b&gt;&lt;br /&gt;Reverse&amp;nbsp;Proxy&amp;nbsp;:&amp;nbsp;&lt;b&gt;프록시&amp;nbsp;서버&amp;nbsp;도메인&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;감춰지는 대상&amp;nbsp;&amp;nbsp;&lt;br /&gt;Forward Proxy : &lt;b&gt;클라이언트 &lt;/b&gt;(요청 받는 서버는 포워드 프록시 서버를 통해서 요청을 받기 때문에 클라이언트의 정보를 알 수 없다.)&lt;br /&gt;Reverse Proxy : &lt;b&gt;서버&amp;nbsp;&lt;/b&gt;(클라이언트는 리버스 프록시 서버에게 요청하기 때문에 실제 서버의 정보를 알 수 없다.)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://bcp0109.tistory.com/194&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://bcp0109.tistory.com/194&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/sjk5766/nginx-reverse-proxy-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-e11e18fcf843&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/sjk5766/nginx-reverse-proxy-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-e11e18fcf843&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Server</category>
      <author>기내식은수박바</author>
      <guid isPermaLink="true">https://soobarkbar.tistory.com/248</guid>
      <comments>https://soobarkbar.tistory.com/248#entry248comment</comments>
      <pubDate>Mon, 2 May 2022 12:19:13 +0900</pubDate>
    </item>
    <item>
      <title>count(*) / count(1) / count(컬럼) 차이</title>
      <link>https://soobarkbar.tistory.com/246</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;count(*) VS count(1)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;66&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Yl5XM/btrztXQDJVV/28BViKfccMQ62OF9xbvlYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Yl5XM/btrztXQDJVV/28BViKfccMQ62OF9xbvlYK/img.png&quot; data-alt=&quot;출처 : mysql 홈페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Yl5XM/btrztXQDJVV/28BViKfccMQ62OF9xbvlYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYl5XM%2FbtrztXQDJVV%2F28BViKfccMQ62OF9xbvlYK%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;1672&quot; height=&quot;66&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;66&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : mysql 홈페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;차이가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;count(*) VS count(컬럼)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;count(*) : null을 포함하여 모든 Row 수를 카운트한다.&lt;/li&gt;
&lt;li&gt;count(컬럼명) : null을 제외한 Row 수를 카운트한다.&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;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_count&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_count&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://asktom.oracle.com/pls/apex/f?p=100:11:0::NO::P11_QUESTION_ID:1156159920245&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://asktom.oracle.com/pls/apex/f?p=100:11:0::NO::P11_QUESTION_ID:1156159920245&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database</category>
      <author>기내식은수박바</author>
      <guid isPermaLink="true">https://soobarkbar.tistory.com/246</guid>
      <comments>https://soobarkbar.tistory.com/246#entry246comment</comments>
      <pubDate>Fri, 15 Apr 2022 17:59:29 +0900</pubDate>
    </item>
    <item>
      <title>Spring MVC (Model, View, Controller)</title>
      <link>https://soobarkbar.tistory.com/244</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spring MVC?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Model / View / Controller로 역할을 나누는 패턴이다.&lt;/li&gt;
&lt;li&gt;MVC 패턴의 목적은 Business Logic과 Presentation Logic을 분리하는 것이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Business Logic : 어떤 특정한 값을 얻기 위해 데이터 처리를 수행하는 응용프로그램의 일부 (즉, 원하는 값을 얻기 위해 백엔드에서 일어나는 각종 처리)&lt;/li&gt;
&lt;li&gt;Presentation Logic : 화면상의 디자인 구성을 위한 처리 (즉, 화면을 보여주기 위한 처리)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;역할 별로 코드를 분리하여 하나의 파일에 코드가 모이는 것을 방지하기 때문에, 가독성과 코드 재사용이 증가한다.&lt;/li&gt;
&lt;li&gt;단점으로 View와 Model 사이의 높은 의존성 때문에 애플리케이션이 커질수록 복잡해지고 유지보수가 어렵다.&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;320&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RO5QC/btrzraH9Fvs/KqAFxev7ivroOcuH7OG4vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RO5QC/btrzraH9Fvs/KqAFxev7ivroOcuH7OG4vK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RO5QC/btrzraH9Fvs/KqAFxev7ivroOcuH7OG4vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRO5QC%2FbtrzraH9Fvs%2FKqAFxev7ivroOcuH7OG4vK%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;320&quot; height=&quot;350&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Model&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Controller에서 View로 전달되는 데이터 객체이다.&lt;/li&gt;
&lt;li&gt;Key-Value 형태를 가지며, 하나의 (Key-Value) 객체를 Model Attribute라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjSSQr/btrzuznobGr/Kuk3d79NjzBajWFiNvOKF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjSSQr/btrzuznobGr/Kuk3d79NjzBajWFiNvOKF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjSSQr/btrzuznobGr/Kuk3d79NjzBajWFiNvOKF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjSSQr%2FbtrzuznobGr%2FKuk3d79NjzBajWFiNvOKF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;119&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Controller&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;View와 Model 사이의 인터페이스 역할을 수행한다.&lt;/li&gt;
&lt;li&gt;Request에 따라 적절한 결과를 Model에 담아 View에 전달한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, View Name과 View에 출력할 Model을 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;View&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Model 데이터의 렌더링을 담당하며, HTML Output을 생성한다.&lt;/li&gt;
&lt;li&gt;여러가지 템플릿 엔진이 존재한다. (ex. JSP, Thymleaf, Freemarker, ...)&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;h2 data-ke-size=&quot;size26&quot;&gt;Spring MVC의 흐름&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o8Msx/btrzoN1JQ6B/YmQKp1nnXmOiHlRb4znswK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o8Msx/btrzoN1JQ6B/YmQKp1nnXmOiHlRb4znswK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o8Msx/btrzoN1JQ6B/YmQKp1nnXmOiHlRb4znswK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo8Msx%2FbtrzoN1JQ6B%2FYmQKp1nnXmOiHlRb4znswK%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;1000&quot; height=&quot;588&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;Client 요청 (Request) 이 들어오면 Filter를 거치고, Dispatcher Servlet으로 온다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Dispatcher Servlet은 받은 요청을 Handler Mapping에게 전달하고, Handler Mapping은 요청 URL을 분석하여 적절한 Controller 정보를 찾아 Dispatcher Servlet에게 반환한다.&lt;/li&gt;
&lt;li&gt;적절한 Controller 정보를 받은 Dispatcher Servlet은 Handler Adapter를 호출하고, 전달 받은 Controller 중 요청한 URL에 맞는 적절한 Method를 찾아준다.&lt;/li&gt;
&lt;li&gt;호출된 Controller는 Business Logic을 처리하고 나온 결과를 View에 전달할 수 있도록 Model 객체에 저장한다.&lt;/li&gt;
&lt;li&gt;그 후, 호출된 Controller는 Dispatcher Servlet에게 View Name을 반환한다.&lt;/li&gt;
&lt;li&gt;Dispatcher Servlet은 View Resolver를 호출하여 View Name을 전달하고, View Resolver는 View Name을 바탕으로 적절한 View 객체를 결정한다.&lt;/li&gt;
&lt;li&gt;View 객체는 적절한 View (ex. JSP, Thymeleaf) 를 호출하고, View는 화면 표시에 필요한 데이터를 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;Model 객체에서&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;가져와 화면 표시를 처리한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;렌더링된 View 화면을 Client에게 Response 한다.&lt;/li&gt;
&lt;/ol&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;h2 data-ke-size=&quot;size26&quot;&gt;구성 요소&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Dispatcher Servlet&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;Spring Framework가 제공하는 Servlet 클래스로, Front Controller를 담당하고 있어&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;모든 HTTP 요청을 받는다.&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Handler Mapping&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Request URL에 해당하는 Controller 정보를 저장하는 테이블을 가진다.&lt;/li&gt;
&lt;li&gt;테이블에 저장된 정보에 따라 Request를 처리할 Method에 매핑한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ViewResolver&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;Controller가 반환한 View Name을 바탕으로 적절한 View 객체 (Physical View Files) 를 반환한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;예를 들면 아래와 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;View Name: home&lt;/li&gt;
&lt;li&gt;Prefix: /WEB-INF/views/&lt;/li&gt;
&lt;li&gt;Suffix: .jsp&lt;/li&gt;
&lt;li&gt;즉, &quot;/WEB-INF/views/home.jsp&quot; 위치의 View(JSP) 에 Controller에서 받은 Model을 전달하고, 해당 View에서 Model 데이터를 이용하여 적절한 페이지를 만들고 Client에게 보여준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://aridom.tistory.com/61&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://aridom.tistory.com/61&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.naver.com/PostView.nhn?isHttpsRedirect=true&amp;amp;blogId=semi7623&amp;amp;logNo=100005637337&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.naver.com/PostView.nhn?isHttpsRedirect=true&amp;amp;blogId=semi7623&amp;amp;logNo=100005637337&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://joont92.github.io/spring/DispatcherServlet-Flow/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://joont92.github.io/spring/DispatcherServlet-Flow/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gmlwjd9405.github.io/2018/12/20/spring-mvc-framework.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gmlwjd9405.github.io/2018/12/20/spring-mvc-framework.html&lt;/a&gt;&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;</description>
      <category>Spring</category>
      <author>기내식은수박바</author>
      <guid isPermaLink="true">https://soobarkbar.tistory.com/244</guid>
      <comments>https://soobarkbar.tistory.com/244#entry244comment</comments>
      <pubDate>Fri, 15 Apr 2022 15:01:25 +0900</pubDate>
    </item>
    <item>
      <title>(MAC) HomeBrew + JDK 8버전 설치</title>
      <link>https://soobarkbar.tistory.com/243</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;HomeBrew 설치&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;M1 칩 있는 맥북&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1648782265452&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/bin/bash -c &quot;$(curl -fsSL https://gist.githubusercontent.com/nrubin29/bea5aa83e8dfa91370fe83b62dad6dfa/raw/48f48f7fef21abb308e129a80b3214c2538fc611/homebrew_m1.sh)&quot;&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;M1 칩 없는 맥북&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1648782318568&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그런데 만약 M1 칩 있는 맥북에서 M1 칩 없는 맥북 설치 방법으로 한다면 아래와 같이 인식을 못한다.&lt;/p&gt;
&lt;pre id=&quot;code_1648782372812&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;zsh: command not found: brew&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그러면 아래 명령어를 입력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1648782400210&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;eval $(/opt/homebrew/bin/brew shellenv)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;터미널을 켤 때마다 brew가 실행되도록 하는 방법&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;m1 칩 맥북
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;vi ~/.zshrc의 마지막 줄에 아래 명령어 입력 후 저장 (:wq)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1648782723284&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;eval $(/opt/homebrew/bin/brew shellenv)&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;m1 칩 없는 맥북
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;vi ~/.zshrc에서 환경변수 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1648782800492&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export PATH=/usr/local/bin:/usr/local/sbin:$PATH&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;brew 설치 확인&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;brew -v&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;h2 data-ke-size=&quot;size26&quot;&gt;JDK 8 설치&lt;/h2&gt;
&lt;pre id=&quot;code_1648782969971&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;brew tap AdoptOpenJDK/openjdk
 
brew install --cask adoptopenjdk8
 
/usr/libexec/java_home -v 1.8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;설치 확인&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;java -version&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/co6nXv/btryaG8FxlW/Vj1lvfq8JD9Kg75z2QEzA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/co6nXv/btryaG8FxlW/Vj1lvfq8JD9Kg75z2QEzA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/co6nXv/btryaG8FxlW/Vj1lvfq8JD9Kg75z2QEzA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fco6nXv%2FbtryaG8FxlW%2FVj1lvfq8JD9Kg75z2QEzA0%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;68&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;134&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;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;javac&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;1164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tZ06c/btrx9AgLMdA/YDwCIwbvlsUFxD5I2rgAhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tZ06c/btrx9AgLMdA/YDwCIwbvlsUFxD5I2rgAhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tZ06c/btrx9AgLMdA/YDwCIwbvlsUFxD5I2rgAhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtZ06c%2Fbtrx9AgLMdA%2FYDwCIwbvlsUFxD5I2rgAhK%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;403&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;1164&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;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://tlo-developer.tistory.com/267&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tlo-developer.tistory.com/267&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://designdepot.tistory.com/209&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://designdepot.tistory.com/209&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>기내식은수박바</author>
      <guid isPermaLink="true">https://soobarkbar.tistory.com/243</guid>
      <comments>https://soobarkbar.tistory.com/243#entry243comment</comments>
      <pubDate>Fri, 1 Apr 2022 12:19:27 +0900</pubDate>
    </item>
    <item>
      <title>Factory Method Pattern</title>
      <link>https://soobarkbar.tistory.com/241</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;팩토리 메서드 패턴?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;객체 생성 코드를 별도의 클래스 또는 메서드로 분리하여 객체 생성 변화에 효과적으로 대응할 수 있는 디자인 패턴&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;등장 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;여러 대의 엘리베이터가 있다고 생각해보자. 사용자가 버튼 (FloorButton) 을 눌렀을 때, 여러 대의 엘리베이터 중 하나를 선택하여 이동시켜야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 목적지 층과 가까우면서 목적지 층의 방향으로 이동 중인 엘리베이터 선택&lt;/li&gt;
&lt;/ul&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;1662&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ziygn/btrinQFK49q/roXKk8lqSeY0z979h9mhZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ziygn/btrinQFK49q/roXKk8lqSeY0z979h9mhZ1/img.png&quot; data-alt=&quot;복수의 엘리베이터를 스케줄링해 엘리베이터를 이동시키는 클래스 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ziygn/btrinQFK49q/roXKk8lqSeY0z979h9mhZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fziygn%2FbtrinQFK49q%2FroXKk8lqSeY0z979h9mhZ1%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;1662&quot; height=&quot;638&quot; data-origin-width=&quot;1662&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;복수의 엘리베이터를 스케줄링해 엘리베이터를 이동시키는 클래스 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ElevatorManager 클래스 : 이동 요청을 처리하는 클래스 (ThroughputScheduler 객체, ElevatorController 객체 복수 개를 갖는다.)&lt;/li&gt;
&lt;li&gt;requestElevator 메서드 : 요청 (목적지 층, 방향) 을 받았을 때, 우선 ThroughputScheduler 클래스의 selectElevator 메서드를 호출해 적절한 엘리베이터를 선택한 뒤, 선택된 엘리베이터에 해당하는 ElevatorController 객체의 gotoFloor 메서드를 호출해 엘리베이터를 이동시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1634718541932&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ElevatorManager {
    private List&amp;lt;ElevatorController&amp;gt; controllers;
    private ThroughputScheduler scheduler;
    
    // 주어진 수만큼의 ElevatorController를 생성함
    public Elevatormanager(int controllerCount) {
        controllers = new ArrayList&amp;lt;&amp;gt;(controllerCount);
        
        for (int i = 0; i &amp;lt; controllerCount; ++i) {
            ElevatorController controller = new ElevatorController(i);
            controllers.add(controller);
        }
        
        scheduler = new ThroughputScheduler(); // ThroughputScheduler 객체를 생성함
    }
    
    void requestElevator(int destination, Direction direction) {
        // ThroughputScheduler를 이용해 엘리베이터를 선택함
        int selectedElevator = scheduler.selectElevator(this, destination, direction);
        
        // 선택된 엘리베이터를 이동시킴
        controllers.get(selectedElevator).gotoFloor(destination);
    }
}

public class ElevatorController {
    private int id;       // 엘리베이터 ID
    private int curFloor; // 현재 층
    
    public ElevatorController(int id) {
        this.id = id;
        curFloor = 1;
    }
    
    public void gotoFloor(int destination) {
        System.out.println(&quot;Elevator [&quot; + id + &quot;] Floor: &quot; + curFloor);
        
        // 현재 층 갱신, 즉 주어진 목적지 층 (destination) 으로 엘리베이터가 이동함
        curFloor = destination;
        System.out.println(&quot; ==&amp;gt; &quot; + curFloor);
    }
}

public class ThroughputScheduler {
    public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
        return 0; // 임의로 선택함
    }
}&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;h2 data-ke-size=&quot;size26&quot;&gt;문제점&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) 현재 ElevatorManager 클래스는 ThroughputScheduler 클래스를 이용한다. 만약 다른 스케줄링 전략을 사용해야 한다면?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우선 다른 스케줄링인 대기 시간을 최소화하는 전략을 수행하기 위한 스케줄링 클래스가 필요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1637395487572&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ElevatorManager {
    private List&amp;lt;ElevatorController&amp;gt; controllers;
    
    public ElevatorManager(int controllerCount) {
        controllers = new ArrayList&amp;lt;ElevatorController&amp;gt;(controllerCount);
        for (int i = 0; i &amp;lt; controllerCount; ++i) {
            ElevatorController controller = new ElevatorController(i + 1);
            controllers.add(controller);
        }
    }
    
    void requestElevator(int destination, Direction direction) {
        ElevatorScheduler scheduler;
        
        // 0..23
        int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
        
        if (hour &amp;lt; 12) // 오전에는 ResponseTimeScheduler를 이용함
            scheduler = new ResponseTimeScheduler();
        else
            scheduler = new ThroughputScheduler();
            
        int selectedElevator = scheduler.selectElevator(this, destination, direction);
        controllers.get(selectedElevator).gotoFloor(destination);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;888&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PwBSj/btrlGJXE4aR/aDJpdvhUs0UqcokaPIafT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PwBSj/btrlGJXE4aR/aDJpdvhUs0UqcokaPIafT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PwBSj/btrlGJXE4aR/aDJpdvhUs0UqcokaPIafT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPwBSj%2FbtrlGJXE4aR%2FaDJpdvhUs0UqcokaPIafT0%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;1674&quot; height=&quot;888&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;888&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;스트래티지 패턴을 사용하게 되면서 ElevatorManager 클래스는 ThroughputScheduler 또는 ResponseTimeScheduler 중 한 클래스를 동적으로 선택할 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그러나 ElevatorManager 클래스는 엘리베이터 스케줄링 전략이 변경될 때 requestElevator 메서드도 수정되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;결국, 엘리베이터 스케줄링 전략이 추가되거나 동적 스케줄링 방식으로 전략을 선택하도록 변경되면 아래 사항들이 수행되어야 하고 이는 바람직하지 않다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 스케줄링 전략을 지원하는 구체적인 클래스를 생성해야 한다.&lt;/li&gt;
&lt;li&gt;requestElevator 메서드도 수정해야 한다.&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;h2 data-ke-size=&quot;size26&quot;&gt;해결책&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;주어진 기능을 실제로 제공하는 적절한 클래스 생성 작업을 별도의 클래스 / 메서드로 분리시키는 것이 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 엘리베이터 스케줄링 전략에 일치하는 클래스를 생성하는 코드를 requestElevator 메서드에서 분리해 별도의 클래스 / 메서드를 정의&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;1678&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pOFzJ/btrlDB7BA5L/8BxA8iB1o8kfoQcnYNBcKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pOFzJ/btrlDB7BA5L/8BxA8iB1o8kfoQcnYNBcKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pOFzJ/btrlDB7BA5L/8BxA8iB1o8kfoQcnYNBcKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpOFzJ%2FbtrlDB7BA5L%2F8BxA8iB1o8kfoQcnYNBcKk%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;1678&quot; height=&quot;940&quot; data-origin-width=&quot;1678&quot; data-origin-height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1637396673381&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public enum SchedulingStrategyID { RESPONSE_TIME, THROUGHPUT, DYNAMIC }

public class SchedulerFactory {
    public static ElevatorScheduler getScheduler(SchedulingStrategyID strategyID) {
        ElevatorScheduler scheduler = null;
        
        switch (strategyID) {
            case RESPONSE_TIME: // 대기 시간 최소화 전략
                scheduler = new ResponseTimeScheduler();
                break;
            case THROUGHPUT:    // 처리량 최대화 전략
                scheduler = new ThroughputScheduler();
                break;
            case DYNAMIC:       // 오전에는 대기 시간 최소화 전략, 오후에는 처리량 최대화 전략
                int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
                if (hour &amp;lt; 12)  // 오전
                    scheduler = new ResponseTimeScheduler();
                else            // 오후
                    scheduler = new ThroughputScheduler();
                break;
        }
        
        return scheduler;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;SchedulerFactory 클래스의 getScheduler 메서드는 인자로 주어진 SchedulingStrategyID에 따라 적절한 스케줄링 객체를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 ElevatorManager 클래스의 requestElevator 메서드에서는 직접 스케줄링 클래스를 생성하는 대신 SchedulerFactory 클래스의 getScheduler 메서드를 호출하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1637396899921&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ElevatorManager {
    private List&amp;lt;ElevatorController&amp;gt; controllers;
    private SchedulingStrategyId strategyID;
    
    public Elevatormanager(int controllerCount, SchedulingStrategyId strategyID) {
        controllers = new ArrayList&amp;lt;&amp;gt;(controllerCount);
        
        for (int i = 0; i &amp;lt; controllerCount; ++i) {
            ElevatorController controller = new ElevatorController(i);
            controllers.add(controller);
        }
        
        this.strategyID = strategyID; // 스케줄링 전략을 설정
    }
    
    public void setStrategyID(SchedulingStrategyID strategyID) {
        this.strategyID = strategyID;
    }
    
    void requestElevator(int destination, Direction direction) {
        // 주어진 전략 ID에 해당하는 ElevatorScheduler 사용
        ElevatorScheduler scheduler = schedulerFactory.getScheduler(strategyID);
        
        int selectedElevator = scheduler.selectElevator(this, destination, direction);
        controllers.get(selectedElevator).gotoFloor(destination);
    }
}

public class Client {
    public static void main(String[] args) {
        ElevatorManager emWithResponseTimeScheduler =
            new ElevatorManager(2, SchedulingStrategyID.RESPONSE_TIME);
        emWithResponseTimeScheduler.requestElevator(10, Direction.UP);
        
        ElevatorManager emWithThroughputScheduler =
            new ElevatorManager(2, SchedulingStrategyID.THROUGHPUT);
        emWithThroughputScheduler.requestElevator(10, Direction.UP);
        
        ElevatorManager emWithDynamicScheduler =
            new ElevatorManager(2, SchedulingStrategyID.DYNAMIC);
        emWithDynamicScheduler.requestElevator(10, Direction.UP);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 동일한 스케줄링 방식을 사용한다면 스케줄링 객체를 여러 번 생성하지 않고 한 번 생성한 것을 계속 사용하는 것이 바람직할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 싱글톤 패턴을 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1638599965423&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SchedulerFactory {
    public static ElevatorScheduler getScheduler(SchedulingStrategyID strategyID) {
        ElevatorScheduler scheduler = null;
        
        switch (strategyID) {
            case RESPONSE_TIME:
                scheduler = ResponseTimeScheduler.getInstance();
                break;
            case THROUGHPUT:
                scheduler = ThroughputScheduler.getInstance();
                break;
            case DYNAMIC:
                int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
                if (hour &amp;lt; 12)
                    scheduler = ResponseTimeScheduler.getInstance();
                else
                    scheduler = ThroughputScheduler.getInstance();
                break;
        }
        
        return scheduler;
    }
}

// 싱글톤 패턴으로 구현한 ThroughputScheduler 클래스
public class ThroughputScheduler implements ElevatorScheduler {
    private static ElevatorScheduler scheduler;
    private ThroughputScheduler() { };
    
    public static ElevatorScheduler getInstance() {
        if (scheduler == null)
            scheduler = new ThroughputScheduler();
        
        return scheduler;
    }
    
    public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
        return 0;
    }
}

// 싱글톤 패턴으로 구현한 ResponseTimeScheduler 클래스
public class ResponseTimeScheduler implements ElevatorScheduler {
    private static ElevatorScheduler scheduler;
    private ResponseTimeScheduler() { };
    
    public static ElevatorScheduler getInstance() {
        if (scheduler == null)
            scheduler = new ResponseTimeScheduler();
        
        return scheduler;
    }
    
    public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
        return 0;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>디자인 패턴</category>
      <author>기내식은수박바</author>
      <guid isPermaLink="true">https://soobarkbar.tistory.com/241</guid>
      <comments>https://soobarkbar.tistory.com/241#entry241comment</comments>
      <pubDate>Sat, 4 Dec 2021 15:48:05 +0900</pubDate>
    </item>
    <item>
      <title>Decorator Pattern</title>
      <link>https://soobarkbar.tistory.com/240</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;데코레이터 패턴?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;기본 기능에 추가할 수 있는 기능의 종류가 많은 경우, 필요한 기능들만을 골라 조합하여 설계하는 디자인 패턴&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 기본 도로 표시 기능 (+ 차선 표시, 교통량 표시, 교차로 표시, 단속 카메라 표시)&lt;/li&gt;
&lt;li&gt;4가지 추가 기능이 있을 때, 이 들의 조합은 총 15가지가 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데코레이터 패턴을 사용하면 이를 4가지 Decorator 클래스만 구현하여 객체 형태로 조합하여 추가 기능의 조합을 구현할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;등장 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;네비게이션에 아래와 같은 기능이 있다고 해보자. 그리고 이를 클래스 다이어그램과 코드로 표현하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 기능 : 도로를 간단한 선으로 표시&lt;/li&gt;
&lt;li&gt;추가 기능 : 도로의 차선을 표시하는 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;474&quot; width=&quot;250&quot; height=&quot;274&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bURDzx/btrhqgefhnt/qA3jmOlC1BMLKlVZ9K3Ya0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bURDzx/btrhqgefhnt/qA3jmOlC1BMLKlVZ9K3Ya0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bURDzx/btrhqgefhnt/qA3jmOlC1BMLKlVZ9K3Ya0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbURDzx%2Fbtrhqgefhnt%2FqA3jmOlC1BMLKlVZ9K3Ya0%2Fimg.png&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;474&quot; width=&quot;250&quot; height=&quot;274&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1633926662059&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본 도로 표시 클래스
public class RoadDisplay {
    public void draw() {
        System.out.println(&quot;기본 도로 표시&quot;);
    }
}

// 기본 도로 표시 + 차선 표시 클래스
public class RoadDisplayWithLane extends RoadDisplay {
    public void draw() {
        super.draw();
        drawLane();
    }
    
    private void drawLane() {
        System.out.println(&quot;차선 표시&quot;);
    }
}

public class Client {
    public static void Main(String[] args) {
        RoadDisplay road = new RoadDisplay();
        road.draw(); // 기본 도로만 표시
        
        RoadDisplayWithLane roadWithLane = new RoadDisplayWithLane();
        roadWithLane.draw(); // 기본 도로 + 차선 표시
    }
}&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;h2 data-ke-size=&quot;size26&quot;&gt;문제점&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) 또 다른 도로 표시 기능을 추가할 경우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;교통량을 표시하는 클래스를 만들기 위해 RoadDisplayWithLane 클래스와 마찬가지로 RoadDisplay 클래스를 상속받아 RoadDisplayWithTraffic 클래스를 만든다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;494&quot; width=&quot;450&quot; height=&quot;258&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvcWyS/btrhfy1OMS8/4jrzaQ89JkLT3eLvg1VEV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvcWyS/btrhfy1OMS8/4jrzaQ89JkLT3eLvg1VEV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvcWyS/btrhfy1OMS8/4jrzaQ89JkLT3eLvg1VEV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvcWyS%2Fbtrhfy1OMS8%2F4jrzaQ89JkLT3eLvg1VEV1%2Fimg.png&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;494&quot; width=&quot;450&quot; height=&quot;258&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1633927074302&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본 도로 표시 + 교통량 표시 클래스
public class RoadDisplayWithTraffic extends RoadDisplay {
    public void draw() {
        super.draw();
        drawTraffic();
    }
    
    private void drawTraffic() {
        System.out.println(&quot;교통량 표시&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) 추가 기능을 조합해야 하는 경우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;기본 기능 클래스의 하위 클래스로 추가 기능을 만드는 것은 적절할 수도 있지만, &lt;b&gt;기능의 다양한 조합&lt;/b&gt;을 고려할 경우에 상속을 통한 기능의 확장은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;각 기능별로 클래스를 추가해야 한다는 단점&lt;/b&gt;&lt;/span&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;기본 기능에 추가로 사용할 수 있는 기능이 3가지 (차선 표시, 교통량 표시, 교차로 표시) 가 있다고 했을 때, 아래 표와 같이 8가지 조합이 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;638&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWbYgp/btrhfzGnI3r/27jc1mA43WSjnsrXykFvPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWbYgp/btrhfzGnI3r/27jc1mA43WSjnsrXykFvPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWbYgp/btrhfzGnI3r/27jc1mA43WSjnsrXykFvPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWbYgp%2FbtrhfzGnI3r%2F27jc1mA43WSjnsrXykFvPk%2Fimg.png&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;638&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이를 상속을 통해 설계한다면 각 조합별로 하위 클래스를 구현해야 한다. 아래는 추가 기능의 조합을 설계한 모습이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;874&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biXJf6/btrhqgZCU37/KXphbbyDPtlqNW4Dkek0p0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biXJf6/btrhqgZCU37/KXphbbyDPtlqNW4Dkek0p0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biXJf6/btrhqgZCU37/KXphbbyDPtlqNW4Dkek0p0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiXJf6%2FbtrhqgZCU37%2FKXphbbyDPtlqNW4Dkek0p0%2Fimg.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;874&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결책&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;상속을 이용한 기능 추가 방법을 설명했지만, 이 방법은 추가 기능이 늘어날때마다 기능의 조합별로 하위 클래스를 구현해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;조합 수가 늘어나는 문제를 해결하기 위해서는 각 추가 기능별로 개별 클래스를 설계하고, &lt;b&gt;기능을 조합할 때 각 클래스의 &lt;span style=&quot;color: #ee2323;&quot;&gt;객체 조합&lt;/span&gt;을 사용한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;736&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RBtCf/btrhd6K7RbX/TcILGiGJ2d9FHQpNZjVkC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RBtCf/btrhd6K7RbX/TcILGiGJ2d9FHQpNZjVkC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RBtCf/btrhd6K7RbX/TcILGiGJ2d9FHQpNZjVkC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRBtCf%2Fbtrhd6K7RbX%2FTcILGiGJ2d9FHQpNZjVkC1%2Fimg.png&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;736&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1633929613426&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public abstract class Display {
    public abstract void draw();
}

// 기본 도로 표시 클래스
public class RoadDisplay extends Display {
    public void draw() {
        System.out.println(&quot;기본 도로 표시&quot;);
    }
}

// 다양한 추가 기능에 대한 공통 클래스
public abstract class DisplayDecorator extends Display {
    private Display decoratedDisplay;
    
    public DisplayDecorator(Display decoratedDisplay) {
        this.decoratedDisplay = decoratedDisplay;
    }
    
    public void draw() {
        this.decoratedDisplay.draw();
    }
}

// 차선 표시 클래스
public class LaneDecorator extends DisplayDecorator {
    public LaneDecorator(Display displayDecorator) {
        super(displayDecorator);
    }
    
    public void draw() {
        super.draw();
        drawLane();
    }
    
    private void drawLane() {
        System.out.println(&quot;\t차선 표시&quot;);
    }
}

// 교통량 표시 클래스
public class TrafficDecorator extends DisplayDecorator {
    public TrafficDecorator(Display displayDecorator) {
        super(displayDecorator);
    }
    
    public void draw() {
        super.draw();
        drawTraffic();
    }
    
    private void drawTraffic() {
        System.out.println(&quot;\t교통량 표시&quot;);
    }
}&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;도로를 표시하는 기본 기능만 필요하다면 RoadDisplay 객체만을 이용하면 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;차선 표시 기능이 필요하다면 RoadDisplay + LandDecorator&lt;/li&gt;
&lt;li&gt;교통량 표시 기능이 필요하다면 RoadDisplay + TrafficDecorator&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1633929905558&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Client {
    public static void main(String[] args) {
        Display road = new RoadDisplay();
        road.draw(); // 기본 도로 표시
        
        Display roadWithLane = new RoadDisplay(new LaneDecorator());
        roadWithLane.draw(); // 기본 도로 표시 + 차선 표시
        
        Display roadWithTraffic = new RoadDisplay(new TrafficDecorator());
        roadWithTraffic.draw(); // 기본 도로 표시 + 교통량 표시
    }
}&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;road, roadWithLane, roadWithTraffic 객체의 접근 모두 Display 클래스를 통해 이루어진다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 어떠한 기능을 추가하던 상관 없이 Client 클래스는 Display 클래스만을 이용하여 일관성 있는 방식으로 도로 정보를 표시할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;교차로를 표시하는 기능을 추가해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;954&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2jKrk/btrhgk3iHQU/oOPmrzlgazhisVjcyHUxoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2jKrk/btrhgk3iHQU/oOPmrzlgazhisVjcyHUxoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2jKrk/btrhgk3iHQU/oOPmrzlgazhisVjcyHUxoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2jKrk%2Fbtrhgk3iHQU%2FoOPmrzlgazhisVjcyHUxoK%2Fimg.png&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;954&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1633930361439&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 교차로 표시 클래스
public class CrossingDecorator extends DisplayDecorator {
    public CrossingDecorator(Display displayDecorator) {
        super(displayDecorator);
    }
    
    public void draw() {
        super.draw();
        drawCrossing();
    }
    
    private void drawCrossing() {
        System.out.println(&quot;\t교차로 표시&quot;);
    }
}

public class Client {
    public static void main(String[] args) {
        Display roadWithLaneTrafficCrossing = new RoadDisplay(
            new LaneDecorator(new TrafficDecorator(new CrossingDecorator()))
        );
        roadWithLaneTrafficCrossing.draw(); // 기본 도로 표시 + 차선 표시 + 교통량 표시 + 교차로 표시
    }
}&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;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도서 'UML과 GoF 디자인 패턴 핵심 10가지로 배우는 JAVA 객체 지향 디자인 패턴'&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>디자인 패턴</category>
      <author>기내식은수박바</author>
      <guid isPermaLink="true">https://soobarkbar.tistory.com/240</guid>
      <comments>https://soobarkbar.tistory.com/240#entry240comment</comments>
      <pubDate>Mon, 11 Oct 2021 14:38:46 +0900</pubDate>
    </item>
    <item>
      <title>정렬 기법 비교</title>
      <link>https://soobarkbar.tistory.com/239</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;정렬 기법 비교&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stable : 동일한 값에 대해 순서가 뒤바뀌지 않고 정렬 (먼저 등장한 동일한 값이 정렬 후에도 먼저 등장)&lt;/li&gt;
&lt;li&gt;In-Place : 새로운 배열을 만들 필요 없이 입력 배열 내부에서 정렬&lt;/li&gt;
&lt;li&gt;Comparison : 배열 내 요소값들을 서로 비교하여 정렬&lt;/li&gt;
&lt;li&gt;Bucket 정렬의 경우, 각 Bucket에 대해 어떤 정렬 알고리즘을 사용하느냐에 따라 Comparison 유무가 달라질 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 200px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 12.5%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Algorithm&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Stable&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;In-Place&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Comparison&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;최선 (Best)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;평균 (Average)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;최악 (Worst)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;Bubble&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N^2)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N^2)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N^2)$&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;Selection&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N^2)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N^2)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N^2)$&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;Insertion&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N^2)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N^2)$&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;Shell&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N^2)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N^2)$&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;Merge&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;X&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(NlogN)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(NlogN)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(NlogN)$&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;Heap&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;X&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(NlogN)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(NlogN)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(NlogN)$&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;Quick&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;X&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(NlogN)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(NlogN)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N^2)$&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;Counting&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;X&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;X&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N)$&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;Radix&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;X&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;X&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N)$&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;Bucket&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;X&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;?&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N)$&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$O(N)$&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/Sort</category>
      <author>기내식은수박바</author>
      <guid isPermaLink="true">https://soobarkbar.tistory.com/239</guid>
      <comments>https://soobarkbar.tistory.com/239#entry239comment</comments>
      <pubDate>Sat, 2 Oct 2021 16:47:48 +0900</pubDate>
    </item>
  </channel>
</rss>