<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dawn chorus</title>
        <link>https://velog.io/</link>
        <description>chop chop. mish mash. 재밌게 개발하고 있습니다.</description>
        <lastBuildDate>Sat, 18 Dec 2021 07:57:37 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dawn chorus</title>
            <url>https://images.velog.io/images/hsbang_thom/profile/f12ac197-3dff-4853-aa43-d36ddfef611f/hummus.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dawn chorus. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hsbang_thom" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Java] Thread - 4  Thread Pool]]></title>
            <link>https://velog.io/@hsbang_thom/Java-Thread-4-Thread-Pool</link>
            <guid>https://velog.io/@hsbang_thom/Java-Thread-4-Thread-Pool</guid>
            <pubDate>Sat, 18 Dec 2021 07:57:37 GMT</pubDate>
            <description><![CDATA[<h1 id="스레드풀">스레드풀</h1>
</br>

<p>병렬 작업 처리가 늘어나게 되면 스레드 개수가 증가되고, 그에 따른 스레드 생성과 스레드 스케줄링으로 인해 CPU의 메모리 처리량이 늘어나게 된다. 이는 애플리케이션 성능의 저하와 직결된다. 이렇게 갑작스레 늘어날 수 있는 병렬 작업 처리량을 관리하기 위해서 스레드풀을 사용한다. 
</br></p>
<p><strong>스레드풀은 스레드가 처리해야할 작업량을 조절</strong>할 수 있도록 한다. 스레드풀은 <strong>작업 큐 (Queue)</strong>에 작업을 저장해놓고 병렬로 처리할 수 있는 <strong>스레드의 개수를 제한</strong>한다. 각 스레드는 큐에서 작업을 얻어와 처리하고, 작업이 끝나면 다음 작업을 다시 큐에서 얻어온다. 결과적으로 작업 요청이 폭증하여도 애플리케이션 기능이 급갑하지 않게 된다. 
</br>
자바에서는 스레드풀을 생성하고 사용할 수 있도록 <code>java.util.concurrent.ExecutorService</code> 인터페이스와 <code>Executors</code> 클래스를 제공한다. <code>Executors</code>의 정적 메서드를 통해 <code>ExecutorService</code>를 구현할 수 있는데, 이 구현 객체가 스레드풀을 의미한다. </p>
<p><img src="https://images.velog.io/images/hsbang_thom/post/00de46bd-6d24-460e-8586-8ff46c29d91a/thread-6.png" alt="">[스레드풀의 작업 방식]
</br></p>
<ol>
<li>작업 요청이 들어온다.</li>
<li>스레드풀이 작업을 큐에 적재해놓는다.</li>
<li>실행 대기 상태인 스레드가 순서대로 큐를 처리한다.</li>
<li>결과를 리턴한다.</li>
</ol>
</br>
그림에서 보는 것처럼 ThreadPool은 병렬 처리를 할 스레드 개수를 제한하고, 작업 요청이 있을 때 요청을 작업장에 모은다. 해당 작업들은 들어온 순서대로 작업이 가능한 스레드에 의해 차례로 처리된다. (FIFO, Queue)
</br></br>

<h2 id="스레드풀-생명주기-관리생성-종료">스레드풀 생명주기 관리(생성, 종료)</h2>
<p>스레드풀을 생성하기 위해서는 <code>ExecutorService</code>를 구현해야 한다. 기본적으로는 <code>ExecutorService</code> 를 구현하고 있는 <code>ThreadPoolExecutor</code> 클래스를 생성해서 쓰면 되지만, <code>Executor</code> 클래스에서 간편하게 스레드풀을 생성할 수 있는 static 메서드를 제공한다. </br></br></p>
<ul>
<li><code>newCachedThreadPool()</code></li>
</ul>
</br>
초기 스레드 수와 코어 스레드 수가 0개, 최대 스레드가 Integer.MAX_VALUE인 스레드풀을 생성한다. 스레드 개수보다 작업 수가 많으면 스레드를 하나 생성시켜 작업을 처리하게 된다. 스레드의 idle 상태가 60초 이상 지속되면 스레드를 종료하고 풀에서 제거한다. 
</br>
</br>

<ul>
<li><p><code>newFixedThreadPool(int nThreads)</code></p>
<pre><code class="language-java">  public static ExecutorService newFixedThreadPool(int nThreads) {
          return new ThreadPoolExecutor(nThreads, nThreads,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue&lt;Runnable&gt;());
      }</code></pre>
<p>  초기 스레드 수는 0개이고, 코어 스레드와 최고 스레드 수는 생성 시 입력한 int 값, 고정된 값으로 스레드풀을 생성하는 메서드이다. idle한 스레드를 따로 처리하지 않고 그냥 둔다.</p>
</li>
</ul>
<p>위처럼 설정되어 있는 스레드풀 이외에 작업자가 원하는 상태의 스레드를 얻고 싶다면 ThreadPoolExecutor 객체를 생성하면 된다.</p>
<pre><code class="language-java">public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize, 
                          long keepAliveTime, 
                          TimeUnit unit,
                          BlockingQueue&lt;Runnable&gt; workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(),defaultHandler);
}</code></pre>
<h3 id="스레드풀-종료">스레드풀 종료</h3>
<p>스레드풀은 데몬 스레드가 아니기 때문에 main 스레드가 종료되더라도 계속 실행 상태로 남아있다. 그래서 main() 호출이 종료되어도 애플리케이션 프로세스는 종료되지 않는다. 애플리케이션을 종료하려면 스레드풀을 종료시켜서 스레드들이 종료상태가 되도록 처리해주어야 한다. ExecutorService는 종료와 관련하여 세 개의 메서드를 제공한다.</p>
</br>

<ul>
<li><p><code>**void shutdown()**</code></p>
<p>  현재 처리 중인 작업 뿐 아니라 작업 큐에 대기하고 있는 모든 작업을 처리한 뒤에 스레드풀을 종료 시킨다.</p>
</li>
<li><p><code>**List&lt;Runnable&gt; shutdownNow()**</code></p>
<p>  현재 작업 처리 중인 스레드를 interrupt 해서 작업 중지를 시도하고 스레드풀을 종료 시킨다. 작업을 완료하지 못한 스레드에 대한 정보가 List<Runnable>로 리턴된다.</p>
</li>
<li><p><code>**boolean awaitTermination(long timeout, TimeUnit unit)**</code></p>
<p>  <code>shutdown()</code> 호출 이후, 모든 작업 처리를 timeout 시간 내 완료하면 <code>true</code>, 완료하지 못하면 작업 처리 중인 스레드를 interrupt하고 <code>false</code>를 리턴한다.</p>
</li>
</ul>
</br>

<p>따라서 모든 작업을 마무리하고 스레드풀을 종료할 때에는 <code>shutdown()</code>, 바로 작업을 종료하려면 <code>shutdownNow()</code>을 호출한다.</p>
</br>
</br>

<h2 id="작업-생성과-처리-요청">작업 생성과 처리 요청</h2>
<h3 id="작업-생성">작업 생성</h3>
<p>작업은 <code>Runnable</code> 또는 <code>Callable</code> 을 구현하는 것으로 표현한다. 두 인터페이스의 차이점은 리턴값의 유무이다. Callable은 파라미터 T 타입을 <code>call()</code>의 리턴값으로 반환한다.</p>
<pre><code class="language-java">Callable&lt;T&gt; task = new Callable&lt;T&gt;(){

    @Override
    public T call() throws Exception{
        // code
        return T;
    }
}</code></pre>
<p>스레드풀의 스레드는 작업 큐에서 <code>Runnable</code> 또는 <code>Callable</code> 객체를 가져와 <code>run()</code>, <code>call()</code>을 실행한다.</p>
</br>


<h3 id="작업-처리-요청">작업 처리 요청</h3>
<p>작업 처리 요청이란 <code>ExecutorService</code>의 작업 큐에 생성한 작업 (<code>Runnable</code> or <code>Callable</code>) 객체를 넣는 행위를 말한다. 이 처리를 위해 두 가지 메서드를 제공한다.</p>
</br>

<ul>
<li><code>void execute(Runnable command)</code>: Runnable을 작업 큐에 저장. </li>
</ul>
<ul>
<li><p><code>Future&lt;?&gt; submit(Runnable task)</code></p>
</li>
<li><p><code>Future&lt;V&gt; submit(Runnable task, V result)</code></p>
</li>
<li><p><code>Future&lt;V&gt; submit(Callable&lt;V&gt; task)</code></p>
<p>  Runnable 또는 Callable을 작업 큐에 저장하며, Future 라는 객체가 리턴되어 작업 결과를 확인 가능하다.</p>
</li>
</ul>
<p>리턴값의 유무와 별개로, 두 메서드의 또 다른 차이점은 <code>execute()</code>의 경우 스레드에서 예외가 발생했을 때 스레드가 종료되고 해당 스레드는 스레드풀에서 제거된다. 따라서 스레드풀은 다른 작업을 처리하기 위해서 새로 스레드를 생성하게 된다. 반면 <code>submit()</code>의 경우 스레드가 남아있기 때문에 스레드를 재사용할 수 있다. 따라서 생성 ovehead를 줄이기 위해 <code>submit()</code>를 사용하는것이 좋다.</p>
</br>

<p>다음 예제는 의도적으로 <code>NumberFormatException</code>을 발생시켜서 위 두 메서드의 차이를 확인하는 예제이다.</p>
<pre><code class="language-java">public class ThreadPoolA {
    public static void main(String[] args) throws InterruptedException {

        ExecutorService executorService = Executors.newFixedThreadPool(2);

        for(int i=0;i&lt;10;i++){

            Runnable runnable = new Runnable() {

                @Override
                public void run() {
                    ThreadPoolExecutor threadPoolExecutor = 
                            (ThreadPoolExecutor) executorService;
                    int poolSize = threadPoolExecutor.getPoolSize();
                    String threadName = Thread.currentThread().getName();
                    System.out.println(&quot;총 스레드 개수 :&quot; + poolSize + &quot; / 작업 스레드 이름 : &quot;+ threadName);

                    int value = Integer.parseInt(&quot;삼&quot;);
                }
            };
            executorService.execute(runnable);
//            executorService.submit(runnable);
            Thread.sleep(1000);
        }
        executorService.shutdown();
    }

}</code></pre>
</br>

<pre><code class="language-java">총 스레드 개수 :1 / 작업 스레드 이름 : pool-1-thread-1
Exception in thread &quot;pool-1-thread-1&quot; java.lang.NumberFormatException: For input string: &quot;삼&quot;
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at org.java.chap12.thread_pool.ThreadPoolA$1.run(ThreadPoolA.java:21)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-3
Exception in thread &quot;pool-1-thread-3&quot; java.lang.NumberFormatException: For input string: &quot;삼&quot;
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at org.java.chap12.thread_pool.ThreadPoolA$1.run(ThreadPoolA.java:21)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-2
Exception in thread &quot;pool-1-thread-2&quot; java.lang.NumberFormatException: For input string: &quot;삼&quot;
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at org.java.chap12.thread_pool.ThreadPoolA$1.run(ThreadPoolA.java:21)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-4
Exception in thread &quot;pool-1-thread-4&quot; java.lang.NumberFormatException: For input string: &quot;삼&quot;
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at org.java.chap12.thread_pool.ThreadPoolA$1.run(ThreadPoolA.java:21)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-5
Exception in thread &quot;pool-1-thread-5&quot; java.lang.NumberFormatException: For input string: &quot;삼&quot;
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at org.java.chap12.thread_pool.ThreadPoolA$1.run(ThreadPoolA.java:21)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-6</code></pre>
<p><code>execute()</code>로 스레드풀에 작업을 수행했을 때에는 작업 중 예외가 발생했을 때 스레드를 종료하고 새 스레드를 만든다. 스레드가 1부터 10까지 계속 생성되어서 작업을 처리하고 있는 것을 볼 수 있다.</p>
<p>반면 <code>submit()</code>으로 처리했을 때를 보자.</p>
<pre><code class="language-java">총 스레드 개수 :1 / 작업 스레드 이름 : pool-1-thread-1
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-2
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-1
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-2
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-1
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-2
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-1
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-2
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-1
총 스레드 개수 :2 / 작업 스레드 이름 : pool-1-thread-2</code></pre>
<p>총 스레드 개수는 두 메서드 모두 2개로 같지만, 작업 스레드 개수가 <code>submit()</code>의 경우 2개인 것을 확인할 수 있다. 스레드가 종료되지 않고 계속해서 다른 작업을 수행하기 때문이다.
</br></p>
<h2 id="블로킹-방식의-작업-완료-통보">블로킹 방식의 작업 완료 통보</h2>
<p><code>ExecutorService.submit()</code> 은 <code>Runnable</code> 또는 <code>Callable</code>의 작업을 작업 큐에 저장하고 <code>Future&lt;V&gt;</code> 를 리턴한다. <code>Future</code> 객체는 하나의 작업 결과가 아닌 최종 결과를 얻는데 사용된다. 즉 하나의 작업 결과를 <strong>블로킹(지연)</strong>하여 모든 결과를 기다린다는 의미이다. 이 떄문에 <code>Future</code>을 지연 완료 (pending completion) 객체라고도 한다. <code>Furture.get()</code>를 호출하면 스레드가 작업을 완료할 때까지 블로킹되었다가 작업을 완료하면 처리 결과를 리턴한다. </p>
</br>

<ul>
<li><code>**V get()</code>:** 작업이 완료될 때까지 블로킹되었다가 처리 결과 리턴</li>
<li><code>V get(long timeout, TimeUnit unit)</code>: timeout 내에 리턴하지 않으면 <code>TimeOutException</code> 발생</li>
</ul>
</br>

<p>리턴 타입인 V는 submit()의 매개변수에 따라 Callable 또는 임이의 타입 파라미터 V이다. </p>
<p><code>Future</code>을 이용한 블로킹 방식의 작업 완료 통보에서 주의할 점은 작업을 처리하는 스레드가 작업을 완료하기 전까지는 <code>get()</code> 호출이 블로킹(지연) 되므로 다른 코드를 실행할 수가 없다는 점이다. 따라서 새로운 스레드 또는 스레드풀의 또 다른 스레드가 <code>get()</code> 호출을 담당해야 한다.
</br></p>
<pre><code class="language-java">new Thread(new Runnable(){

    @Override
    public void run(){
        try{
        future.get();
        } catch (Exception e){}
    }

}).start();</code></pre>
<pre><code class="language-java">
executorService.submit(new Runnable(){
    @Override
    public void run(){
        try{
        future.get();
        } catch (Exception e){}
    }
});
</code></pre>
<p>Future에는 get() 말고도 작업이 종료되었는지 확인하는 <code>isDone()</code>, 작업 취소 여부를 확인하는 <code>isCancled()</code> 등 추가 메서드가 있다.</p>
</br>

<h3 id="리턴값이-없는-작업-완료-통보">리턴값이 없는 작업 완료 통보</h3>
<p>작업 완료에 대해서 리턴값이 필요 없다면 <code>submit(Runnable task)</code>를 이용하면 된다. submit()의 매개변수로 리턴값이 있는 <code>Callable</code>이나 Runnable과 더불어 타입 파라미터를 넣어주면 거기에 맞는 타입을 리턴하지만, Runnable만 매개했을 경우 null이 리턴된다.</p>
</br>

<p>정상적으로는 null이 리턴되지만, 스레드가 작업 처리 도중 interrupt 되면 <code>InterruptedException</code>을 발생시키고, 예외가 있을 경우 <code>ExecutionException</code>을 발생시킨다. </p>
<pre><code class="language-java">try{
  executorService.submit(runnable).get();
} catch (InterruptedException e){
  System.out.println(&quot;interrupted&quot;); // interrupted 되었을 때 코드
}   catch (ExecutionException e){
  System.out.println(&quot;error occurred&quot;); // 예외 발생 시 코드
  e.printStackTrace();
}</code></pre>
<p>아래 예시를 보자.</p>
<pre><code class="language-java">package org.java.chap12.thread_pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolB {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        System.out.println(&quot;task is being processed&quot;);

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                int sum = 0;
                for(int i = 1;i&lt;=10;i++){
                    sum +=i;
                    System.out.println(&quot;output : &quot;+ sum);
                }
            }
        };

        Future future = executorService.submit(runnable);

        try{
            future.get();
            System.out.println(&quot;task done&quot;);
        } catch (Exception e){
            System.out.println(e.getMessage());
        }

        executorService.shutdown();

    }
}</code></pre>
<p>코드를 수행해보면 아래와 같다.</p>
<pre><code class="language-java">task is being processed
output : 1
output : 3
output : 6
output : 10
output : 15
output : 21
output : 28
output : 36
output : 45
output : 55
task done</code></pre>
<p>반면 <code>future.get()</code>을 쓰지 않으면 코드가 아래처럼 수행이 된다.</p>
<pre><code class="language-java">task is being processed
task done
output : 1
output : 3
output : 6
output : 10
output : 15
output : 21
output : 28
output : 36
output : 45
output : 55</code></pre>
<p><code>future.get()</code>이 블로킹하는 부분을 여기서 확인할 수 있다. 스레드의 작업의 종료를 기다리지 않고 main 스레드가 바로 <code>Syso(&quot;task done&quot;)</code> 코드를 수행하는 것을 확인할 수 있다.</p>
<h3 id="리턴값이-있는-작업-완료">리턴값이 있는 작업 완료</h3>
<p>스레드풀의 스레드가 작업 완료 후 작업 값을 얻어야 한다면 작업 객체를 <code>Callable</code>로 생성하면 된다. 이 경우 리턴 타입은 <code>Callable</code>의 리턴 타입인 제네릭 타입 파라미터 T이다.</p>
<p>다음 예제를 보자.</p>
<pre><code class="language-java">package org.java.chap12.thread_pool;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolWithResult {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        System.out.println(&quot;processing...&quot;);

        Callable&lt;Integer&gt; callable = new Callable&lt;Integer&gt;() {

            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for(int i=1;i&lt;=90;i++){
                    sum += i;
                }

                return sum;
            }
        };

        Future&lt;Integer&gt; result = executorService.submit(callable);

        try{
            int sum = result.get();
            System.out.println(&quot;result returned : &quot;+sum);
            System.out.println(&quot;process done&quot;);

        } catch (Exception e){
            System.out.println(e.getMessage());
        }
        executorService.shutdown();
    }
}</code></pre>
<p>Callable의 리턴 타입을 Integer로 정했기 때문에 Future도 같은 타입으로 출력할 수 있다.
</br></p>
<h3 id="작업-처리-결과를-외부-객체에-저장하기">작업 처리 결과를 외부 객체에 저장하기</h3>
<p>스레드의 작업 처리 결과를 외부 객체에 저장해야할 경우도 있다. 예컨대 한 스레드의 작업 처리를 완료하고 다른 스레드의 결과물과 취합해야 할 때처럼 말이다. 이럴 때는 값을 저장할 수 있는 임의의 객체를 만들어(Result) <code>submit(Runnable, Result)</code>를 이용하면 된다. Result는 대개 공유 객체가 되는데, 스레드에서 결과를 저장하기 위해서 사용되어야 하므로 생성자를 통해 Result 객체를 주입받도록 해야 한다.</p>
<pre><code class="language-java">class Task implements Runnable {

    Result result;

    public Task(Result result){
        this.result = result;
    }

    @Override 
    public void run(){

    //.. code
    this.result.someMethod(); // result에 처리 결과 저장
    }

}</code></pre>
<p>다음은 두 개의 스레드의 작업 값을 Result 객체에 담는 예제이다. </p>
<pre><code class="language-java">package org.java.chap12.thread_pool;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolWithResult {

    public static void main(String[] args) {

        ExecutorService executorService = 
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        System.out.println(&quot;processing&quot;);

        class Task implements Runnable{

            Result result;

            Task(Result result){
                this.result = result;
            }

            @Override
            public void run() {
                int sum = 0;
                for(int i =1;i&lt;=10;i++){
                    sum += i;
                }
                result.addValue(sum);
            }

        }

        Result result = new ThreadPoolWithResult.Result();
        Runnable task1 = new Task(result);
        Runnable task2 = new Task(result);

        Future&lt;Result&gt; future1 = executorService.submit(task1, result);
        Future&lt;Result&gt; future2 = executorService.submit(task2, result);

        try{
            result = future1.get();
            System.out.println(result.accumulatedValue); // 110
            result = future2.get();
            System.out.println(result.accumulatedValue); // 110

        } catch(Exception e){
            e.printStackTrace();
        }

        executorService.shutdown();
    }

    static class Result {

        int accumulatedValue;
        synchronized void addValue(int value){
            accumulatedValue += value;
        }
    }
}</code></pre>
<p>공유 객체인 Result를 만들어서 동기화 메서드를 통해 값을 넣어준다. task1과 2가 모두 result를 참조하고 있기 때문에 addValue가 각각 호출되면서 값이 저장된다.</p>
<h3 id="작업-완료-순으로-통보">작업 완료 순으로 통보</h3>
<p>스레드는 스케줄링에 따라서 임의 순서대로 동작한다. 따라서 작업 요청 순서대로 작업 처리가 완료된다고 보장할 수는 없다. 여러 개의 작업들이 순차적으로 처리될 필요가 없고, 처리 결과도 순차적으로 이용할 필요가 없다면 작업 처리가 완료된 것부터 결과를 얻어 이용하면 된다. 스레드풀에서 작업 처리가 완료된 것만 통보받고 싶다면 <code>CompletionService</code> 인터페이스를 이용하면 된다. 인터페이스에서는 <code>poll()</code>, <code>take()</code>를 제공한다.</p>
<ul>
<li><strong><code>Future&lt;V&gt; poll()</code>:</strong> 완료된 Future을 가져옴. 완료된 작업이 없으면 즉시 null 리턴</li>
<li><code>**Future&lt;V&gt; take()</code>:** 완료된 작업의 Future 가져옴. 완료된 작업이 없다면 있을 때까지 블로킹(지연)</li>
</ul>
<p><code>CompletionService</code> 는 <code>ExecutorCompletionService&lt;V&gt;</code>를 통해 구현된다. 생성자 매개값으로 <code>ExecutorService</code>를 제공하면 된다.</p>
<pre><code class="language-java">ComopletionService completionService = ExecutorCompletionService&lt;V&gt;(executorService);</code></pre>
<p>위 인터페이스의 메서드를 이용하려면 <code>Future</code> 가 있어야 하므로, 스레드풀에 작업을 추가할 때 <code>submit()</code>의 리턴값이 있는 메서드 오버로딩을 이용해야 한다. </p>
<p>다음은 <code>take()</code>를 이용해 스레드 결과값을 얻는 예제이다.</p>
<pre><code class="language-java">public class Test {
    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        CompletionService&lt;Result&gt; completionService = new ExecutorCompletionService&lt;&gt;(executorService);

        System.out.println(&quot;submit tasks&quot;);

        for(int i =0;i&lt;3;i++) {

            completionService.submit(new Callable&lt;Result&gt;() {

                @Override
                public Result call() throws Exception {

                    Result result = new Result();
                    result.threadName = Thread.currentThread().getName();

                    for(int i=1;i&lt;=10;i++)
                        result.val += i;

                    return result;
                }

            });
        }

        System.out.println(&quot;take result from completed task&quot;);

        executorService.submit(new Runnable() {

            @Override
            public void run() {

                while(true){
                    try{
                        Result result;
                        Future&lt;Result&gt; future = completionService.take();

                        result = future.get();
                        System.out.println(&quot;result : &quot;+ result.val);
                        System.out.println(&quot;threadName : &quot;+ result.threadName);

                    } catch (Exception e){
                        break;
                    }
                }
            }
        });

        try {

            Thread.sleep(3000);

        } catch (Exception e){}

        System.out.println(&quot;shutting down&quot;);
        executorService.shutdownNow();
    }

    public static class Result{
        String threadName;
        int val;

    }
}</code></pre>
<p><code>CompletionService</code> 를 구현한 <code>ExecutorCompletionService</code> 는 <code>ExecutorService</code>를 주입받았기 때문에 <code>ExecutorService</code>의 메서드를 사용할 수 있다. <code>CompletionService</code>가 총 세 개의 작업을 생성해 큐에 추가한다. 스레드풀에 제한을 따로 걸지 않았으므로 CPU에 여유가 있다면 스레드 3개를 만들어 병렬처리할 것이다.
</br></p>
<p><code>take()</code>는 작업이 끝날 때까지 블로킹된다. 따라서 <code>future.get()</code>은 블로킹 없이 바로 수행될 것이다. 또한 이 작업은 앱이 실행되는 동안 계속 수행되어야 하므로, <code>while</code> 루핑으로 스레드풀에서 동작하고 있다가 <code>shutdownNow()</code>가 수행되어서 <code>interruptException</code>를 발생시켰을 때 <code>break</code>하면 된다. 스레드 이름을 확인하기 위해 <code>Callable</code>의 리턴값을 이름과 값을 담을 수 있는 객체 <code>Result</code>로 설정했다.</p>
<p>코드를 실행하면 다음과 같은 결과가 나온다.</p>
<pre><code class="language-java">submit tasks
take result from completed task
result : 55
threadName : pool-1-thread-3
result : 55
threadName : pool-1-thread-2
result : 55
threadName : pool-1-thread-1
shutting down</code></pre>
<p>스레드 3, 2, 1 순으로 작업이 완료된 것을 확인할 수 있다. 스레드 순서는 코드를 수행할 때마다 당연히 바뀐다. 작업은 스레드 스케줄링에 의해 작동하기 때문이다.
</br></p>
<h2 id="콜백-방식의-작업-완료-통보">콜백 방식의 작업 완료 통보</h2>
<p>앞에서는 작업이 완료될 때까지 코드 수행을 블로킹하는 메서드 <code>get()</code>을 통해서 작업 완료를 확인하는 방법을 알아보았다. 이번에는 콜백 방식을 통해서 작업 완료를 확인해본다. </p>
<p>이름만 들어도 대충 두 방법의 차이가 느껴질 것이다. 콜백은 작업 요청을 호출한 스레드가 작업 중인 스레드의 결과를 기다리지 않고 다른 작업을 계속할 수 있다. 작업이 끝나면 스레드에서 콜백 메서드로 결과를 알려주기 때문이다.</p>
<p><img src="https://images.velog.io/images/hsbang_thom/post/a4df6fbc-7118-43ba-a48f-e2693d3f0cdc/thread-7.png" alt=""></p>
</br>

<p>스레드풀을 구현하고 있는 <code>ExecutorService</code>에서는 이런 콜백 메서드 기능이 없으므로 <code>Runnable</code>을 구현할 때 콜백 기능을 함께 구현해야 한다. 이것은 직접 개발자가 구현할 수도 있겠지만, <code>java.nio.channels.CompletionHandler</code>를 이용하면 좀 더 편하게 할 수 있다. 해당 인터페이스는 비동기 통신에서 콜백 객체를 만들 때 사용한다. 기본적인 사용 코드는 아래와 같다.</p>
<pre><code class="language-java">CompletionHandler&lt;V, A&gt; completionHandler = new CompletionHandler&lt;V, A&gt;() {

    @Override
    public void completed(V result, A attachment) {

    }

    @Override
    public void failed(Throwable exc, A attachment) {

    }
};</code></pre>
<p>각 메서드는 작업이 정상 처리 되었을 때와 실패했을 때를 호출되는 콜백 메서드이다. V 타입 파라미터는 리턴 타입이고, A는 첨부값의 타입이다. 다음 예제를 보자.</p>
<pre><code class="language-java">public class CallbackExam {

    private ExecutorService executorService;

    public CallbackExam(){
        this.executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }

    private CompletionHandler&lt;Integer, Void&gt; callback = new CompletionHandler&lt;Integer, Void&gt;() {

        @Override
        public void completed(Integer result, Void attachment) {
            System.out.println(&quot;task completed. the result : &quot;+ result);
        }

        @Override
        public void failed(Throwable exc, Void attachment) {
            System.out.println(&quot;task failed. &quot; + exc.toString());
        }
    };

    public void doWork(final String x, final String y){
        Runnable task = new Runnable() {
            @Override
            public void run() {
                try{
                    int intX = Integer.parseInt(x);
                    int intY = Integer.parseInt(y);
                    int result = intX + intY;
                    callback.completed(result, null);
                } catch (NumberFormatException e){
                    callback.failed(e, null);
                }
            }
        };
        executorService.submit(task);
    }

    public void finish(){
        executorService.shutdown();
    }

    public static void main(String[] args) {

        CallbackExam callbackExam = new CallbackExam();
        callbackExam.doWork(&quot;3&quot;,&quot;3&quot;);
        callbackExam.doWork(&quot;3&quot;,&quot;삼&quot;);
        callbackExam.finish();

    }
}</code></pre>
<p>String 두 개를 인자로 넣어 각각 파싱했을 때 성공하면 <code>completed()</code>, 실패하면 <code>failed()</code>가 호출되도록 한 코드이다.  결과는 다음과 같이 출력된다.</p>
<pre><code class="language-java">task completed. the result : 6
task failed. java.lang.NumberFormatException: For input string: &quot;삼&quot;</code></pre>
<hr>
<p><strong>source</strong>: <a href="https://github.com/thom-droid/basic-java/tree/master/src/org/java/chap12/thread_pool">https://github.com/thom-droid/basic-java/tree/master/src/org/java/chap12/thread_pool</a></p>
<p>*<em>reference *</em>: 「이것이 자바다」, 신용권</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Thread - 3 스레드 그룹]]></title>
            <link>https://velog.io/@hsbang_thom/Java-Thread-3-%EC%8A%A4%EB%A0%88%EB%93%9C-%EA%B7%B8%EB%A3%B9</link>
            <guid>https://velog.io/@hsbang_thom/Java-Thread-3-%EC%8A%A4%EB%A0%88%EB%93%9C-%EA%B7%B8%EB%A3%B9</guid>
            <pubDate>Sat, 18 Dec 2021 07:44:42 GMT</pubDate>
            <description><![CDATA[<h1 id="스레드-그룹">스레드 그룹</h1>
<p>스레드 그룹은 관련된 스레드를 묶어서 관리할 목적으로 이용한다. JVM이 실행되면 system 스레드 그룹을 만들고, JVM 운영에 필요한 스레드들을 생성해서 system 스레드 그룹에 포함시킨다. 그리고 system의 하위 스레드 그룹으로 main을 만들고 메인 스레드를 main 스레드 그룹에 포함시킨다.</br></br>
스레드는 반드시 하나의 스레드 그룹에 포함되고, 메서드를 통해서 스레드의 그룹을 지정해주지 않으면 기본적으로 자신을 생서한 스레드와 같은 스레드 그룹에 속하게 된다. </br></br></p>
<h2 id="스레드-그룹-이름-얻기">스레드 그룹 이름 얻기</h2>
</br>
해당 스레드를 수행 중인 스레드의 그룹을 얻고 싶다면 static method인 getThreadGroup() d을 이용하면 된다.</br>
</br>

<pre><code class="language-java">ThreadGroup group = Thread.currentThread().getThreadGroup();
String groupName = group.getName();</code></pre>
</br>

<p>static method인 <code>getAllStackTraces()</code>를 이용하면 현재 애플리케이션에서 실행 중인 모든 스레드의 정보를 <code>Map</code> 형태로 얻어올 수 있다.</br></br></p>
<pre><code class="language-java">public class AutoSaveApp {
    public static void main(String[] args) {
        AutoSaveThread autoSaveThread = new AutoSaveThread();
        autoSaveThread.setDaemon(true);

        autoSaveThread.start();

        try{
            Thread.sleep(3000);
        }catch (InterruptedException e){}

        Map&lt;Thread, StackTraceElement[]&gt; threadMap = Thread.getAllStackTraces();
        Set&lt;Thread&gt; threadSet = threadMap.keySet();

        for (Thread t : threadSet){
            System.out.println(&quot;thread name : &quot; +t.getName());
                        System.out.println(&quot;thread group : &quot; +t.getThreadGroup().getName());
            System.out.println(&quot;is daemon? : &quot; +t.isDaemon());
        }

        System.out.println(&quot;main thread now shutting down. Daemon also stops&quot;);

    }
}</code></pre>
<p>아래 코드만 출력한 결과는 다음과 같다.</p>
<pre><code class="language-java">thread name : Attach Listener
thread group : system
is daemon? : true

thread name : Finalizer
thread group : system
is daemon? : true

thread name : Monitor Ctrl-Break
thread group : main
is daemon? : true

thread name : Thread-0
thread group : main
is daemon? : true

thread name : main
thread group : main
is daemon? : false

thread name : Signal Dispatcher
thread group : system
is daemon? : true

thread name : Reference Handler
thread group : system
is daemon? : true</code></pre>
<p>GC를 담당하는 Finalizer 스레드를 비롯한 일부 스레드들이 system 그룹에 속하고, main은  main스레드 그룹에 속해있다.
</br></br></p>
<h2 id="스레드-그룹-생성">스레드 그룹 생성</h2>
<p>ThreadGroup을 생성하는 생성자는 두 가지가 있다.</br></br></p>
<pre><code class="language-java">ThreadGroup(String name);
ThreadGroup(ThreadGroup parent, String name);</code></pre>
<p></br></br>
첫 번째 생성자는 이름만 지정하는 것이고, 두 번째 생성자는 부모 스레드 그룹을 지정하는 것이다. 부모 스레드 그룹을 명시하지 않고 스레드 그룹을 생성하면, 이 스레드 그룹을 생성한 스레드의 하위 스레드 그룹이 된다. 예를 들어 main 스레드에서 <code>new ThreadGroup(&quot;threadGroup1&quot;)</code>을 호출한 뒤 <code>getThreadGroup().getName()</code>의 리턴값은 <code>&quot;main&quot;</code>이 된다.
</br></br>
새로운 스레드를 스레드 그룹에 포함 시키려면 스레드 클래스의 생성자 오버로딩을 이용하면 된다. 네 가지가 있다.</p>
<pre><code class="language-java">Thread(ThreadGroup group, Runnable target);
Thread(ThreadGroup group, Runnable target, String name);
Thread(ThreadGroup group, Runnable target, String name, long stackSize);
Thread(ThreadGroup group, String name);</code></pre>
<p><code>stackSize</code>는 JVM이 이 스레드에 할당할 스택 사이즈를 의미한다.
</br></br></p>
<h2 id="스레드-그룹-interrupt">스레드 그룹 <code>interrupt()</code></h2>
<p>스레드 그룹으로 스레드를 관리하면 어떤 점이 좋을까? 스레드 그룹에서 제공하는 <code>interrupt()</code>를 호출하면 그룹 내 모든 스레드를 일괄적으로 <code>interrupt</code>할 수 있다. 스레드 그룹의 <code>interrupt()</code>는 내부적으로 그룹에 포함된 모든 스레드의 <code>interrupt()</code>를 호출한다. </br></br>다만 앞서 학습한 것처럼 <code>interrupt()</code>는 스레드가 실행 대기 또는 실행 중이라면 예외를 발생시키지 않으므로 그룹에서 <code>interrupt()</code>해도 모든 스레드가 종료되지 않을 수 있다. 따라서 안전한 종료를 위해서는 개별 스레드가 예외 처리를 해야 한다. 아래는 <code>ThreadGroup.interrupt()</code> 코드이다.
</br></br></p>
<pre><code class="language-java">public final void interrupt() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            checkAccess();
            for (int i = 0 ; i &lt; nthreads ; i++) {
                threads[i].interrupt();
            } // 각 스레드의  interrupt() 호출

            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0 ; i &lt; ngroupsSnapshot ; i++) {
            groupsSnapshot[i].interrupt();
        }
    }</code></pre>
<p></br></br>
이 외에도 그룹에 속한 모든 스레드를 제거하는 <code>destroy()</code>, 스레드를 추가하는 <code>add()</code>, 하나의 스레드만 제거하는 <code>remove()</code> 등 다양한 메서드가 제공된다. 다음 예제에서는 스레드 그룹을 통해 일괄적으로 스레드를 종료시키는 기능을 구현해본다.</p>
<pre><code class="language-java">package org.java.chap12.thread_group;

public class AThread extends Thread {

    public AThread(ThreadGroup parent, String threadName){
        super(parent,threadName);
    }

    @Override
    public void run() {
        while(true){
            try{
                System.out.println(getName() +  &quot; is running&quot;);
                Thread.sleep(1000);
            } catch (InterruptedException e){
                System.out.println(this.getName()+ &quot;is interrupted&quot;);
                break; // interrupted로 예외가 발생하면 catch해서 break            }
        }

        System.out.println(this.getName() + &quot; stops&quot;);
    }
}</code></pre>
<pre><code class="language-java">package org.java.chap12.thread_group;

public class ThreadGroupTestApp {

    public static void main(String[] args) {
        ThreadGroup threadGroup = new ThreadGroup(&quot;myGroup&quot;);
        AThread threadA = new AThread(threadGroup, &quot;threadA&quot;); 
        AThread threadB = new AThread(threadGroup, &quot;threadB&quot;); 

        threadA.start();
        threadB.start();

        System.out.println(&quot;main thread list&quot;);
        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
        mainGroup.list();
        System.out.println();

        try{
            Thread.sleep(3000);
        } catch (InterruptedException e){}

        System.out.println(&quot;thread group interrupts all threads&quot;);
        threadGroup.interrupt();
    }
}</code></pre>
</br>

<pre><code class="language-java">main thread list
threadB is running
threadA is running
java.lang.ThreadGroup[name=main,maxpri=10]
    Thread[main,5,main]
    Thread[Monitor Ctrl-Break,5,main]
    java.lang.ThreadGroup[name=myGroup,maxpri=10]
        Thread[threadA,5,myGroup]
        Thread[threadB,5,myGroup]

threadA is running
threadB is running
threadA is running
threadB is running
thread group interrupts all threads
threadBis interrupted
threadB stops
threadAis interrupted
threadA stops</code></pre>
<p>코드 수행 결과.</br></br></p>
<p>AThread는 Thread의 생성자 오버로딩을 통해서 생성자 호출 시 스레드 그룹과 스레드 이름으로 초기화된다. 
</br>
앱에서 threadA와 threadB는 &quot;myGroup&quot;이라는 이름을 가진 스레드 그룹에 속하게 된다. 또한 myGroup 스레드그룹은 스레드그룹을 지정하지 않은 채로 main에서 생성되었으므로 main의 하위 그룹이 된다.
</br>
AThread에서 <code>InterruptedException</code> 예외를 처리하고 있으므로 3초 뒤 정상적으로 모든 스레드가 myGroup의 <code>interrupt()</code>에 의해서 정상적으로 종료된다.
</br></br>
이번 장에서는 스레드를 그룹으로 묶어 관리하는 스레드 그룹에 대해 알아보았다.</br>
다음 장에서는 스레드의 개수를 제한하고 작업 순서를 관리하여 애플리케이션의 과부화를 막을 수 있도록 도와주는 기능인 스레드풀에 대해 알아보도록 한다.
</br>
</br></p>
<hr>
<p><strong>source:</strong> 
<a href="https://github.com/thom-droid/basic-java/tree/master/src/java/chap12/daemon">github1</a>
<a href="https://github.com/thom-droid/basic-java/tree/master/src/java/chap12/thread_group">github2</a>
</br>
<strong>reference</strong>: 「이것이 자바다」, 신용권</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Thread - 2 스레드 상태 제어]]></title>
            <link>https://velog.io/@hsbang_thom/Java-Thread-2-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%83%81%ED%83%9C-%EC%A0%9C%EC%96%B4</link>
            <guid>https://velog.io/@hsbang_thom/Java-Thread-2-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%83%81%ED%83%9C-%EC%A0%9C%EC%96%B4</guid>
            <pubDate>Sat, 18 Dec 2021 07:18:30 GMT</pubDate>
            <description><![CDATA[<h2 id="스레드-상태">스레드 상태</h2>
<h3 id="스레드-실행-순서">스레드 실행 순서</h3>
<p>스레드를 생성하고 <code>start()</code> 를 호출하면 바로 스레드가 실행될 것 같지만, 사실 <strong>실행 대기(Runnable)</strong> 상태가 된다. 실행 대기 상태란 스케줄링이 되지 않아서 실행을 기다리고 있는 상태를 말한다. </br></br>실행 대기 상태에 있는 스레드 중에서 스레드 스케줄링으로 선택된 스레드가 CPU를 점유하고 <code>run()</code>메소드를 실행한다. 이때를 <strong>실행(Running)</strong> 상태라고 한다. </br></br>실행 상태의 스레드는 <code>run()</code> 메서드를 실행 완료하기 전에 스레드 스케줄링(time slice)에 의해 다시 실행 대기 상태로 돌아갈 수 있다. 그리고 또 다른 스레드가 스케줄링에 의해 실행 상태가 된다. 이렇게 번갈아가며 여러 스레드가 자신의 <code>run()</code> 를 조금씩 실행한다. 실행 상태에서 <code>run()</code> 가 종료되면 스레드의 실행도 멈추게 된다. 이 상태를 <strong>종료 상태(Terminated)</strong>라 한다.</br></br></br></p>
<p><img src="https://images.velog.io/images/hsbang_thom/post/1afb20c6-6397-4d96-9c70-bc3075134606/thread-4.png" alt=""></p>
</br>
실행 순서와 관련하여 아래 코드를 보자.
</br>
</br>

<pre><code class="language-java">public static void main(String[] args) throws InterruptedException {
        Thread mainThread = Thread.currentThread();

        Calc calc = new Calc();
        User1 user1 = new User1();
        user1.setCalc(calc);

        user1.start(); // user1 

        User2 user2 = new User2();
        user2.setCalc(calc);

        user2.start(); // user2
        System.out.println(calc.getMemory()); // main</code></pre>
</br>
앞선 예제에서와 같이 main, user1, user2 스레드를 실행시켰다. calc는 공유되고 있고 user2 스레드가 user1보다 더 늦게 값을 세팅하므로 `calc.getMemory()` 의 결과는 50일 것이라고 예상했다. 하지만 위 실행 결과는 다음과 같다.
</br>
</br>

<pre><code class="language-java">User1 :100 // user1
100 // main
User2 :50 // user2 </code></pre>
</br>
코딩된 순서로 보면 user2가 user1 다음에 출력되고 main 스레드의 syso가 마지막에 출력될 것 같지만, 그렇지 않다. 그리고 코딩 상으로는 user2 스레드 시작 후에 calc 값을 호출했으므로 당연히 50이 출력될 것으로 예상했지만, user1의 값이었던 100이 출력되었다.
</br></br></br>
main 스레드를 잠시 멈추고 `user2.setCalc()`가 호출될 때까지 기다린 뒤 다시 출력하는 코드를 추가했다.
</br>
</br>

<pre><code class="language-java">public static void main(String[] args) throws InterruptedException {
        Thread mainThread = Thread.currentThread();

        Calc calc = new Calc();
        User1 user1 = new User1();
        user1.setCalc(calc);

        user1.start(); // user1 

        User2 user2 = new User2();
        user2.setCalc(calc);

        user2.start(); // user2
        System.out.println(calc.getMemory()); // main
                **mainThread.sleep(1000);
        System.out.println(calc.getMemory());
}**</code></pre>
</br>
출력결과는 다음과 같다.
</br>

<pre><code class="language-java">User1 :100 // user1
100 // main
User2 :50 // user2
50 // main</code></pre>
</br>
</br>

<p>보는 것처럼 시간을 조금 두고 다시 <code>calc.getMemory()</code> 를 해보면 값이 변해있다는 걸 확인할 수 있다. 즉 <code>start()</code> 를 호출한다고 해서 바로 스레드가 실행되는 것이 아니라 <strong>스레드 스케줄링에 의해서 적절한 시점에 시작되며, 시작하는 시간까지의 차이가 있다</strong>는 것이다.</br></br></br></p>
<h3 id="일시정지-상태">일시정지 상태</h3>
<p><img src="https://images.velog.io/images/hsbang_thom/post/806ebeb5-335d-47ce-842f-1f76ff599e5c/thread-5.png" alt=""></p>
<p>스레드의 생명주기는 생성 → 실행대기 → 실행 → 실행 종료이다. 스레드가 실행 대기 상태일 때는 위와 같이 세 가지 상태가 있다. </br></br></p>
<p>스레드의 상태를 코드로 확인하고 싶다면 Thread의 <code>getState()</code>를 호출하면 된다. enum 상수 값으로 스레드의 상태를 리턴한다. 각 값이 의미하는 바는 다음과 같다.
</br></p>
<ul>
<li><code>Thread.State.NEW</code>: 스레드 객체가 생성</li>
<li><code>Thread.State.RUNNABLE</code> : 실행 대기. 언제든 실행상태가 될 수 있음</li>
<li><code>Thread.State.WAITING</code> : 다른 스레드가 통지할 때까지 기다리는 상태</li>
<li><code>Thread.State.TIMED_WAITING</code> : 주어진 시간 동안 기다리는 상태</li>
<li><code>Thread.State.BLOCKED</code>: 사용하고자 하는 객체의 락이 풀릴 때까지 기다리는 상태</li>
<li><code>Thread.State.TERMINATED</code>: 실행을 마친 상태</br>
</br>


</li>
</ul>
<p>스레드의 상태와 관련해서, 아래의 코드를 보자.</p>
<pre><code class="language-java">public class StatePrintThread extends Thread {

    private Thread target;
    private Thread target2;

    public StatePrintThread(Thread target, Thread target2){
        this.target = target;
        this.target2 = target2;
    }

    @Override
    public void run() {
        while(true){
            Thread.State state = target.getState();
            Thread.State state2 = target2.getState();

            System.out.println(&quot;target Thread Name: &quot; +target.getName());
            System.out.println(&quot;target2 Thread Name: &quot; +target2.getName());
            System.out.println(&quot;current Thread Name: &quot; +Thread.currentThread().getName());

            System.out.println(&quot;thread status : &quot; + state);
            System.out.println(&quot;thread2 status : &quot; + state2);

            if(state == Thread.State.NEW){
                target.start();
            }

            if(state == Thread.State.TERMINATED){
                break;
            }

            try{
                Thread.sleep(500);
            } catch (Exception e){}
        }
    }
}</code></pre>
<pre><code class="language-java">public class Target extends Thread {

    @Override
    public void run() {

        System.out.println(&quot;here we go&quot;);
        Thread thread = Thread.currentThread();
        thread.setName(&quot;target&quot;);
        System.out.println(&quot;thread name: &quot; +thread.getName());
        System.out.println(&quot;thread group: &quot; +this.getThreadGroup());
        for(int i = 0; i&lt;100000000; i++){}

        try{
            Thread.sleep(1500);
        } catch (Exception e){}

        for(int i = 0; i&lt;100000000; i++){}

    }
}</code></pre>
<pre><code class="language-java">public class ThreadStatusApp {
    public static void main(String[] args) {
        StatePrintThread printThread = new StatePrintThread(new Target(), new TestThread());

        printThread.start();

    }
}</code></pre>
<pre><code class="language-java">target Thread Name: Thread-0
target2 Thread Name: Thread-1
current Thread Name: Thread-2
thread status : NEW
thread2 status : NEW
here we go
thread name: target
thread group: java.lang.ThreadGroup[name=main,maxpri=10]
target Thread Name: target
target2 Thread Name: Thread-1
current Thread Name: Thread-2
thread status : TIMED_WAITING
thread2 status : NEW
target Thread Name: target
target2 Thread Name: Thread-1
current Thread Name: Thread-2
thread status : TIMED_WAITING
thread2 status : NEW
target Thread Name: target
target2 Thread Name: Thread-1
current Thread Name: Thread-2
thread status : TERMINATED
thread2 status : NEW</code></pre>
<p>StatePrintThread가 while문을 돌리는 동안 Target 에서는 1억번 for 루핑, 1.5초 멈추었다가 다시 1억번 for 루핑이 일어난다. 처음 target 스레드가 만들어졌을 때에는 상태가 NEW 이고, 루핑을 통과하면서 일시정지 상태가 되어서 이를 StatePrintThread가 출력하고, 다시 target이 모든 루핑을 통과하여 run() 실행이 끝나 TERMINATED 상태가 되고, 이를 printThread가 출력하고 while 루핑에서 break하고 있다.</br></br></br></p>
<h3 id="runnablerunvs-threadstart"><code>Runnable.run()</code>vs. <code>Thread.start()</code></h3>
<p>두 메서드의 가장 큰 차이점은 <code>run()</code>은 메서드가 호출되고 있는 현재 스레드에서 코드를 수행한다는 점이다. 반면 <strong><code>start()</code>가 호출되면 새 스레드가 생기고 그 스레드에서 <code>run()</code>이 수행</strong>된다. <code>start()</code> 같은 경우 해당 스레드가 실행되고 있는지 상태를 체크한 뒤에 스레드를 생성하기 때문에 같은 스레드에서 두 번 <code>start()</code>를 호출하면 <code>IllegalThreadStateException()</code>이 발생한다. 반면 <code>run()</code>은 코드만 수행하기 때문에 여러 번 호출해도 상관은 없지만, 새로운 스레드가 생성되지 않기 때문에 <code>run()</code>을 직접 사용할 이유가 없다.</p>
</br>
</br>

<hr>
<p><strong>reference:</strong></p>
<p>「이것이 자바다」, 신용권</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Thread - 1 개요]]></title>
            <link>https://velog.io/@hsbang_thom/Java-Thread-1</link>
            <guid>https://velog.io/@hsbang_thom/Java-Thread-1</guid>
            <pubDate>Sat, 11 Dec 2021 07:58:44 GMT</pubDate>
            <description><![CDATA[<h1 id="1-프로세스-스레드-process--thread">1. 프로세스, 스레드 Process &amp; Thread</h1>
</br>

<ul>
<li><p><strong>Process</strong>: 
  메모리를 할당 받아 애플리케이션을 수행하는 것, 또는 그 애플리케이션이다.</p>
  </br>
</li>
<li><p><strong>Thread</strong>: 
  프로세스 내에서 한 가지 작업을 수행하기 위한 코드의 실행 흐름을 뜻한다. 코드들이 실처럼 이어져 있다고 해서 붙여진 이름이다.</p>
</li>
</ul>
</br> 

<p>프로세스 간의 작업은 독립적이다. 따라서 한 프로세스가 다른 프로세스의 작업에 영향을 주지 않는다. 간단한 예로 크롬과 사파리를 같이 켜놨을 때 크롬이 오류가 생겨 꺼지더라도 사파리는 문제없이 계속 작동한다. 반면 스레드는 한 프로세스 안에서 발생하기 때문에 하나의 스레드에서 오류가 발생하면 프로세스 자체가 종료될 수 있으므로 다른 스레드에도 영향을 미치게 된다. 크롬을 쓰는데 한 탭에서 에러가 발생하면 크롬 자체가 꺼지는 것과 같다. </p>
</br>


<h2 id="메인-스레드">메인 스레드</h2>
</br>

<p>모든 자바 애플리케이션은 메인 스레드에서 <code>main()</code>을 실행하면서 시작된다. 기본적으로 <code>main()</code> 이 실행되면 순차적으로 코드를 수행하고, 코드 수행이 끝나면 실행이 종료된다.
</br>
멀티 스레드 작업 환경을 만들면 모든 스레드가 실행 완료될 때까지 프로세스가 종료되지 않는다. 아래는 멀티 스레드를 간략히 도식화한 것이다.
</br>
</br></p>
<p><img src="https://images.velog.io/images/hsbang_thom/post/beaf485f-32fd-4423-9dcc-e70458b44c78/thread-1.png" alt=""></p>
</br>
</br>

<h1 id="2-작업-스레드-생성과-실행">2. 작업 스레드 생성과 실행</h1>
</br>

<p>멀티 스레드 애플리케이션을 개발하려면 먼저 몇 개의 작업을 병렬로 실행할지 결정하고 작업별로 스레드를 생성해야 한다. 
</br></p>
<p>스레드를 생성하는 방법은 크게 두 가지이다.
</br></p>
<ol>
<li><code>java.lang.Thread</code>를 직접 생성</li>
<li><code>Thread</code> 를 상속하는 서브 클래스를 통해 생성</li>
</ol>
</br>

<p>차례대로 알아보도록 하자.</p>
</br>
</br>

<h2 id="스레드-생성-thread-클래스로부터-직접-생성">스레드 생성: Thread 클래스로부터 직접 생성</h2>
<p><code>Thread</code> 클래스를 생성할 때 <code>Runnable</code>를 매개값으로 갖는 생성자를 호출하여 생성하는 방법이다. </p>
</br>

<pre><code class="language-java">Thread thread = new Thread(Runnable target);</code></pre>
</br>

<p><code>Runnable</code> 은 추상 메서드로 <code>run()</code>을 하나 가지고 있다. 따라서 <code>Runnable</code>을 구현하는 클래스에서 오버라이딩하여 스레드에서 작업할 코드를 써주면 된다.</p>
</br>

<pre><code class="language-java">public class Task implements Runnable{

    @Override
    public void run(){

        //..실행할 코드
    }
}</code></pre>
</br>
</br>

<p>그리고 <code>Runnable</code>을 구현한 클래스를 <code>Thread</code> 생성자의 인자로 넣어주어서 객체를 생성하면된다.
</br></p>
<pre><code class="language-java">Ruunable task = new Task();
Thread thread = new Thread(task);</code></pre>
</br>
</br>

<p>익명 구현 객체를 작성하면 구현 클래스를 따로 작성할 필요 없이 코드를 좀 더 간결하게 짤 수 있다. 
</br></p>
<pre><code class="language-java">Thread thread = new Thread(Runnable() {

    public void run(){
        // .. code
    }

}</code></pre>
</br>

<p>작업 스레드는 생성되는 즉시 실행되는 것이 아니라 <code>Thread</code>의 <code>start()</code> 를 호출해야 실행된다. 작성한<code>Runnable</code>의 <code>run()</code>을 호출하면 될 것 같지만, 그러면 스레드가 실행되지 않는다. 두 메서드의 차이점은 아래에서 좀 더 자세히 다뤄본다.
</br>
</br></p>
<p>0.5초 주기로 비프음을 발생시키면서 동시에 프린팅하는 작업이 있다고 가정하고 코드로 구현해보면 아래와 같다.</p>
<pre><code class="language-java">public class PrintExample {
    public static void main(String[] args) {
        Toolkit toolkit = Toolkit.getDefaultToolkit();

        for(int i=0 ; i&lt;5 ; i++){
            toolkit.beep();
            try{ Thread.sleep(500);} catch (Exception e){}
        }

        for(int i=0 ;i&lt;5; i++){
            System.out.println(&quot;ding&quot;);
            try{ Thread.sleep(500);} catch (Exception e){}
        }
    }
}</code></pre>
</br>

<p>위 코드에서는 메인 스레드에서만 작업이 실행되고 있다. 따라서 위 코드를 실행하면 0.5초마다 beep()이 5번 수행되고, 이어서 ding이 0.5초 간격으로 5번 출력되는 결과가 나올 것이다. 병렬처리를 위해 작업 스레드를 추가해서 처리해보자. 먼저 <code>Runnable</code>을 구현하고 있는 클래스를 하나 만든다.
</br></p>
<pre><code class="language-java">public class BeepTask implements Runnable {

    @Override
    public void run() {
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        for(int i=0 ; i&lt;5 ; i++){
            toolkit.beep();
            System.out.println(&quot;beep&quot;);
            try{ Thread.sleep(500);} catch (Exception e){}
        }
    }

}</code></pre>
<pre><code class="language-java">public class PrintExample2 {

    public static void main(String[] args) {
        BeepTask beepTask = new BeepTask();
        Thread beepThread = new Thread(beepTask);
        beepThread.start(); // 작업 스레드 시작

        for(int i=0 ;i&lt;5; i++){
            System.out.println(&quot;ding&quot;);
            try{ Thread.sleep(500);} catch (Exception e){}
        }
    }
}</code></pre>
<pre><code class="language-java">ding
beep
ding
beep
ding
beep
ding
beep
ding
beep</code></pre>
<p>동시에 작업이 일어나고 있는 것을 확인할 수 있다. 정확한 같은 시간이라기보다 병렬적으로 작업이 수행된다는 것이 좀 더 정확하겠다. 스레드를 실행하더라도 반드시 같은 시점에 동작하지는 않기 때문이다. 
</br>
</br></p>
<p><code>BeepTask</code> 와 같은 클래스를 만들지 않고 익명 객체 또는 람다식을 이용해서도 스레드를 작성할 수 있다. 
</br></p>
<pre><code class="language-java">public class PrintExample2 {

    public static void main(String[] args) {
//        BeepTask beepTask = new BeepTask();
//        Thread beepThread = new Thread(beepTask);
//        beepThread.start();

        // anonymous object 이용
        Thread beepThread = new Thread(new Runnable(){
            @Override
            public void run(){
                Toolkit toolkit = Toolkit.getDefaultToolkit();
                for(int i=0 ; i&lt;5 ; i++){
                    toolkit.beep();
                    System.out.println(&quot;beep&quot;);
                    try{ Thread.sleep(500);} catch (Exception e){}
                }
            }
        });

    // lambda
        Thread beepThread = new Thread(()-&gt;{
            Toolkit toolkit = Toolkit.getDefaultToolkit();
                for(int i=0 ; i&lt;5 ; i++){
                    toolkit.beep();
                    System.out.println(&quot;beep&quot;);
                    try{ Thread.sleep(500);} catch (Exception e){}
                }
        });

        beepThread.start();

        for(int i=0 ;i&lt;5; i++){
            System.out.println(&quot;ding&quot;);
            try{ Thread.sleep(500);} catch (Exception e){}
        }
    }
}</code></pre>
</br>
</br>

<h2 id="스레드-생성-thread-서브-클래스로부터-생성">스레드 생성: Thread 서브 클래스로부터 생성</h2>
</br>

<p><code>Thread</code>는 <code>Runnable</code>을 구현하고 있다. 따라서 <code>Thread</code>를 상속하는 클래스를 만들면 <code>Runnable.run()</code>을 재정의할 수 있고, <code>Thread.start()</code>도 사용할 수 있다. 
</br>
</br>
아래는 Thread를 상속하는 서브 클래스로 스레드를 작성하는 예시이다. 생성자를 통해 인자를 받으면 객체의 필드와 비교해서 메세지를 출력하는 스레드이다.
</br></p>
<pre><code class="language-java">public class WorkThread extends Thread {

    private final int comparison = 80;
    private int number;

    public WorkThread (int number){
        this.number = number;
    }

    @Override
    public void run(){
        String msg = &quot;your num is bigger&quot;;
        if(number&gt;comparison){
            System.out.println(msg);
        }

    }
}</code></pre>
</br>

<pre><code class="language-java">public class ThreadExam {

    public static void main(String[] args) {
        WorkThread workThread = new WorkThread(90);
        workThread.start();
    }
}</code></pre>
</br>

<p>이 역시 익명 객체로 만들 수 있다.</p>
<pre><code class="language-java">public class ThreadExam {

    public static void main(String[] args) {
//        WorkThread workThread = new WorkThread(90);
//        workThread.start();

        Thread thread = new Thread(){

          public void run(){
              for(int i=0 ; i&lt;5 ; i++){
                  System.out.println(&quot;beep&quot;);
                  try{ Thread.sleep(500);} catch (Exception e){}
              }
          }

        };
        thread.start();

        for(int i=0 ;i&lt;5; i++){
            System.out.println(&quot;ding&quot;);
            try{ Thread.sleep(500);} catch (Exception e){}
        }
    }
}</code></pre>
</br>
</br>
`Thread`를 직접 작성하거나 서브 클래스를 이용하거나 하는 방법을 봤는데, 익명 구현 객체를 만드는 것이 가장 깔끔하기는 하다. 다만 객체의 기능을 명확하게 분리하기 위해서 따로 작성하는 것이 좋을 듯하다. 

</br>
</br>
</br>

<h2 id="스레드-이름">스레드 이름</h2>
<p>스레드는 식별을 목적으로 이름을 가지고 있다. <code>main()</code>은 main 스레드의 이름이고, 개발자가 직접 생성한 스레드는 Thread-n이라는 이름이 설정된다. <code>Thread</code>의 <code>setName()</code>, <code>getName()</code> 통해 이름을 변경하거나 확인할 수 있다. 그런데 두 메서드는 인스턴스 메서드이므로 <code>Thread</code> 객체의 참조가 필요하다. 스레드의 참조를 알 수 없을 때 static method인 <code>Thread.currentThread()</code>을 이용하면 현재 코드를 수행하고 있는 스레드의 인스턴스를 얻을 수 있다. 
</br>
</br></p>
<h1 id="3-스레드-우선순위concurrency-parallelism">3. 스레드 우선순위(Concurrency, Parallelism)</h1>
<p>멀티 스레드는 동시 또는 병렬적으로 실행된다. 동시성은 싱글 코어에서 멀티 스레드를 실행할 때 각 스레드를 번갈아가며 수행해나가는 것을 말한다 (앞서 잠깐 언급했듯 동시라는 것이 정확히 같은 시간에 일어난다는 것과는 조금 다르다). 동시성은 각 스레드를 조금씩 번갈아가며 빠르게 수행한다. 병렬성은 멀티 코어 상황에서 코어마다 스레드를 담당하여 작업을 한 번에 처리하는 것을 말한다. 
</br>
</br></p>
<p><img src="https://images.velog.io/images/hsbang_thom/post/0410ff4f-06ef-472c-8cab-eeee971ce705/thread-2.png" alt=""></p>
</br>
</br>

<p>작동하는 스레드의 개수가 cpu보다 많을 경우 스레드를 어떤 순서로 동시적으로 실행할 것인지를 정해야 하는데, 이를 <strong>스레드 스케줄링</strong>이라고 한다. 이 스케줄링에 의해 스레드들은 아주 짧은 시간에 번갈아가면서 해당 스레드의 <code>run()</code>을 조금씩 실행한다.</p>
</br>

<h3 id="스레드-스케줄링-priority-vs-round-robin">스레드 스케줄링: priority vs. round robin</h3>
<p>자바에는 스케줄링에 두 가지 개념이 있다.
</br></p>
<ul>
<li><p><strong>Priority</strong></br>
우선순위를 정해 우선순위가 높을수록 실행 상태를 더 많이 가지도록 한다. 1부터 10까지 개발자가 부여할 수 있기 때문에 개발자가 스레드를 제어할 수 있다. <code>Thread.setPriority()</code>를 통해서 우선순위를 설정할 수 있으며, 설정하지 않았을 때 스레드는 default로 5의 우선순위를 가진다. 값을 설정할 때에는 Thread에 제공되어 있는 상수를 이용해도 된다. </p>
</br>
``` java
  Thread.setPriority(Thread.MAX_PRIORITY); // 10
  Thread.setPriority(Thread.NORM_PRIORITY); // 5
  Thread.setPriority(Thread.MIN_PRIORITY); // 1
```
</br>
</li>
<li><p><strong>Round-Robin scheduling</strong></br>
순환할당방식은 시간 할당량(time slice)을 정해서 하나의 스레드를 정해진 시간만큼 실행하고 다른 스레드를 실행하는 방식이다. 이는 JVM에 의해 정해지므로 코드로 제어할 수는 없다. </p>
</li>
</ul>
</br>
</br>

<h1 id="4-동기화-메소드와-동기화-블록">4. 동기화 메소드와 동기화 블록</h1>
<p>다음은 멀티 스레드에서 중요한 기능인 개념인 메서드와 동기화 블럭에 대해 설명한다. 먼저 이 개념들을 왜 사용해야 하는지에 대해서 알아보자.</p>
</br>
</br>

<h2 id="공유-객체를-사용할-때-주의점">공유 객체를 사용할 때 주의점</h2>
<p>싱글 스레드 프로그램에서는 한 개의 스레드가 객체를 독차지해서 사용하면 되지만, 멀티 스레드 프로그램에서는 여러 스레드가 객체를 공유해서 작업해야 하는 경우가 있다. 이 경우, 스레드 A를 사용하던 객체가 스레드 B에 의해 상태가 변경될 수 있기 때문에 스레드 A가 의도했던 것과는 다른 결과를 산출할 수도 있다. 
</br></p>
<p>User1이 Calculator라는 객체의 a 필드를 100이라고 저장한다고 하자. 그런데 User1가 스레드를 끝내기 전에 User2가 해당 객체의 a 필드를 다시 50으로 저장해버렸다. User1가 작업을 끝내고 a 필드를 출력하면 100이 정상적으로 출력될까? 아래 코드는 위 내용을 표현하고 있다.
</br></p>
<pre><code class="language-java">package org.java.chap12.multi_threads;

public class Calc {

    private int memory;

    public int getMemory(){
        return this.memory;
    }

    public void setMemory(int memory){
        this.memory = memory;
        try{
            Thread.sleep(2000);
        } catch (Exception e){}
        System.out.println(Thread.currentThread().getName() + &quot; :&quot;+this.memory);
    }
}</code></pre>
<pre><code class="language-java">package org.java.chap12.multi_threads;

public class User1 extends Thread {

    private Calc calc;

    public void setCalc(Calc calc){
        setName(&quot;User1&quot;);
        this.calc = calc;
    }

    public void run(){
        calc.setMemory(100);
    }
}</code></pre>
<pre><code class="language-java">package org.java.chap12.multi_threads;

public class User2 extends Thread{

    private Calc calc;

    public void setCalc(Calc calc){
        setName(&quot;User2&quot;);
        this.calc = calc;
    }

    public void run(){
        calc.setMemory(50);
    }
}</code></pre>
<pre><code class="language-java">package org.java.chap12.multi_threads;

public class SharedObject {

    public static void main(String[] args) {
        Calc calc = new Calc();
        User1 user1 = new User1();
        user1.setCalc(calc);
        user1.start();

        User2 user2 = new User2();
        user2.setCalc(calc);
        user2.start();

    }
}</code></pre>
<pre><code>50
50</code></pre></br>

<p>위 코드에서는 user1과 user2라는 스레드가 있고 두 스레드 모두 Calc 객체를 공유 참조하고 있다. user1이 먼저 실행되어서 Calc 객체의 memory 필드의 값을 100으로 설정했다. 그런데 user2 스레드가 시작되고 값을 50으로 설정하자 두 스레드 모두에서 memory 값이 50으로 변경되었다. </br>
</br></p>
<h2 id="동기화-메서드-및-동기화-블록">동기화 메서드 및 동기화 블록</h2>
<p><strong>위와 같이 멀티 스레드 상황에서 객체를 공유할 때, 한 스레드가 작업 중인 내용을 다른 스레드가 변경할 수 없게 하려면 객체에 잠금을 걸어서 다른 스레드가 사용할 수 없도록</strong> 해야 한다. </br>
멀티 스레드에서 단 하나의 스레드만 실행할 수 있는 코드 영역을 <strong>임계 영역(critical section)</strong>이라고 한다. 자바는 임계 영역을 지정하기 위해 <strong>synchronized</strong> 메서드와 동기화 블록을 제공한다. 스레드가 객체 내부의 동기화 메서드 또는 블록에 들어가면 즉시 객체에 잠금을 걸어 다른 스레드가 임계 영역 코드를 실행하지 못하도록 한다. </br></br> 메서드 선언에 synchronized 키워드를 붙이면 해당 메서드 또는 코드 블록에 들어가는 스레드는 잠금이 걸린다. 해당 키워드는 인스턴스, 정적 메서드 모두에 사용될 수 있다.
</br></br></p>
<pre><code class="language-java">public synchronized void method(){
    //.. 단 하나의 스레드만 실행됨
}</code></pre>
</br>
</br>
동기화 메서드는 메서드 전체가 임계 영역이므로 해당 메서드를 실행하면 스레드에 잠금이 걸리고, 메서드가 종료될 때 잠금이 풀린다. 메서드 전체가 아니라 메서드 일부에만 임계 영역을 지정하고 싶으면 동기화 블록을 만들면 된다.
</br>
</br>

<pre><code class="language-java">public void method(){

    // 여러 스레드가 실행 가능

    synchronized(공유 객체){
        // 하나의 스레드만 실행
    }

    // 여러 스레드 실행 가능
}</code></pre>
<p><img src="https://images.velog.io/images/hsbang_thom/post/3a19236a-c151-422a-9271-6c47098955b8/thread-3.png" alt="">
</br></p>
<p>그림에서 보는 것처럼 동기화가 걸려있는 블럭이나 메서드는 한 스레드가 사용 중이면 다른 스레드에서 실행할 수 없다. 하지만 일반 메서드는 같이 실행이 가능하다. </p>
<p>이를 이전 예제에 적용해보면 아래와 같다.</br></p>
<pre><code class="language-java">
public class Calc {

    private int memory;

    public int getMemory(){
        return this.memory;
    }

    public synchronized void setMemory(int memory){
        this.memory = memory;
        try{
            Thread.sleep(2000);
        } catch (Exception e){}
        System.out.println(Thread.currentThread().getName() + &quot; :&quot;+this.memory);
    }
}</code></pre>
</br>

<p>각 스레드에서 값을 입력하는 메서드였던 setMemory()에 동기화를 적용했다. 이제 수행 결과를 보자.
</br></p>
<pre><code class="language-java">public class SharedObject {

    public static void main(String[] args) {
        Calc calc = new Calc();
        User1 user1 = new User1();
        user1.setCalc(calc);
        user1.start();

        User2 user2 = new User2();
        user2.setCalc(calc);
        user2.start();

    }
}</code></pre>
<pre><code class="language-java">User1 :100
User2 :50</code></pre>
</br>

<p>스레드 user1이 값을 입력하는 동안에는 user2스레드에서 <code>setMemory()</code>를 실행하지 않으므로 <code>user1.setMemory()</code>가 정상적으로 100을 출력할 때까지 대기하게 된다. 따라서 user1이 작업을 끝날때까지 안전하게 calc 객체를 사용할 수 있게 된다.
</br>
</br></p>
<p>다음은 동기화 메서드가 아닌 동기화 블럭을 통해 동기화를 구현한 것이다.</p>
<pre><code class="language-java">public void setMemory(int memory){

        synchronized (this){
            this.memory = memory;
//        try{
//            Thread.sleep(500);
//        } catch (Exception e){}
            System.out.println(Thread.currentThread().getName() + &quot; :&quot;+this.memory);
        }

    }</code></pre>
<p>동기화 블럭을 이용하면 해당 블럭에만 동기화를 걸고 나머지 메서드 블럭에는 공동으로 실행할 수 있는 코드를 적어줄 수 있다.
</br>
</br></p>
<hr>
<p><strong>source:</strong></p>
<p><a href="https://github.com/thom-droid/basic-java/tree/master/src/main/java/chap12/thread/cooperation_thread">github</a></p>
<p><a href="https://github.com/thom-droid/basic-java/tree/master/src/main/java/chap12/thread/cooperation_thread2">github2</a></p>
<p><strong>reference:</strong></p>
<p>「이것이 자바다」, 신용권</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Lombok] @Builder.Default]]></title>
            <link>https://velog.io/@hsbang_thom/Lombok-Builder.Default</link>
            <guid>https://velog.io/@hsbang_thom/Lombok-Builder.Default</guid>
            <pubDate>Thu, 02 Dec 2021 12:54:50 GMT</pubDate>
            <description><![CDATA[<h1 id="builder"><code>@Builder</code></h1>
<p>Lombok에서 제공하는 이 어노테이션은 생성자 인자를 메서드 체인을 통해 명시적으로 대입하여 생성자를 호출할 수 있게 빌더 클래스를 생성 해준다. 빌더 클래스와 IDE의 자동 완성 기능을 같이 활용하면 생성자 작성 시 오기입 확률과 인자를 누락할 확률을 획기적으로 낮출 수 있다.
</br></p>
<p>doc을 보면 <code>@Builder</code>는 생성자, 메서드 또는 클래스 레벨에서 쓰일 수 있다고 설명되어 있다. 또한 클래스 레벨에서 쓰일 경우 기본적으로 전체 멤버를 생성자의 매개값으로 갖는 <code>private</code> 생성자를 만들어 준다. 이 생성자는 <code>@XArgsConstructor</code>(NoArgs, RequiredArgs) 또는 어떤 생성자도 클래스 내부에 선언하지 않았을 경우에만 생성된다. 반대로 위의 두 조건 중 하나를 했을 경우, 모든 필드를 매개값으로 하는 생성자를 자동으로 선언해서 사용한다. 따라서 이 경우 All Args Constructor가 없으면 컴파일 에러가 발생한다. 
</br>
정리하면 <code>@Builder</code> 클래스 레벨에서 쓰려면 All args constructor가 있어야 한다. 이 외의 생성자는 컴파일 에러를 일으킨다.</p>
</br>

<p>예를 들어 <code>@NoArgsConstructor</code> 를 쓰고 클래스 레벨의 <code>@Builder</code>를 쓰게 되면 All args constructor 없이 기본 생성자만 선언한 것과 같으므로 컴파일 에러가 뜬다. 
</br></br></p>
<p>그런데 JPA나 json parser와 같은 라이브러리를 쓸 때에는 반드시 클래스에 기본 생성자가 있어야 한다.  이 경우 <code>@NoArgsConstructor</code>를 쓸 수 밖에 없다. 그러면 클래스 레벨에서 <code>@Builder</code>를 쓸 수가 없어진다. 방법은 전체 필드를 사용하는 생성자를 직접 선언하고 그 생성자에 <code>@Builder</code> 어노테이션을 쓰든가, 아니면 <code>@NoArgsConstructor</code> 와 <code>@AllArgsConstructor</code>를 모두 쓰면 된다. 
</br></p>
<h2 id="builderdefault"><code>@Builder.Default</code></h2>
<p>Builder 어노테이션은 편리하고 명확하게 객체를 생성할 수 있게 도와준다. </p>
<pre><code class="language-java">@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Pojo {

    private String name;
    private String nickname;
    private List&lt;PojoTwo&gt; pojoTwos = new ArrayList&lt;PojoTwo&gt;();

}</code></pre>
<pre><code class="language-java">public class PojoApp {

    public static void main(String[] args) {
        Pojo pojo = Pojo.builder().name(&quot;철수&quot;).nickname(&quot;짱구친구&quot;).build();
        System.out.println(pojo.toString());

    }
}</code></pre>
</br>

<p>위의 예시처럼 필드 이름을 명시적으로 넣을 수 있어서 생성자 오버로딩 사용 시 시그니처를 신경쓸 필요가 없어서 좋다. 결과는 다음과 같다.
</br></p>
<pre><code class="language-java">Pojo(name=철수, nickname=짱구친구, pojoTwos=null)</code></pre>
</br>

<p>여기서 빌더 패턴을 통해 인스턴스를 만들 때 특정 필드를 특정 값으로 초기화하고 싶다면 <code>@Builder.Default</code>를 쓰면 된다.
</br></p>
<pre><code class="language-java">@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Pojo {

    @Builder.Default
    private String name = &quot;짱구엄마&quot;;
    private String nickname;
    private List&lt;PojoTwo&gt; pojoTwos = new ArrayList&lt;PojoTwo&gt;();

}</code></pre>
<pre><code class="language-java">public class PojoApp {

    public static void main(String[] args) {
        Pojo pojo = Pojo.builder().nickname(&quot;짱구친구&quot;).build();
        System.out.println(pojo.toString());

    }
}</code></pre>
<pre><code class="language-java">Pojo(name=짱구엄마, nickname=짱구친구, pojoTwos=null)</code></pre>
</br>
</br>

<p>이렇게 객체를 원하는 값으로 초기화해서 반환 받을 수 있다. 이번엔 아래 코드의 결과값을 보자.
</br></p>
<pre><code class="language-java">public class PojoApp {

    public static void main(String[] args) {
        Pojo pojo = Pojo.builder().nickname(&quot;짱구친구&quot;).build();
        System.out.println(pojo.toString());

        Pojo pojo1 = new Pojo();
        System.out.println(pojo1.toString());

    }
}</code></pre>
<pre><code class="language-java">Pojo(name=짱구엄마, nickname=짱구친구, pojoTwos=null)
Pojo(name=짱구엄마, nickname=null, pojoTwos=[])</code></pre>
</br>
</br>

<p>빌더를 통해 만든 객체는 <code>List</code> 필드가 <code>null</code>로 초기화 되었고, 빌더 없이 기본 생성자로 생성한 pojo1의 <code>List</code> 필드는 정상적으로 empty List로 초기화되었다. 클래스에서는 분명히 <code>new ArrayList&lt;&gt;</code>로 초기화를 했는데 왜 이런걸까?</p>
</br>
doc을 살펴보면 빌더는 아래와 같이 코드를 만들어준다.

<pre><code class="language-java">class Example&lt;T&gt; {
       private T foo;
       private final String bar;

       private Example(T foo, String bar) {
           this.foo = foo;
           this.bar = bar;
       }

       public static &lt;T&gt; ExampleBuilder&lt;T&gt; builder() {
           return new ExampleBuilder&lt;T&gt;();
       }

       public static class ExampleBuilder&lt;T&gt; {
           private T foo;
           private String bar;

           private ExampleBuilder() {}

           public ExampleBuilder foo(T foo) {
               this.foo = foo;
               return this;
           }

           public ExampleBuilder bar(String bar) {
               this.bar = bar;
               return this;
           }

           @java.lang.Override public String toString() {
               return &quot;ExampleBuilder(foo = &quot; + foo + &quot;, bar = &quot; + bar + &quot;)&quot;;
           }

           public Example build() {
               return new Example(foo, bar);
           }
       }
   }</code></pre>
</br>

<p>필드를 사용하는 생성자와 각 필드의 setter 메서드로 구성된 inner 클래스를 하나 만들어서 그 안에서 원본 클래스의 인스턴스를 리턴한다. 여기서 객체 타입의 필드가 있다고 가정하면, 당연히 내부 클래스에서는 이 객체 타입을 초기화하는 코드가 없다. 따라서 null로 초기화 될 것이고, 이 필드를 포함해서 원본 클래스가 만들어지므로 빌더를 통한 객체 생성에서는 객체 타입 필드가 null인 것이다.</p>
</br>
</br>

<p>이를 해결하기 위해서도 역시 <code>@Builder.Default</code>를 쓰면 된다. 
</br></p>
<pre><code class="language-java">@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Pojo {

    @Builder.Default
    private String name = &quot;짱구엄마&quot;;
    private String nickname;
    @Builder.Default
    private List&lt;PojoTwo&gt; pojoTwos = new ArrayList&lt;PojoTwo&gt;();

}</code></pre>
<pre><code class="language-java">Pojo(name=짱구엄마, nickname=짱구친구, pojoTwos=[])
Pojo(name=짱구엄마, nickname=null, pojoTwos=[])</code></pre>
</br>

<p>두 방식 모두 원하는 대로 초기화가 되었다.
</br>
</br></p>
<h1 id="tl-dr-🙄"><strong>TL; DR 🙄</strong></h1>
</br>
</br>

<ul>
<li><p><code>@Builder</code>를 클래스 레벨에서 쓰면 모든 필드로 생성자를 생성하는 빌더 생성. 이 때 모든 필드 생성자가 선언되어 있어야 함. 이 생성자 없이 다른 생성자 오버로딩 쓰면 컴파일 에러 발생.</p>
</br>
</li>
<li><p>결과적으로는 NoArgsConstructor + AllArgsConstructor + Builder 콤보를 쓰면 됨</p>
</br>
</li>
<li><p><code>@Builder.Default</code>는 빌더로 인스턴스 생성 시 초기화할 값을 정할 수 있음. 빌더 패턴을 쓰는데 필드에 객체 타입이 있다면 꼭 써주자.</p>
</li>
</ul>
</br>
</br>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Class 클래스]]></title>
            <link>https://velog.io/@hsbang_thom/Java-Class-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@hsbang_thom/Java-Class-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Wed, 01 Dec 2021 07:49:15 GMT</pubDate>
            <description><![CDATA[<h1 id="class-클래스">Class 클래스</h1>
<p>자바는 클래스와 인터페이스의 메타 데이터를 <code>java.lang.Class</code>의 클래스를 통해 관리한다. 메타 데이터란 클래스 이름, 필드, 생성자, 메서드 등을 말한다.
</br></p>
<h2 id="class-객체-얻기">Class 객체 얻기</h2>
<p><code>Object</code>의 <code>getClass()</code>를 통해 얻을 수 있다. 해당 메서드는 인스턴스를 생성했을 때만 사용할 수 있다. 인스턴스를 통하지 않고 <code>Class</code>의 정적 메서드인 <code>forName()</code> 을 통해 직접 클래스를 얻어오는 방법도 있다. 이 때 <code>forName()</code>의 인자로는 패키지가 포함된 클래스 또는 인터페이스 이름을 넣어주어야 한다. 
</br></p>
<pre><code class="language-java">@CallerSensitive
public static Class&lt;?&gt; forName(String className)
            throws ClassNotFoundException {
    Class&lt;?&gt; caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}</code></pre>
<p><code>String</code>으로 된 클래스 또는 인터페이스를 찾아 내부적으로 <code>Reflection</code>을 이용하고 있는 걸 알 수 있다. 
</br></p>
<p>아래는 간단한 예제이다. 해당 메서드를 이용할 때 인자로 대입된 값으로 클래스를 찾지 못하면 <code>ClassNotFoundException</code>을 발생시키므로 예외처리를 적절히 해주어야 한다.
</br></p>
<pre><code class="language-java">public class ClassExam {

    public static void main(String[] args) throws ClassNotFoundException {


        Author author = new Author(); 
        Class authorClass = author.getClass(); // 인스턴스 생성하여 Class 객체 얻어오기


        Class clazz = Class.forName(&quot;org.java.chap11.apis.clone.Author&quot;); // forName()으로 Class 객체 얻어오기
        System.out.println(clazz.getName());
        System.out.println(Arrays.toString(clazz.getDeclaredMethods()));
        System.out.println(Arrays.toString(clazz.getClasses()));
    }

}</code></pre>
<pre><code class="language-java">org.java.chap11.apis.clone.Author
[public java.lang.String org.java.chap11.apis.clone.Author.toString(), protected java.lang.Object org.java.chap11.apis.clone.Author.clone() throws java.lang.CloneNotSupportedException, public java.lang.String org.java.chap11.apis.clone.Author.getName(), public java.lang.String org.java.chap11.apis.clone.Author.getId(), public void org.java.chap11.apis.clone.Author.setName(java.lang.String), public java.lang.String org.java.chap11.apis.clone.Author.getCountry(), public org.java.chap11.apis.clone.Biography org.java.chap11.apis.clone.Author.getBiography(), public void org.java.chap11.apis.clone.Author.setCountry(java.lang.String), public java.lang.String org.java.chap11.apis.clone.Author.getAge(), public void org.java.chap11.apis.clone.Author.setBooks(java.lang.String[]), public org.java.chap11.apis.clone.Author org.java.chap11.apis.clone.Author.cloneAuthor(), public void org.java.chap11.apis.clone.Author.setBiography(org.java.chap11.apis.clone.Biography), public void org.java.chap11.apis.clone.Author.setId(java.lang.String), public java.lang.String[] org.java.chap11.apis.clone.Author.getBooks(), public void org.java.chap11.apis.clone.Author.setAge(java.lang.String)]</code></pre>
</br>
위와 같은 두 가지 방식으로 클래스를 찾아올 수 있다. 패키지까지 모두 검색하는 것은 복잡할 수 있으므로 인스턴스를 통해 Class 객체를 얻어오는 것이 편해보인다.
</br>
</br>

<h1 id="reflection">Reflection</h1>
<p>위와 같이 클래스나 인터페이스의 메타 정보를 찾아내는 것을 리플렉션이라고 한다. 위 예시에서 사용한 메서드 말고 다양한 메서드를 통해 클래스의 정보를 알아낼 수 있다. </p>
<p>예를 들어 <code>getDeclaredMethods()</code> 는 클래스 내부에 선언된 생성자의 정보를 배열의 형태로 받아온다. 상속받은 생성자는 무시하는데, 상속된 내용까지 확인하고 싶다면 <code>getMethods()</code> 를 이용하면 된다. 
</br>
</br></p>
<h1 id="동적-객체-생성">동적 객체 생성</h1>
<p><code>Class</code> 클래스의 <code>newInstance()</code>를 활용하면 런타임 시에 지정해둔 클래스를 동적으로 객체를 생성할 수도 있다.
</br></p>
<p><code>newInstance()</code>는 두 가지 예외를 발생시킨다. 따라서 예외 처리를 해주어야 한다.
</br></p>
<ul>
<li><strong><code>InstantiationException</code></strong>:    
  대입된 클래스가 추상 클래스이거나 인터페이스여서 인스턴스화할 수 없는 경우  </br></li>
<li><strong><code>IllegalAccessException</code></strong>: 
클래스나 생성자가 접근제한자로 인해 접근할 수 없을 경우</br>

</li>
</ul>
<p><code>newInstance()</code> 리턴 타입은 <code>Object</code>이므로 생성한 인스턴스를 변수에 저장하기 위해서는 변수 타입에 맞게 캐스팅을 해주어야 한다. 그런데 컴파일 단계에서는 어떤 타입의 변수를 쓸지 알 수가 없으므로 인터페이스를 구현하여 처리한다(구현 클래스의 형태는 인터페이스만 구현하면 형을 다양하게 바꿀 수 있으므로).  아래의 예시를 보자.
</br></p>
<pre><code class="language-java">public interface TestInterface {

    void method1();
}</code></pre>
<pre><code class="language-java">public class RuntimeClass1 implements TestInterface{

    @Override
    public void method1() {
        System.out.println(&quot;runtime class 1&quot;);
    }
}</code></pre>
<pre><code class="language-java">public class RuntimeClass2 implements TestInterface {

    @Override
    public void method1() {
        System.out.println(&quot;RuntimeClass 2&quot;);
    }
}</code></pre>
<pre><code class="language-java">public class NewInstanceExam {

    public static void main(String[] args) {

        try {
            Class clazz = Class.forName(&quot;org.java.chap11.apis.clazz.RuntimeClass1&quot;);
            TestInterface testInterface = (TestInterface) clazz.newInstance();
            testInterface.method1();
        } catch (ClassNotFoundException | InstantiationException |IllegalAccessException e ){
            e.printStackTrace();
        }

    }
}</code></pre>
<pre><code class="language-java">runtime class 1</code></pre>
</br>

<p>위와 같이 동적으로 생성할 인스턴스를 <code>forName()</code>으로 얻어온 뒤 <code>TestInterface</code> 타입으로 캐스팅해주면 인터페이스를 구현하는 인스턴스를 생성할 수 있다.
</br></p>
<hr>
<p><strong>source</strong>: <a href="https://github.com/thom-droid/basic-java/tree/master/src/org/java/chap11/apis/clazz">github</a></p>
<p><strong>reference</strong>: 「이것이 자바다」, 신용권</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Proxy]]></title>
            <link>https://velog.io/@hsbang_thom/Proxy</link>
            <guid>https://velog.io/@hsbang_thom/Proxy</guid>
            <pubDate>Wed, 01 Dec 2021 07:19:46 GMT</pubDate>
            <description><![CDATA[<h1 id="proxy">Proxy</h1>
<p>클라이언트와 서버의 통신 사이에 놓여 있는 서버로, 클라이언트 대신 서버로부터 정보를 얻거나 처리하는 역할을 한다. 소프트웨어 디자인 관점으로 보면, 두 객체 간에 존재하는 인터페이스로써, 보이지 않는 곳에서(behind the scenes) 클라이언트의 요청을 처리하는 위임 객체(agent object)라고 할 수있다. 
</br></p>
<p>가장 큰 특장점은 아래와 같다.</p>
<ul>
<li><strong>익명성 (anonymity)</strong>
  클라이언트의 요청을 프록시에서 받으면, 프록시가 직접 서버와 통신한다. 이를 통해 클라이언트의 public IP를 숨기고 프록시의 IP를 통해 통신할 수 있다.  </br></li>
<li><strong>캐싱을 이용한 속도</strong>
  최초 리소스에 대한 요청이 있을 때, 프록시는 해당 요청의 결과를 서버로부터 받아 지정된 곳에 캐싱한다. 같은 요청이 왔을 때 다시 서버로 통신할 필요가 없이, 캐싱된 리소스를 통해 속도를 높일 수 있다. 또한 같은 요청에 대해 매번 서버에 접속할 필요도 줄어들게 되므로 bandwitdth도 줄일 수 있다.  </br></li>
<li><strong>로깅</strong>
  요청 IP와 요청된 리소스, 방문 시간 등 다양한 로그를 저장하므로 다양하게 활용할 수 있다.</li>
</ul>
</br>
</br>
앞선 주제였던 Http 의 관점에서 보면 Http message을 통해 요청/응답을 할 때 클라이언트와 서버 사이에 존재해서 요청/응답에 가공, 캐싱, authentication 등의 추가 작업을 하는 모든 객체를 말한다고 할 수 있다.

<p><img src="https://images.velog.io/images/hsbang_thom/post/cb89a7cd-5507-4dee-a40f-a55b23ba9f23/image.png" alt=""></p>
<hr>
<p><strong>reference:</strong> </p>
<ul>
<li><a href="https://www.notion.so/Proxy-40db2e7849b04b75a5fb202300030cf4">what is a proxy server</a>?</li>
<li><a href="https://en.wikipedia.org/wiki/Proxy_pattern"><strong>https://en.wikipedia.org/wiki/Proxy_pattern</strong></a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Object.clone()]]></title>
            <link>https://velog.io/@hsbang_thom/JAVA-Object.clone</link>
            <guid>https://velog.io/@hsbang_thom/JAVA-Object.clone</guid>
            <pubDate>Mon, 29 Nov 2021 11:10:25 GMT</pubDate>
            <description><![CDATA[<h1 id="objectclone">Object.clone()</h1>
<p>객체를 하나 복제하는 것을 말한다. 구현 범위에 따라 thin clone, deep clone가 있다. 복제하고자 하는 클래스는 <code>Cloneable</code> 인터페이스를 구현하여 해당 클래스가 복제 가능하다고 명시해주어야 한다. 그리고 <code>clone()</code> 을 호출할 때에는 <code>CloneNotSupportedException</code>을 예외처리 해주어야 한다.</p>
</br>
</br>

<p><code>clone()</code>은 복제한 클래스의 필드값을 복사한다. 정확히는 같은 클래스 타입의 인스턴스를 만들고 필드 내의 필드를 원본 클래스의 값들로 초기화하는 것이다 (primitive의 경우는 값 자체를 복사하고, reference 타입은 reference 번지를 복사해서 대입한다). 따라서 <strong>기본적으로는</strong> 복제, 원본 간에 동등성이 있으나 동일성은 없다. </p>
</br>

<pre><code class="language-java">x.clone() != x; // true
x.clone().equals(x); // true
x.clone().getClass() == x.getClass(); // true</code></pre>
</br>
</br>
뒤에 설명하겠지만, 원본과 복제가 완벽하게 독립적으로 존재하게 하려면 `clone()`을 오버라이딩 해야할 수 있다. 만약 원본의 필드값이 `primitive`이거나 immutable 상태이면 복제 객체에서 필드를 변경해도 원본과는 상관없지만, mutable한 reference 타입을 변경하면 복제에서 변경된 내용이 원본에도 적용되기 때문이다. 전자와 후자의 경우를 고려했을 때의 차이를 thin clone, deep clone이라고 한다.

</br>
</br>
</br>
</br>

<h2 id="thin-clone">thin clone</h2>
<p>아래는 복제하고자 하는 객체의 멤버들이 모두 <code>primitive</code>인 경우이다. 이런 경우에는 단순한 복사, 얕은 복사를 해도 원본과 복제가 독립적으로 존재한다고 봐도 무방하다.</p>
</br>
</br>

<pre><code class="language-java">public class Author implements Cloneable{

    private String id;
    private String name;
    private String age;
    private String country;

    public Author(String id, String name, String age, String country) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.country = country;
    }

    public Author() {
    }

//getter/setter / toString()

public Author cloneAuthor(){
        Author cloned = null;
        try {
            cloned = (Author) this.clone();
        } catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return cloned;
    }</code></pre>
<p>위와 같이 <code>clone()</code> 오버라이딩 할 때 위임하는 예외를 잡아주는 코드를 써주면 된다. 그리고 <code>Cloneable</code> 인터페이스를 구현해야 한다.
</br>
</br></p>
<pre><code class="language-java">public class CloneTest {

    public static void main(String[] args) {

        Author author1 = new Author(&quot;1&quot;,&quot;franz kafka&quot;,&quot;30&quot;,&quot;czechia&quot;);

        Author clonedAuthor = author1.cloneAuthor();
        clonedAuthor.setName(&quot;Guy de Maupassant&quot;);

        System.out.println(author1.toString());
        System.out.println(clonedAuthor.toString());

                System.out.println(author1.equals(clonedAuthor));
        System.out.println(author1.getClass() == clonedAuthor.getClass());
        System.out.println(author1 == clonedAuthor);

    }
}</code></pre>
<pre><code class="language-java">Author{id=&#39;1&#39;, name=&#39;franz kafka&#39;, age=&#39;30&#39;, country=&#39;czechia&#39;}
Author{id=&#39;1&#39;, name=&#39;Guy de Maupassant&#39;, age=&#39;30&#39;, country=&#39;czechia&#39;}

false
true
false</code></pre>
<p>결과는 아래와 같이 나온다. 앞에 설명했듯 원본과 복제는 같은 타입이지만 다른 인스턴스이다.</p>
</br>
</br>

<h2 id="deep-clone">deep clone</h2>
<p>이번엔 아래의 예시를 보자.</p>
</br>
</br>

<pre><code class="language-java">public class CloneTest {

    public static void main(String[] args) {

        Author author1 = new Author(&quot;1&quot;,&quot;franz kafka&quot;,&quot;30&quot;,&quot;czechia&quot;);
        String[] books = {&quot;the Metamorphosis&quot;, &quot;a country doctor&quot;};
        author1.setBooks(books);

        Author clonedAuthor = author1.cloneAuthor();

        clonedAuthor.setName(&quot;Guy de Maupassant&quot;);
        clonedAuthor.getBooks()[0] =&quot;bel ami&quot;;

    }
}</code></pre>
</br>
</br>

<pre><code class="language-java">original :Author{id=&#39;1&#39;, name=&#39;franz kafka&#39;, age=&#39;30&#39;, country=&#39;czechia&#39;, books=[the Metamorphosis, a country doctor]}
cloned   :Author{id=&#39;1&#39;, name=&#39;franz kafka&#39;, age=&#39;30&#39;, country=&#39;czechia&#39;, books=[the Metamorphosis, a country doctor]}

==========before mod============

modification from the cloned

original :Author{id=&#39;1&#39;, name=&#39;franz kafka&#39;, age=&#39;30&#39;, country=&#39;czechia&#39;, books=[**bel ami**, a country doctor]}
cloned   :Author{id=&#39;1&#39;, name=&#39;Guy de Maupassant&#39;, age=&#39;30&#39;, country=&#39;czechia&#39;, books=[**bel ami**, a country doctor]}</code></pre>
</br>
</br>

<p>앞선 예시와 기본 틀은 같지만, book이라는 <code>String[]</code>이 필드로 추가되었다. 배열은 자바에서 reference type에 해당한다. 결과를 보면 <code>primitive</code>인 필드가 수정되었을 경우에 원본과 복제에 독립적으로 값이 수정되지만, <strong>복제 객체에서 참조하고 있는 객체의 값을 bel ami 로 수정했더니 원본도 값이 변하는 걸 확인</strong>할 수 있다. 
</br>
</br></p>
<p>이것은 원본과 복제 모두 같은 배열의 참조 값을 가지고 있기 때문이다. 현재 참조된 객체는 mutable하므로 한 쪽에서 값을 수정하면 참조하고 있는 다른 한 쪽에도 영향을 미치게 된다. 즉, 원본과 복제 객체의 완벽한 독립이 이루어지지 않는다.
</br>
</br></p>
<p>이것을 해결하려면 <code>clone()</code>을 오버라이딩하여, 복제 시에 reference 타입의 멤버들도 새로 만들어 해당 참조 값을 대입해주어야 한다. 아래의 코드는 Author 클래스에 <code>Object.clone()</code>을 오버라이딩 한 것이다.</p>
</br>
</br>

<pre><code class="language-java">    @Override
        protected Object clone() throws CloneNotSupportedException {
        // thin clone
          Author cloned = (Author) super.clone();
          // deep clone
          cloned.books = Arrays.copyOf(this.books, this.books.length);
          cloned.biography = new Biography();
          return cloned;
       }</code></pre>
</br>
</br>

<p><code>primitive</code> 또는 immutable한 필드는 <code>Object.clone()</code>을 통해 복제를 한다. 그리고 mutable한 reference type의 필드인 books 배열과 Biography의 경우 새로 참조를 만들어 복제 객체의 해당 필드에 대입해준다. 이제 타입은 같지만 다른 인스턴스인 객체를 참조하게 된다.
</br>
</br></p>
<p>마지막으로 수정된 코드는 아래와 같다.</p>
<pre><code class="language-java">public class CloneTest {

    public static void main(String[] args) {
                // author1 
        Author author1 = new Author(&quot;1&quot;,&quot;franz kafka&quot;,&quot;41&quot;,&quot;czechia&quot;);
        String[] books = {&quot;the Metamorphosis&quot;, &quot;a country doctor&quot;};
        author1.setBooks(books);

        Biography bio1 = new Biography(&quot;1883&quot;,&quot;1924&quot;);
        author1.setBiography(bio1);

                // clone 
        Author clonedAuthor = author1.cloneAuthor();

                // modification in clone
        clonedAuthor.setName(&quot;Guy de Maupassant&quot;);
        clonedAuthor.setAge(&quot;43&quot;);
        clonedAuthor.getBooks()[0] =&quot;bel ami&quot;;
        clonedAuthor.getBiography().setBirth(&quot;1850&quot;);
        clonedAuthor.getBiography().setDeath(&quot;1893&quot;);

    }
}</code></pre>
<p> 출력하면 아래와 같다.</p>
<pre><code class="language-java">original :Author{id=&#39;1&#39;, name=&#39;franz kafka&#39;, age=&#39;41&#39;, country=&#39;czechia&#39;, books=[the Metamorphosis, a country doctor], biography=Biography{death=&#39;1924&#39;, birth=&#39;1883&#39;}}
cloned   :Author{id=&#39;1&#39;, name=&#39;franz kafka&#39;, age=&#39;41&#39;, country=&#39;czechia&#39;, books=[the Metamorphosis, a country doctor], biography=Biography{death=&#39;null&#39;, birth=&#39;null&#39;}}

==========before mod============

modification from the cloned

original :Author{id=&#39;1&#39;, name=&#39;franz kafka&#39;, age=&#39;41&#39;, country=&#39;czechia&#39;, books=[the Metamorphosis, a country doctor], biography=Biography{death=&#39;1924&#39;, birth=&#39;1883&#39;}}
cloned   :Author{id=&#39;1&#39;, **name=&#39;Guy de Maupassant**&#39;, age=&#39;43&#39;, country=&#39;czechia&#39;, books=[**bel ami, a country doctor**], biography=Biography{**death=&#39;1893&#39;, birth=&#39;1850&#39;}**}</code></pre>
<p>위와 같이 복제된 객체에서의 변경은 복제 객체 내에서만 일어난 것을 확인할 수 있다. 
</br>
</br></p>
<hr>
<p><strong>source</strong>: <a href="https://github.com/thom-droid/basic-java/tree/master/src/org/java/chap11/apis/clone">github</a></p>
<p><strong>reference</strong>: 「이것이 자바다」, 신용권</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JPA] 1. 개요]]></title>
            <link>https://velog.io/@hsbang_thom/JPA-1.-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@hsbang_thom/JPA-1.-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Mon, 29 Nov 2021 10:47:33 GMT</pubDate>
            <description><![CDATA[<h1 id="jpa-java-persistence-api">JPA: Java Persistence API</h1>
<p>자바 진영 ORM 기술 표준. hibernate를 비롯한 다른 ORM 프레임워크의 기능을 추상화하여 만든 인터페이스, <strong>ORM 표준 기술 명세</strong>이다. </p>
<h1 id="1-jpa의-등장">1. JPA의 등장</h1>
<p>기존 애플리케이션 개발 방식에서는 DB와 통신하기 위해서 데이터 접근 계층에 많은 SQL 관련 코드를 작성해야 했다. 쿼리를 일일이 작성하는 것은 물론이고, 요구사항이 바뀌는 경우 DTO부터 SQL 쿼리를 모두 수정해야 해서 유지보수도 힘들었다. 애플리케이션 개발은 점점 데이터 코드 작성에 의존적이게 되었다. 실제로 나도 myBatis로 개발을 할 때 로직보다 SQL을 짜는 데에 더 많은 시간을 보내면서 &#39;이게 개발인가?&#39; 싶은 생각도 들었었다.
</br>
</br></p>
<h2 id="11-패러다임-불일치">1.1 패러다임 불일치</h2>
<p>이러한 문제는 객체 지향적인 개발 언어와 테이블의 관계로 구성된 데이터베이스의 근본적인 지향점이 다르기 때문에 발생했다. 객체 지향 언어에서는 상속, 추상, 다형성 등 객체 간의 복잡성을 통해 데이터를 관리한다. 반면 관계형 데이터베이스는 이러한 개념을 거의 사용하지 않으며, 데이터 자체에 중점을 두고 구조화되어 있다. </p>
</br>

<h3 id="111-상속">1.1.1 상속</h3>
<p>자바에서는 상속을 통해서 객체 간의 관계를 표현한다. 하지만 DB에 저장될 때 이를 구현하려면 번거롭다. 예를 들어 Cuisine이라는 슈퍼 클래스가 있고 서브 클래스 Chinese, Korean, Japanese가 각각 이를 상속한다고 해보자. 기존의 JDBC template으로 Chinese 객체를 저장한다고 하면, Chinese의 내용과 Cuinese을 따로 SQL로 저장해야 할 것이다. 조회하는 것은 더 귀찮다. 저장한 Chinese 객체를 찾으려면 Cuisine과 Chinese 테이블을 조인해서 조회한 결과를 Chinese 객체를 반환하는 코드를 짜야할 것이다. </p>
</br>
JPA에서는 위의 번거로운 로직을 알아서 수행해준다. 서브 클래스인 Chinese를
``em.persist(chinese)`` 와 같이 저장하면, 알아서 Cuisine과 Chinese 를 insert하는 쿼리를 수행한다. 조회할 때에도 마찬가지로 알아서 조인 쿼리를 작성하여 조회해준다.

</br>
</br>

<h3 id="112-연관관계">1.1.2 연관관계</h3>
<p>자바와 RDBMS에서 모델링의 가장 큰 차이점은 <strong>객체는 참조</strong>를 사용하지만 <strong>RDBMS는 외래키</strong>를 사용한다는 점이다. <strong>외래키로는 관련된 테이블을 모두 조회할 수 있지만, 자바에서는 참조를 가진 쪽만 데이터에 접근할 수 있다</strong>. 객체도 테이블처럼 cuisineId와 같이 외래키와 동일한 필드를 객체 내에 직접 저장하는 방법을 생각해볼 수 있지만, cuinse의 정보를 불러올 때 결국 <code>chinese.getCuisineId()</code>를 사용하고 다시 해당 id로 DB에 접근해야 하므로 Cuisine과 Chinese 간의 관계를 제대로 표현할 수 없는, 객체지향적이지 못한 코드를 써야한다.</p>
<p>Chinese를 조회하는 코드를 보면 아래와 같다.</p>
<pre><code class="language-java">public class Chinese extends Cuisine{

    String id;
    String cuisineId;
    String ingredient;

}</code></pre>
<pre><code class="language-java">String cuisineId = chinese.getCuisineId();
Cuinise cuisine = dao.selectCuisine(cuisineId);</code></pre>
<p>좀 더 객체 지향적으로 모델링을 한다고 하면 아래와 같이 관계된 객체의 참조값을 조회하면 될 것이다.</p>
<pre><code class="language-java">Cuisine cuisine = dao.selectCuisine(chinese.getCuisine());</code></pre>
</br>
이렇게 되면 Chinese 객체에 저장된 Cuisine을 불러온다는 표현이 되므로 두 객체 간의 관계는 표현할 수 있지만, DB에도 이런 과정을 적용하기 위해서는 Chinese를 저장할 때 Cuisine의 참조값을 개발자가 직접 저장을 해주어야 한다. 객체 - DB 간의 패러다임을 해소하기 위해 작업이 늘어나는 것이다.
</br>
</br>

<p>JPA를 통해 엔티티 간의 관계를 설정해놓고, 아래와 같이 수행하면 chinese에 cuisine으로 접근할 수 있는 참조값을 외래키로 변환하여 적절하게 insert 쿼리를 작성하여 저장해준다. 
</br></p>
<pre><code class="language-java">chinese.setCuisine(cuisine);
jpa.persist(chinese);</code></pre>
</br>
</br>
후에 데이터를 조회할 때, 이와 같이 작성하면 외래키를 참조로 변환시켜 chinese 에 관련된 cuisine을 손쉽게 얻어올 수 있다.
</br>
</br>

<pre><code class="language-java">Chinese chinese = jpa.find(Chinese.class, chineseId);
Cuisine cuisine = chinese.getCuisine();</code></pre>
</br>
</br>
객체 간의 연관관계를 객체 지향적인 코드를 사용하면서도 실제 DB에도 편리하게 적용할 수 있게 된다.

</br>
</br>
</br>
</br>

<h2 id="12-객체-그래프-object-graph-탐색">1.2 객체 그래프 (object graph) 탐색</h2>
<p><strong>객체 간의 관계를 통해 표현되는 네트워크, 관계도</strong>. 객체 참조를 통해 객체를 찾는 것을 객체 그래프 탐색이라고 한다. 예를 들어 Member, Team, Order, OrderItem 등 여러 객체가 관계를 맺고 있을 때,
</br>
</br></p>
<pre><code class="language-java">    member.getOrder().getOrderItem()...</code></pre>
<p>이와 같은 방식으로 객체 그래프를 탐색할 수 있다.
</br>
</br></p>
<p>SQL을 사용하여 직접 객체 그래프를 탐색하게 되면 탐색할 범위를 정해놔야 한다. 위 코드를 쿼리로 보면 아래와 같이 짜야 한다.</p>
<pre><code class="language-java">SELECT member.*, order.*, orderItem.*
FROM member m
JOIN order o ON o.id = m.order_id 
....</code></pre>
<p>이와 같은 상황에서 검색할 컬럼이 하나 더 있거나, order_id를 통해 또 다른 테이블의 정보를 얻어오고자 한다면 쿼리 일부와 dao 일부를 모두 건드려야 할 것이다.
</br>
</br></p>
<p>따라서 아래와 같은 코드가 있다고 할 때</p>
<pre><code class="language-java">Member member = dao.find(memberId);
member.getOrder();
member.getOrder().getOrderItem();</code></pre>
</br>
</br>
기존 myBatis와 같이 ORM을 사용하지 않는 환경에서는 2번 3번 라인의 코드가 제대로 작동하는지는 확신할 수가 없다. order 필드가 null이라면 3번 라인에서 NPE가 발생할 것이다. 이를 방지하려면 개발자가 SQL코드를 일일이 확인해서 사용할 수 있는 범위를 파악할 수 밖에 없다. 그래서 위와 같이 개발을 하다보면 조인한 테이블마다 조회하는 메서드를 만들게 된다. 코드가 불필요하게 많아지고, 쿼리가 길어지고 복잡해지니 내가 작성하지 않은 경우 어떻게 써야 하나 막막해서 안쓰게 된다. 그래서 또 새로운 쿼리를 만들게 되고, 그러면 코드 관리는 더욱 어렵게 된다.

</br>
</br>
</br>
</br>

<h3 id="121-jpa에서의-객체-그래프-탐색">1.2.1 JPA에서의 객체 그래프 탐색</h3>
<p>JPA에서는 객체를 사용하는 시점에 적절히 쿼리를 만들어 수행해준다. 그래서 따로 조인 쿼리를 짜지 않더라도 관계 설정이 되어 있는 여러 엔티티에서 다른 엔티티를 호출하는 메서드를 사용하면, 해당 엔티티의 정보를 객체로 반환 받을 수 있다. 따라서 연관관계를 맺어놓은 객체들을 마음껏 탐색할 수 있게 된다. 이것이 가능한 것은 <strong>프록시 객체</strong>를 이용한 <strong>지연 로딩 기능</strong>이 있기 때문인데, 이는 뒤에서 천천히 설명한다.
</br>
</br></p>
<h2 id="13-동일성-비교">1.3 동일성 비교</h2>
<p>같은 조건으로 데이터를 검색했을 떄, 기존의 JDBC에서는 동일성을 보장하기 위해서는 대부분의 객체마다 equals()와 hashcode()를 오버라이딩해서 사용하여야 했다. </p>
<pre><code class="language-java">public Member getMember(String memberId){

    String sql = &quot;SELECT * FROM MEMBERS WHERE MEMBER_ID = ?&quot;;

    /..../
    return new Member(....);
}</code></pre>
</br>

<pre><code class="language-java">String memberId = &quot;100&quot;;
Member m1 = dao.getMember(&quot;100&quot;);
Member m2 = dao.getMember(&quot;100&quot;);</code></pre>
</br>
</br>


<p>하지만 <strong>JPA는 같은 트랜잭션에서 같은 객체가 조회되는 것을 보장한다.</strong> </p>
<pre><code class="language-java">String memberId = &quot;100&quot;;
Member m1 = jpa.find(Member.class, memberId);
Member m2 = jpa.find(Member.class, memberId);</code></pre>
<p>위와 같은 코드에서 m1==m2는 true를 리턴한다. 이는 JPA에서 사용하는 <strong>영속성 컨텍스트</strong>를 통해 가능한 것인데, 자세한 것은 이 다음 장에서부터 확인할 수 있다.</p>
</br>
</br>

<hr>
</br>
</br>
reference:   **「자바ORM표준JPA」, 김영한 ** ]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Annotation]]></title>
            <link>https://velog.io/@hsbang_thom/Java-annotation</link>
            <guid>https://velog.io/@hsbang_thom/Java-annotation</guid>
            <pubDate>Fri, 05 Nov 2021 08:05:47 GMT</pubDate>
            <description><![CDATA[<h1 id="어노테이션">어노테이션</h1>
<p>어노테이션은 메타데이터와 비슷하다. 적용되는 클래스나 메서드, 파라미터 등 자바 내에서 사용되는 다양한 데이터에 추가적인 정보를 제공한다. 이런 특징을 이용해서 특정 어노테이션을 붙인 파라미터를 아규먼트 리졸버로 처리한다든가 활용할 수 있는 방법들이 있다.
</br></p>
<p>용도는 크게 아래와 같이 세 가지이다.
</br></p>
<ul>
<li>컴파일러에게 코드 문법 에러 체크하도록 정보 제공</li>
<li>빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공</li>
<li>런타임 시 특정 기능하도록 정보 부여</br>
</br>
# 어노테이션 타입 정의와 적용

</li>
</ul>
</br>

<pre><code class="language-java">public @interface AnnotationName{}</code></pre>
</br>

<p>interface 키워드 앞에 @를 붙여 선언할 수 있다. 
</br></p>
<h2 id="어노테이션-엘리먼트">어노테이션 엘리먼트</h2>
</br>
어노테이션 내부에 작성되는 멤버를 element라고 한다. 엘리먼트는 String Enum, Class, 배열 타입을 가질 수 있으며, 작성법은 아래와 같다.
</br>

<pre><code class="language-java">public @interface AnnotationName{

    String element1();
    int element2() default 5;

}</code></pre>
<p>특이한 점은 메서드를 선언하는 것처럼 선언 후 소괄호를 붙여주어야 한다는 것이다.</p>
<p>또한 엘리먼트 별로 default라는 키워드로 초기값을 설정해줄 수 있다. default 값을 선언해놓으면 해당 엘리먼트를 명시하지 않거나 엘리먼트 표기 후 값을 넣징 않았을 경우 default 값이 적용된다. 하지만 반대로 default를 선언하지 않은 엘리먼트를 사용할 때 값을 적지 않으면 컴파일 에러가 발생한다. </br>
</br></p>
<p>기본적으로 어노테이션을 사용하는 방법은 아래와 같다.</p>
<pre><code class="language-java">
    @AnnotationName
       (element1 = &quot;value must be assigned&quot;, element2=3 // 기재 안 할 경우 5)
       public void doSomething(){

       }</code></pre>
</br>

<p>또한 value라는 엘리먼트는 이름을 명시하지 않아도 바로 쓸 수도 있다. Spring에서 많이 쓰는 @PathVariable이나 @RequestMapping에 변수명이나 주소를 바로 쓸 수 있는 것도 사실은 value 엘리먼트를 생략할 수 있기 때문이다. 
</br></p>
<pre><code class="language-java">
public @interface AnnotationName{

    String value();

}


@AnnotationName(&quot;value&quot; // 또는 value = &quot;value&quot;로 적어도 됨)
public void doSomething(){

    //... code here

}</code></pre>
</br>

<h2 id="어노테이션-적용-대상">어노테이션 적용 대상</h2>
<p><code>@Target</code> 이라는 어노테이션을 통해 특정 어노테이션이 적용될 범위를 지정할 수 있다. <code>@Target</code> 어노테이션에는 <code>ElementType</code>이라는 enum 타입의 엘리먼트가 있어서 이를 통해 명시적으로 적용 범위를 표시할 수 있다. <code>ElementType</code> enum의 필드는 아래와 같다.</p>
<pre><code class="language-java">
public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}</code></pre>
</br>

<p>명시적으로 표현되어 있어서 따로 부연할 부분은 없는 것 같다. 예를 들어 <code>ElmentType.METHOD</code>라고 명시한다면 해당 어노테이션은 method에만 적용된다는 의미이다.
</br></p>
<p><code>@Target</code> 어노테이션에는 위에서도 언급한 기본 엘리먼트인 value 엘리먼트가 있다. <code>ElementType</code> 배열 타입 형태이다. 따라서 여러 값을 한 번에 지정할 수 있다. 아래는 <code>@Target</code>의 api doc 설명이다.
</br></p>
<pre><code class="language-java">@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}</code></pre>
</br>
</br>
코드로 표현해보면 아래와 같다. 
</br>
</br>

<pre><code class="language-java">@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface AnnotationName{
    elements...
}</code></pre>
</br>

<p>해석하자면 <code>@Target</code>으로 설정된 <code>@AnnotationName</code> 은 파라미터와 필드에만 적용될 수 있다는 의미이다.</p>
</br>
</br>

<h2 id="어노테이션-유지-정책-retention">어노테이션 유지 정책 (@Retention)</h2>
<p>어노테이션을 어느 범위까지 유지할 것인가에 대해 설정하는 어노테이션이다. 
</br></p>
<pre><code class="language-java">@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}</code></pre>
</br>

<p><code>@Target</code>과 마찬가지로 <code>RetentionPolicy</code> 열거 타입의 엘리먼트를 기본적으로 가지고 있다. 아래는 api doc이다.
</br></p>
<pre><code class="language-java">public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}</code></pre>
</br>

<ul>
<li><strong><em>SOURCE</em></strong>: 소스코드 상태에서만 유지. 컴파일 될 때 유지 안됨.</li>
<li><strong><em>CLASS</em></strong>: 바이트코드까지 정보 유지. 설정 안되어 있을 때 기본 동작. Reflection으로 읽어올 수 없음.</li>
<li><strong><em>RUNTIME</em></strong>: 바이트 코드 까지 어노테이션 정보 유지. Reflection으로 읽어올 수 있음.</li>
</ul>
</br>

<p>Reflection은 런타임 시에 클래스 메타 정보 얻는 기능이다. Class 클래스와 java.lang.reflect의 메서드를 통해 특정 클래스나 인터페이스의 메타데이터(필드, 메서드, 생성자 정보 등)를 얻어오는 것을 말한다. 어노테이션의 유지 정책이 RUNTIME인 경우가 많다.</p>
</br>

<h2 id="런타임-시-어노테이션-정보-사용하기">런타임 시 어노테이션 정보 사용하기</h2>
</br>
어노테이션 자체는 메타데이터를 담고 있는 표식에 불과하지만 리플렉션을 이용해 어노테이션 적용 여부, 엘리먼트 값을 처리할 수 있다. 클래스에 적용된 어노테이션은 java.lang.Class 클래스를 이용하면 되나, 이외 메서드, 필드, 생성자 등은 java.lang.reflect 패키지의 배열 타입을 얻어내야 한다. 해당되는 메서드는 getMethods(), getConstructors() 등이 있다. 해당 내용은 뒤에 소개될 Class 클래스 편에서 좀 더 알아보도록 한다.
</br>
</br>

<hr>
<p><a href="https://github.com/thom-droid/basic-java/tree/master/src/org/java/chap06/annotation">예제에 사용된 코드</a></p>
<p><strong>reference</strong>: </p>
<ul>
<li>신용권, 이것이 자바다 </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Set collection]]></title>
            <link>https://velog.io/@hsbang_thom/Set-collection</link>
            <guid>https://velog.io/@hsbang_thom/Set-collection</guid>
            <pubDate>Thu, 30 Sep 2021 13:45:53 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">public Interface Set&lt;E&gt;
extends Collection&lt;E&gt;</code></pre>
<h1 id="set">Set</h1>
<p>List, Map과 함께 대표적인 Java 인터페이스이다. List와 함께 Collection interface를 상속하는 인터페이스다. 집합이라는 의미이고, 수학에서 쓰이는 용어와 같은 뜻을 가지고 있다. </p>
<h1 id="set의-특징">Set의 특징</h1>
<p>Set 인터페이스의 가장 큰 특징은 List 인터페이스와 달리 unordered이다. 따라서 인덱스를 관리하지 않기 때문에 값을 저장하고 값을 불러올 때 순서를 고려하지 않는다. 또한 duplicate를 허용하지 않는다. 같은 데이터가 입력되었을 때 중복저장하지 않는다. null은 최대 하나만 입력을 허용한다. </p>
<p>Index가 관리되지 않기 때문에 set을 검색하게 될 경우 Iterator 인터페이스를 주로 활용한다. 활용은 다른 collection interface와 동일하다.</p>
<pre><code class="language-java">Set&lt;String&gt; set = new HashSet&lt;&gt;();
set.add(&quot;des bonbons&quot;);
set.add(&quot;le chocolat&quot;);
...
Iterator&lt;String&gt; itr = set.iterator();
while(itr.hasNext()){
    String str = itr.next();
}</code></pre>
<p>또는 for each를 통해 iteration하면 된다</p>
<pre><code class="language-java">for(String str : set){
    syso(str);
}
</code></pre>
<p>주의할 것은 Iterator 인터페이스의 remove()를 사용했을 때 실제 set에서 데이터가 지워진다는 점이다.</p>
<pre><code class="language-java">while(itr.hasNext()){
    String str = itr.next();
    if(str.equals(&quot;des bonbons&quot;)){
        itr.remove(); // set에서 삭제
    }
}</code></pre>
<p>물론 set에도 remove()가 있어서 특정 인자를 넣게 되면 해당 인자에 해당하는 데이터가 지워지게 된다.
</br></p>
<h2 id="hashset">HashSet</h2>
<p>Set 인터페이스의 가장 대표적인 구현 클래스이다. HashTable, 정확히는 HashMap을 기반으로 한 클래스이다. 따라서 bucket, capacity와 같은 개념들이 그대로 적용된다. </p>
<p><a href="https://velog.io/@hsbang_thom/HashMap">참고: HashMap</a></p>
<p>HashMap과 마찬가지로 데이터를 저장할 때 hashCode()로 hash code를 얻어내고, 이를 토대로 이미 저장되어 있는 데이터의 hash code를 비교한다. 만약 새로 저장하려는 값의 hash code가 이미 존재하고 있으면 equals()로 두 값을 비교하고, 같은 값이면 저장하지 않는다. 개발자가 지정한 DTO의 경우 이 두 메서드를 overriding하여 다른 인스턴스여도 같은 값을 가진 객체이면 중복 저장하지 못하게 하는 이유이다. 역시 HashMap의 자료를 참고하면 이해가 될 것이다.</p>
<p>hash code로 값을 저장, 접근하기 때문에 add(), remove(), contains(), size()에 대해서는 시간 복잡도가 상수값을 갖는다.</p>
<p>HashMap에서처럼 capacity의 threshold에 다다르게 되면 doubling 한 뒤 rehashing되므로, iteration 성능이 중요한 요소일 경우 capacity를 높게 설정해주는 것이 좋을 수 있다.</p>
<p>또한 동기화처리가 내부적으로 되어있지 않기 때문에 multi-thread 환경에서 set에 대해 구조적인 변동이 발생했을 때 값이 제대로 저장되지 않는 경우가 발생한다. 보통 Collection.synchronizeSet()와 같이 &quot;wrap&quot; 해주면 된다.</p>
</br>

<p>TL;DR</p>
<ul>
<li>non-dulplicate</li>
<li>unordered</li>
<li>null은 하나만 허용</li>
<li>HashSet은 HashMap에 기반 </li>
</ul>
<hr>
<p><strong>references</strong>:</p>
<p><a href="https://www.baeldung.com/java-hashset">https://www.baeldung.com/java-hashset</a></p>
<p><a href="https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html">https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html</a></p>
<p><a href="https://docs.oracle.com/javase/8/docs/api/java/util/Set.html">https://docs.oracle.com/javase/8/docs/api/java/util/Set.html</a></p>
<p>신용권, 『이것이 자바다』, 한빛미디어</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ArrayList]]></title>
            <link>https://velog.io/@hsbang_thom/ArrayList</link>
            <guid>https://velog.io/@hsbang_thom/ArrayList</guid>
            <pubDate>Fri, 17 Sep 2021 13:24:09 GMT</pubDate>
            <description><![CDATA[<h1 id="arraylist">ArrayList</h1>
<p>List collection을 구현하는 대표적인 클래스. AbstractList 추상 클래스를 상속하고, List를 비롯한 다른 세 interface를 구현한다. 나머지 interface에 대해서는 차차 다루도록 한다.</p>
<pre><code class="language-java">public class ArrayList 
    extends AbstractList
    implements List, Cloneable, Serialization, RandomAccess
</code></pre>
</br>

<h2 id="특징">특징</h2>
</br>

<h3 id="동적-array">동적 array</h3>
<p>List의 기능을 모두 구현하며, 마찬가지로 null과 객체를 상관 없이 저장할 수 있다. 또한 List 를 구현하는 다른 클래스를 ArrayList에 추가할 수도 있다. 동적 array로써 크기를 자유롭게 변경할 수 있다.</p>
<p>ArrayList도 List와 같이 zero-indexed array이며, index를 지정하지 않고 객체를 추가하면 순차적으로 index가 증가한다. 반면 특정 index에서 제거 또는 저장을 수행하면 바로 뒤 index부터 마지막 index까지 모두 앞으로 한 칸 움직여서 다시 정렬한다. </p>
<p>데이터 저장과 삭제 시에 모든 index를 재정렬하므로 이런 작업이 빈번한 곳에서는 ArrayList를 쓰기보다 LinkedList나 HashMap 등을 사용하는 것이 성능 면에서 좋을 수 있다. 하지만 index를 통해 객체에 접근하거나, 마지막 index에 순차적으로 삽입하는 단순 작업이라면 ArrayList를 쓰는 것이 무난할 것이다. 이는 뒤에 설명할 time complexity를 보면 좀 더 자세히 알 수있다.
</br></p>
<h3 id="capacity와-doubling-time-complexity">capacity와 doubling, time complexity</h3>
<p>ArrayList의 기본 capacity는 10으로, 항상 실제 list size보다 크게 설정되어 있다. index가 늘어남에 따라 자동으로 capacity가 doubling, 두 배로 늘어난다. 그리고 doubling 할 때 배열의 크기를 두 배 늘리고, list 내에 있는 값들을 모두 복사하여 옮긴다.</p>
<p>따라서 Capacity가 여유 있을 때 add()를 수행할 경우 time complexity는 O(1)이지만, capacity에 도달하여 doubling 될 때는 값들을 모두 복사하여야 하여야 하므로 값의 개수만큼(n)의 time complexity를 가지게 되어 O(n) worst case가 된다. 하지만 doubling 되는 경우가 단순 저장보다 빈번하지 않기 때문에 O(1)의 성능을 O(n)으로 평가해버리면 불합리할 수 있다. 이럴 때 amortized O(1)이라는 표현을 써서 doubling이 되지 않을 조건일 때 O(1)의 time complexity를 가진다는 것을 표현한다. (이 부분은 내가 알고리즘을 자세히 공부하지 않아서 디테일하게는 모르지만, 정리하여 얻은 부분이다. 기회가 된다면 time complexity와 space complexity에 대해서 공부해봐야겠다.). </p>
<p>get()의 경우 하나의 index에서만 값을 얻어오므로 항상 O(1)의 time complexity를 가진다.</p>
<p>메서드들의 time complexity를 정리해보면 다음과 같다.</p>
<ul>
<li><strong>add</strong> : amortized O(1). O(n) worst case.</li>
<li><strong>add(idx, obj)</strong>: O(n). 지정한 index 뒤로 한 칸씩 밀려나야 하므로 그렇다.</li>
<li><strong>get</strong>: O(1)</li>
<li><strong>remove, contain, indexOf</strong>: linear search 이므로, O(n) (앞 index부터 순차적 으로 iteration)</li>
</ul>
<h3 id="iteration">iteration</h3>
<p>Iterable interface를 구현하므로 이를 통해 iteration할 수 있지만, List collection에서는 ListIterator를 제공한다. ListIterator는 bidirectional search를 수행하게 해주며, 이를 통해 List를 구현하는 클래스가 특정 index에서 객체를 저장하거나 삭제할 수 있도록 한다.</p>
<pre><code class="language-java">List&lt;Integer&gt; list = new ArrayList&lt;Integer&gt;();
    for(int i = 0 ; i&lt;10;i++) {
        list.add(i);

    }
    ListIterator&lt;Integer&gt; it = list.listIterator(list.size());
    List&lt;Integer&gt; result = new ArrayList&lt;Integer&gt;(list.size());

    while(it.hasPrevious()) { // last index부터 검색가능
        result.add(it.previous());
    }

    Collections.reverse(result); // order reverse
    System.out.println(list.equals(result)); // true

</code></pre>
<p>또 하나 알 수 있는 점은 List의 경우 index와 저장된 값이 일치하는 두 List의 경우 equals() 수행 시 true를 반환한다는 것이다.
</br></p>
<p>TL;DR</p>
<ul>
<li>ArrayList는 List collection의 대표적인 구현 클래스. </li>
<li>Dynamic array로 크기를 바꿀 수 있음</li>
<li>get(), add()의 경우 O(1)의 time complexity. 따라서 단순 검색, 마지막 index에 순차적 저장의 경우 성능이 괜찮음.</li>
<li>특정 index에 값을 저장, 삭제할 경우 요소를 재배열하게 되어 비효율적일 수 있음(LinkedList가 대안)</li>
<li>linear search 이므로 remove(), contain(), indexOf()의 경우 O(n). </li>
</ul>
</br>

<hr>
</br>

<p><strong>references</strong> : 
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/List.html">https://docs.oracle.com/javase/8/docs/api/java/util/List.html</a>
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html">https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html</a>
<a href="https://www.baeldung.com/java-arraylist">https://www.baeldung.com/java-arraylist</a></p>
<p>time complexity
<a href="https://medium.com/@satorusasozaki/amortized-time-in-the-time-complexity-of-an-algorithm-6dd9a5d38045">https://medium.com/@satorusasozaki/amortized-time-in-the-time-complexity-of-an-algorithm-6dd9a5d38045</a>
<a href="https://zeddios.tistory.com/60">https://zeddios.tistory.com/60</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[List Collection]]></title>
            <link>https://velog.io/@hsbang_thom/List-Collection</link>
            <guid>https://velog.io/@hsbang_thom/List-Collection</guid>
            <pubDate>Fri, 17 Sep 2021 02:31:55 GMT</pubDate>
            <description><![CDATA[<p>우리가 일반적으로 리스트라는 단어를 떠올리면 순서대로 적힌 목록을 떠올린다. 그것과 비슷한 개념의 interface라고 보면 되겠다.</p>
<p>List collection은 ordered collection이다. 삽입된 요소마다 index를 순차적으로 부여하며, 이 index를 통해 요소가 관리된다. </p>
<p>후에 알아볼 또 다른 collection인 Set과의 가장 큰 차이점은, List는 null을 허용한다는 점이다. 또한 null 또는 중복된 요소가 삽입될 수도 있다.</p>
<p>배열과 같이 zero-indexed 이다. 알아둘만한 점은 크기가 정해진 배열과 달리 값이 저장될 때 자동으로 capacity가 증가한다.</p>
<p>List 구현체에서 linear search를 하도록 설계되어 있는 경우가 많기 때문에 iteration을 수행할 때는 이런 점을 고려해서 수행해야 한다.</p>
<p>가장 대표적인 List 구현체로는 ArrayList와 Vector, LinkedList가 있다. </p>
</br>

<p><strong>TL;DR</strong>
</br></p>
<ol>
<li>순서를 가지는 collection</li>
<li>index를 통해 요소가 관리됨</li>
<li>null 허용, duplicate 허용</li>
<li>linear search의 경우가 많음 (iteration performance 고려)</br>
</br>
</li>
</ol>
<hr>
<p>*<em>reference: *</em>
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/List.html">https://docs.oracle.com/javase/8/docs/api/java/util/List.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HashMap]]></title>
            <link>https://velog.io/@hsbang_thom/HashMap</link>
            <guid>https://velog.io/@hsbang_thom/HashMap</guid>
            <pubDate>Tue, 14 Sep 2021 13:49:42 GMT</pubDate>
            <description><![CDATA[<h1 id="hashmap">HashMap</h1>
<p>Map 을 구현하는 대표적인 클래스. 
</br></br>
Key를 저장할 때 hash function을 사용하므로 HashMap이라 한다. Value가 저장될 때 key 값이 hashing 처리되고, 처리된 hash code가 compressed되어 index를 생성한다. 해당 index에 entry가 저장된다. 특징을 정리해보면 다음과 같다.</p>
<h2 id="특징">특징</h2>
</br>

<ul>
<li>Map의 성질을 가지고 있으므로 Key, Value pair의 구조를 가지고 있는 Entry 클래스로 데이터를 관리한다. </br></li>
<li>Key 는 반드시 객체여야 한다. 데이터를 저장하고 데이터에 접근할 때, Object의 hashCode()와 equals()를 통해 Key가 일치하는지 확인하기 때문이다. 기본자료형으로 Key를 지정하면 Wrapper class에 의해 boxing 된다.</br></li>
<li>논리적으로 같은 키의 값이 중복으로 입력되면, 마지막으로 입력된 키와 paring된 값으로 업데이트 된다. 즉 중복 키가 허용되지 않는다. </br></li>
<li>Key와 Value 에 null이 참조될 수 있다. 하지만 필요하지 않은 경우 지양해야 할 것이다.</br></li>
<li>배열 기반의 Map이다. 데이터를 검색할 때 hash로 접근하므로 단순 저장/검색으로 계산했을 때 평균 O(1)의 time complexity를 가진다. 따라서 저장된 데이터에 순차적으로 접근하여 검색하는 ArrayList보다 검색 속도가 빠르다. 검색하고자 하는 데이터가 정렬되어 있다면 O(n), binary search를 한다면 O(log n)까지 성능이 올라갈 수 있다. HashMap의 가장 큰 특징이라고 할 수 있다.</br></li>
<li>하지만 각 entry의 입력 순서를 기억하지 않으므로 데이터가 입력되는 순서가 중요하다면 LinkedHashMap을 사용하는 것이 선택지가 될 수 있다.</br></li>
<li>내부적으로 동기화가 되지 않기 때문에 multi thread 환경에서 HashMap에 대한 구조적인 변경(value 변경이 아닌 add나 delete, remove과 같이 entry를 조정하는 등의 변경)을 대비해 반드시 외부적으로 동기화를 선언해주거나, Collections.synchronizedMap(new HashMap()); 과 같이 동기화 설정을 해주는 것이 좋다. (동기화에 대해서는 개념만 알고 있기 때문에 지금은 이 정도로만 써둔다)</br>

</li>
</ul>
<p>TL;DR</p>
<blockquote>
<ol>
<li>Hashing function을 사용하는 Map이다.</li>
<li>단순 데이터 탐색, iteration을 할 경우 ArrayList보다 훨씬 빠르다. </li>
<li>Key 중복이 안된다.</li>
</ol>
</blockquote>
</br>

<h2 id="hashmap을-이루고-있는-개념">HashMap을 이루고 있는 개념</h2>
</br>

<p>HashMap을 비롯한 Map 컬렉션을 구현하는 클래스들은 대부분 아래와 같은 개념으로 이루어져 있다.</p>
<h3 id="bucket">Bucket</h3>
<p>HashMap은 앞서 말한 것처럼 배열 기반의 Map이며, 각 array 내의 node를 bucket이라고 한다. entry, 저장된 요소를 bucket이라고 보면 되겠다. </p>
<h3 id="capacity">Capacity</h3>
<p>저장할 수 있는 bucket 의 수. HashMap의 용량 정도라고 생각하면 되겠다. HashMap은 기본 생성자로 생성할 경우 기본 capacity가 16이다. HashMap을 생성할 때에 생성자의 인자로 capacity를 설정해줄 수 있다</br></p>
<h3 id="threshold">Threshold</h3>
<p>역치. HashMap에 저장 공간이 커져야 되는 시점이다. load factor * capacity로 계산할 수 있다. load factor는 언제 값이 늘어나야 하는지 정해주는 단위이다. 기본 설정은 0.75f 로, 이는 capacity의 75%를 말한다.</br>
예를 들어 기본 생성자로 HashMap을 생성했다고 하면, capacity는 16, load factor는 0.75f로 설정되어 있어, threshold는 16 * 0.75 = 12 이다. 즉 해당 HashMap은 16개의 entry를 저장할 수 있으며, map의 size가 12개에 도달했을 때 HashMap의 크기가 커지게 된다. 크기는 두 배 가까이로 커지게 된다. 따라서 16이었던 크기는 32로 커지게 된다.</p>
<h3 id="rehashing">Rehashing</h3>
<p>HashMap이 threshold에 도달해 크기가 커질 때 내부적으로 모든 bucket에 대해 rehashing이 발생한다. 구조적인 변경이 발생한다는 의미이다. load factor를 낮게 설정해서 complexity를 낮출 수는 있지만, 그만큼 rehashing이 자주 일어날 수 있다. rehashing 자체가 자원 소모가 큰 편이므로 HashMap에 저장될 데이터가 예측 가능하다면 capacity를 적절히 높이 설정하여 이런 문제를 어느 정도 해결 할 수 있겠다.</br>
</br></p>
<h2 id="hashmap-methods-constructor">HashMap methods, constructor</h2>
<h3 id="constructor">constructor</h3>
<p>생성자는 기본 생성자와 더불어 위에서 언급한 capacity, load factor를 파라미터로 하는 생성자와, Map 구현 클래스를 파라미터로 받는 생성자가 있다. </p>
<pre><code class="language-java">
HashMap&lt;String, String&gt; bandNameAndCountry = new HashMap&lt;&gt;;
bandName.put(&quot;radiohead&quot;, &quot;Britain&quot;);
bandName.put(&quot;interpol&quot;, &quot;America&quot;);

HashMap&lt;String, String&gt; bandNameAndCountry2 = new HashMap&lt;&gt;(bandNameAndCountry);

System.out.println(bandNameAndCountry.equals(bandNameAndCountry2)); // true
</code></pre>
<h3 id="method">method</h3>
<p>Map의 메서드를 모두 구현하고 있다. put, get, remove, containValues 등을 모두 사용할 수 있다.</p>
<h4 id="getordefault">getOrDefault</h4>
<p>JDK 1.8부터 추가된 기능. Key를 통해 값을 얻어오지만, Key 가 존재하지 않거나 Key에 매핑된 Vaule가 없는 경우 default 값을 지정해서 리턴시킬 수 있다.</p>
<pre><code class="language-java">HashMap&lt;String, String&gt; hashMap = new HashMap&lt;&gt;();
hashMap.put(&quot;hungry&quot;, &quot;yes&quot;);
System.out.println(hashMap.getOrDefault(&quot;thirsty&quot;, &quot;yeah&quot;)); // yeah
</code></pre>
<p>후술할 entrySet()과 keySet(), values() 등을 통해 HashMap을 iterate시킬 수 있다.
</br></p>
<h2 id="hashmap-기본-활용">HashMap 기본 활용</h2>
<p>HashMap의 entry를 다양한 방법으로 출력할 수 있다.</p>
<h3 id="hashmap-예제">hashMap 예제</h3>
<pre><code class="language-java">HashMap&lt;String, String&gt; hashMap = new HashMap&lt;String, String&gt;();
hashMap.put(&quot;food1&quot;, &quot;hamburger&quot;);
hashMap.put(&quot;food2&quot;, &quot;pizza&quot;);
hashMap.put(&quot;food3&quot;, &quot;ramen&quot;);
hashMap.put(&quot;food4&quot;, &quot;ramen&quot;);</code></pre>
<h3 id="entryset">entrySet()</h3>
<p>Map의 entry를 Set view로 만든 뒤 iterate 시키는 방법이다.</p>
<pre><code class="language-java">for(Map.Entry&lt;String, String&gt; entries : hashMap.entrySet()) {
    System.out.println(&quot;key :&quot;+ entries.getKey() + &quot; value : &quot;+entries.getValue()); 
}

//key :food1 value : hamburger
//key :food3 value : ramen
//key :food2 value : pizza
//key :food4 value : ramen
</code></pre>
<p>앞서 설명했듯 HashMap에서는 데이터의 저장 순서가 중요하지 않기 때문에, iterate 시켰을 때 순서가 다르게 나올 수도 있다.
</br></p>
<h3 id="keyset">keySet()</h3>
</br>
Key만 Set view로 변환하여 해당 Key를 통해 get()로 iterate하는 방법이다. 

<pre><code class="language-java">
for(String s : hashMap.keySet()) {
    System.Out.println(&quot;key + &quot;+s + &quot; value : &quot;+hashMap.get(s));
}

//key :food1 value : hamburger
//key :food3 value : ramen
//key :food2 value : pizza
//key :food4 value : ramen</code></pre>
<h3 id="iterator-활용">Iterator 활용</h3>
<p>Map을 iterate 시킬 때 사용하는 Iterator를 활용할 수 있다.</p>
<pre><code class="language-java">Iterator&lt;Entry&lt;String, String&gt;&gt; entries = hashMap.entrySet().iterator();

while(entries.hasNext()) {
    Entry&lt;String, String&gt; entry = entries.next();
    System.out.println(entry.getKey() + entry.getValue());
}</code></pre>
<p>keySet() 를 통해 위 예제와 같이 get(key)로 iterate 시켜도 된다.</p>
</br>

<h2 id="두-개의-hashmap-비교">두 개의 HashMap 비교</h2>
<p>두 개의 HashMap을 비교하고자 한다면 equals()를 통해 비교할 수 있다. 기본적으로 equals()가 객체에서 사용되었을 때에는 두 객체가 같은 인스턴스를 참조하고 있는지 확인하지만, HashMap에 equals()를 사용했을 때에는 두 개의 HashMap이 가지고 있는 entry들이 논리적으로 동등한지에 대해서만 확인한다. 앞에서 언급했듯 entry의 순서는 상관이 없다. 예를 들어서</p>
<pre><code class="language-java">HashMap&lt;Integer, String&gt; map1 = new HashMap&lt;&gt;();

map1.put(1, &quot;A&quot;);
map1.put(2, &quot;B&quot;);
map1.put(3, &quot;C&quot;);

//Same as map1
HashMap&lt;Integer, String&gt; map2 = new HashMap&lt;&gt;();

map2.put(3, &quot;C&quot;);
map2.put(1, &quot;A&quot;);
map2.put(2, &quot;B&quot;);

//Different from map1
HashMap&lt;Integer, String&gt; map3 = new HashMap&lt;&gt;();

map3.put(1, &quot;A&quot;);
map3.put(2, &quot;B&quot;);
map3.put(3, &quot;C&quot;);
map3.put(3, &quot;D&quot;);

System.out.println(map1.equals(map2));  //true
System.out.println(map1.equals(map3));  //false</code></pre>
<p>위와 같을 경우 map1과 map2가 모두 new 키워드를 통해 생성된 객체이지만 equals() 비교 시 true가 리턴된다.</p>
<h2 id="key의-불변성-immutability">Key의 불변성 (immutability)</h2>
<p>HashMap에서 POJO를 Key로 사용할 때에는 equals()와 hashCode()를 오버라이딩해서 논리적으로 동등한(객체의 데이터가 같은) 비교를 할 수 있도록 해야 한다. Key가 hashCode()의 값으로 저장되는데, 값을 가져올 때에 hashCode()로 bucket의 위치를 찾은 다음, 저장되어있는 hash code와 일치하는지 equals()로 판단한다. 따라서 hashCode()의 결과값이 같더라도 equals()에서 다른 값이라고 판단되면 다른 키로 인식하여 원하지 않는 결과가 나올 수 있다. 
</br></p>
<p>다음의 예를 보면</p>
<pre><code class="language-java">public class MutableKey{
    private String name;

    @Override
    public boolean equals(Object o){
        // 같은 인스턴스를 참조하고 있으면
        if(this == o){
            return true;
        }

        // 파라미터로 받은 객체가 null을 참조하거나 둘의 인스턴스가 다르면
        if(o == null || this.getClass() !=o.getClass()){
            return false;
        }

        MutableKey that = (MutableKey) o;

        //두 데이터의 값 일치를 비교해서 결과 리턴
        return Objects.equals(name, that.name);
    }

    @Override
    public int hashCode(){
        // name이 같을 경우 같은 hashcode 리턴
        return Objects.hash(name);
    }
}</code></pre>
<p>이렇게 Key로 사용할 POJO 내에서 equals()와 hashCode()를 overriding 했을 경우, MutableKey 클래스의 name 필드를 따로 변경하지 않는 이상 HashMap의 키로 사용해도 항상 같은 hashcode를 리턴할 것이다.
</br></p>
<pre><code class="language-java">HashMap&lt;MutableKey, String&gt; hashMap = new HashMap&lt;MutableKey, String&gt;();

hashMap.put(new MutableKey(&quot;name&quot;), &quot;John Doe&quot;);
String value = hashMap.get(new MutableKey(&quot;name&quot;));
System.out.println(value); // John Doe</code></pre>
</br>
</br>

<p>하지만 name field를 변경한 뒤 HashMap에서 값을 얻어오려고 하면, 변경된 변수에 대한 hash code를 가진 Key에 접근하려 하는 것과 같으므로 저장된 Value가 없어 null을 리턴하게 된다.</p>
<pre><code class="language-java">MutableKey key = new MutableKey(&quot;John Doe&quot;);

hashMap.put(key, &quot;happy&quot;);
System.out.println(hashMap.get(key)); // happy

key.setName(&quot;punch&quot;);
System.out.println(hashMap.get(key)); // null</code></pre>
</br>

<p>따라서 이런 점을 예상해서 코드를 짜거나, 되도록이면 immutable한 값을 키로 활용하는 것이 좋을 것 같다.
</br>
</br></p>
<p>이번 포스트에서는 HashMap에 대해서 알아보았다. </p>
<hr>
</br>

<p><strong>references:</strong> </p>
<p><a href="https://www.baeldung.com/java-hashmap-load-factor">https://www.baeldung.com/java-hashmap-load-factor</a></p>
<p><a href="https://www.baeldung.com/java-hashmap">https://www.baeldung.com/java-hashmap</a></p>
<p><a href="https://d2.naver.com/helloworld/831311">https://d2.naver.com/helloworld/831311</a></p>
<p><a href="https://stackoverflow.com/questions/55333952/is-there-an-array-based-map-implementation">https://stackoverflow.com/questions/55333952/is-there-an-array-based-map-implementation</a></p>
<p><a href="https://stackoverflow.com/questions/37959941/what-exactly-is-bucket-in-hashmap">https://stackoverflow.com/questions/37959941/what-exactly-is-bucket-in-hashmap</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP message]]></title>
            <link>https://velog.io/@hsbang_thom/HTTP-message</link>
            <guid>https://velog.io/@hsbang_thom/HTTP-message</guid>
            <pubDate>Thu, 09 Sep 2021 07:23:56 GMT</pubDate>
            <description><![CDATA[<h1 id="http-message">HTTP Message</h1>
<p>이전 포스트 (<a href="https://velog.io/@hsbang_thom/HTTP-overview">HTTP overview</a>)에서 알아본 바와 같이, HTTP 기반의 웹 환경에서 요청과 응답의 데이터를 담고 있는 것이 HTTP message라고 하였다. 이번 포트스에서는 간단히 HTTP Message가 어떤 것인지 간단히 알아본다. 현재 HTTP/2가 등장했지만, 그 전에 HTTP message의 기본 구조를 알아보는 것이 목표이므로  HTTP/1을 중점으로 적는다. </p>
<p>HTTP Message는 ASCII로 인코딩된 <strong>요청과 응답의 데이터</strong>를 가지고 있다. 따라서 
&#39;HTTP Message = HTTP 요청/응답&#39; 이라고 봐도 무방할 것 같다. 이 요청과 응답은 <em>start-line*과 *header</em>, <em>body</em> 세 가지로 구성되어 있다. </p>
<h2 id="start-line">start-line</h2>
<p>Chrome 같은 경우 개발자 도구 network에서 general 영역에 있는 항목과 같다. 크게 세 가지로 구분되어 있다.</p>
<ul>
<li>HTTP method : GET, POST, PUT, DELETE, PATCH 등 요청 시 사용된 method</li>
<li>URL : 
요청하고자 하는 자원의 위치. HTTP method에 따라 요청 시 생성되는 URL의 형태가 달라질 수 있다. 예를 들어 GET의 경우 파라미터가 query string의 형태로 URL에 포함되어 요청이 생성되기 때문에 &quot;<a href="http://abc.org?param=value&quot;">http://abc.org?param=value&quot;</a> 와 같이 &#39;?&#39; 뒤에 지정해놓은 파라미터가 key:value 형태로 넘어가게 된다. </br>
POST의 경우 query string으로도 파라미터를 넘길 수 있지만, form data인 body의 데이터를 전송하게 되므로 URL는 보통 &quot;<a href="http://abc.org/post&quot;">http://abc.org/post&quot;</a> 와 같이 작성하게 된다.</br>    </li>
<li>status code: 
응답의 start-line에 포함되어 있다. 요청을 수행한 결과에 따라 100 ~ 500번대로 나뉜다. </br></br> - <code>100</code> : informational response. 한 번도 만나보지 못한 코드인데, 클라이언트의 추가작업이 필요하거나, 프로토콜의 변경 등이 필요할 때 발생한다고 한다.<ul>
<li><code>200</code> : successful response. 요청을 성공적으로 수행했을 때. 테스트까지 마치고 앱을 실행했을 때 나오면 뿌듯한 응답 상태.</li>
<li><code>300</code> : redirection message. 요청한 URI의 자원이 변경되었거나 하는 경우 등장. POST의 경우 리소스를 만들어서 등록만 하는 method 이므로 redirection 해주지 않으면 302가 뜨는 것을 종종 볼 수 있다.</li>
<li><code>400</code> : client errors. 유효하지 않은 URI나 method로 요청했을 경우 발생하는 상태. 오타이거나 파라미터를 추가 안했다거나 하는 단순한 실수가 많고 클라이언트 측 에러이므로 해결하기 비교적 쉽다.</li>
<li><code>500</code> : server errors. 서버 측 문제. 개발을 연습하는 나의 입장으로는 500과 511을 종종 봤다. 500은 서버 내부 문제, 즉 Spring 쪽에서 코드를 잘 못 썼거나 예외 처리를 하지 않아서 runtime error가 난 경우이고, 511은 Spring security를 쓸 때 발생했었다.</li>
</ul>
</li>
</ul>
<h2 id="headers">Headers</h2>
<p>해당 요청의 메타데이터들을 담긴 영역. 요청/응답의 주체 (요청의 경우 user-agent, host / 응답읭 경우 server)가 누구인지, 어떤 포맷으로 요청, 응답하는지 등에 대한 정보가 담겨 있다. 요청과 응답에 따라 조금 다르지만, 전반적인 구조는 같다.</p>
<p><strong>request header</strong>
<img src="https://images.velog.io/images/hsbang_thom/post/c46c71a3-b52a-40db-80a3-5b755b1a0581/image.png" alt=""> </p>
</br>


<p><strong>response header</strong>
<img src="https://images.velog.io/images/hsbang_thom/post/2c115436-56d0-4b77-a4dd-02350b5b15ae/image.png" alt=""></p>
<p>서버 측에서는 header 정보를 읽어들여 이에 맞는 방식으로 데이터를 바인딩하거나 적절히 포맷된 데이터로 응답할 수 있다. </p>
<h2 id="body">Body</h2>
<p>요청/응답 마지막에 위치한 영역으로 payload의 영역과 같다. 다만 모든 요청/응답이 body를 가지고 있는 것은 아니다. 요청의 경우 GET 방식으로 자원의 URI만 요청한 경우에는 body가 없을 수 있다. 응답의 경우에도 마찬가지인데, POST 방식의 경우 리소스의 생성만 요청하므로 서버에서는 요청 수행 후 성공했다는 응답만 보여주면 된다. 그래서 보통 POST 수행 후에는 개발자가 임의로 redirect를 하는 방식으로 처리해준다.</p>
<p>Spring MVC를 통해 REST api를 구현할 때 <code>@RequestBody</code>나 <code>@ResponseBody</code>를 사용하는데 이 때 해당하는 부분이 이 HTTP body이다. 요청의 body를 받아들여서 바인딩하겠다는 것이거나 서버의 응답을 body로 하겠다는 의미이다.
</br>
</br></p>
<hr>
</br>

<p>*<em>references: *</em> <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages">https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP overview]]></title>
            <link>https://velog.io/@hsbang_thom/HTTP-overview</link>
            <guid>https://velog.io/@hsbang_thom/HTTP-overview</guid>
            <pubDate>Wed, 08 Sep 2021 10:39:05 GMT</pubDate>
            <description><![CDATA[<h1 id="http">HTTP</h1>
<p>HTTP message에 대해 알아보다가, HTTP에 대해 기본적인 내용을 확실하게 정리해본 적이 없다는 것을 느꼈다. Mozilla doc을 참고해서, 내가 이해할 수 있는 방식으로 다시 정리해본다.</p>
</br>
</br>



<p>서버와 클라이언트 간의 통신을 위한 프로토콜. 해당 프로토콜을 통해 클라이언트는 원하는 자원을 요청할 수 있다. 응답 받은 자원(html, css, javascript)을 렌더링하고 각 uri에 해당하는 자원을 포함해 페이지를 재구축하는 작업은 웹 브라우저가 담당한다.</p>
<p><img src="https://images.velog.io/images/hsbang_thom/post/f32e68eb-0a43-4f55-8a29-957b086d145b/image.png" alt=""></p>
<p>서버와 클라이언트 간의 상호작용은 HTTP message를 통해 이루어지며, 이 message를 요청이라고도 한다. </p>
<p><img src="https://images.velog.io/images/hsbang_thom/post/0ecbf802-630f-487e-82c4-50c9717f85f1/image.png" alt=""></p>
<p>HTTP에 맞는 요청이 만들어지면 TCP통신을 통해 서버와 통신하고, 요청에 맞는 응답을 얻어온다. 어떤 통신 방식이든 상관없지만, HTTP는 TCP 통신에 기반하여 설계되었다. 뛰어난 확장성이 특장점으로, 자원을 얻어오는 것 뿐만 아니라 새로운 데이터를 저장하거나 기존 데이터를 수정할 수 있도록 서버에 요청하는 것도 가능하다. 이는 뒤에서 알아보겠지만, HTTP GET, POST, PUT, DELETE와 같은 메서드로 구현되어 있다.
</br></p>
<h2 id="http-components">HTTP components</h2>
<p>거의 모든 요청은 일반적으로 클라이언트로부터 만들어진다. HTTP에 따라 만들어진 요청은 서버로 보내지고, 서버에서 MessageConverter 등을 통해 적절히 읽어들인 뒤 응답을 만들어 다시 클라이언트로 전송한다. HTTP response 는 종류에 따라 100 ~ 500 으로 나뉜다.
</br></p>
<h3 id="client-the-user-agent">Client: the user-agent</h3>
<p>요청을 생성하여 서버에 전송하는 주체이다. 서버에서 얻은 응답을 parsing하여 응답에 해당하는 web page를 렌더링하여 클라이언트에게 보여준다. 예를 들어 login 페이지를 클라이언트에서 요청했다면, 서버 쪽에 해당 페이지를 요청, 서버에서는 login.html의 위치를 응답에 담아 전송한다. 대표적인 클라이언트인 웹 브라우저에서는 서버로부터 받은 login.html을 불러와 렌더링하고, css나 js 같은 파일을 읽어들여 웹 페이지를 완성시켜 사용자에게 보여준다.</p>
</br>

<h3 id="web-server">Web server</h3>
<p>자원의 정보를 저장하고 있는 웹 서버이다. 하나 이상일 수 있고, 이전 포스트에서 다룬 것처럼 WAS도 포함될 수 있다. DB만을 저장하고 있는 서버, 캐시를 담당하는 웹 서버, load balancing을 위한 서벙 등 여러 서버가 있을 수 있지만, 클라이언트에 대해서는 하나의 서버처럼 동작한다. 
</br></p>
<h3 id="proxies">proxies</h3>
<p>아직 내가 다뤄보지 않아 확실히는 모르는 부분이지만, 곧 배우거나 알아야 할 부분이라 정리한다. 간단히 말해 서버와 클라이언트 사이에 관여하는 모든 entity를 proxy라고 한다. authentication entity, cache 를 관리하는 entity 등 다양한 proxy가 있으며, 클라이언트 서버 간 통신의 성능에 영향을 미칠 수 있다. </p>
<p>대부분 proxy는 전송, 네트워크 단계에서 수행되는 경우가 많으며, 서버로 보내는 요청을 추가로 가공한다든지, 요청을 포워딩한다든지 다양한 역할을 한다. doc에서는 HTTP message를 relay 한다는 표현을 썼는데, 그림에서 보는 것처럼 message를 각 entity에서 entity로 넘기는 것을 알 수 있다.</p>
<p><img src="https://images.velog.io/images/hsbang_thom/post/3398b926-e0dc-4ba7-aae4-6ef08945fcb1/image.png" alt=""></p>
<p>proxy는 아래와 같은 역할을 수행할 수 있다:</p>
<ul>
<li>caching (브라우저 캐시와 비슷함)</li>
<li>filtering </li>
<li>load balancing (다른 서버들을 함께 사용하기 위해)</li>
<li>authentication (자원에 대한 접근 제한)</li>
<li>logging (통신 간 history 제공)</li>
</ul>
</br>

<h2 id="aspects-of-http">Aspects of HTTP</h2>
<h3 id="http-is-simple">HTTP is simple</h3>
<p>human readable. 표준화되어 있고 읽기 쉬운구조로 되어 있다. header와 body로 되어 있고 각 정보들의 표현 방식은 accept, contentType 이런 식으로 되어 있어서 의미 파악이 쉽다.
</br></p>
<h3 id="http-is-extensible">HTTP is extensible</h3>
<p>header를 수정/가공하기 편하기 때문에 테스트하기 편하다. 
</br></p>
<h3 id="http-is-stateless-but-not-sessionless">HTTP is stateless, but not sessionless</h3>
<p>요청-응답 통신은 한 번 수행되고, 각 연결은 통신이 만들어 진 뒤 유지되지 않는다. 어떤 페이지를 요청해서 불러왔다고 하더라도 해당 페이지를 새로고침한다면 다시 요청을 수행하여 응답을 받아온다. 이를 무상태성, stateless라고 한다. 통신의 효율을 높이기 위해서인데, 쇼핑과 같이 유저가 선택한 목록을 유지하여야 하는 등의 경우 불편함이 발생한다. 이를 해결하기 위해 쿠키와 세션이 존재한다. 쿠키 세션 모두 상태 관리를 위한 조그마한 데이터인데, 쿠키는 브라우저에, 세션은 서버에 저장되고 관리된다. 기본적으로 쿠키는 서버 쪽에서 같은 클라이언트에서 온 요청인지 확인하기 위해 사용된다. 세션은 같은 쿠키를 가지고 요청한 클라이언트에 대해 저장소(세션)를 만들어 같은 쿠키를 가진 요청이 왔을 때 세션에 접근할 수 있도록 한다.
</br></p>
<h3 id="http-and-connection">HTTP and connection</h3>
<p>웹에서의 통신은 transport layer에서 수행되므로 HTTP layer과는 별도의 분야이다. HTTP는 어떤 통신 환경이든 작동하지만, message가 소멸되지 않는(최소한 에러가 발생했을 때 message가 표시될 수 있는) reliable한 환경을 요구하므로 TCP 통신 방식에 의존적이다.
</br></p>
<h2 id="what-can-be-controlled-by-http">What can be controlled by HTTP</h2>
<p>앞서 살펴본 바와 비슷하게, HTTP를 통해 주로 컨트롤하는 부분은 아래와 같다.</p>
<ul>
<li><p><strong>Caching</strong>:
Cache에 대한 관리 -어떤 데이터를 얼마 동안 관리할 것인지 등-를 HTTP를 통해 할 수 있다. </p>
</li>
<li><p><strong>Authentication</strong>:
HTTP에서 제공하는 header를 사용하거나, 특정 HTTP-Cookie를 설정함으로써 접근 제한을 구현할 수 있다.</p>
</li>
<li><p><strong>Proxies and tunneling</strong>:
IP가 숨겨져 있거나 인트라넷과 같이 외부에서 접근이 제한되어 있는 곳으로부터 요청이 올 때, HTTP는 해당 요청이 오기까지의 proxy를 찾아 요청을 수행한다. 하지만 모든 proxy가 HTTP proxy가 아닌 경우도 있어 다른 프로토콜의 proxy인 경우 해당 프로토콜을 사용해야할 수도 있다. 이 부분은 잘 모르는 부분이지만 일단 체크해둔다.</p>
</li>
<li><p><strong>Session</strong>
서버는 Cookie를 통해 세션을 생성하고, 같은 클라이언트에서 온 Cookie인 경우 지정한 세션에 접근을 허용한다. </p>
</br>
## HTTP flow
클라이언트가 요청을 보낼 때, 먼저 TCP 커넥션을 만들고, header와 body를 포함한 HTTP message를 만들어 서버로 보낸다.

</li>
</ul>
<blockquote>
<p>GET / HTTP/1.1
Host: developer.mozilla.org
Accept-Language: fr</p>
</blockquote>
</br>

<p>해당 정보를 통해 서버에서 어떤 자원을 어떤 방식으로 읽어들이고 응답해야 하는지 알 수 있다. REST 방식으로 api를 작성할 때 JSON format으로 데이터를 통신하므로 보통 accept: application/json을 많이 쓰는데, 이를 통해 Spring에서는 MessageConverter를 통해 요청이 온 message가 json format인 걸 알 수 있다. </p>
<p>위와 같은 요청에 해당하는 자원이 있을 경우, 서버에서 응답이 만들어지고 클라이언트로 전송된다.</p>
<blockquote>
<p>HTTP/1.1 200 OK
Date: Sat, 09 Oct 2010 14:28:02 GMT
Server: Apache
Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT
ETag: &quot;51142bc1-7449-479b075b2891b&quot;
Accept-Ranges: bytes
Content-Length: 29769
Content-Type: text/html</p>
</blockquote>
<p>혼자 프로젝트를 개발하면서, 201, 415, 400 등 다양한 response status를 보았다. 응답에 대한 내용은 차차 다루도록 한다.
</br>
</br></p>
<p>TL;DR 😒
</br></p>
<ul>
<li>HTTP 는 클라이언트-서버 간 통신 규약이다.</li>
<li>HTTP 는 stateless이며, human-readable하며, extensible하다</li>
<li>HTTP component : client, server, and proxy</li>
<li>proxy 는 client와 server 사이에서 HTTP message 를 전달하는 과정에 관여하는 모든 entity를 말한다.</li>
<li>HTTP를 통해 session, caching, authentication 등의 기능을 관리할 수 있다.</br>
</br>

</li>
</ul>
<hr>
</br>
</br>

<p><strong>references</strong> : <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview">https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JSP와 Servlet]]></title>
            <link>https://velog.io/@hsbang_thom/JSP%EC%99%80-Servlet</link>
            <guid>https://velog.io/@hsbang_thom/JSP%EC%99%80-Servlet</guid>
            <pubDate>Fri, 03 Sep 2021 14:22:12 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@hsbang_thom/WAS%EC%99%80-Web-Server">이전 포스트 : WAS와 Web server </a>에서 짤막하게 다루었던 WAS는 클라이언트 - 서버 간의 상호작용을 처리하기 위해 만들어진 컴포넌트라는 것을 언급했다. 
</br>
이번에는 HTTP 기반으로 수행되는 웹 환경에서 WAS(tomcat)가 실제로 요청을 받아 수행하고 응답하기까지, 즉 비즈니스 로직을 처리할 수 있게 해주는 기술들을 알아본다.
</br>
</br> 
</br>
</br></p>
<h1 id="자바에서-개발한-웹-애플리케이션-프로그램">자바에서 개발한 웹 애플리케이션 프로그램</h1>
</br>
먼저 JSP와 Servlet 모두 클라이언트 - 서버 간의 통신을 위해 만들어진 기술이다. 
</br>
</br>
</br>
</br>

<h2 id="servlet-server-applet">Servlet; Server Applet</h2>
</br> 

<p>자바 언어로 동작하는 <span style="color:dodgerblue"><strong>server-side</strong></span> 프로그램이다. 즉 확장자가.java 이다. front endpoint에서 요청을 받으면, 해당 uri에 매핑된 Serlvet이 서버에 접근하여 요청을 수행하고, 동작 결과를 동적으로 생성하여 클라이언트로 응답한다. 
</br></p>
<p>Servlet으로 선언하고 싶은 클래스를 HttpServlet 또는 GenericServlet 클래스를 상속하거나 해당 부모 클래스가 구현하고 있는 최상위 인터페이스인 Servlet 인터페이스를 구현하여 사용한다. <span style="color:slategrey">(Servlet의 컨테이너 생성과 요청, 응답 객체에 대한 내용도 있는데, 이는 다른 포스팅에서 다루도록 한다)</span>
</br></p>
<p>Servlet만을 통해 개발하는 방식을 Model 1이라고도 한다. 내가 느낀 이 개발방법의 가장 큰 단점은, 요청과 Servlet의 매핑이 1대1로만 가능하기 때문에 요청 개수만큼 Servlet도 생성하여야 한다는 점이다. 예를 들어 상품 목록 화면이 있을 때 상품 목록 호출, 등록, 수정, 세부 내용 등 모든 기능에 대해 Servlet이 각각 필요하다. 이런 과정에서 불필요하게 중복되는 코드들이 발생한다. 
</br>
</br>
</br>
</br></p>
<h2 id="jsp-jarkarta-server-page">JSP; Jarkarta Server Page</h2>
</br> 
html 기반의 페이지에 자바 코드를 사용할 수 있게 해주는 기술이다. 클라이언트에게 보이는 페이지를 PrintWriter 클래스로 하드 코딩 할 수 있지만, 일일이 코드를 작성해야 하는 번거로움이 있기에 등장했다. JDBC를 통해 SQL 쿼리 작성할 때 하드코딩 해야하는 번거로움을 해결하기 위해 JDBC 템플릿이나 Mybatis가 등장한 것과 같은 맥락이다. 
</br> 
</br> 
</br>
지정해놓은 태그를 통해 자바 코드를 사용할 수 있다.

<ul>
<li><strong>scriptlet:</strong>  &lt;% %&gt; 안에 자바코드 사용</li>
<li><strong>declaration tag :</strong> &lt;%@ %&gt;  선언문. 필드 선언</li>
<li><strong>expression tag :</strong> &lt;%= %&gt; 표현식. 코드 실행 후 리턴 등을 출력</li>
</ul>
</br> 
</br>
하지만 MVC 패턴의 개발에서는 front와 backend의 명확한 분리와 유지보수를 위해 위의 자바 태그들도 사용하지 않고, apache에서 지원하는 EL과 JSTL, 또는 mustache와 같은 템플릿 엔진을 통해 서버에서 얻은 응답을 더욱 깔끔하게 출력할 수 있다.
</br> 
</br> 
</br>
</br>

<h2 id="servlet과-jsp의-차이점">Servlet과 JSP의 차이점</h2>
</br> 

<table>
<thead>
<tr>
<th align="center"><span style="color:dodgerblue">Servlet</span></th>
<th align="center"><span style="color:dodgerblue">JSP</span></th>
</tr>
</thead>
<tbody><tr>
<td align="center">자바 코드이므로 바로 컴파일된다. 속도가 JSP에 비해 빠르다</td>
<td align="center">.jsp-&gt; .java-&gt;.class로 컴파일</td>
</tr>
<tr>
<td align="center">html을 직접 작성할 경우 하드코딩해야 하므로 번거롭다</td>
<td align="center">html 기반에 자바코드만 쓰면 되므로 더 쉽다</td>
</tr>
<tr>
<td align="center">MVC패턴에서 컨트롤러의 역할과 같음</td>
<td align="center">view의 역할과 같음</td>
</tr>
<tr>
<td align="center">세션 관리를 따로 설정해야 한다</td>
<td align="center">세션 설정이 기본적으로 포함되어 있다</td>
</tr>
<tr>
<td align="center">비즈니스로직과 프레젠테이션(리디렉션 또는 뷰)을 모두 한 Servlet에 포함해야 한다</td>
<td align="center">로직과 뷰를 분리할 수 있다</td>
</tr>
<tr>
<td align="center"></br></td>
<td align="center"></td>
</tr>
<tr>
<td align="center"></br></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">이 정도의 차이점이 있다. 이렇게 굳이 Servlet을 배웠던 이유는 기본적인 웹 동작이 어떻게 되는지 알기 위해서라고 했는데, 틀린 말은 아닌 것 같다는 생각을 한다.</td>
<td align="center"></td>
</tr>
<tr>
<td align="center"></br></td>
<td align="center"></td>
</tr>
<tr>
<td align="center"></br></td>
<td align="center"></td>
</tr>
<tr>
<td align="center"></br></td>
<td align="center"></td>
</tr>
<tr>
<td align="center"></br></td>
<td align="center"></td>
</tr>
<tr>
<td align="center"></br></td>
<td align="center"></td>
</tr>
<tr>
<td align="center"></br></td>
<td align="center"></td>
</tr>
<tr>
<td align="center"></br></td>
<td align="center"></td>
</tr>
<tr>
<td align="center"></br></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">내가 배울 때에는 두 가지 내용에 대해 확실하게 짚어 배우지는 않았다. 어차피 Spring MVC를 많이 쓰는 환경에서 해당 주제가 크게 중요하지 않을 수도 있지만, 여전히 Servlet이 자바 웹 개발의 기본이 되는 부분인 만큼 알아두면 좋을 내용이라고 생각한다.</td>
<td align="center"></td>
</tr>
</tbody></table>
</br> 
</br> 
</br> 
</br> 

<hr>
 </br> 

<p>*<em>references *</em> : <a href="https://www.upgrad.com/blog/jsp-vs-servlet/">https://www.upgrad.com/blog/jsp-vs-servlet/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WAS와 Web Server ]]></title>
            <link>https://velog.io/@hsbang_thom/WAS%EC%99%80-Web-Server</link>
            <guid>https://velog.io/@hsbang_thom/WAS%EC%99%80-Web-Server</guid>
            <pubDate>Fri, 03 Sep 2021 11:16:58 GMT</pubDate>
            <description><![CDATA[<h1 id="was와-web-server의-차이">WAS와 Web Server의 차이</h1>
</br>
이미 둘 다 혼용되어 많이 쓰이기 때문에 두 용어를 엄중하게 구분하는 것이 큰 의미가 없을 수도 있지만, 초보의 입장에서 개념을 정리해둔다는 느낌으로 글을 써본다.
</br>
</br>
요점만 말하면,
</br>
</br>

<ul>
<li><p><strong>Web server는 static한 자원</strong>을 주로 관리한다. 지정한 uri에 저장되어 있는 html 문서, 이미지, 비디오, 파일과 같은 자원들이다.</p>
</li>
<li><p><strong>WAS는 static, dynamic 자원</strong>을 모두 관리한다. 비즈니스 로직이라 불리는 클라이언트와 서버 간의 상호 작용, 트랜잭션의 결과를 동적으로 생성하여 응답한다. </p>
</br>


</li>
</ul>
<p>WAS는 middleware라고도 할 수 있는데 이는 클라이언트와 서버 간의 중간 소프트웨어 역할을 하기 때문이다. WAS가 없던 웹 초기에는 클라이언트의 요청이 있을 때마다 JDBC 드라이버를 통해 서버에 접속하고 쿼리를 수행하고 접속을 닫아야 했으므로 자원의 낭비가 심했다. WAS를 통해 커넥션 풀과 같은 기술이 도입되면서 이런 비효율을 낮출 수 있었다.
</br></p>
<h1 id="대표적인-was">대표적인 WAS</h1>
</br> 
가장 대표적인 WAS는 apache tomcat과 Nginx가 있다. tomcat은 java를 기반을 하는 대표적인 WAS로 Servlet과 Jsp, JavaEE application을 지원한다. Spring boot에서는 내장 WAS로 채택되어 따로 설치 없이 사용이 가능하다.

</br>
</br>
Nginx는 netflix, dropbox와 같은 글로벌 기업에서 대표적으로 사용하고 있는 WAS라고 한다. 솔직히 써볼 기회가 없어서 두 WAS의 비교를 하기가 어렵다. 현재 Spring boot + jpa로 토이 프로젝트를 하고 있는데 여기서 Nginx를 쓸 예정이다. 둘 다 체험해보고 비교해보는 시간을 가져봐야겠다.
</br>
</br>
</br>
</br>

<hr>
</br>

<p><strong>references :</strong> <a href="https://www.ibm.com/cloud/learn/web-server-vs-application-server">https://www.ibm.com/cloud/learn/web-server-vs-application-server</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 좋은 쿼리문을 쓰는 습관 ]]></title>
            <link>https://velog.io/@hsbang_thom/SQL-%EC%A2%8B%EC%9D%80-%EC%BF%BC%EB%A6%AC%EB%AC%B8%EC%9D%84-%EC%93%B0%EB%8A%94-%EC%8A%B5%EA%B4%80</link>
            <guid>https://velog.io/@hsbang_thom/SQL-%EC%A2%8B%EC%9D%80-%EC%BF%BC%EB%A6%AC%EB%AC%B8%EC%9D%84-%EC%93%B0%EB%8A%94-%EC%8A%B5%EA%B4%80</guid>
            <pubDate>Thu, 02 Sep 2021 14:03:50 GMT</pubDate>
            <description><![CDATA[<h1 id="old-style">old style</h1>
</br>
내가 배웠던 join은 from 절에 join하고자 하는 테이블 명을 모두 쓰고 where 조건으로 검색하는 방식이었다. 
</br></br>

<pre><code class="language-sql">
SELECT m.no, m.name, m.profileImage, 
    p.no, p.name, p.price
FROM member m, product p
WHERE m.no = {no}
</code></pre>
</br> 
하지만 프로젝트 진행 시 join하는 테이블의 수가 많아지면서 가독성이 떨어졌다. 여기에 페이징 처리를 위한 TOP n 방식과 동적 쿼리문까지 쓰다보면 내가 쓰지 않은 쿼리를 수정할 때 힘든 것은 물론 내가 쓴 쿼리도 알아보기가 힘들었다. 
</br>
</br>

</br>

<h1 id="good-practice-of-sql">Good practice of sql</h1>
</br>
그래서 어떤 방식으로 쿼리를 써야 좋은 것인지 한 번 찾아보았다. 여러 포스팅에 걸쳐 공통적으로 언급된 내용들과, 또 그중 내 수준에서 필요한 부분들만 모아서 정리해보았다.
</br></br>


<h2 id="avoid-using-asterik">Avoid using Asterik(*)</h2>
</br>
테이블에 컬럼 수가 적으면 괜찮을지 모르지만, 컬럼 수가 많을 때 asterik를 쓰면 실제 쿼리에 필요 없는 컬럼까지 색인하게 되므로 쿼리 성능이 떨어질 수 있다. 또 개인적인 경험으로는 내가 만들지 않은 쿼리를 다룰 때 *로만 표기되어 있으면 어느 컬럼을 추출하려는지 확실하지 않아 코드의 목적을 파악하기가 어려울 때가 있었다. 그러면 쿼리를 수정해야 하는지, dto를 손봐야 하는지 헷갈리기도 했다.

</br>
</br>
따라서 반드시는 아니더라도 필요한 컬럼을 명시해주는 쿼리를 지향해야겠다.
</br>
</br>

<h2 id="table-alias">Table alias</h2>
</br>
from 절에 여러 테이블을 쓸 경우 alias가 선호된다. 가독성이 좋고, 다른 테이블에 같은 컬럼명이 있을 때에도 혼란을 방지한다. 너무 당연한 것이지만 그래도 적어둔다.
</br>
</br>

<h2 id="ansi-sql-style">ANSI SQL style</h2>
</br>
예시처럼 내가 배운 join 방식은 from 절에 관련된 모든 테이블을 적고 where 절로 필터링하는 식의 방법이었다. 작성하지 간단하지만 쿼리가 길어질수록 가독성이 떨어진다. 또 불필요한 컬럼까지 모두 색인한 뒤 마지막에 where 절로 필터링하기 때문에, 내가 진행한 작은 규모의 프로젝트에서는 문제가 되지 않지만 데이터가 큰 경우 성능의 차이가 발생할 수 있다. 
</br>
</br>
ANSI sql은 표준 sql으로 모든 DBMS에 공통적으로 사용할 수 있다. 위의 예시를 ANSI style로 수정해보면
</br>
</br>

<pre><code class="language-sql">SELECT m.no, m.name, m.profileImage,
    p.no, p.name, p.price
FROM member m, 
JOIN product p 
ON m.no = p.no
WHERE m.no = {no}
</code></pre>
</br>
정도가 된다. JOIN도 종류가 네 가지로 나뉜다.
</br>

<p><img src="https://images.velog.io/images/hsbang_thom/post/bf5ae9aa-8524-4b09-aa4d-724467aae201/image.png" alt="">
source: <a href="https://www.w3schools.com/sql/sql_join.asp">https://www.w3schools.com/sql/sql_join.asp</a></p>
</br>
JOIN만 쓰면 INNER JOIN이다. 내가 배웠던 예전 방식으로 한다면 full outer join을 한 뒤에 where로 필터링만 하는 것으로 이해하면 되겠다. 
</br>
</br>

<p>JOIN과 ON 절을 쓸 때 알아 두어야 할 점은 아래 링크에 설명이 잘 되어있다. 간략히 정리하면 where의 경우 join이 <strong>끝난 후</strong>에 마지막에 필터링을 하고, on의 경우 join <strong>이전</strong>에 하기 필터링을 한다는 점이다. </p>
<p><a href="https://stackoverflow.com/questions/354070/sql-join-where-clause-vs-on-clause">sql join where vs. on - Stackoverflow</a></p>
</br>

<h2 id="exists-not-exists-than-in-not-in">EXISTS, NOT EXISTS than IN, NOT IN</h2>
</br>
EXISTS, IN 모두 서브쿼리를 활용할 때 사용할 수 있는 구문이다. EXISTS가 훨씬 선호되는데 가장 큰 이유는 EXISTS는 해당 서브쿼리에 해당하는 값이 하나라도 있으면 검색을 멈추지만, IN의 경우는 모든 인덱스를 검색하기 때문에 성능 상의 차이가 난다. 같은 맥락으로 검색하는 경우가 아니고서는 LIKE보다 항등연산자(=)를 사용하는 것이 바람직하다.
</br>
</br>
</br>



</br>
sql을 쓸 때에도 협업과 유지보수를 고려하여 가독성 있게 짜는 것이 중요하겠다. 이상으로 오늘은 간단히 좋은 sql을 쓰는 방법 몇 가지를 알아보았다.

</br>
</br>
</br>
</br>

<hr>
<h3 id="references">references</h3>
<p><a href="https://www.dpriver.com/blog/2011/09/27/a-list-of-sql-best-practices/">https://www.dpriver.com/blog/2011/09/27/a-list-of-sql-best-practices/</a>
<a href="https://codingsight.com/sql-query-optimization-tips/">https://codingsight.com/sql-query-optimization-tips/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] MVC @RestController 사용 시 발생한 문제와 해결 ]]></title>
            <link>https://velog.io/@hsbang_thom/Spring-MVC-RestController-Java-Serialization</link>
            <guid>https://velog.io/@hsbang_thom/Spring-MVC-RestController-Java-Serialization</guid>
            <pubDate>Mon, 30 Aug 2021 11:50:14 GMT</pubDate>
            <description><![CDATA[<h1 id="배경--ajax-를-통한-목록-불러오기">배경 : ajax 를 통한 목록 불러오기</h1>
</br>
마이페이지에서 회원이 자신이 쓴 글의 목록을 볼 수 있는 기능을 구현하고자 했다. ajax, GET 요청으로 목록을 불러오는 간단한 기능이었다. 
</br>
</br>

<p><strong>Front endopint</strong></p>
<pre><code class="language-javascript">getMyRcps : function(){

        const myRcpsTmpl = _.template($(&quot;#myRcpsTmpl&quot;).html());
        const $myRcpsUl = $(&quot;.mypage_myrecipe_tab.tab_wrap ul&quot;);

        $.ajax({
            url: &#39;/mypage/ajax/myrecipes&#39;,
            type: &#39;GET&#39;,
            dataType:&#39;json&#39;,
            error: function(){ alert(&#39;something went wrong&#39;)},
            success: function(json){
                console.log(json);
                $myRcpsUl.html(myRcpsTmpl({myRcps:json}));
            }

        });
    }</code></pre>
</br>
템플릿 엔진은 underscore.js를 썼다. 서버에서 얻은 데이터를 Spring이 json으로 리턴해주면 리턴한 json을 지정한 템플릿 영역에서 쓸 수 있다.
</br>
</br>
지정한 uri로 ajax 요청을 수행할 때 따로 회원번호를 파라미터로 넘기지 않는다. 컨트롤러 쪽에서 아규먼트 리졸버를 통해 유저 정보를 얻어올 수 있게 해놓았기 때문이다.
</br>
</br>
</br>

<p><strong>Api controller</strong></p>
<pre><code class="language-java">@Slf4j
@RequiredArgsConstructor
@RestController
public class MypageApiController {

    private final RecipesService recipesService; 

    @GetMapping(&quot;/mypage/ajax/myrecipes&quot;)
    public List&lt;Rcp&gt; getMyRecipes(@LoginUser SessionUser user) {

        if(user!=null) {
            int memberNo = user.getNo();

            return recipesService.getRecipesByMemberNo(memberNo);
        }

        return null;

    }
</code></pre>
</br>
</br>
코드 관리를 편하게 하기 위해 ajax를 사용하는 컨트롤러는 rest controller로 만들어 뷰만 리턴하는 controller와 분리했다.
</br>
</br>
앞서 말한 것처럼 아규먼트 리졸버를 통해 SessionUser 클래스가 메서드 파라미터로 있는 경우 세션으로부터 유저 정보를 얻어온다. Spring Security 프레임워크를 이용해 '/mypage/**' 로 매핑된 메서드는 ROLE_USER 권한을 가진 사용자만 접근 가능하게 해놓았기 때문에, 임의로 uri를 입력해 해당 페이지에 접근할 수는 없다. 
 </br>
</br>
</br>

<p><strong>SessionUser</strong></p>
<pre><code class="language-java">@Getter
public class SessionUser {

    private int no, addressNo;
    private String email, name, nickname, profileImg;
    private char marketkeeperStep;

    @Builder
    public SessionUser(int no, int addressNo, String email, String name, String nickname, String profileImg, char marketkeeperStep) {
        super();
        this.no = no;
        this.addressNo = addressNo;
        this.email = email;
        this.name = name;
        this.nickname = nickname;
        this.profileImg = profileImg;
        this.marketkeeperStep = marketkeeperStep;
    }

}</code></pre>
</br>
</br>
유저 정보를 불러올 때 HttpSession을 활용할 수도 있지만, 매번 HttpSession을 파라미터로 넘기고 getSession() 메서드 수행 후 형 변환하는 코드를 쓰는 것보다 위와 같은 dto를 하나 만들어 유저 정보를 담을 수 있도록 했다. (책을 참고했다)
</br></br>



<h1 id="문제--bindexception-conversionfailedexception">문제 : BindException, ConversionFailedException</h1>
</br>
기능을 구현하는 과정은 간단했지만, 문제는 테스트하는 과정에서 발생했다. 
</br>
__ApiTestConfig.java__

<pre><code class="language-java">@Configuration
@Import(MvcTestConfig.class)
@EnableTransactionManagement
@ComponentScan(basePackages = {&quot;com.ktx.ddep.controller&quot;,&quot;com.ktx.ddep.service&quot;, &quot;com.ktx.ddep.argumentresolver&quot;})
public class ApiTestConfig {

    @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public HttpSession sessionCreator() {
        return new MockHttpSession();
    }

}
</code></pre>
</br>
</br>

<p><strong>MvcTestConfig</strong></p>
<pre><code class="language-java">@Configuration
public class MvcTestConfig implements WebMvcConfigurer{

    private LoginUserArgResolver loginUserArgResolver;

    // enable default servlet when requests other than that made for spring is made
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }

        // jsp view resolver
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp(&quot;/WEB-INF/view/&quot;, &quot;.jsp&quot;);
        }

        // redirect request &quot;/&quot; to &quot;/main&quot;
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addRedirectViewController(&quot;/&quot;, &quot;/main&quot;);
        }

        @Override
        public void addArgumentResolvers(List&lt;HandlerMethodArgumentResolver&gt; resolvers) {
            resolvers.add(loginUserArgResolver);
        }
}
</code></pre>
</br>
테스트하기 위해 필요한 설정만 모아놓은 TestConfig를 작성했다. PasswordEncoder와 HttpSession 빈 설정은 원래 다른 설정 파일에 있는 설정인데 컨트롤러와 아규먼트 리졸버에서 각각 의존성을 필요로 하고 있기 때문에, 테스트하는 과정에서 에러를 피하기 위해 실제 코드를 수정하기보다 테스트 설정 파일에서 따로 빈으로 등록해주었다. 
</br>
</br>

<p><strong>MypageApiControllerTest.java</strong></p>
<pre><code class="language-java">@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {ApiTestConfig.class})
@Transactional
public class MypageApiControllerTest {

    // mock 객체를 생성하고 해당 mock 객체에 Mock으로 설정한 객체들을 주입
    @InjectMocks
    private MypageApiController apiController;

    @Mock
    private RecipesService rcpService;

    @Mock
    private List&lt;Rcp&gt; rcpList;

    private MockMvc mockMvc;

    @Before
    public void instantiate() {
        MockitoAnnotations.openMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetup(apiController).build();    
        }

    @Test
    public void givenUser_whenGetRequest_thenIsStatusOk() throws Exception {

        //given
        SessionUser user = SessionUser.builder()
                .no(8)
                .addressNo(8)
                .email(&quot;wlgypark@gmail.com&quot;)
                .name(&quot;박지효&quot;)
                .nickname(&quot;짜릿한슬라임&quot;)
                .marketkeeperStep(&#39;i&#39;)
                .profileImg(&quot;profile.jpg&quot;)
                .build();

        //when
        when(apiController.getMyRecipes(user)).thenReturn(rcpList);

        //then
        mockMvc.perform(get(&quot;/mypage/ajax/myrecipes&quot;)).andExpect(status().isOk()).andDo(print());


    }

}</code></pre>
</br>
처음 코드를 짤 때는, 메서드의 수행 여부만 확인하면 된다고 생각했으므로 api 컨트롤러 메서드 호출 시 파라미터는 Mockito를 통해 해결하고자 했다. 그래서 처음에는 SessionUser 객체를 mock으로 만들어 apiController의 파라미터로 넣어주었다. 그런데 

<blockquote>
<p>HttpMessageNotWritableException: nested Exception - jackson.databind.JsonMappingException: cannot invoke &#39;java.util.Iterator.hasNext()&quot; because it is null</p>
</blockquote>
<p>위와 같은 예외가 발생하며 테스트가 실패했다. 
</br></p>
<p>따라서 위와 같이 임의의 값을 담은 SessionUser 객체를 생성한 뒤 get 요청을 다시 수행하는 코드를 짠 뒤 테스트를 실행했다. 그러자 </p>
<blockquote>
<p>[org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult] 
Field error in object &#39;sessionUser&#39; on field &#39;no&#39;: rejected value [null];
[typeMismatch.sessionUser.no,typeMismatch.no,typeMismatch.int,typeMismatch]; 
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [sessionUser.no,no]; arguments []; default message [no]]; 
default message [Failed to convert value of type &#39;null&#39; to required type &#39;int&#39;; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [null] to type [int] for value &#39;null&#39;; nested exception is java.lang.IllegalArgumentException: <strong><em>A null value cannot be assigned to a primitive type</em></strong>]</p>
</blockquote>
</br>
와 같은 예외가 발생했다. 생성자를 통해 필드를 초기화하는 과정에서 null value가 기본 자료 타입 변수에 대입될 수 없다는 것이다. 한동안 원인을 찾지 못해 시간을 좀 보냈다. 왜 mock으로 설정했을 때는 jackson이 예외를 일으켰는지, 객체에 값을 넣어주어도 Spring에서 BindException이 발생했는지 의문이었다.
</br>
</br>
</br>

<h1 id="해결--no-arg-constructor">해결 : no arg constructor</h1>
</br>
</br>
DispatcherServlet에서 요청을 받으면, Multipart나 Locale같은 commons service를 처리한다. 그 뒤 HandlerMapping을 통해 메서드 체인이 호출되고, 요청된 uri와 매핑된 메서드가 호출된다. 이 과정에서 요청에 포함된 정보가 포맷되어 메서드에 바인딩 된다.</br>
</br>

<p>컨트롤러 메서드는 요청의 Accept와 Content-type이 포함된 header를 읽어들여 요청 정보의 미디어 타입을 확인하고, 이어 HttpMessageConverters를 통해 해당 미디어 타입을 핸들링할 수 있는 컨버터를 찾게 된다. 그리고 이 컨버터는 자바 엔티티를 클라이언트가 요청한 포맷으로 가공해주는 역할을 한다. 나는 <code>@RestController</code>를 사용할 계획이었으므로 json/xml로 응답하여야 했고, 컨버터로 jackson-databind 라이브러리를 사용했다. </p>
</br>
위의 내용까지 정리한 뒤, 다시 json, rest controller, data bind와 같은 키워드로 계속 검색하다 직렬화에 대해 알게 되었다. 사실 직렬화에 대한 개념은 알고만 있었지 자세히는 몰랐던 터라, 이참에 구글링을 계속하며 api doc문서와 참고할 만한 글들을 들여다 보게 되었다. 
</br>
</br>

<blockquote>
<p><strong><em>During deserialization, the fields of non-serializable classes will be initialized using the public or protected no-arg constructor of the class.</em></strong> <strong>_A no-arg constructor must be accessible to the subclass that is serializable. _</strong>The fields of serializable subclasses will be restored from the stream.</p>
</blockquote>
</br>
역직렬화를 하는 과정에서는 non-serializable 한 클래스(Serializable 인터페이스를 구현하지 않는 클래스)는 기본 생성자를 통해 초기화를 하게 된다. 직렬화/역직렬화를 하는 과정은 Serializable 인터페이스를 구현하는 클래스를 모두 나열한 뒤에 순차적으로 스트림으로 바꾸는 과정을 거친다고 하는데, 좀 더 자세한 것은 다음 번에 다루는 시간을 가져야겠다.
</br>
</br>
저 글을 읽고 생각해보니 내가 이전에 설정해놓은 SessionUser 클래스에는 lombok 라이브러리의 `@Builder`  를 통해서만 생성되는 파라미터가 있는 생성자만 선언되어 있었다. 곧바로 기본 생성자를 생성하는 `@NoArgConstructor` 를 선언해주었고, 테스트는 정상적으로 통과되었다. 결국 응답을 출력하려는 과정에서 역직렬화를 해야 하는데 해당 객체의 기본 생성자가 없으니 예외가 발생했던 것이다. 
</br>
</br>
</br>
자바의 기본 개념인데, 잘 몰랐던 탓에 한 줄의 코드를 통해 문제를 해결하기까지 꽤나 오랜 시간을 잡아먹었다. 하지만 또 하나를 배웠고, 앞으로도 차근차근히 학습하면서 문제를 해결해 나가야 겠다는 생각이 든다.


<hr>
<p></br></br></p>
<p><strong>references:</strong> </p>
<ul>
<li><a href="https://www.baeldung.com/spring-httpmessageconverter-rest">https://www.baeldung.com/spring-httpmessageconverter-rest</a></li>
<li><a href="https://www.baeldung.com/java-serialization">https://www.baeldung.com/java-serialization</a></li>
<li><a href="https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html">https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html</a></li>
<li><a href="https://wondongho.tistory.com/76">https://wondongho.tistory.com/76</a> </li>
</ul>
</br>
</br>
** 제가 스스로 종합한 내용들을 정리하는 글이다보니 틀린 내용이 있을 수 있습니다. 그런 부분들에 대해서 충고, 지적해주시면 감사하겠습니다.]]></description>
        </item>
    </channel>
</rss>