<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Program Development</title>
    <link>https://jodu.tistory.com/</link>
    <description>aq3aq4@gmail.com
틀린부분 댓글이나 메일로 보내주시면 수정하겠습니다.</description>
    <language>ko</language>
    <pubDate>Sun, 28 Jun 2026 06:56:31 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Jodu</managingEditor>
    <image>
      <title>Program Development</title>
      <url>https://tistory1.daumcdn.net/tistory/2332308/attach/e034b7f15ddf4896adb69fdb1d11b7ba</url>
      <link>https://jodu.tistory.com</link>
    </image>
    <item>
      <title>MapStruct란?</title>
      <link>https://jodu.tistory.com/50</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring을 사용을 하든 안하든 우리는 프로그램을 만들어가면서 Entity &amp;rarr; Dto, Dto &amp;rarr; Entity 변환을 많이 사용하게 되는데 다음과 같은 행위를 객체 Mapping이라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 객체 Mapping 행위를 손쉽게 할 수 있도록 이미 많은 객체 Mapping라이브러리가 존재하며, 종류는 Jackson의 ObjectMapper, ObjectUtils, JMapper등 여러개가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이 많은 것들 중에서 어떤 것을 써야할까?? &lt;b&gt;개발의 편의성을 고려한다면 ObjectMapper, ObjectUtils등 별도의 설정이나 코딩이 없는 것을 사용하는게 편할 것이다. 하지만 다음 &lt;a href=&quot;https://www.baeldung.com/java-performance-mapping-frameworks&quot;&gt;링크&lt;/a&gt;를 보면 알 수 있듯이 MapStruct와 JMapper 두개의 퍼포먼스가 빠른 것을 확인 할 수 있다. 해당 글을 읽게 되는 사람들의 99프로가 서버 개발자일 거고 서버 개발자는 서버의 리소스를 효율적으로 사용&lt;/b&gt;해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번 글에서는 MapStruct에 대해서 간략하게 알아보는 내용을 준비 했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MapStruct란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mapstruct.org/documentation/stable/reference/pdf/mapstruct-reference-guide.pdf&quot;&gt;공식문서&lt;/a&gt;에서는 다음과 같이 MapStruct를 소개하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MapStruct는 타입에 안전한 Bean 맵핑 클래스를 생성하기 위한 자바 annotation processing이라고 설명하고 있다. 즉 Lombok, QueryDSL처럼 빌드나 컴파일을 통해 맵핑 클래스를 만들어주는 라이브러리라고 생각하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Annotation Processing Tool(APT) apt는 어노테이션 처리를 위한 명령라인 유틸이다. apt는 reflective API와 프로그램 주석 처리를 위한 지원 인프라가 포함되어있다. 여기서 reflective API들은 빌드 타임, 소스 기반, 프로그램 구조의 읽기 전용 뷰를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;APT 동작과정&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 소스 코드 및 기타 파일을 생성할 수 잇는 주석 프로세서를 실행&lt;/li&gt;
&lt;li&gt;원본 파일과 생성된 소스 파일을 모두 컴파일&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 흔히 아는 QueryDSL, JPA, Lomobk 라이브러리가 APT에 의해 동작하게 되는 라이브러리이다. 해당 &lt;a href=&quot;https://www.baeldung.com/java-annotation-processing-builder&quot;&gt;링크를&lt;/a&gt; 보면 어떻게 APT를 사용하는지 알 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;동작 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MapStruct는 ObjectMapper와 ObjectUtils처럼 그냥 단순하게 객체 Mapping을 해주지 않는다. 기본적으로 매핑에 필요한 매핑 방법을 선언하는 맵퍼 인터페이스를 정의해줘야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵퍼 인터페이스를 정의하면 컴파일하는 동안 MapStruct는 이 인터페이스의 구현을 생성하고, 이 구현체 내용에 맵핑에 관련된 로직이 생성되어 있어 객체 맵핑이 발생되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MapStruct에 의해 구현 된 구현체의 내부를 보게 되면 setter를 이용해서 객체 맵핑을 하는 것을 볼 수 있는데 이 내용은 추후 정리에서 상세하게 확인해보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 MapSturct가 빠른 이유를 알 수 있다. ObjectMapper나 ObjectUtils는 내부에서 맵핑 대상이 되는 클래스에 리플렉션을 사용해서 객체 맵핑을 진행하는데, 이 리플렉션이 엄청나게 퍼포먼스가 떨어지는 기능이다. 하지만 MapStruct는 내부 구현체에서 setter를 이용해서 객체를 맵핑하기 때문에 퍼포먼스가 좋은 것이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MapStruct를 사용하면 얻는 이점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에서는 3가지 이점을 들고 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리플렉션 대신 일반 메서드 호출을 사용하여 빠른 실행&lt;/li&gt;
&lt;li&gt;컴파일 타임 유형 안정성 : 서로 매핑되는 개체와 속성만 매핑 할 수 있다. 이 말은 주문 엔터티를 고객 DTO 등에 실수로 맵핑하는 실수를 방지 해준다는 뜻인데, 그 이유는 맵퍼 인터페이스에 정의 한 방식으로만 맵핑이 진행 되기 때문이다.&lt;/li&gt;
&lt;li&gt;다음과 같은 경우 빌드 시 오류 보고서 지우기
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매핑이 불완전함&lt;/li&gt;
&lt;li&gt;매핑이 올바르지 않음&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MapStruct가 무엇이며 annotation processing에 의해 구현체까지 만들어진다는 것과 왜 성능이 좋은지에 대해서 간랸하게 알아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 정리에서는 MapStruct를 설정하는 방법 및 사용법에 대해서 정리해보자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mapstruct.org/documentation/stable/reference/pdf/mapstruct-reference-guide.pdf&quot;&gt;https://mapstruct.org/documentation/stable/reference/pdf/mapstruct-reference-guide.pdf&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/java-performance-mapping-frameworks&quot;&gt;https://www.baeldung.com/java-performance-mapping-frameworks&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/java-annotation-processing-builder&quot;&gt;https://www.baeldung.com/java-annotation-processing-builder&lt;/a&gt;&lt;/p&gt;</description>
      <category>Java</category>
      <author>Jodu</author>
      <guid isPermaLink="true">https://jodu.tistory.com/50</guid>
      <comments>https://jodu.tistory.com/50#entry50comment</comments>
      <pubDate>Sun, 1 Aug 2021 19:40:22 +0900</pubDate>
    </item>
    <item>
      <title>HttpServletRequest의 getInputStream 사용시 주의 사항</title>
      <link>https://jodu.tistory.com/49</link>
      <description>&lt;p style=&quot;font-size: 1.25em;&quot;&gt;얼마전 회사에서 개발한 Spring으로 구현 된 REST API, Apache web server에서 php로 돌아가는 서비스, expressJS로 돌아가는 서비스, Netty로 동작하고 있는 실시간 Push 서버에 동일한 보안 로직을 적용하기 위한 프로젝트를 진행하였다. 기존에는 각각의 보안 정책을 가지고 있어서 관리하기가 힘들고 또 여러개의 보안 프레임워크와 독자적으로 개발된 보안 로직을 관리 해야 했다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.25em;&quot;&gt;서비스 되는 서비스들의 크기가 크지 않을 때는 이러한 방법이 편리할 수도 (?) 있겠지만, 이미 우리는 상당히 꽤 많은 프로젝트 들이 생겨 버려서 더 이상은 프로젝트 별 보안 로직을 관리하기가 힘들어 졌다. 그래서 프로젝트 별로 동일하게 적용되는 보안 정책과 개별 정책들을 모아서 하나의 보안 서버를 만들었고, Tomcat기반에서 돌아가는 프로젝트를 위한 보안체킹을 위해서 보안 필터를 라이브러리를 만들기 위해서 개발 하던 중 HttpServletRequest의 getInputStream을 Servlet의 Filter에서 사용해버리면 다른 필터나 Controller에서 해당 Request에 대해서 다시는 getInputStream을 호출 해서 값을 가져 갈 수 없다는 것을 알았다.... 그래서 이번 포스팅에서는 Servlet Filter에서 getInputStream을 불러도 이후에 또 다시 부를 수 있는 방법에 대해서 설명한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.25em;&quot;&gt;방법은 간단했다. HttpServletRequestWrapper를 상속받아서 정의 해주면 된다. 먼저 소스를 보자.&lt;/p&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
  package com.hiball.client.filter;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class HiBallHttpServletRequestWrapper extends HttpServletRequestWrapper {
	private final String body;
	
	public HiBallHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
		//So that other request method behave just like before
        super(request);
         
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) &amp;gt; 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append(&quot;&quot;);
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    throw ex;
                }
            }
        }
        //Store request pody content in 'body' variable
        body = stringBuilder.toString();
	}
	
	@Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }
 
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
 
    //Use this method to read the request body N times
    public String getBody() {
        return this.body;
    }
	
}
&lt;/pre&gt;
&lt;p style=&quot;font-size: 1.25em;&quot;&gt;HttpServletRequestWrapper를 상속하게 되면 먼저 생성자를 만들게 하는데 해당 생성자에서 구현 할 내용은 InputStream을 읽어와 전역변수로 있는 body라는 변수에 읽어온 내용을 저장해 준다. 만약 여기서만 끝내 버린다면 이후에 getInputStream을 사용했을 경우 참조 할 수 없다는 에러를 똑같이 보게 될것이다. 그래서 getInputStream(): ServletInputStream, getReader(): BufferedReader 두개의 메소드를 Override를 해주게 되고 getInputStream에서는 전역변수 body에 저장된 내용을 가지고 InputStream을 다시 만들어 주게 된다.&amp;nbsp;&lt;br /&gt;또한 getReader에서 BufferedReader를 다시 생성하게 구현을 해주게 되면 Filter에서 getInputStream()을 호출 하더라도 필터를 지나 이후의 필터라든지 Spring의 Controller에서 HttpServletRequest의 InputStream을 참조 할 수 있게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java</category>
      <author>Jodu</author>
      <guid isPermaLink="true">https://jodu.tistory.com/49</guid>
      <comments>https://jodu.tistory.com/49#entry49comment</comments>
      <pubDate>Thu, 28 Mar 2019 01:22:06 +0900</pubDate>
    </item>
    <item>
      <title>IndexedDB 사용시 주의 사항</title>
      <link>https://jodu.tistory.com/48</link>
      <description>&lt;p&gt;&lt;span style=&quot;font-size: 18pt;&quot;&gt;&lt;b&gt;[서론]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;이번 개발을 진행하면서 IndexedDB를 사용하게 되었다. 그 이유는 서버로부터 많은 데이터를 받아오게 되는 우리 서비스 환경상 계속해서 서버로 부터 많은 데이터를 계속해서 가져가면 조회 속도에 문제가 발생하여 이를 개선하고자 IndexedDB를 사용하게 되었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;이번 포스팅에서는 IndexedDB를 사용하면서 내가 실수 했던 두가지에 대해서 포스팅 할 것이다. 전체적인 소스코드는 별도로 공유하지 않습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 18pt;&quot;&gt;&lt;b&gt;[문제점]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;1. indexedDB에서 데이터를 넣고 조회하는 작업이 모두 비동기라는 점&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;2. getAll() 메소드가 많은 데이터를 호출 할 때는 동작하지 않았던 점&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;위 두가지가 내가 실수 했던 요소 두가지이며 어떻게 해결 했는지에&amp;nbsp;대해서 공유한다. getAll()이 동작하지 않는 부분에서는 많이 시간을 허비 하였다....&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 18pt;&quot;&gt;&lt;b&gt;[IndexedDB 비동기 동작]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;IndexedDB는 모든 동작이 비동기 호출이다. 사용하고자 하는 DB를 오픈하면 오픈 되었다는 onsuccess 메소드 안에서 동작할 코드를 작성하거나 CallBack함수를 실행 하여야 한다.&amp;nbsp;&amp;nbsp;이 부분은 &lt;/span&gt;&lt;b&gt;&lt;u&gt;&lt;a class=&quot;tx-link&quot; href=&quot;https://developer.mozilla.org/ko/docs/IndexedDB/Basic_Concepts_Behind_IndexedDB&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #0900ff; font-size: 12pt;&quot;&gt;Document&lt;/span&gt;&lt;/a&gt;&lt;/u&gt;&lt;/b&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;에 명시되어 있고 확인을 한 부분이 였다. 하지만 서버로 부터 데이터를 받아오고 저장을 한 뒤 저장된 데이터로부터 특정 조건의 데이터를 조회하는 소스코드를 작성하는데서 실수를 하였다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;먼저 데이터를 저장하는 부분의 초기 소스코드를 보자!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;brush:javascript&quot;&gt;&lt;code&gt;this.saveData = function(datas) {
        var openDB = openDBFunc();
        openDB.onsuccess = function(event) {
            var db = getIndexedDB(event.target);

            for(var i=0; i&amp;lt;datas.length; i++) {
                var data = datas[i];
                db.store.put(data);
            }

            db.tx.oncomplete = function(event) {
                console.log(&quot;complete&quot;);
            }
        }
    },
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;여기서 비동기 호출이므로 onsuccess, opcomplete를 잘 사용하여 소스를 구현하였다. 이렇게만 보면 해당 메소드 자체로는 별 오류가 없는 소스가 되는데 다른 indexedDB 접근 메소드와 함께 쓰이면 문제를 발생 시킬 수 있다. 바로 아래에 해당 소스를 보도록 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;brush:javascript&quot;&gt;&lt;code&gt;this.getAllData = function(callback) {
        var openDB = openDBFunc();
        var getDatas = [];

        openDB.onsuccess = function(event) {
            console.log(&quot;DB Open&quot;);
            var db = getIndexedDB(event.target);
            //모든 데이터를 가져온다.
            var getData = db.store.getAll();
            getData.onsuccess = function(event) {
                var data = getData.result;
                getDatas.push(data);
            }

            db.tx.oncomplete = function() {
                callback(getDatas);
            }
        }
    }

function errorWork() {
     var receivedData = [...] //서버로 부터 가져온 10만건의 데이터
      saveData();
      getData();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;errorWork()를 보게 되면 saveData로 서버로 부터 가져온 10만건의 데이터인 receivedData를 저장을 하고 바로 다시 전체&amp;nbsp;데이터를 조회해 오는 동작을 생각하고 구현한 소스이다. 하지만 이 소스 코드는 다음과 같이 동작하게 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;559&quot; height=&quot;328&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;328&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9990AA3B5BF654F730?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9990AA3B5BF654F730?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9990AA3B5BF654F730&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9990AA3B5BF654F730&quot; width=&quot;559&quot; height=&quot;328&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;328&quot; data-ke-mobilestyle=&quot;widthContent&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&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;errorWork()&lt;/span&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;메소드가 동작하면 saveData를 호출 하게 되고&amp;nbsp;이 호출은 완료까지 꽤 긴 시간이 걸린다. IndexedDB는 특정 트랜잭션이 종료 되어야지만 Commit이 되므로 모든 데이터 저장이 완료 되어야지만 Commit이 된다.&amp;nbsp;&amp;nbsp;하지만 저장되고 난뒤 동작해야할 getAllData메소드가 saveData가 호출 되자 마자 곧바로 동작하게 되는것을 볼 수 있다. 여기서는 눈에 잘 보이도록 하기 위해 화살표의 간격이 존재 하지만 실제&amp;nbsp;동작에서는 화살표의 간격이 없는것처럼 동작하게 될 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;그래서 데이터를 가져와야 할 getAllData는 10만건의 데이터가 추가 되기전의 데이터를 가져오거나 혹은 에초에 비어있는 데이터를 가져오게 되는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;그렇다면 제대로 동작하도록 만들기 위해서는 어떻게 해야 할까? 해결방법에는 callback 함수를 사용하거나 Promise를 사용하는 방법 두가지가 있는데 여기서는 Promise를 사용해서 설명하도록 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;saveData메소드를 먼저 수정해보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:javascript&quot;&gt;&lt;code&gt;this.saveData = function(datas) {
        return new Promise(function(resolve, reject) {
            var openDB = openDBFunc();
            openDB.onsuccess = function(event) {
                var db = getIndexedDB(event.target);

                for(var i=0; i&amp;lt;datas.length; i++) {
                    var data = datas[i];
                    db.store.put(data);
                }

                db.tx.oncomplete = function(event) {
                    console.log(&quot;complete&quot;);
                    resolve(true);
                }
            }
        });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;Promise를 사용해서 onComplete되면 resolve함수로 불리언 값을 던져 준다. 여기서 resolve에 던져주는 값은 필요따라 달라 질 수 있다. Promise에 대해서 잘 모를 경우 &lt;/span&gt;&lt;u&gt;&lt;a class=&quot;tx-link&quot; href=&quot;https://joshua1988.github.io/web-development/javascript/promise-for-beginners/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #0900ff;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;여기&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/a&gt;&lt;/u&gt;&lt;b&gt;&lt;/b&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt; 를 참고하자. 이렇게 되면 errorWork1 메소드 또한 살짝 수정을 해주어야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:javascript arcade&quot;&gt;&lt;code&gt;function work() {
        saveData().then(result =&amp;gt; {
            getData();
        });
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;위 코드는 정상적으로 동작하게 되며 아래의 흐름으로 동작하게 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;547&quot; height=&quot;301&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;301&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99CAF1405BF659B637?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99CAF1405BF659B637?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99CAF1405BF659B637&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99CAF1405BF659B637&quot; width=&quot;547&quot; height=&quot;301&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;301&quot; data-ke-mobilestyle=&quot;widthContent&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 style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;이렇게 두가지 호출을 동시에 하면서 각 호출이 서로 상호 관계를 가진다면 callback함수를 사용하던지 Promise를 사용하여야 한다. 그렇지 않으면 예상한 결과와 전혀 다른 결과로 동작하게 되므로 주의하자!!&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 18pt;&quot;&gt;&lt;b&gt;[getAll 메소드의 대용량 데이터 호출 시 동작하지 않는 문제]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;100건, 1000건, 10000건을 IndexedDB에서 제공하는 getAll()메소드로 가져오면 정상적으로 잘 동작하게 된다. 하지만 나의 경우 5만건 이상?? 호출 하게 되니까 result값이 undefined으로 반환되는 것과 동시에 onsuccess의 호출이 발생하지 않아서 로직이 아예 동작하지 않는 문제를 접하게 되었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;이 문제는 getAll로 한번에 많은 데이터를 가져오면 메모리 관리 측면에서 문제가 발생하므로 막아 둔거 같은 느낌이 들었다. (찾아도 안나오던데... 저의&amp;nbsp;추측... 입니다.. )&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;그래서 이 문제는 openCursor로 하나하나 불러와서 처리하는 방식으로 해결 하였다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:javascript&quot;&gt;&lt;code&gt;this.getAllData = function(callback) {
        $('#jqxLoader').jqxLoader('open');
        var openDB = openDBFunc();

        openDB.onsuccess = function(event) {
            var datas = [];
            var db = getIndexedDB(event.target);
            var getData = db.store.openCursor();

            getData.onsuccess = function(event) {
                var cursor = event.target.result;
                if(cursor) {
                    datas.push(cursor.value);
                    cursor.continue();
                }
            }

            db.tx.oncomplete = function() {
                callback(gameRecords);
            }
        }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>FrontEnd</category>
      <author>Jodu</author>
      <guid isPermaLink="true">https://jodu.tistory.com/48</guid>
      <comments>https://jodu.tistory.com/48#entry48comment</comments>
      <pubDate>Fri, 23 Nov 2018 00:30:00 +0900</pubDate>
    </item>
    <item>
      <title>Deep Learning from Scratch - 밑바닥부터 시작하는 딥러닝 리뷰</title>
      <link>https://jodu.tistory.com/47</link>
      <description>&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 400px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99E35F3B5BF2CE7418&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99E35F3B5BF2CE7418&quot; width=&quot;400&quot; height=&quot;533&quot; filename=&quot;KakaoTalk_Photo_2018-11-19-23-52-11.jpg&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;지금까지&amp;nbsp;책들을 읽어오면서 책에 대한 글을 블로그로 남기는 것은 이번이 첨인거 같다. 앞으로는&amp;nbsp;읽고 좋은 책에 대해서는 느낀점을 적어 보려 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;우선 이 책은 딥러닝의 근본인 신경망에 대해서 설명해 간다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;신경망을 설명하기 전에 퍼셉트론이라는 이론 및 구현을&amp;nbsp;시작으로 하여 신경망에 대한&amp;nbsp;설명 및 구현을 하는데, 구현은 어렵지 않은 수준으로 잘 설명하고 있다. 여기서 중요한거는 이 책에서 구현한 소스코드 보다 어떻게 신경망이 동작하는지 이해 하는것이 중요한 포인트라는 느낌을 받았다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;신경망이 어떻게 학습되고 이 학습이란 개념이 어떤 것이며 어떻게 학습 되는지를 이해도 하지 못한채 책에서 제공하는 소스코드만 무작정 따라 친다고 도움이 될 것이 없다는 말이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;나는 이 책을 읽기 전부터도&amp;nbsp;딥러닝에 대해서 듣기는 많이 들었다. 그때마다 느꼈던 것이&amp;nbsp;입력층, 출력층 그리고 은닉층은 어떻게 정하며 학습은 어떻게 스스로 하는것인지에 대해서 항상 궁금했었는데 이 책을 읽고 인터넷에서 찾아서 정리를 하고 나니 이해가 되었다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;그리고 마지막 부분에는 책에서만 설명했던 수학적 접근(Relu, Sigmoid) 등의 문제점에 대한 해결책까지 설명되어 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;오랜만에 좋은 책을 읽어서 너무 기분이 좋다. (사실 이 책 산지는... 2017년 12월 29일... 맘 잡고 본지는 2018년 10월 22일... 반성하자 책을 사놓고 묵혀두다니....)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Book Review</category>
      <author>Jodu</author>
      <guid isPermaLink="true">https://jodu.tistory.com/47</guid>
      <comments>https://jodu.tistory.com/47#entry47comment</comments>
      <pubDate>Tue, 20 Nov 2018 00:04:46 +0900</pubDate>
    </item>
    <item>
      <title>Spring RestTemplate사용시 주의 사항</title>
      <link>https://jodu.tistory.com/46</link>
      <description>&lt;p&gt;&lt;span style=&quot;font-size: 18pt;&quot;&gt;&lt;b&gt;서론&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;최근 하나의&amp;nbsp;회사가 제공하는 서비스의 규모가 커지고 복잡해지면서 이 서비스를 지탱하기 위해서 큰 서비스를 여러 개의 작은 서비스로 나누고 개발, 배포 하는 Micro Service Architectur(이하: MSA) 가 유행하고 있다. 이렇게 MSA에서는 각 서비스들은 독립적이며 자신을 Restful&amp;nbsp;API로 자신을 노출 시킨다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;MSA에서 각 서비스들은 서로 독립적 이기는 하나 그렇지 못한 부분들이 존재 할 수 있다. 예를 들어 사용자의 인증 및 권한 부여 같은 경우 서비스들의 독립성을 위해 각 서비스 마다 구현 또는 라이브러리를 넣고 사용 할 수 있지만 이렇게 되면 개발 언어마다 사용자 인증, 권한부여 로직을 구현을 해야 하며 관리 또한 복잡하게 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;그래서 인증 및 권한 부여 서비스를 따로 만들고 Restful API명세를 통해 다른 서비스에서 사용자에 대한 권한을 체크 하는 방법을 사용 할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;이 방법을 사용하기 위해서 Spring에서는 RestTemplate을 사용할 수 있는데 이 RestTemplate을 사용시 주의해야 할 점 그리고 해결책에 대해서 설명한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 18pt;&quot;&gt;&lt;b&gt;Connection Issue&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;RestTemplate은 Spring 으로 구현된&amp;nbsp; A서버에서 Spring으로 구현된 B서버로 요청을 보낼때 사용하는 객체로서 상당히 무거운 객체이다. 그만큼 내부적으로 하는일이 많다는 말인데 이 RestTemplate은 요청을 할 때마다 Connection을 만들게 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;이렇게 만들어진 Connection들은 서버의 자원을 점차 차지하면서 서비스 배포 초기에는 멀쩡하던 서버가 아무 이유 없이 죽거나 속도가 급격히 느려 질 수 있거나 A서버가 B서버를 호출 하지 못하고 계속 대기하는 상태가 될 수 있다... 실제 서비스 되는 상태에서 이 상황이 발생하면... 생각만해도 끔찍하다!!!&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;그래서 RestTemplate을 사용하기 위해서는 Connection Pool을 직접 설정을 해주어야 한다. 여기서는 Connection Pool을 사용하기 위해서 Apache의 HttpClient를 사용할 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:java&quot;&gt;public abstract class AbstractCommonInterface implements HandlerInterceptor {
	private final String authServerUrl;
	RestTemplate rt = null;

	public AbstractCommonInterface(String authServerUrl) {
		this.authServerUrl = authServerUrl;
		rt = new RestTemplate(getHttpRequestFactory());
	}

	
	private HttpComponentsClientHttpRequestFactory getHttpRequestFactory() {
		HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
		cm.setMaxTotal(200);
		cm.setDefaultMaxPerRoute(20);
		HttpHost localhost = new HttpHost(authServerUrl);
		
		cm.setMaxPerRoute(new HttpRoute(localhost), 50);
		
		RequestConfig config = RequestConfig.custom()
				.setConnectTimeout(5000)
				.setConnectionRequestTimeout(5000)
				.setSocketTimeout(5000).build();
		
		CloseableHttpClient client = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(config).build();
		
		factory.setHttpClient(client);
		
		return factory;
	}
	
}


&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;먼저 전역변수로 RestTemplate을 사용한 것을 볼 수 있는데 RestTemplate은 Thread Safe하기 때문에 Condition Race가 발생하지 않기 때문에 전역변수로 사용해도 무방하다. 외부 라이브러리의 클래스를 전역 변수로 사용하기 전에 꼭 공식 문서에서 Thread Safe한지 확인하는 습관을 가지도록 하자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;약간 말이 중간으로 셋다. 꼭 저 부분을 나 스스로도 다시 한번 다짐하고 가고 싶어서 적어보았다. 여기서 중요한것은 getHttpRequestFactory()라는 메소드이다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;PoolingHttpClientConnectionManager 객체를 생성해서 setMaxTotal 메소드에 총 conneciton수를 지정할 수 있는데 여기서는 200개를 할당한 모습을 볼 수 있다. 예시라서 200개라는 커넥션 수를 무작정 부여 하였지만 &lt;/span&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;무작정 갯수를 정하는 것이 아니라 서버의 자원 상황, 요청 수를 계산하여 주기를 바란다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;다음으로는 setDefaultMaxPerRoute라는 메소드를 볼 수 있는데 처음에는 이 메소드가 무엇을 의미하는지 몰라서 한참을 찾았다. 이 메소드는 어떤 경로든 최대 연결수를 나타낸다. 예를 들어 http://localhost:8080/testA, http://localhost:8080/testB 두개의 경로에 대해서 별도로 설정이 없다면 최대 20개의 연결이 생성된다는 뜻이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;마지막으로 setMaxPerRoute라는 메소드에 대해서 설명하면 전달되는 경로에 대해서 최대 연결 갯수를 지정할 수 있다. 여기서는 50개를 지정해주었는데 이렇게 되면 이전에 설정한 20개의 연결이 아니라 저 해당 경로만 50개의 연결을 가질 수 있게 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;Tomcat Heap Size Issue&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;위와 같이 설정하고 서비스를 배포하고 테스트를 하는데 서버들간의 호출이 일어나면서 갑자기 heap 메모리 관련 오류가 발생하는 것을 확인 할 수 있다. 이것은 Connection이 생성되면서 허용 가능한 heap 메모리 크기를 넘어 섯기 때문이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;이렇게 되면 커넥션 수를 줄여주거나 Tomcat의 heap 메모리 크기를 늘려 주면 해결 할 수 있다.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 18pt;&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;RestTemplate이 요청마다 Connection을 만들어 낸다는 것을 알고 바로 Connection Pool을 만들어야지 생각했고 쉽게 적용가능했다. 하지만 문제는 두번째 Tomcat Heap Size Issue였는데 내가 메모리 설정 관련해서 잘못 알고 있어서 그랬다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;항상 이클립스 heap을 늘려주면 늘어나는 줄 알고 있었는데.... Tomcat설정을 따로 해줘야 했던것.... 참 부끄러웠던 순간.... 그래도 하나 깨달아서 행복!!!&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Spring</category>
      <author>Jodu</author>
      <guid isPermaLink="true">https://jodu.tistory.com/46</guid>
      <comments>https://jodu.tistory.com/46#entry46comment</comments>
      <pubDate>Tue, 6 Nov 2018 19:58:47 +0900</pubDate>
    </item>
    <item>
      <title>Bubble Sort - 버블 정렬</title>
      <link>https://jodu.tistory.com/43</link>
      <description>&lt;script type=&quot;text/javascript&quot; src=&quot;http://cdn.mathjax.org/mathjax/latest/MathJax.js&quot;&gt;
	MathJax.Hub.Config({
	extensions: [&quot;tex2jax.js&quot;,&quot;TeX/AMSmath.js&quot;,&quot;TeX/AMSsymbols.js&quot;],
	jax: [&quot;input/TeX&quot;, &quot;output/HTML-CSS&quot;],
	tex2jax: {
     		 inlineMath: [ ['$','$'], [&quot;\\(&quot;,&quot;\\)&quot;] ],
		 displayMath: [ ['$$','$$'], [&quot;\\[&quot;,&quot;\\]&quot;] ],
	},
	&quot;HTML-CSS&quot;: { availableFonts: [&quot;TeX&quot;] }
});
&lt;/script&gt; 
&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;소개&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;인접한 두 수를 비교하면서 대소 비교에 적합한 수를 뒤로 보내는 알고리즘이다. 평균적으로 시간 복잡도는&amp;nbsp; $O(n^2)$를 나타낸다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: square;&quot;&gt;&lt;li&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;현재 인덱스의 바로 다음번 인덱스와의 크기를 비교해서 조건에 맞다면 위치를 옮겨준다. 여기서 조건은 대소 비교의 조건이다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;비교하면서 한 번 순회를 끝내면 순회 시 제일 마지막 인덱스는 자연스럽게 제일 크거나 작은 값이다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;다음번 순회에서는 최종 인덱스 까지 순회할 필요 없다. 즉 순회마다 -1만큼 순회할 크기가 줄어 드는 것이다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;시간 복잡도 계산&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;전체 원소의 수가 $n$ 개라면 $(n-1) + (n-2) + ... + 2 + 1 = (n - 1) * n / 2 = (n^2-n)/2$의 값을 연산을 하게 될 것이다. 그래서 평균적인 시간 복잡도는 $O(n^2)$ 이 되는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;소스코드 - JAVA&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;pre class=&quot;brush:java&quot;&gt;public class BubbleSort {
	public static void main(String[] args) {
		int[] beforeSortingValues = ;
		// 버블정렬은 순회 할 때마다 순회 하는 크기가 줄어 든다. 
		// 그래서 데이터 전체 길이로 시작해서 하나 씩 줄어 드는 가장 바깥쪽 for루프.
		for(int i=beforeSortingValues.length-1; i&amp;gt;=0; i--) {
			//원소들 끼리 비교하는 for루프
			for(int j=0; j&amp;lt;i; j++) {
				int afterJIndex = j + 1;
				int value1 = beforeSortingValues[j];
				int value2 = beforeSortingValues[afterJIndex];
				
				//대소 비교 부등호 방향에 따라 정렬이 바뀐다. 
				if(value1 &amp;gt; value2) {
					int temp = value1;
					beforeSortingValues[j] = value2;
					beforeSortingValues[afterJIndex] = temp;
				}
			}
		}
		
		for(int value : beforeSortingValues) {
			System.out.println(value);
		}
	}
}
&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;참고 블로그&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;a href=&quot;http://bowbowbow.tistory.com/10&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;span style=&quot;color: rgb(0, 85, 255);&quot;&gt;http://bowbowbow.tistory.com/10&lt;/span&gt;&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <author>Jodu</author>
      <guid isPermaLink="true">https://jodu.tistory.com/43</guid>
      <comments>https://jodu.tistory.com/43#entry43comment</comments>
      <pubDate>Sat, 29 Sep 2018 09:37:57 +0900</pubDate>
    </item>
    <item>
      <title>Selection Sort - 선택 정렬</title>
      <link>https://jodu.tistory.com/44</link>
      <description>&lt;script type=&quot;text/javascript&quot; src=&quot;http://cdn.mathjax.org/mathjax/latest/MathJax.js&quot;&gt;
	MathJax.Hub.Config({
	extensions: [&quot;tex2jax.js&quot;,&quot;TeX/AMSmath.js&quot;,&quot;TeX/AMSsymbols.js&quot;],
	jax: [&quot;input/TeX&quot;, &quot;output/HTML-CSS&quot;],
	tex2jax: {
     		 inlineMath: [ ['$','$'], [&quot;\\(&quot;,&quot;\\)&quot;] ],
		 displayMath: [ ['$$','$$'], [&quot;\\[&quot;,&quot;\\]&quot;] ],
	},
	&quot;HTML-CSS&quot;: { availableFonts: [&quot;TeX&quot;] }
});
&lt;/script&gt; 
&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;소개&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;특정 인덱스 값의 값을 남은 값들을 순회 하면서 가장 크거나 작은 값으로 바꾸는 정렬 알고리즘으로, 메모리가 제한적인 경우 사용하면 성능 상 이점이 있는 알고리&lt;/span&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;즘이다.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;1. 주어진 데이터의 첫번째 인덱스의 값을 첫번째를 제외한 나머지 데이터 중에서 가장 크거나 작은 값으로 변경한다&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;2. 순회를 끝내면&amp;nbsp;인덱스 값을 증가 시켜서 나머지 데이터 중에서 가장 크거나 작은 값으로 변경한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;3. 위의 동작을 순회가 끝날때 까지 반복한다.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;시간복잡도&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;선택정렬 또한 순회를 할 때마다 -1씩 순회 할 데이터 양이 줄어 든다. 그러면 다음과 같은 수식을 얻을 수 있게 된다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size:12pt;&quot;&gt;$(n-1) + (n-2) + ... + 2 + 1 = (n - 1) * n / 2 = (n^2-n)/2$ 그래서 선택정렬의 시간 복잡도는&lt;/span&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;$O(n^2)$이다.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;소스코드&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:java&quot;&gt;public class SelectionSort {
	public static void main(String[] args) {
		int[] values = ;
		
		for(int i=0; i&amp;lt;values.length-1; i++) {
			int minIndex = i;
			for(int j=i+1; j&amp;lt;values.length; j++) {
				if(values[j] &amp;lt; values[minIndex]) {
					minIndex = j;
				}
			}
			
			int tmp = values[minIndex];
			values[minIndex] = values[i];
			values[i] = tmp;
		}
		
		for(int i=0; i&amp;lt;values.length; i++) {
			System.out.println(values[i]);
		}
	}
}
&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%84%A0%ED%83%9D_%EC%A0%95%EB%A0%AC&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;span style=&quot;color: rgb(0, 85, 255); font-size: 12pt;&quot;&gt;위키백과 - 선택정렬&lt;/span&gt;&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <author>Jodu</author>
      <guid isPermaLink="true">https://jodu.tistory.com/44</guid>
      <comments>https://jodu.tistory.com/44#entry44comment</comments>
      <pubDate>Sat, 29 Sep 2018 09:37:47 +0900</pubDate>
    </item>
    <item>
      <title>Greedy algorithm - 탐욕 알고리즘</title>
      <link>https://jodu.tistory.com/45</link>
      <description>&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;소개&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;최적해를 구하는 데에 사용되는 근사적인 방법, 여러 경우 중 하나를 결정해야 할 때마다 그 순간에 최적이라고 생각되는 것을 선택해나가는 방식이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;여기서 &lt;b&gt;&lt;span style=&quot;color: rgb(255, 0, 0);&quot;&gt;&lt;i&gt;중요한 것은 선택들을 계속 수집해서 최종적인 해답을 얻었을 경우 이 해답이 최적의 답이라는 보장은 없다.&amp;nbsp;&lt;/i&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;탐욕 알고리즘은 동적알고리즘이 지나치게 많은 일을 한다는 것에 착안되어 만들어진 알고리즘이다.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;적용이 잘되는 경우&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;1. Greedy Choice Property (탐욕 선택 조건)&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;앞의 선택이 이후 선택에 영향을 주지 않는 경우를 적용이 잘된다. 즉, 각 사건들이 서로 독립적일 때 잘 맞는다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;2. Optimal Substructure (최적 부분 구조 조건)&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;문제에 대한 최적해가 부분 문제에 대해서도 최적인 경우&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;대표적인 해결 문제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;1. 배낭 문제&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;2. 동전 문제&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;3. 활동 문제 등&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;소스코드&lt;/b&gt;&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;
&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:java&quot;&gt;import java.util.Arrays;

public class KnapackProblem {
	public static void main(String[] args) {
		int permitWeight = 20;
		int resultValue = 0;
		int[][] jewels = {
			// value, weight
			, , , 
		};
		
		//보석의 가치 별로 정렬
		Arrays.sort(jewels, (cur, prev) -&amp;gt; {
			int prevValue = (prev[0] / prev[1]);
			int curValue = (cur[0] / cur[1]);
			return prevValue - curValue;
		});
		
		for(int i=0; i&amp;lt;jewels.length; i++) {
			//1. 한도 무게가 남아 있고,
			if(permitWeight &amp;gt; 0) {
				int[] curJewel = jewels[i]; //현재 보석
				int v = curJewel[0]; //현재 보석의 가치
				int w = curJewel[1]; //현재 보석의 무게
				//1.1 보석의 무게가 한도 무게보다 작다면
				if(permitWeight &amp;gt;= w) {
					permitWeight -= w;
					resultValue += v;
				} else { //1.2 보석의 무게가 한도 무게보다 크면 다음번 보석으로 넘어간다. 
					continue;
				}
			} else {
				break;
			}
		}
		
		System.out.println(resultValue);
	}
}
&lt;/pre&gt;&lt;div&gt;&lt;font face=&quot;system-ui&quot;&gt;&lt;span style=&quot;font-size: 14px; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;font face=&quot;system-ui&quot;&gt;&lt;span style=&quot;font-size: 14px; white-space: pre-wrap;&quot;&gt;&lt;span style=&quot;font-size: 12pt; white-space: pre-wrap; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;우선 무분별하게 나열되어 있는 보석들을 무게당 가치의 비율로 정렬을 한다. 이렇게 되면 배낭의 무게가 허용할 때까지 순서대로 넣기만 하면 최적의 가치를 가진 배낭을 만들 수 있게 된다. &lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;문제점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;탐욕 알고리즘은 소개 부분에서 설명 한 것처럼 작은 사건들의 최적의 값을 모았을 경우 이 결과가 문제 전체에 대해서는 최적의 해가 되지 못한다고 하엿는데 위에서 작성한 소스 코드에서도 똑같이 발생한다. 예를 들어 배낭의 무게가 20으로 주어진다면 최고의 배낭 가치는 100이지만 탐욕 알고리즘의 결과는 70이라는 결과를 나타내게 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;이와 같이 탐욕 알고리즘은 문제에 대한 근사치를 구할 때 유용하며 정확한 값을 찾는 문제에서는 다소 위험 할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%ED%83%90%EC%9A%95_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;span style=&quot;color: rgb(0, 85, 255); font-size: 12pt;&quot;&gt;위키백과 - 탐욕알고리즘&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.zerocho.com/category/Algorithm/post/584ba5c9580277001862f188&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;span style=&quot;color: rgb(0, 85, 255); font-size: 12pt;&quot;&gt;https://www.zerocho.com/category/Algorithm/post/584ba5c9580277001862f188&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://m.blog.naver.com/PostView.nhn?blogId=twoa0&amp;amp;logNo=220984166738&amp;amp;proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;span style=&quot;color: rgb(0, 85, 255); font-size: 12pt;&quot;&gt;https://m.blog.naver.com/PostView.nhn?blogId=twoa0&amp;amp;logNo=220984166738&amp;amp;proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <author>Jodu</author>
      <guid isPermaLink="true">https://jodu.tistory.com/45</guid>
      <comments>https://jodu.tistory.com/45#entry45comment</comments>
      <pubDate>Sat, 29 Sep 2018 09:37:35 +0900</pubDate>
    </item>
    <item>
      <title>HashTable Algorithm 사용</title>
      <link>https://jodu.tistory.com/41</link>
      <description>&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;HashTable 사용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;저번 포스팅에서 HashTable을 구현 해보았다. 다소 많이 부족한 소스이지만 이번 포스팅에서는 해당 HashTable을 사용하여 알고리즘 문제를 하나 풀어보려고 한다. 본 문제는&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class=&quot;tx-link&quot; href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42576&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 12pt; color: #0055ff;&quot;&gt;&lt;b&gt;https://programmers.co.kr/learn/courses/30/lessons/42576&lt;/b&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt; 해당 링크에서 풀어 볼 수 있다. 해당 문제는 해시로 풀어 낼 수 있어서 이전에 구현 했던 HashTable을 사용해 보았다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;해당 문제를 해시로 풀 수 있는 이유는 각 선수들의 이름을 키로 해쉬 테이블을 구성 한 뒤 찾으면서 찾은 선수들을 지워가면서 지워지지 않는 선수는 완주를 하지 못한 사람임이 보장되기 때문이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;여기서 솔루션 부분을 구현해야 했는데 솔루션 클래스의 내용은 아래와 같고, 나머지 HashTable 구현 내용은 이전 &lt;/span&gt;&lt;a class=&quot;tx-link&quot; href=&quot;http://jodu.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 12pt; color: #0055ff;&quot;&gt;&lt;b&gt;Hash&amp;amp;HashTable Algorithm 설명 및 구현&lt;/b&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt; 포스팅에서 구현한 것을 그대로 사용하였다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class=&quot;brush:java&quot;&gt;class Solution {
    public String solution(String[] participant, String[] completion) {
        String answer = &quot;&quot;;
        HashTable hashTable = new HashTable(100000);
        
        for(String com : completion) {
            hashTable.put(com, com);
        }
        
        for(String parti : participant) {
            if(!hashTable.remove(parti)) {
                answer = parti;
            }
        }
		
        return answer;
    }
}
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;최초에 해시&lt;/span&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;&amp;nbsp;테이블 크기를 정하여서 객체를 생성한 뒤 성공한 사람들의 배열 기준으로 해시테이블을 생성한다.&amp;nbsp; 그러고 참여자를 기준으로 해시테이블에서 참여자와 동일한 이름의 사람을 찾으면서 지워나가면서 지워지지 않은 사용자는 완주하지 못한 사람임으로 결과 값으로 리턴 되어진다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;결과 확인&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;960&quot; height=&quot;291&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;291&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9915AD4A5BA485F811?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9915AD4A5BA485F811?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9915AD4A5BA485F811&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9915AD4A5BA485F811&quot; width=&quot;960&quot; height=&quot;291&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;291&quot; data-ke-mobilestyle=&quot;widthContent&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&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;깔끔하게 성공하는 모습을 볼 수 있다. 속도 또한 상당히 빠른 것을 볼 수 있는데 해시 테이블의 장점이다. &lt;/span&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;물론 해당 문제는 다른 방법으로도 풀 수 있다. 다른 사람들 풀이를 보니까 정렬을 이용한 방법, 기존 자바에서 제공하는 HashTable을 사용한 경우 등 여러가지 있었지만 직접 만든 HashTable알고리즘을 테스트 해보고자 만든 HashTable을 사용하였다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <author>Jodu</author>
      <guid isPermaLink="true">https://jodu.tistory.com/41</guid>
      <comments>https://jodu.tistory.com/41#entry41comment</comments>
      <pubDate>Fri, 21 Sep 2018 14:50:04 +0900</pubDate>
    </item>
    <item>
      <title>Hash &amp;amp; HashTable Algorithm 설명 및 구현</title>
      <link>https://jodu.tistory.com/40</link>
      <description>&lt;p&gt;&lt;span style=&quot;font-size: 18pt;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;해쉬 알고리즘이란??&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;키 값을 입력 받아서 해쉬 테이블의 주소가 되는 값을 반환해주는 함수&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 18pt;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;해쉬 테이블이란??&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;Key &amp;amp; Value로 이루어진 테이블 형태의 자료 구조로서 Key값이 Hash 함수를&amp;nbsp;거쳐 나온 Hash 값을 테이블 상의 저장 주소로 사용한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;해쉬 값은 최대한 중복이 발생 되지 않는게 좋겠지만 현실적으로 불가능 하며 이것을 해결 하기 위한 방법들이 몇 가지 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;해쉬 테이블 &lt;/span&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;충돌 해결 방법&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;866&quot; height=&quot;400&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;399&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99215C345BA4769930?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99215C345BA4769930?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99215C345BA4769930&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99215C345BA4769930&quot; width=&quot;866&quot; height=&quot;400&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;399&quot; data-ke-mobilestyle=&quot;widthContent&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 style=&quot;text-align: center; clear: none; float: none; line-height: 1;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none; line-height: 1.8;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;위 그림&lt;/span&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;처럼 키 값은 다르지만 Hash함수를 거치고 난 뒤의 해쉬 값이 동일하여 충돌이 발생하는 경우에는 데이터를 안전하게 저장하고 관리하기 위해서 충돌에 대한 해결을 해주어햐 하며 2가지가 존재한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;Separate Chaining 방식&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;이 방법은 Linked List를 사용하는 방법이다. 인덱스에서는 데이터에 대한 참조 주소값만을 저장하고 충돌이 발생하는 인덱스의 경우 기존에 참조하고 있는 데이터에 충돌이 발생한 데이터를 연결 해주는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;text-align: center; clear: none; float: none; line-height: 1.8;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99BD1A465BA479511C&quot; width=&quot;500&quot; height=&quot;218&quot; /&gt;&lt;/li&gt;
&lt;li style=&quot;line-height: 1.8;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;Open Addressing 방식&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;이 방법은 Separate Chaining방식과는 다르게 추가적인 메모리를 사용하지 않고 기존에 정해진 Hash table의 배열값을 사용한다. 만약 충돌이 발생하게 된다면 충돌이 발생한 인덱스 다음 값을 참조하려 할 것이고, 이 값조차 비어있지 않다면 비어있는 공간을 찾기 위해서 계속해서 다음 번 인덱스 값을 참조 할 것이다. &lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/ol&gt;
&lt;div style=&quot;line-height: 1.8;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;line-height: 1.8;&quot;&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;해쉬 테이블 구현&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;line-height: 1.8;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;구현 클래스 항목으로는 HashTable, Bucket, Node가 존재한다. 먼저 Node 클래스의 구현을 살펴 보자.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;line-height: 1.8;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p&gt;&lt;pre class=&quot;brush:java&quot;&gt;class Node {
	public String key;
	public Object value;
	public Node afterNode;
	
	public Node(String key, Object value) {
		this.key = key;
		this.value = value;
	}
	
	public Node(Node afterNode) {
		this.afterNode = afterNode;
	}

	public Node getAfterNode() {
		return afterNode;
	}

	public void setAfterNode(Node afterNode) {
		this.afterNode = afterNode;
	}
}
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;Node클래스에서는 노드에 저장될 key와 value, 그리고 충돌에 의해서 해당 노드가 자신의 다음번 노드를 가르키기 위한 값을 관리한다. 그 외에는 별다른 로직이 들어가지는 않는다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;다음으로는 Bucket에 대한 구현을 살펴 보자. Bucket 또한 데이터를 저장하는 용도로만 사용하는 클래스이기 때문에 &lt;/span&gt;상당히 간단하다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class=&quot;brush:java&quot;&gt;class Bucket {
	public String key;
	public Node node;
	
	public Bucket(String key, Node node) {
		this.key = key;
		this.node = node;
	}
}
&lt;/pre&gt;&lt;/p&gt;
&lt;div&gt;&lt;span&gt;&lt;span style=&quot;font-size: 12px; white-space: pre-wrap;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span style=&quot;font-size: 12pt; white-space: pre-wrap;&quot;&gt;이제 실제 HashTable의 삽입, 삭제, 가져오기가 구현된 HashTable클래스를 살펴보도록 하자. &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;&lt;pre class=&quot;brush:java&quot;&gt;class HashTable {
	//우리가 흔히 생각하는 HashTable의 크기. 여기서는 그냥 10만으로 ~
	private int hashSize = 100000;
	private Bucket[] buckets = null;
	
	public HashTable(int hashSize) {
		this.hashSize = hashSize;
		this.buckets = new Bucket[this.hashSize];
	}
	
	
	/**
	 * 삽입하는 메소드
	 * @param key
	 * @param value
	 */
	public void put(String key, Object value) {
		//1. hash Index값을 가져온다. 
		int hashIndex = hashFunction(key);
		//2. 해당 인덱스에 맞는 bucket을 가져온다. 
		Bucket bucket = buckets[hashIndex];
		//3. key와 value를 저장하는 node를 생성한다. 
		Node newNode = new Node(key, value);
		
		//4. 충돌 체크(충돌 안함) - bucket이 존재 하지 않으므
		if(bucket == null) {
			buckets[hashIndex] = new Bucket(key, newNode);
		} else { //5. 충돌 체크(충돌 함) - bucket이 존재하므로 
			Node curNode = bucket.node;
			Node curAfterNode = curNode.afterNode;
			//현재노드의 다음번 노드가 비어있으면 추가
			if(curAfterNode == null) {
				curAfterNode = new Node(key, value);
				curNode.afterNode = curAfterNode;
			} else { //현재의 노드의 다음번 노드가 존재하면 해당 노드로 
				//bucket이 가지고 있던 기본 노드값을 새 노드로 바꿔주고
				bucket.node = newNode;
				//바꿔준 노드 값에 현재의 노드를 달아준다. 
				bucket.node.afterNode = curNode;
			}
		}
	}
	
	/**
	 * 지우는 메소드
	 * @param key
	 */
	public boolean remove(String key) {
		boolean remove = false;
		//1. hash함수로 부터 hash인덱스를 가져와서
		int hashIndex = hashFunction(key);
		//2. 해당 인덱스의 bucket을 가져온다. 
		Bucket bucket = buckets[hashIndex];
		//3. bucket이 존재 한다면
		if(bucket != null) {
			//4. bucket의 노드를 가져와서
			Node node = bucket.node;
		 	
			String nodeKey = node.key;
			
			//5. 키가 존재 하고 afterNode가 존재하면 
			if(key.equals(nodeKey) &amp;amp;&amp;amp; node.afterNode != null) {
				//5.1 bucket이 가르키는 노드에 afterNode로 바꿔서 저장한뒤.
				bucket.node = node.afterNode;
				//5.2 기존 노드는 null로 초기화 해서 없애 버린다.
				node = null; 
				remove = true;
			} else if(key.equals(nodeKey) &amp;amp;&amp;amp; node.afterNode == null) { //6. 키가 존재하고 afterNode가 존재하지 않는다면,
				//6.1 bucket을 초기화 해버린다. 
				buckets[hashIndex] = null;
				node = null; //기존 노드만 null로 초기화 한다.
				remove = true;
			} else if(!key.equals(nodeKey)) { //7. 키가 존재 하지 않는데
				while(true) {
					//7.1 afterNode가 존재한다면
					if(node.afterNode != null) {
						Node afterNode = node.afterNode;
						String afterNodeKey = afterNode.key;
						//7.2 키값이 존재 하고 afterNode가 존재 한다면, 
						if(key.equals(afterNodeKey) &amp;amp;&amp;amp; afterNode.afterNode != null) {
							//7.2.1 node의 afterNode값을 변환
							node.afterNode = afterNode.afterNode;
							afterNode = null;
							remove = true;
							//7.2.2 반복문 나간다.
							break;
						} else if(key.equals(afterNodeKey) &amp;amp;&amp;amp; afterNode.afterNode == null) { //7.3 키 값이 존재하고 afterNode값이 존재하지 않는다면.
							//7.3.1 node의 afternode를 비워버린다. 
							node.afterNode = null;
							remove = true;
							//7.3.2 반복문 나간다.
							break;
						} else {
							node = afterNode;
						}
					} else {
						//afterNode가 존재하지 않는다면 루프 탈출
						break;
					}
				}
			}
		}
		
		return remove;
	}

	/**
	 * 값을 가져오는 메소드
	 * @param key
	 * @return
	 */
	public Object get(String key) {
		Object chooseValue = null;
		//1. hash함수로 부터 hash인덱스를 가져와서
		int hashIndex = hashFunction(key);
		//2. 해당 hash인덱스가 가르키는 버켓을 가져온다.
		Bucket bucket = buckets[hashIndex];
		//3. Bucket이 존재 한다면, 
		if(bucket != null) {
			//3. 버켓이 가르키는 노드를 가져온 뒤
			Node node = bucket.node;
			String nodeKey = node.key;
			//4. 유효성 검사를 한다.
			//5. 노드의 키와 전달 받은 키 값이 값다면 노드의 값을 곧바
			if(key.equals(nodeKey)) {
				chooseValue = node.value;
			} else { //6. 노드 키와 전달 받은 키가 다르다면 탐색을 시작하기 위한 반복문 진입.
				while(true) {
					//7. 현재 노드의 다음번 노드를 가져와
					node = node.afterNode;
					//8. 가져온 다음번 노드가 존재한다면
					if(node.afterNode != null) {
						//8-1. 키 값을 비교해서 같다면
						if(key.equals(node.key)) {
							//8-2. chooseValue에 저장하
							chooseValue = node.value;
							//8-3. 반복문을 나간다.
							break;
						} 
					} else { // 9. 가져온 다음번 노드가 존재안한다면 곧바로 반복문을 나간다. 
						break;
					}
					
				}
			}
		}
		
		return chooseValue;
	}
	
	/**
	 * Hash함수
	 * @param key
	 * @return
	 */
	private int hashFunction(String key) {
		char[] ch = key.toCharArray();
		
		int hash = 0;
		for(int i=0; i&amp;lt;ch.length; i++) {
			hash = hash + ch[i];
		}
		
		return hash;
	}
}
&lt;/pre&gt;&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;HashTable에 대한 소스 설명은 소스의 내용이 많아서 소스에 주석으로서 설명을 하였다.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <author>Jodu</author>
      <guid isPermaLink="true">https://jodu.tistory.com/40</guid>
      <comments>https://jodu.tistory.com/40#entry40comment</comments>
      <pubDate>Fri, 21 Sep 2018 14:27:09 +0900</pubDate>
    </item>
  </channel>
</rss>