<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>wisdom.log</title>
        <link>https://velog.io/</link>
        <description>백엔드 개발자</description>
        <lastBuildDate>Sun, 12 Mar 2023 13:53:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>wisdom.log</title>
            <url>https://velog.velcdn.com/images/wisdom-one/profile/e0919143-f034-44c0-b962-72e1a5946867/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. wisdom.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/wisdom-one" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[아이템 80. 스레드보다는 실행자, 태스크, 스트림을 애용하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-80.-%EC%8A%A4%EB%A0%88%EB%93%9C%EB%B3%B4%EB%8B%A4%EB%8A%94-%EC%8B%A4%ED%96%89%EC%9E%90-%ED%83%9C%EC%8A%A4%ED%81%AC-%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%84-%EC%95%A0%EC%9A%A9%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-80.-%EC%8A%A4%EB%A0%88%EB%93%9C%EB%B3%B4%EB%8B%A4%EB%8A%94-%EC%8B%A4%ED%96%89%EC%9E%90-%ED%83%9C%EC%8A%A4%ED%81%AC-%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%84-%EC%95%A0%EC%9A%A9%ED%95%98%EB%9D%BC</guid>
            <pubDate>Sun, 12 Mar 2023 13:53:25 GMT</pubDate>
            <description><![CDATA[<h3 id="실행자-서비스">실행자 서비스</h3>
<p><code>java.util.concurrent</code> 패키지는 실행자 프레임워크(Executor Framework)라고 하는 인터페이스 기반의 유연한 태스크 실행 기능을 담고 있다. </p>
<p>이것을 이용하면, 다음의 아주 짧은 코드로 작업 큐를 사용할 수 있다.</p>
<pre><code class="language-java">// 작업 큐를 생성한다.
ExecutorService exec = Executors.newSingleThreadExecutor();

// 실행자에 실행할 태스크를 넘긴다.
exec.execute(runnable);

// 실행자를 종료시킨다. 이 작업이 실패하면 VM 자체가 종료되지 않는다.
exec.shutdown();</code></pre>
<p>실행자 서비스의 주요 기능은 다음과 같다.</p>
<ul>
<li>특정 태스크가 완료되기를 기다린다.</li>
<li>태스크 모음 중 아무것 하나(invokeAny 메서드) 혹은 모든 태스크(invokeAll 메서드)가 완료되기를 기다린다.</li>
<li>실행자 서비스가 종료하기를 기다린다(awaitTermination 메서드).</li>
<li>완료된 태스크들의 결과를 차례로 받는다(ExeutorCompletionService 이용).</li>
<li>태스크를 특정 시간에 혹은 주기적으로 실행하게 한다(ScheduledThreadPoolExecutor 이용).</li>
</ul>
<p>큐를 둘 이상의 스레드가 처리하게 하고 싶다면 간단히 다른 정적 팩터리를 이용하여 다른 종류의 실행자 서비스(스레드 풀)을 생성하면 된다. 우리에게 필요한 실행자 대부분은 <code>java.util.concurrent.Executors</code> 의 정적 팩터리들을 이용해 생성할 수 있을 것이다.</p>
<h4 id="threadpoolexecutor">ThreadPoolExecutor</h4>
<p>평범하지 않은 실행자를 원한다면 <code>ThreadPoolExecutor</code> 클래스를 직접 사용해도 된다. 이 클래스로는 스레드 풀 동작을 결정하는 거의 모든 속성을 설정할 수 있다.</p>
<h4 id="cachedthreadpool">CachedThreadPool</h4>
<p>작은 프로그램이나 가벼운 서버라면 <code>Executors.newCachedThreadPool</code> 가 일반적으로 좋은 선택일 것이다. 특별히 설정할 게 없고 일반적인 용도에 적합하게 동작한다.</p>
<p>하지만, <code>CachedThreadPool</code> 은 무거운 프로덕션 서버에는 좋지 못하다! CachedThreadPool 에서는 요청받은 태스크들이 큐에 쌓이지 않고 즉시 스레드에 위임돼 실행된다. 이때 가용한 스레드가 없다면 새로 하나를 생성한다. 서버가 아주 무겁다면 CPU 사용률이 100%로 치닫고, 새로운 태스크가 도착하는 족족 또 다른 스레드를 생성하며 상황을 더욱 악화시킨다. 따라서 무거운 프로덕션 서버에서는 스레드 개수를 고정한 <code>Executors.newFixedThreadPool</code> 을 선택하거나 완전히 통제할 수 있는 <code>ThreadPoolExecutor</code> 를 직접 사용하는 편이 훨씬 낫다.</p>
<h4 id="태스크">태스크</h4>
<p>작업 큐를 손수 만들거나, 스레드를 직접 다루는 일은 일반적으로 삼가향 한다. 스레드를 직접 다루면 Thread 가 작업 단위와 수행 메커니즘 역할을 모두 수행하게 된다.
반면 실행자 프레임워크에서는 작업 단위와 실행 메커니즘이 분리된다. 작업 단위를 나타내는 핵심 추상 개념이 태스크다. 태스크에는 <code>Runnable</code> 과 그 사촌인 <code>Callable</code> , 2가지가 있다. <code>Callable</code> 은 <code>Runnable</code> 과 비슷하지만 값을 반환하고 임의의 예외를 던질 수 있다. 
그리고 태스크를 수행하는 일반적인 메커니즘이 바로 실행자 서비스다. 태스크 수행을 실행자 서비스에게 맡기면 원하는 태스크 수행 정책을 선택할 수 있고, 언제든 변경할 수 있다. 핵심은 실행자 프레임워크가 작업 수행을 담당해준다는 것이다.</p>
<p>자바 7이 되면서, 실행자 프레임워크는 포크-조인(fork-join) 태스크를 지원하도록 확장되었다. 포크-조인 태스크는 포크-조인 풀이라는 특별한 실행자 서비스가 실행해준다. <code>ForkJoinTask</code> 의 인스턴스는 작은 하위 태스크로 나뉠 수 있고, <code>ForkJoinPool</code> 을 구성하는 스레드들이 이 태스크들을 처리하며, 일을 먼저 끝낸 스레드는 다른 스레드의 남은 태스크를 가져와 대신 처리할 수도 있다. 이렇게 하여 모든 스레드가 바쁘게 움직여 CPU를 최대한 활용하면서 높은 처리량과 낮은 지연시간을 달성한다. 
포크-조인 태스크를 직접 작성하고 튜닝하는 것은 어렵지만, 포크-조인 풀을 이용해 만든 병렬 스트림을 이용하면 적은 노력으로 그 이점을 얻을 수 있다.</p>
<br/>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 79. 과도한 동기화는 피하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-79.-%EA%B3%BC%EB%8F%84%ED%95%9C-%EB%8F%99%EA%B8%B0%ED%99%94%EB%8A%94-%ED%94%BC%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-79.-%EA%B3%BC%EB%8F%84%ED%95%9C-%EB%8F%99%EA%B8%B0%ED%99%94%EB%8A%94-%ED%94%BC%ED%95%98%EB%9D%BC</guid>
            <pubDate>Sun, 12 Mar 2023 07:09:19 GMT</pubDate>
            <description><![CDATA[<p>과도한 동기화는 성능을 떨어뜨리고, 교착상태에 빠뜨리고, 심지어 예측할 수 없는 동작을 낳기도 한다.</p>
<p><strong>응답 불가와 완전 실패를 피하려면 동기화 메서드나 동기화 블록 안에서는 제어를 절대로 클라이언트에 양도하면 안 된다.</strong></p>
<p>동기화된 영역 안에서는 재정의할 수 있는 메서드는 호출하면 안 되며, 클라이언트가 넘겨준 함수 객체를 호출해서도 안 된다. 동기화된 영역을 포함한 클래스 관점에서는 이런 메서드는 모두 외계인이다. 그 메서드가 무슨 일을 할지 알지 못하며 통제할 수도 없다. 외계인 메서드(alien method)가 하는 일에 따라 동기화된 영역은 예외를 일으키거나, 교착상태에 빠지거나 데이터를 훼손할 수도 있다.</p>
<h3 id="동기화-블록에서의-외계인-메서드-호출">동기화 블록에서의 외계인 메서드 호출</h3>
<p>동기화 블록에서 외계인 메서드를 호출하는 구체적인 예를 보자.
다음은 어떤 집합을 감싼 래퍼 클래스이고, 이 클래스의 클라이언트는 집합에 원소가 추가되면 알름을 받는 관찰자 패턴이다. 원소 제거 시 알림 기능은 생략했다.</p>
<p><code>ForwardingSet</code> 은 <a href="https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C18.-%EC%83%81%EC%86%8D%EB%B3%B4%EB%8B%A4%EB%8A%94-%EC%BB%B4%ED%8F%AC%EC%A7%80%EC%85%98%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC">아이템 18</a>에서 사용한 것을 재사용했다.</p>
<pre><code class="language-java">public class ObservableSet&lt;E&gt; extends ForwardingSet&lt;E&gt; {
    public ObservableSet(Set&lt;E&gt; set) { super(set); }

    private final List&lt;SetObserver&lt;E&gt;&gt; observers
            = new ArrayList&lt;&gt;();

    public void addObserver(SetObserver&lt;E&gt; observer) {
        synchronized(observers) {
            observers.add(observer);
        }
    }

    public boolean removeObserver(SetObserver&lt;E&gt; observer) {
        synchronized(observers) {
            return observers.remove(observer);
        }
    }

    private void notifyElementAdded(E element) {
        synchronized(observers) {
            for (SetObserver&lt;E&gt; observer : observers)
                observer.added(this, element);
        }
    }

    @Override public boolean add(E element) {
        boolean added = super.add(element);
        if (added)
            notifyElementAdded(element);
        return added;
    }

    @Override public boolean addAll(Collection&lt;? extends E&gt; c) {
        boolean result = false;
        for (E element : c)
            result |= add(element);  // notifyElementAdded를 호출한다.
        return result;
    }
}</code></pre>
<p>관찰자들은 addObserver와 removeObserver 메서드를 호출해 구독을 신청하거나 해지한다. 두 경우 모두 다음 콜백 인터페이스의 인스턴스를 메서드에 건넨다.</p>
<pre><code class="language-java">public interface SetObserver&lt;E&gt; {
    // ObservableSet에 원소가 더해지면 호출된다.
    void added(ObservableSet&lt;E&gt; set, E element);
}</code></pre>
<h4 id="예외-발생">예외 발생</h4>
<p>ObservableSet은 잘 작동할 것으로 보이지만, 클라이언트 코드가 다음과 같다면 어떨까?</p>
<pre><code class="language-java">public static void main(String[] args) {
    ObservableSet&lt;Integer&gt; set =
            new ObservableSet&lt;&gt;(new HashSet&lt;&gt;());

    set.addObserver(new SetObserver&lt;&gt;() {
            public void added(ObservableSet&lt;Integer&gt; s, Integer e) {
                System.out.println(e);
                if (e == 23) // 값이 23이면 자신을 구독해지한다.
                    s.removeObserver(this);
            }
        });

    for (int i = 0; i &lt; 100; i++)
        set.add(i);
}</code></pre>
<p>이 프로그램은 0부터 23까지 출력한 후 관찰자 자신을 구독 해지한 다음 종료하길 기대했지만, 실제로는 23까지 출력한 후 <code>ConcurrentModificationException</code> 을 던진다.</p>
<p>그 이유는 관찰자의 <code>added</code> 메서드 호출이 일어난 시점이 <code>notifyElementAdded</code> 가 관찰자들의 리스트를 순회하는 도중이기 때문이다. 
<code>observers.remove</code> 메서드를 호출할 때, <code>notifyElementAdded</code> 메서드에서 이 리스트를 순회하는 도중이기 때문에 허용되지 않은 동작이다. <code>notifyElementAdded</code> 메서드 내의 동기화 블록은 동시 수정이 일어나지 않도록 보장하지만, 정작 콜백을 거쳐 되돌아와 수정하는 것까지 막지는 못한다.</p>
<h4 id="교착상태">교착상태</h4>
<p>이번엔 구독해지를 하는 관찰자가 <code>removeObserver</code> 를 직접 호출하지 않고 실행자 서비스(<code>ExecutorService</code>)를 사용해 다른 스레드에게 부탁하는 예를 보자.</p>
<pre><code class="language-java">public static void main(String[] args) {
    ObservableSet&lt;Integer&gt; set =
            new ObservableSet&lt;&gt;(new HashSet&lt;&gt;());

    // 쓸데없이 백그라운드 스레드를 사용하는 관찰자
    set.addObserver(new SetObserver&lt;&gt;() {
        public void added(ObservableSet&lt;Integer&gt; s, Integer e) {
            System.out.println(e);
            if (e == 23) {
                ExecutorService exec =
                        Executors.newSingleThreadExecutor();
                try {
                    exec.submit(() -&gt; s.removeObserver(this)).get();
                } catch (ExecutionException | InterruptedException ex) {
                    throw new AssertionError(ex);
                } finally {
                    exec.shutdown();
                }
            }
        }
    });

    for (int i = 0; i &lt; 100; i++)
        set.add(i);
}</code></pre>
<p>이 프로그램을 실행하면 예외는 발생하지 않지만 교착상태에 빠진다.</p>
<p>백그라운드 스레드가 s.removeObserver 를 호출하면, 관찰자를 잠그려 하지만 락을 얻을 수 없다. notifyElementAdded 메서드를 실행 중이기 때문에 메인 스레드가 락을 쥐고 있기 때문이다. 그리고 메인 스레드는 백그라운드 스레드가 관찰자를 제거하기를 기다리고 있다. (교착상태)</p>
<h3 id="외계인-메서드-호출을-동기화-블록-바깥으로-옮기기">외계인 메서드 호출을 동기화 블록 바깥으로 옮기기</h3>
<p>이런 문제는 외계인 메서드 호출을 동기화 블록 바깥으로 옮기면 해결할 수 있다.</p>
<p>notifyElementAdded 메서드에서라면 관찰자 리스트를 복사해 쓰면 락 없이도 안전하게 순회할 수 있다.</p>
<pre><code class="language-java">private void notifyElementAdded(E element) {
    List&lt;SetObserver&lt;E&gt;&gt; snapshot = null;
    synchronized(observers) {
        snapshot = new ArrayList&lt;&gt;(observers);
    }
    for (SetObserver&lt;E&gt; observer : snapshot)
        observer.added(this, element);
}</code></pre>
<p>이 방법 외에도 자바의 동시성 컬렉션 라이브러리의 <code>CopyOnWriteArrayList</code> 를 사용하는 방법도 있다. 이는 ArrayList 를 구현한 클래스로, 내부를 변경하는 작업은 항상 깨끗한 복사본을 만들어 수행하도록 구현되어 있다. 내부의 배열은 절대 수정되지 않으니 순회할 때 락이 필요 없어 매우 빠르다. 다른 용도로 쓰인다면 끔찍이 느리겠지만, 수정할 일이 드믈고 순회만 빈번히 일어나는 관찰자 리스트 용도로는 최적이다.</p>
<pre><code class="language-java">private final List&lt;SetObserver&lt;E&gt;&gt; observers =
        new CopyOnWriteArrayList&lt;&gt;();

public void addObserver(SetObserver&lt;E&gt; observer) {
    observers.add(observer);
}

public boolean removeObserver(SetObserver&lt;E&gt; observer) {
    return observers.remove(observer);
}

private void notifyElementAdded(E element) {
    for (SetObserver&lt;E&gt; observer : observers)
        observer.added(this, element);
}</code></pre>
<h4 id="열린-호출-open-call">열린 호출 (open call)</h4>
<p>동기화 영역 바깥에서 호출되는 외계인 메서드를 열린 호출(open call) 이라고 한다. 외계인 메서드는 얼마나 오래 실행될지 알 수 없는데, 동기화 영역 안에서 호출된다면 그 동안 스레드는 보호된 자원을 사용하지 못하고 대기해야만 한다. 따라서 열린 호출은 실패 방지 효과 외에도 동시성 효율을 크게 개선해준다.</p>
<p><strong>기본 규칙은 동기화 영역에서는 가능한 한 일을 적게 하는 것이다.</strong> 락을 억고, 공유 데이터를 검사하고, 필요하면 수정하고, 락을 놓는다. 오래 걸리는 작업이라면 <a href="https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-78.-%EA%B3%B5%EC%9C%A0-%EC%A4%91%EC%9D%B8-%EA%B0%80%EB%B3%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%8A%94-%EB%8F%99%EA%B8%B0%ED%99%94%ED%95%B4-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC">아이템 78</a>의 지침을 어기지 않으면서 동기화 영역 바깥으로 옮기는 방법을 찾아보자. </p>
<h3 id="과도한-동기화의-비용">과도한 동기화의 비용</h3>
<p>멀티코어가 일반화된 오늘날, 과도한 동기화가 초래하는 진짜 비용은 락을 얻는 데 드는 CPU 시간이 아니다. 
바로 <strong>경쟁하느라 낭비하는 시간</strong>, 즉 병렬로 실행할 기회를 잃고 모든 코어가 메모리를 일관되게 보기 위한 지연시간이 진짜 비용이다. <strong>가상머신의 코드 최적화를 제한</strong>한다는 점도 숨겨진 비용이다.</p>
<h3 id="가변-클래스-지침">가변 클래스 지침</h3>
<p>가변 클래스를 작성한다면 두 선택지 중 하나를 따르자.</p>
<ol>
<li><p>동기화를 전혀 하지 말고, 그 클래스를 동시에 사용해야 하는 클래스가 외부에서 알아서 동기화하게 하자.</p>
</li>
<li><p>동기화를 내부에서 수행해 스레드 안전한 클래스로 만들자. 단, 클라이언트가 외부에서 객체 전체에 락을 거는 것보다 동시성을 월등히 개선할 수 있을 때만 선택해야 한다.</p>
</li>
</ol>
<p>선택하기 어렵다면 동기화하지 말고, 대신 문서에 &quot;스레드 안전하지 않다&quot;고 명시하자.</p>
<p>만약 클래스 내부에서 동기화하기로 했다면, 락 분할(lock splitting), 락 스트라이핑(lock striping), 비차단 동시성 제어(nonblocking concurrency control) 등 다양한 기법을 동원해 동시성을 높여줄 수 있다.</p>
<br/> 

<blockquote>
<h4 id="📌-핵심-정리">📌 핵심 정리</h4>
<p>교착상태와 데이터 훼손을 피하려면 동기화 영역 안에서 외계인 메서드를 절대 호출하지 말자. 일반화해 이야기하면, 동기화 영역 안에서의 작업은 최소한으로 줄이자.
가변 클래스를 설계할 때는 스스로 동기화해야 할지 고민하자.
멀티코어 세상인 지금은 과도한 동기화를 피하는 게 어느 때보다 중요하다. 합당한 이유가 있을 때만 내부에서 동기화하고, 동기화했는지 여부를 문서에 명확히 밝히자(아이템 82).</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 78. 공유 중인 가변 데이터는 동기화해 사용하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-78.-%EA%B3%B5%EC%9C%A0-%EC%A4%91%EC%9D%B8-%EA%B0%80%EB%B3%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%8A%94-%EB%8F%99%EA%B8%B0%ED%99%94%ED%95%B4-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-78.-%EA%B3%B5%EC%9C%A0-%EC%A4%91%EC%9D%B8-%EA%B0%80%EB%B3%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%8A%94-%EB%8F%99%EA%B8%B0%ED%99%94%ED%95%B4-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</guid>
            <pubDate>Sun, 12 Mar 2023 05:12:17 GMT</pubDate>
            <description><![CDATA[<h3 id="synchronized">synchronized</h3>
<p><code>synchronized</code> 키워드는 해당 메서드나 블록을 한번에 한 스레드씩 수행하도록 보장한다. 많은 프로그래머가 동기화를 배타적 실행 용도로만 생각한다. <strong>배타적 실행</strong>이란, 한 스레드가 변경하는 중이라서 상태가 일관되지 않은 순간의 객체를 다른 스레드가 보지 못하게 막는 것을 의미한다.</p>
<p>그러나 동기화에는 중요한 기능이 하나 더 있다. 자바 언어 명세는 스레드가 필드를 읽을 때 항상 &#39;수정이 완전히 반영된&#39; 값을 얻는다고 보장하지만, 한 스레드가 저장한 값이 다른 스레드에게 &#39;보이는가&#39;는 보장하지 않는다. 동기화 없이는 한 스레드가 만든 변화를 다른 스레드에서 확인하지 못할 수 있다. 동기화는 동기화된 메서드나 블록에 들어간 스레드가 같은 락의 보호하에 수행된 모든 이전 수정의 최종 결과를 보게 해준다. </p>
<p><strong>동기화는 배타적 실행뿐 아니라 스레드 사이의 안정적인 통신에 꼭 필요하다.</strong>
이는 한 스레드가 만든 변화가 다른 스레드에게 언제 어떻게 보이는지를 규정한 자바의 메모리 모델 때문이다.</p>
<h3 id="동기화-예제">동기화 예제</h3>
<p>공유 중인 가변 데이터를 원자적으로 읽고 쓸 수 있을지라도(언어 명세상 <code>long</code> 과 <code>double</code> 외의 변수를 읽고 쓰는 동작은 원자적(atomic)이다) 동기화에 실패하면 처참한 결과로 이어질 수 있다. 다른 스레드를 멈추는 작업을 예로 들어보자.</p>
<blockquote>
<h4 id="threadstop">Thread.stop</h4>
<p><code>Thread.stop</code> 메서드는 안전하지 않아 오래전부터 deprecated API로 지정되었으며, <code>Thread.stop(Throwable obj)</code> 메서드는 자바 11에서 제거되었다. 
그러니 <strong>Thread.stop은 사용하지 말자.</strong> </p>
</blockquote>
<p><code>Thread.stop</code> 메서드를 사용하지 않고 다른 스레드를 멈추는 올바른 방법은, boolean 필드를 폴링하면서 그 값이 <code>true</code> 가 되면 멈추도록 하면 된다. 이 필드를 <code>false</code> 로 초기화해놓고, 다른 스레드에서 이 스레드를 멈추고자 할 때 <code>true</code> 로 변경하는 방식이다. <code>boolean</code> 필드를 읽고 쓰는 작업은 원자적이다.</p>
<pre><code class="language-java">public class StopThread {
    private static boolean stopRequested;

    public static void main(String[] args)
            throws InterruptedException {
        Thread backgroundThread = new Thread(() -&gt; {
            int i = 0;
            while (!stopRequested)
                i++;
        });
        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}</code></pre>
<p>이 프로그램은 1초 후에 종료될까? 이 책의 저자의 컴퓨터에서는 종료되지 않았다. 원인은 동기화에 있다.
동기화하지 않으면 메인 스레드가 수정한 값을 백그라운드 스레드가 언제쯤에나 보게 될지 보증할 수 없다. 
동기화가 빠지면 가상 머신이 끌어올리기(hosting)라는 기법을 사용해 다음과 같이 최적화를 수행할 수도 있다.</p>
<pre><code class="language-java">// 원래 코드
while (!stopRequested)
    i++;

// 최적화한 코드
if (!stopRequested)
    while (true)
        i++;</code></pre>
<p>이 결과, 프로그램은 <strong>응답 불가(liveness failure)</strong> 상태가 되어 더 이상 진전이 없다.</p>
<h4 id="동기화-사용">동기화 사용</h4>
<p><code>stopRequested</code> 필드를 동기화해 접근하면 이 문제를 해결할 수 있다.</p>
<pre><code class="language-java">public class StopThread {
    private static boolean stopRequested;

    // 추가
    private static synchronized void requestStop() { 
        stopRequested = true;
    }

    // 추가
    private static synchronized boolean stopRequested() {
        return stopRequested;
    }

    public static void main(String[] args)
            throws InterruptedException {
        Thread backgroundThread = new Thread(() -&gt; {
            int i = 0;
            while (!stopRequested())
                i++;
        });
        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);
        requestStop();
    }
}  </code></pre>
<p>쓰기 메서드(<code>requestStop</code>)와 읽기 메서드(<code>stopRequested</code>) 모두를 동기화했음에 주목하자. 쓰기와 읽기 모두가 동기화되지 않으면 동작을 보장하지 않는다. </p>
<h4 id="volatile-필드">volatile 필드</h4>
<p>반복문에서 매번 동기화하는 비용이 크진 않지만 속도가 더 빠른 대안으로는, <code>stopRequested</code> 필드를 <code>volatile</code> 로 선언하고 동기화를 생략하는 것이다. 
<code>volatile</code> 한정자는 배타적 수행과는 상관없지만 항상 가장 최근에 기록된 값을 읽게 됨을 보장한다.</p>
<pre><code class="language-java">public class StopThread {
    private static volatile boolean stopRequested; // volatile 선언

    public static void main(String[] args)
            throws InterruptedException {
        Thread backgroundThread = new Thread(() -&gt; {
            int i = 0;
            while (!stopRequested)
                i++;
        });
        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}</code></pre>
<p>단, <code>volatile</code> 은 주의해서 사용해야 한다. 다음의 예제는 동기화가 필요하다.</p>
<pre><code class="language-java">private static volatile int nextSerialNumber = 0;

public static int generateSerialNumber() {
    return nextSerialNumber++;
}</code></pre>
<p>이 메서드는 매번 고유한 값을 반환할 의도로 만들어졌다. <code>nextSerialNumber</code> 필드는 원자적으로 접근할 수 있고 굳이 동기화하지 않더라도 불변식을 보호할 수 있어 보인다. 그러나 동기화 없이는 올바로 동작하지 않는다. 
이는 증가 연산자(++)가 실제로는 <code>nextSerialNumber</code> 필드에 두 번 접근하기 때문이다. 만약 두 번째 스레드가 이 두 접근 사이를 비집고 들어와 값을 읽어가면 첫 번째 스레드와 똑같은 값을 돌려받게 될 것이다. 
이렇듯, 프로그램이 잘못된 결과를 계산해내는 이런 오류를 <strong>안전 실패(safety failure)</strong> 라고 한다.</p>
<p>이 문제는 generateSerialNumber 메서드에 synchronized 한정자를 붙이고 nextSerialNumber 필드에 volatile 을 제거하면 해결된다. </p>
<h4 id="락-프리lock-free-동기화">락-프리(lock-free) 동기화</h4>
<p><code>java.util.concurrent.atomic</code> 패키지의 <code>AtomicLong</code> 을 사용하는 방법도 있다. 이 패키지에는 락 없이도(lock-free; 락-프리) 스레드 안전한 프로그래밍을 지원하는 클래스들이 담겨 있다.</p>
<p><code>volatile</code> 은 동기화의 두 효과 중 통신 쪽만 지원하지만 이 패키지는 원자성(배타적 실행)까지 지원한다. 성능도 동기화 버전보다 우수하다.</p>
<pre><code class="language-java">private static final AtomicLong nextSerialNumber = new AtomicLong();

public static long generateSerialNumber() {
    return nextSerialNumber.getAndIncrement();
}</code></pre>
<h3 id="가변-데이터는-단일-스레드에서만-쓰자">가변 데이터는 단일 스레드에서만 쓰자</h3>
<p>지금까지 언급한 문제들을 피하는 가장 좋은 방법은 애초에 가변 데이터를 공유하지 않는 것이다. 불변 데이터만 공유하거나 아무것도 공유하지 말자.</p>
<p><strong>가변 데이터는 단일 스레드에서만 쓰도록 하자.</strong></p>
<p>이 정책을 받아들였다면 그 사실을 문서에 남겨 유지보수 과정에서도 정책이 계속 지켜지도록 하는 것이 중요하다. 또한 프레임워크와 라이브러리를 깊이 이해하는 것도 중요하다. 외부 코드가 인지하지 못한 스레드를 수행하는 복병으로 작용하는 경우도 있기 때문이다.</p>
<h3 id="안전-발행">안전 발행</h3>
<p>한 스레드가 데이터를 다 수정한 후 다른 스레드에 공유할 때는 해당 객체에서 공유하는 부분만 동기화해도 된다. 이런 객체를 <strong>사실상 불변(effectively immutable)</strong> 이라 하고, 다른 스레드에 이런 객체를 건네는 행위를 <strong>안전 발행(safe publication)</strong> 이라 한다.</p>
<p>객체를 안전하게 발행하는 방법은 많다.</p>
<ul>
<li>정적 필드에 저장</li>
<li><code>volatile</code> 필드에 저장</li>
<li><code>final</code> 필드에 저장</li>
<li>보통의 락을 통해 접근</li>
<li>동시성 컬렉션에 저장</li>
</ul>
<br/>

<blockquote>
<h4 id="📌-핵심-정리">📌 핵심 정리</h4>
<p><strong>여러 스레드가 가변 데이터를 공유한다면 그 데이터를 읽고 쓰는 동작은 반드시 동기화해야 한다.</strong>
동기화하지 않으면 한 스레드가 수행한 변경을 다른 스레드가 보지 못할 수도 있다.
공유되는 가변 데이터를 동기화하는 데 실패하면 응답 불가 상태에 빠지거나 안전 실패로 이어질 수 있다. 이는 디버깅 난이도가 가장 높은 문제에 속한다. 간헐적이거나 특정 타이밍에만 발생할 수 있고, VM에 따라 현상이 달라지기도 한다.
배타적 실행은 필요 없고 스레드끼리의 통신만 필요하다면 <code>volatile</code> 한정자만으로 동기화할 수 있다. 다만 올바로 사용하기가 까다롭다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 77. 예외를 무시하지 말라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-77.-%EC%98%88%EC%99%B8%EB%A5%BC-%EB%AC%B4%EC%8B%9C%ED%95%98%EC%A7%80-%EB%A7%90%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-77.-%EC%98%88%EC%99%B8%EB%A5%BC-%EB%AC%B4%EC%8B%9C%ED%95%98%EC%A7%80-%EB%A7%90%EB%9D%BC</guid>
            <pubDate>Sat, 11 Mar 2023 08:02:45 GMT</pubDate>
            <description><![CDATA[<p>API 설계자가 메서드 선언에 예외를 명시하는 까닭은, 그 메서드를 사용할 때 적절한 조치를 취해달라고 말하는 것이다. API 설계자의 목소리를 흘려버리지 말자. </p>
<p>물론 예외를 무시해야 할 때도 있다. 예를 들어 <code>FileInputStream</code> 을 닫을 때가 그렇다. 파일의 상태를 변경하지 않았으니 복구할 것이 없고, 필요한 정보는 이미 다 읽었기 때문에 남은 작업을 중단할 이유도 없다. </p>
<p><strong>예외를 무시하기로 했다면 <code>catch</code> 블록 안에 그렇게 결정한 이유를 주석으로 남기고 예외 변수의 이름도 <code>ignored</code> 로 바꿔놓도록 하자.</strong></p>
<pre><code class="language-java">Future&lt;Integer&gt; f = exec.submit(planarMap::chroaticNumber);
int numColors = 4;
try {
    numColors = f.get(1L, TimeUnit.SECONDS);
} catch (TimeoutExeption | ExecutionException ignored) {
    // 기본값을 사용한다.
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 76. 가능한 한 실패 원자적으로 만들라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-76.-%EA%B0%80%EB%8A%A5%ED%95%9C-%ED%95%9C-%EC%8B%A4%ED%8C%A8-%EC%9B%90%EC%9E%90%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%A7%8C%EB%93%A4%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-76.-%EA%B0%80%EB%8A%A5%ED%95%9C-%ED%95%9C-%EC%8B%A4%ED%8C%A8-%EC%9B%90%EC%9E%90%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%A7%8C%EB%93%A4%EB%9D%BC</guid>
            <pubDate>Sat, 11 Mar 2023 07:48:28 GMT</pubDate>
            <description><![CDATA[<p><strong>실패 원자적(failure-atomic)</strong> 이란, 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지하는 것을 말한다. </p>
<h3 id="메서드를-실패-원자적으로-만드는-방법">메서드를 실패 원자적으로 만드는 방법</h3>
<p><strong>1. 불변 객체로 설계하기</strong></p>
<p>불변 객체는 태생적으로 실패 원자적이다. 불변 객체의 상태는 생성 시점에 고정되어 변하지 않기 때문에, 메서드가 실패하면 새로운 객체가 만들어지지는 않을 수 있으나 기존 객체가 불안정 상태에 빠지는 일은 없다. </p>
<p><strong>2. 작업 수행 전에 매개변수의 유효성 검사하기</strong> (아이템 49)</p>
<p>객체의 내부 상태를 변경하기 전에 잠재적 예외의 가능성 대부분을 걸러낼 수 있는 방법이다.</p>
<p>Stack.pop 메서드 예를 보자.</p>
<pre><code class="language-java">public Object pop() {
    if (size == 0) 
        throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null;
    return result;
}</code></pre>
<p>또한 이와 비슷한 취지로 실패할 가능성이 있는 모든 코드를 객체의 상태를 바꾸는 코드보다 앞에 배치하는 방법도 있다. 계산을 수행해보기 전에는 인수의 유효성을 검사해볼 수 없을 때 앞서의 방식에 덧붙여 쓸 수 있는 기법이다.</p>
<p><strong>3. 객체의 임시 복사본에서 작업 수행하기</strong></p>
<p>객체의 임시 복사본에서 작업을 수행한 다음, 성공적으로 완료되면 원래 객체와 교체하는 것이다. 데이터를 임시 자료구조에 저장해 작업하는 게 더 빠를 때 적용하기 좋은 방식이다.</p>
<p>예를 들어, 리스트를 정렬하는 메서드에서 입력 리스트의 원소들을 배열로 옮겨 담아 정렬을 수행한다. 배열을 사용할 때 반복문에서 원소들에 훨씬 빠르게 접근할 수 있기 때문이다.</p>
<p><strong>4. 작업 도중 발생하는 실패를 가로채는 복구 코드 작성하기</strong></p>
<p>복구 코드를 통해 작업 전 상태로 되돌리는 방법이다. 주로 내구성(durability)을 보장해야 하는 자료구조에서 쓰인다. 이 방법은 자주 쓰이지는 않는다.</p>
<h3 id="실패-원자적으로-만들지-못하는않는-경우">실패 원자적으로 만들지 못하는(않는) 경우</h3>
<p>실패 원자성은 일반적으로 권장되지만, 항상 달성할 수 있는 것은 아니다. </p>
<p>두 스레드가 동기화 없이 같은 객체를 동시에 수정한다면 그 객체의 일관성이 깨질 수 있다. (<code>ConcurrentModificationException</code> 을 잡아냈다고 해도 그 객체가 여전히 쓸 수 있는 상태라고 가정해서는 안 된다)</p>
<p><code>Error</code> 는 복구할 수 없으므로 <code>AssertionError</code> 에 대해서는 실패 원자적으로 만들려는 시도조차 할 필요가 없다.</p>
<p>또한 실패 원자적으로 만들 수 있더라도, 실패 원자성을 달성하기 위해 비용이나 복잡도가 아주 큰 연산이라면 반드시 그리 해야 하는 것은 아니다. </p>
<h3 id="주의사항">주의사항</h3>
<p>메서드 명세에 기술한 예외라면 예외가 발생하더라도 객체의 상태는 메서드 호출 전과 똑같이 유지돼야 한다는 것이 기본 규칙이다. 
그러나 만약 이 규칙을 지키지 못한다면 실패 시의 객체 상태를 API 설명에 명시해야 한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 75. 예외의 상세 메시지에 실패 관련 정보를 담으라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-75.-%EC%98%88%EC%99%B8%EC%9D%98-%EC%83%81%EC%84%B8-%EB%A9%94%EC%8B%9C%EC%A7%80%EC%97%90-%EC%8B%A4%ED%8C%A8-%EA%B4%80%EB%A0%A8-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EB%8B%B4%EC%9C%BC%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-75.-%EC%98%88%EC%99%B8%EC%9D%98-%EC%83%81%EC%84%B8-%EB%A9%94%EC%8B%9C%EC%A7%80%EC%97%90-%EC%8B%A4%ED%8C%A8-%EA%B4%80%EB%A0%A8-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EB%8B%B4%EC%9C%BC%EB%9D%BC</guid>
            <pubDate>Sat, 11 Mar 2023 07:20:50 GMT</pubDate>
            <description><![CDATA[<p>예외를 잡지 못해 프로그램이 실패하면 자바 시스템은 그 예외의 <code>스택 추적(stack trace)</code> 정보를 자동으로 출력한다. 스택 추적은 예외 객체의 <code>toString</code> 메서드를 호출해 얻은 문자열이다. 이는 보통 예외 클래스 이름 뒤에 상세 메시지가 붙는 형태다. </p>
<p>이 정보는 실패 원인을 분석해야 하는 프로그래머 혹은 사이트 신뢰성 엔지니어(site reliability engineer, SRE)가 얻을 수 있는 유일한 정보인 경우가 많다. 따라서 예외의 <code>toString</code> 메서드에 실패 원인에 관한 정보를 가능한 한 많이 담아 반환하는 일은 아주 중요하다.</p>
<p><strong>실패 순간을 포착하려면 발생한 예외에 관여된 모든 매개변수와 필드의 값을 실패 메시지에 담아야 한다.</strong>  단, 보안과 관련된 정보는 주의하자. 스택 추적 정보는 많은 사람이 볼 수 있으므로 <strong>상세 메시지에 비밀번호나 암호 키 같은 정보까지 담아서는 안 된다.</strong>
또한, 문서와 소스코드에서 얻을 수 있는 정보까지 장황하게 담을 필요는 없다. 스택 추적에는 예외가 발생한 파일 이름과 줄번호, 호출한 다른 메서드들의 파일 이름과 줄번호까지 정확히 기록되어 있기 때문이다.</p>
<h3 id="예외-생성자">예외 생성자</h3>
<p>실패를 적절히 포착하기 위해, 필요한 정보를 예외 생성자에서 모두 받아서 상세 메시지까지 미리 생성해놓는 방법도 괜찮다. 예를 들어 <code>IndexOutOfBoundsException</code> 생성자는 다음과 같이 구현해도 좋았을 것이다. </p>
<pre><code class="language-java">public class IndexOutOfBoundsException extends RuntimeException {
    private final int lowerBound;
    private final int upperBound;
    private final int index;

    /**
     * IndexOutOfBoundsException을 생성한다.
     *
     * @param lowerBound 인덱스의 최솟값
     * @param upperBound 인덱스의 최댓값 + 1
     * @param index      인덱스의 실젯값
     */
    public IndexOutOfBoundsException(int lowerBound, int upperBound,
                                     int index) {
        // 실패를 포착하는 상세 메시지를 생성한다.
        super(String.format(&quot;최솟값: %d, 최댓값: %d, 인덱스: %d&quot;, 
                            lowerBound, upperBound, index));

        // 프로그램에서 이용할 수 있도록 실패 정보를 저장해둔다.
        this.lowerBound = lowerBound;
        this.upperBound = upperBound;
        this.index = index;
    }
}</code></pre>
<p>이렇게 해두면 프로그래머가 던지는 예는 자연스럽게 실패를 더 잘 포착한다.
또한, 클래스 사용자가 메시지를 만드는 작업을 중복하지 않아도 된다.</p>
<h3 id="접근자-메서드">접근자 메서드</h3>
<p>예외는 실패와 관련된 정보를 얻을 수 있는 접근자 메서드를 적절히 제공하는 것이 좋다. 포착한 실패 정보는 예외 상황을 복구하는 데 유용할 수 잇으므로 접근자 메서드는 비검사 예외보다는 검사 예외에서 더 빛을 발한다. 
하지만 &#39;toString이 반환한 값에 포함된 정보를 얻어올 수 있는 API를 제공하자&#39;라는 일반 원칙(<a href="https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C12.-toString%EC%9D%84-%ED%95%AD%EC%83%81-%EC%9E%AC%EC%A0%95%EC%9D%98%ED%95%98%EB%9D%BC">아이템 12</a>)을 따른다는 관점에서, 책의 저자는 비검사 예외라도 상세 정보를 알려주는 접근자 메서드를 제공하라고 권장한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 74. 메서드가 던지는 모든 예외를 문서화하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-74.-%EB%A9%94%EC%84%9C%EB%93%9C%EA%B0%80-%EB%8D%98%EC%A7%80%EB%8A%94-%EB%AA%A8%EB%93%A0-%EC%98%88%EC%99%B8%EB%A5%BC-%EB%AC%B8%EC%84%9C%ED%99%94%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-74.-%EB%A9%94%EC%84%9C%EB%93%9C%EA%B0%80-%EB%8D%98%EC%A7%80%EB%8A%94-%EB%AA%A8%EB%93%A0-%EC%98%88%EC%99%B8%EB%A5%BC-%EB%AC%B8%EC%84%9C%ED%99%94%ED%95%98%EB%9D%BC</guid>
            <pubDate>Fri, 10 Mar 2023 02:20:42 GMT</pubDate>
            <description><![CDATA[<p>메서드가 던지는 예외는 그 메서드를 올바로 사용하는 데 아주 중요한 정보이므로, 각 메서드가 던지는 예외 하나하나를 문서화하는 데 충분한 시간을 쏟아야 한다.</p>
<h3 id="검사-예외">검사 예외</h3>
<p><strong>검사 예외는 항상 따로따로 선언하고, 각 예외가 발생하는 상황을 자바독의 @throw 태그를 사용하여 정확히 문서화하자.</strong>
공통 상위 클래스 하나로 뭉뚱그려 선언하는 일은 삼가자. 이는 메서드 사용자에게 각 예외에 대처할 수 있는 힌트를 주지 못할뿐더러, 같은 맥락에서 발생할 여지가 있는 다른 예외들까지 삼켜버릴 수 있다.
이 규칙의 유일한 예외는 <code>main</code> 메서드다. <code>main</code> 은 오직 JVM만이 호출하므로 <code>Exception</code> 을 던지도록 선언해도 괜찮다.</p>
<h3 id="비검사-예외">비검사 예외</h3>
<p>자바 언어가 요구하는 것은 아니지만 비검사 예외도 검사 예외처럼 문서화해두면 좋다. 
잘 정비된 비검사 예외 문서는 사실상 그 메서드를 성공적으로 수행하기 위한 전제조건이 된다. public 메서드라면 필요한 전제조건을 문서화해야 하며, 그 수단으로 가장 좋은 것이 비검사 예외들을 문서화하는 것이다.</p>
<p><strong>메서드가 던질 수 있는 예외를 각각 @throw 태그로 문서화하되, 비검사 예외는 메서드 선언의 throws 목록에 넣지 말자.</strong> 자바독 유틸리티는 메서드 선언의 throws 절에 등장하고 메서드 주석의 @throw 태그에도 명시항 예외와 @throw 태그에만 명시한 예외를 시각적으로 구분해준다. 그래서 프로그래머는 어느 것이 비검사 예외인지를 바로 알 수 있다.</p>
<h3 id="클래스-문서화-주석">클래스 문서화 주석</h3>
<p><strong>한 클래스에 정의도니 많은 메서드가 같은 이유로 같은 예외를 던진다면 그 예외를 각각의 메서드가 아닌 클래스 설명에 추가하는 방법도 있다.</strong>
예를 들어, 클래스의 문서화 주석에 &quot;이 클래스의 모든 메서드는 인수로 null이 넘어오면 NullPointerException을 던진다&quot;라고 적을 수 있다.</p>
<br/>


<blockquote>
<h4 id="📌-핵심-정리">📌 핵심 정리</h4>
<p>메서드가 던질 가능성이 있는 모든 예외를 문서화하라.
검사 예외든 비검사 예외든, 추상 메서드든 구체 메서드든 모두 마찬가지다.
문서화에는 자바독의 @throw 태그를 사용하자.
검사 예외만 메서드 선언의 throws 문에 일일이 선언하고, 비검사 예외는 메서드 선언에는 기입하지 말자.
발생 가능한 예외를 문서로 남기지 않으면 다른 사람이 그 클래스나 인터페이스를 효과적으로 사용하기 어렵거나 심지어 불가능할 수도 있다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 73. 추상화 수준에 맞는 예외를 던지라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-73.-%EC%B6%94%EC%83%81%ED%99%94-%EC%88%98%EC%A4%80%EC%97%90-%EB%A7%9E%EB%8A%94-%EC%98%88%EC%99%B8%EB%A5%BC-%EB%8D%98%EC%A7%80%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-73.-%EC%B6%94%EC%83%81%ED%99%94-%EC%88%98%EC%A4%80%EC%97%90-%EB%A7%9E%EB%8A%94-%EC%98%88%EC%99%B8%EB%A5%BC-%EB%8D%98%EC%A7%80%EB%9D%BC</guid>
            <pubDate>Fri, 10 Mar 2023 02:01:32 GMT</pubDate>
            <description><![CDATA[<h3 id="예외-번역">예외 번역</h3>
<p>메서드가 저수준 예외를 처리하지 않고 바깥으로 전파해버리면, 종종 수행하려는 일과 관련 없어 보이는 예외가 발생한다. 이는 내부 구현 방식을 드러내어 윗 레벨 API를 오염시킨다. 다음 릴리스에서 구현 방식을 바꾸면 다른 예외가 튀어나와 기존 클라이언트 프로그램을 깨지게 할 수도 있다.</p>
<p>이 문제를 피하려면 <strong>상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꿔 던져야 한다.</strong>
이를 <strong>예외 번역(exception translation)</strong>이라 한다.</p>
<pre><code class="language-java">try {
    ... // 저수준 추상화를 이용한다.
} catch (LowerLevelException e) {
    // 추상화 수준에 맞게 번역한다.
    throw new HigherLevelException(...);
}</code></pre>
<h3 id="예외-연쇄">예외 연쇄</h3>
<p>예외 연쇄(exception chaining)란 문제의 근본 원인(cause)인 저수준 예외를 고수준 예외에 실어 보내는 방식이다.
예외를 번역할 때, 저수준 예외가 디버깅에 도움이 된다면 예외 연쇄을 사용하는 게 좋다. 그러면 별도의 접근자 메서드를 통해 필요하면 언제든 저수준 예외를 꺼내볼 수 있다.</p>
<pre><code class="language-java">try {
    ... // 저수준 추상화를 이용한다.
} catch (LowerLevelException cause) {
    // 저수준 예외를 고수준 예외에 실어 보낸다.
    throw new HigherLevelException(cause);
}</code></pre>
<p>예외 연쇄용으로 설계된 고수준 예외의 생성자는 상위 클래스의 생성자에 이 &#39;원인&#39;을 건네주어, 최종적으로 <code>Throwable(Throwable)</code> 생성자까지 건네지게 된다.</p>
<pre><code class="language-java">class HigherLevelException extends Exception {
    HigherLevelException(Throwable cause) {
        super(cause);
    }
}</code></pre>
<p>대부분의 표준 예외는 예외 연쇄용 생성자를 갖추고 있다. 
그렇지 않은 예외라도 <code>Throwable</code> 의 <code>initCause</code> 메서드를 이용해 &#39;원인&#39;을 직접 못박을 수도 있다.
예외 연쇄는 문제의 원인을 <code>getCause</code> 메서드로 프로그램에서 접근할 수 있게 해주며, 원인과 고수준 예외의 스택 추적 정보를 잘 통합해준다.</p>
<h3 id="예외-전파를-피하는-방법">예외 전파를 피하는 방법</h3>
<p><strong>무턱대고 예외를 전파하는 것보다야 예외 번역이 우수한 방법이지만, 그렇다고 남용해서는 안 된다.</strong> 
가능하다면 저수준 메서드가 반드시 성공하도록 하여 아래 계층에서는 예외가 발생하지 않도록 하는 것이 최선이다. 때론 상위 계층 메서드의 매개변수 값을 아래 계층 메서드로 건네기 전에 미리 검사하는 방법으로 이 목적을 달성할 수 있다. </p>
<p>만약 아래 계층에서의 예외를 피할 수 없다면, 상위 계층에서 그 예외를 조용히 처리하여 문제를 API 호출자에까지 전파하지 않는 방법이 있다.
이 경우 발생한 예외는 <code>java.util.logging</code> 같은 적절한 로깅 기능을 활용하여 기록해두면 좋다. 그렇게 하면 클라이언트 코드와 사용자에게 문제를 전파하지 않으면서도 프로그래머가 로그를 분석해 추가 조치를 취할 수 있게 된다.</p>
<br/>


<blockquote>
<h4 id="📌-핵심-정리">📌 핵심 정리</h4>
<p>아래 계층의 예외를 예방하거나 스스로 처리할 수 없고, 그 예외를 상위 계층에 그대로 노출하기 곤란하다면 예외 번역을 사용하라. 이때 예외 연쇄를 이용하면 상위 계층에는 맥락에 어울리는 고수준 예외를 던지면서 근본 원인도 함께 알려주어 오류를 분석하기에 좋다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 72. 표준 예외를 사용하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-72.-%ED%91%9C%EC%A4%80-%EC%98%88%EC%99%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-72.-%ED%91%9C%EC%A4%80-%EC%98%88%EC%99%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</guid>
            <pubDate>Fri, 10 Mar 2023 01:37:51 GMT</pubDate>
            <description><![CDATA[<p>자바 라이브러리는 대부분 API에서 쓰기에 충분한 수의 예외를 제공한다.
표준 예외를 재사용하면 얻는 점이 많다. </p>
<ul>
<li>다른 사람이 익히고 사용하기 쉬워진다는 것이다. 많은 프로그래머에게 이미 익숙해진 규약을 그대로 따르기 때문이다. </li>
<li>낯선 예외를 사용하지 않으므로 읽기 쉽다.</li>
<li>예외 클래스 수가 적을수록 메모리 사용량도 줄고, 클래스를 적재하는 시간도 적게 걸린다.</li>
</ul>
<hr>
<p>널리 사용되는 표준 예외는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>예외</th>
<th>주요 쓰임</th>
</tr>
</thead>
<tbody><tr>
<td>IllegalArgumentException</td>
<td>허용하지 않는 값이 인수로 건네졌을 때 (단, null은 NullPointerException으로 처리)</td>
</tr>
<tr>
<td>IllegalStateException</td>
<td>객체가 메서드르 수행하기에 적절하지 않은 상태일 때</td>
</tr>
<tr>
<td>NullPointerException</td>
<td>null을 허용하지 않는 메서드에 null을 건넸을 때</td>
</tr>
<tr>
<td>IndexOutOfBoundsException</td>
<td>인덱스가 범위를 넘어섰을 때</td>
</tr>
<tr>
<td>ConcurrentModificationException</td>
<td>허용하지 않는 동시 수정이 발견됐을 때</td>
</tr>
<tr>
<td>UnsupportedOperationException</td>
<td>호출한 메서드를 지원하지 않을 때</td>
</tr>
</tbody></table>
<p><strong>Exception, RuntimeException, Throwable, Error는 직접 재사용하지 말자.</strong>
이 클래스들은 추상 클래스라고 생각하자. 이 예외들은 다른 예외들의 상위 클래스이므로, 즉 여러 성격의 예외들을 포괄하는 클래스이므로 안정적으로 테스트할 수 없다.</p>
<p>&#39;주요 쓰임&#39;이 상호 배타적이지 않기 때문에, 종종 재사용할 예외를 선택하기 어려울 때도 있다. 예를 들어 IllegalArgumentException과 IllegalStateException 둘 중에 어떤 예외를 재사용해야 할지 고민할 수 있다. 일반적인 규칙은 이렇다. <strong>인수 값이 무엇이었든 어차피 실패했을 거라면 IllegalStateException을, 그렇지 않으면 IllegalArgumentException을 던지자.</strong></p>
<hr>
<p>상황에 부합한다면 항상 표준 예외를 재사용하자. 이때 API 문서를 참고해 그 예외가 어떤 상황에서 던져지는지 꼭 확인해야 한다. 예외의 이름뿐 아니라 예외가 던져지는 맥락도 부합할 때만 재사용한다.
더 많은 정보를 제공하길 원한다면 표준 예외를 확장해도 좋다. 단, 예외는 직렬화할 수 있다는 사실을 기억하자(직렬화에는 많은 부담이 따른다). 이 사실만으로도 나만의 예외를 새로 만들지 않아야 할 근거로 충분할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 71. 필요 없는 검사 예외 사용은 피하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-71.-%ED%95%84%EC%9A%94-%EC%97%86%EB%8A%94-%EA%B2%80%EC%82%AC-%EC%98%88%EC%99%B8-%EC%82%AC%EC%9A%A9%EC%9D%80-%ED%94%BC%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-71.-%ED%95%84%EC%9A%94-%EC%97%86%EB%8A%94-%EA%B2%80%EC%82%AC-%EC%98%88%EC%99%B8-%EC%82%AC%EC%9A%A9%EC%9D%80-%ED%94%BC%ED%95%98%EB%9D%BC</guid>
            <pubDate>Thu, 09 Mar 2023 10:47:45 GMT</pubDate>
            <description><![CDATA[<p>검사 예외는 제대로 활용하면 API와 프로그램의 질을 높일 수 있다. 검사 예외는 발생한 문제를 프로그래머가 처리하여 안전성을 높이게끔 해준다.</p>
<p>그러나 검사 예외를 과하게 사용하면 오히려 쓰기 불편한 API가 된다. 어떤 메서드가 검사 예외를 던질 수 있다고 선언됐다면, 이를 호출하는 코드에서는 <code>catch</code> 블록을 두어 그 예외를 붙잡아 처리하거나 더 바깥으로 던져 문제를 전파해야만 하므로 API 사용자에게 부담을 준다. 더구나 검사 예외를 던지는 메서드는 스트림 안에서 직접 사용할 수 없기 때문에 자바 8부터는 부담이 더욱 커졌다.</p>
<p>API를 제대로 사용해도 발생할 수 있는 예외이거나, 프로그래머가 의미있는 조치를 취할 수 있는 경우라면 검사 예외의 부담쯤은 받아들일 수 있을 것이다.
그러나 둘 중 어디에도 해당하지 않는다면 비검사 예외를 사용하는 것이 좋다.</p>
<p>검사 예외를 회피하는 가장 쉬운 방법은 적절한 결과 타입을 담은 <strong>옵셔널</strong>을 반환하는 것이다. 검사 예외를 던지는 대신 단순히 빈 옵셔널을 반환하면 된다.</p>
<p>또 다른 방법은 검사 예외를 던지는 메서드를 2개로 쪼개 비검사 예외로 바꾸는 것이다. 첫 번째 메서드는 예외가 던져질지 여부를 boolean 값으로 반환하는 <strong>상태 검사 메서드</strong>다. 다음의 예시를 보자.</p>
<pre><code class="language-java">if (obj.actionPermitted(args)) { // 상태 검사
    obj.action(args);
} else {
    ... // 예외 상황에 대처한다.
}</code></pre>
<p>단, 상태 검사 메서드는 <a href="https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-69.-%EC%98%88%EC%99%B8%EB%8A%94-%EC%A7%84%EC%A7%9C-%EC%98%88%EC%99%B8-%EC%83%81%ED%99%A9%EC%97%90%EB%A7%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC#:~:text=%EC%99%B8%EB%B6%80%20%EB%8F%99%EA%B8%B0%ED%99%94%20%EC%97%86%EC%9D%B4%20%EC%97%AC%EB%9F%AC%20%EC%8A%A4%EB%A0%88%EB%93%9C%EA%B0%80%20%EB%8F%99%EC%8B%9C%EC%97%90%20%EC%A0%91%EA%B7%BC%ED%95%A0%20%EC%88%98%20%EC%9E%88%EA%B1%B0%EB%82%98%20%EC%99%B8%EB%B6%80%20%EC%9A%94%EC%9D%B8%EC%9C%BC%EB%A1%9C%20%EC%83%81%ED%83%9C%EA%B0%80%20%EB%B3%80%ED%95%A0%20%EC%88%98%20%EC%9E%88%EB%8B%A4%EB%A9%B4%20%EC%98%B5%EC%85%94%EB%84%90%EC%9D%B4%EB%82%98%20%ED%8A%B9%EC%A0%95%20%EA%B0%92%EC%9D%84%20%EC%82%AC%EC%9A%A9%ED%95%9C%EB%8B%A4.%20%EC%83%81%ED%83%9C%20%EA%B2%80%EC%82%AC%20%EB%A9%94%EC%84%9C%EB%93%9C%EC%99%80%20%EC%83%81%ED%83%9C%20%EC%9D%98%EC%A1%B4%EC%A0%81%20%EB%A9%94%EC%84%9C%EB%93%9C%20%ED%98%B8%EC%B6%9C%20%EC%82%AC%EC%9D%B4%EC%97%90%20%EA%B0%9D%EC%B2%B4%EC%9D%98%20%EC%83%81%ED%83%9C%EA%B0%80%20%EB%B3%80%ED%95%A0%20%EC%88%98%20%EC%9E%88%EA%B8%B0%20%EB%95%8C%EB%AC%B8%EC%9D%B4%EB%8B%A4.">아이템 69</a>에서 말했듯이, 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부 요인에 의해 상태가 변할 수 있다면 적절하지 않다.
또한, actionPermitted가 action 메서드의 작업 일부를 중복 수행한다면 성능에서 손해이다.</p>
<br/>


<blockquote>
<h4 id="📌-핵심-정리">📌 핵심 정리</h4>
<p>꼭 필요한 곳에만 사용한다면 검사 예외는 프로그램의 안전성을 높여주지만, 남용하면 쓰기 고통스러운 API를 낳는다. 
API 호출자가 예외 상황에서 복구할 방법이 없다면 비검사 예외를 던지자.
복구가 가능하고 호출자가 그 처리를 해주길 바란다면, 우선 옵셔널을 반환해도 될지 고민하자. 옵셔널만으로는 상황을 처리하기에 충분한 정보를 제공할 수 없을 때만 검사 예외를 던지자.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-70.-%EB%B3%B5%EA%B5%AC%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%83%81%ED%99%A9%EC%97%90%EB%8A%94-%EA%B2%80%EC%82%AC-%EC%98%88%EC%99%B8%EB%A5%BC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%98%A4%EB%A5%98%EC%97%90%EB%8A%94-%EB%9F%B0%ED%83%80%EC%9E%84-%EC%98%88%EC%99%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-70.-%EB%B3%B5%EA%B5%AC%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%83%81%ED%99%A9%EC%97%90%EB%8A%94-%EA%B2%80%EC%82%AC-%EC%98%88%EC%99%B8%EB%A5%BC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%98%A4%EB%A5%98%EC%97%90%EB%8A%94-%EB%9F%B0%ED%83%80%EC%9E%84-%EC%98%88%EC%99%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</guid>
            <pubDate>Thu, 09 Mar 2023 10:27:35 GMT</pubDate>
            <description><![CDATA[<p>자바는 문제 상황을 알리는 타입(throwable)으로 <strong>검사 예외</strong>, <strong>런타임 예외</strong>, <strong>에러</strong>, 이렇게 세 가지를 제공한다.</p>
<p>언제 어떤 것을 사용해야 할까?</p>
<h3 id="검사-예외">검사 예외</h3>
<p>호출하는 쪽에서 복구할 것으로 여겨지는 상황이라면 검사 예외를 사용하자. 이것은 검사와 비검사 예외를 구분하는 기본 규칙이다. 검사 예외를 던지면 호출자가 그 예외를 <code>catch</code> 로 잡아 처리하거나 더 바깥으로 전파하도록 강제하게 된다. 따라서 메서드 선언에 포함된 검사 예외 각각은 그 메서드를 호출했을 때 발생할 수 있는 유력한 결과임을 API 사용자에게 알려주는 것이다.</p>
<p>또한 검사 예외는 일반적으로 복구할 수 있는 조건일 때 발생하므로, 호출자가 예외 상황에서 벗어나는 데 필요한 정보를 알려주는 메서드를 함께 제공하는 것이 중요하다. </p>
<h3 id="비검사-예외">비검사 예외</h3>
<p>비검사 throwable은 두 가지로, <strong>런타임 예외</strong>와 <strong>에러</strong>다. 이 둘은 프로그램에서 잡을 필요가 없거나 통상적으로 잡지 말아야 한다. 이런 throwable을 잡지 않는 스레드는 적절한 오류 메시지를 내뱉으며 중단된다.</p>
<p><strong>프로그래밍 오류를 나타낼 때는 런타임 예외를 사용하자.</strong> 런타임 예외의 대부분은 전제조건을 만족하지 못했을 때 발생한다. 전제조건 위배란 단순히 클라이언트가 해당 API의 명세에 기록된 제약을 지키지 못했다는 뜻이다.</p>
<p>에러는 보통 JVM이 자원 부족, 불변식 깨짐 등 더 이상 수행을 계속할 수 없는 상황을 나타낼 때 사용한다. 따라서 <code>Error</code> 클래스를 상속해 하위 클래스를 만드는 일은 자제하기 바란다. 다시 말해, 우리가 구현하는 비검사 throwable은 모두 <code>RuntimeException</code> 의 하위 클래스여야 한다. <code>Error</code> 는 상속하지 말고, <code>throw</code> 문으로 직접 던지는 일도 없어야 한다(단, <code>AssertionError</code> 는 예외다).</p>
<h3 id="throwable">throwable</h3>
<p>throwable 은 언제 사용하면 좋을까?</p>
<p>throwable은 이로울게 없으니 절대로 사용하지 말자! 정상적인 예외보다 나을 게 하나도 없으면서 API 사용자를 헷갈리게 할 뿐이다.</p>
<br/>

<blockquote>
<h4 id="📌-핵심-정리">📌 핵심 정리</h4>
<p>복구할 수 있는 상황이면 검사 예외를 던지자.
프로그래밍 오류라면 비검사 예외를 던지자.
확실하지 않다면 비검사 예외를 던지자.
검사 예외도 아니고, 런타임 예외도 아닌 throwable은 정의하지도 말자.
검사 예외라면 복구에 필요한 정보를 알려주는 메서드도 제공하자.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 69. 예외는 진짜 예외 상황에만 사용하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-69.-%EC%98%88%EC%99%B8%EB%8A%94-%EC%A7%84%EC%A7%9C-%EC%98%88%EC%99%B8-%EC%83%81%ED%99%A9%EC%97%90%EB%A7%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-69.-%EC%98%88%EC%99%B8%EB%8A%94-%EC%A7%84%EC%A7%9C-%EC%98%88%EC%99%B8-%EC%83%81%ED%99%A9%EC%97%90%EB%A7%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</guid>
            <pubDate>Thu, 09 Mar 2023 10:06:10 GMT</pubDate>
            <description><![CDATA[<p>예외는 오직 예외 상황에서만 써야 한다. 절대로 일상적인 제어 흐름용으로 쓰여서는 안 된다. 예를 들어, 반복문에 예외를 사용하는 것은 직관적이지도 않을 뿐더러 성능도 더 느리다. </p>
<p>표준적이고 쉽게 이해되는 관용구를 사용하고, 성능 개선을 목적으로 과하게 머리를 쓴 기법은 자제하자. 실제로 성능이 좋아지더라도 자바 플랫폼이 꾸준히 개선되고 있는 최적화로 얻은 상대적인 성능 우위가 오래가지 않을 수 있다. 반면 과하게 영리한 기법에 숨겨진 미묘한 버그의 폐해와 어려워진 유지보수 문제는 계속 이어질 것이다.</p>
<p>이 원칙은 API 설계에도 적용된다. 잘 설계된 API라면 클라이언트가 정삭적인 제어 흐름에서 예외를 사용할 일이 없게 해야 한다. 특정 상태에세만 호출할 수 있는 &#39;상태 의존적&#39; 메서드를 제공하는 클래스는 &#39;상태 검사&#39; 메서드도 함께 제공해야 한다. (ex. Iterator 인터페이스의 next, hasNext 메서드)</p>
<p>상태 검사 메서드 대신 사용할 수 있는 선택지도 있다. 올바르지 않은 상태일 때 빈 옵셔널 혹은 null 같은 특수한 값을 반환하는 방법이다. <code>상태 검사 메서드</code> , <code>옵셔널</code>, <code>특정 값</code> 중 하나를 선택하는 지침은 다음과 같다.</p>
<ol>
<li>외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부 요인으로 상태가 변할 수 있다면 옵셔널이나 특정 값을 사용한다. 상태 검사 메서드와 상태 의존적 메서드 호출 사이에 객체의 상태가 변할 수 있기 때문이다.</li>
<li>성능이 중요한 상황에서 상태 검사 메서드가 상태 의존적 메서드의 작업 일부를 중복 수행한다면 옵셔널이나 특정 값을 선택한다.</li>
<li>다른 모든 경우엔 상태 검사 메서드 방식이 조금 더 낫다고 할 수 있다. 가독성이 살짝 더 좋고, 잘못 사용했을 때 발견하기가 쉽다. 상태 검사 메서드 호출을 깜빡 잊었다면 상태 의존적 메서드가 예외를 던져 버그를 확실히 드러낼 것 이다. 반면 특정 값은 검사하지 않고 지나쳐도 발견하기가 어렵다.</li>
</ol>
<br/>


<blockquote>
<h4 id="📌-핵심-정리">📌 핵심 정리</h4>
<p>예외는 예외 상황에서 쓸 의도로 설계되었다. 정상적인 제어 흐름에서는 사용하면 안 되며, 이를 프로그래머에게 강요하는 API를 만들어서도 안 된다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 68. 일반적으로 통용되는 명명 규칙을 따르라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-68.-%EC%9D%BC%EB%B0%98%EC%A0%81%EC%9C%BC%EB%A1%9C-%ED%86%B5%EC%9A%A9%EB%90%98%EB%8A%94-%EB%AA%85%EB%AA%85-%EA%B7%9C%EC%B9%99%EC%9D%84-%EB%94%B0%EB%A5%B4%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-68.-%EC%9D%BC%EB%B0%98%EC%A0%81%EC%9C%BC%EB%A1%9C-%ED%86%B5%EC%9A%A9%EB%90%98%EB%8A%94-%EB%AA%85%EB%AA%85-%EA%B7%9C%EC%B9%99%EC%9D%84-%EB%94%B0%EB%A5%B4%EB%9D%BC</guid>
            <pubDate>Wed, 08 Mar 2023 05:31:43 GMT</pubDate>
            <description><![CDATA[<p>자바 플랫폼은 명명 규칙이 잘 정립되어 있으며, 그중 많은 것이 자바 언어 명세[JLS, 6.1]에 기술되어 있다. </p>
<p>자바의 명명 규칙은 크게 <code>철자 규칙</code> 과 <code>문법 규칙</code>, 두 범주로 나뉜다.</p>
<h3 id="철자-규칙">철자 규칙</h3>
<p>철자 규칙은 패키지, 클래스, 인터페이스, 메서드, 필드, 타입 변수의 이름을 다룬다.</p>
<p><strong>패키지와 모듈</strong></p>
<p>각 요소를 점(.)으로 구분하여 계층적으로 짓는다.
요소들은 모두 소문자 알파벳 혹은 드물게 숫자로 이루어진다.
조직 바깥에서도 사용될 패키지라면 조직의 인테넷 도메인 이름을 역순으로 사용한다. (ex. edu.cmu, com.google, org.eff) 
예외적으로 표준 라이브러리와 선택적 패키지들은 각각 java 와 javax 로 시작한다.</p>
<p>패키지 이름의 나머지는 해당 패키지를 설명하는 하나 이상의 요소로 이루어진다.
각 요소는 일반적으로 8글자 이하의 짧은 단어로 한다.
긴 이름보다는 의미가 통하는 약어를 추천한다. (ex. utilities &rarr; util)
여러 단어로 구성된 이름이라면 각 단어의 첫 글자만 따서 써도 좋다. (ex. awt)</p>
<p><strong>클래스(열거 타입과 애너테이션 포함)와 인터페이스</strong></p>
<p>하나 이상의 단어로 이루어지며, 각 단어는 대문자로 시작한다. 
여러 단어의 첫 글자만 딴 약자나 널리 통용되는 줄임말을 제외하고는 단어를 줄여 쓰지 않도록 한다.</p>
<p>약자의 경우 첫 글자만 대문자로 할지 전체를 대문자로 할지는 살짝 논란이 있다. 전체를 대문자로 쓰는 프로그래머도 있지만 첫 글자만 대문자로 하는 쪽이 훨씬 많다. HttpUrl처럼 여러 약자가 혼합된 경우라도 각 약자의 시작과 끝을 명확히 알 수 있기 때문이다. (HTTPURL은 혼합된 약자를 한 눈에 보기 어렵다)</p>
<p><strong>메서드와 필드</strong></p>
<p>첫 글자를 소문자로 쓴다는 점을 제외하고 클래스 명명 규칙과 같다. 
첫 단어가 약자라면 단어 전체가 소문자여야 한다. (ex. remove, ensureCapacity)</p>
<p><strong>상수 필드</strong></p>
<p>상수 필드의 경우 일반적인 필드와는 다르게, 모든 문자를 대문자로 쓰며 단어 사이는 밑줄로 구분한다. (ex. VALUES, NEGATIVE_INFINITY)</p>
<p>상수 필드는 값이 불변(기본 타입이나 불변 참조 타입)인 <code>static final</code> 필드를 말한다.</p>
<p><strong>지역변수</strong></p>
<p>다른 멤버와 비슷한 명명 규칙이 적용된다. 단, 약어를 써도 좋다.
약어를 써도 그 변수가 사용되는 문맥에서 의미를 쉽게 유추할 수 있기 때문이다.</p>
<p>입력 매개변수도 지역변수의 일종이다. 하지만 메서드 설명 문서에까지 등장하는 만큼 일반 지역변수보다는 신경써야 한다.</p>
<p><strong>타입 매개변수</strong></p>
<p>보통 한 글자로 표현한다. 대부분은 다섯 가지 중 하나다.</p>
<ul>
<li>임의의 타입 T</li>
<li>컬렉션 원소의 타입 E</li>
<li>맵의 키와 값 K,V</li>
<li>예외 X</li>
<li>메서드의 반환 타입 R</li>
<li>그 외에 임의 타입의 시퀀스 T,U,V 혹은 T1,T2,T3</li>
</ul>
<h3 id="문법-규칙">문법 규칙</h3>
<p>문법 규칙은 철자 규칙과 비교하면 더 유연하고 논란도 많다.</p>
<p><strong>패키지</strong></p>
<p>패키지에 대한 규칙은 따로 없다.</p>
<p><strong>클래스</strong></p>
<p>객체를 생성할 수 있는 클래스(열거 타입 포함)의 이름은 보통 단수 명사나 명사구를 사용한다. (ex. Thread, PriorityQueue)
객체를 생성할 수 없는 클래스의 이름은 보통 복수형 명사로 짓는다. (ex. Collectors, Collections)</p>
<p><strong>인터페이스</strong></p>
<p>클래스와 똑같이 짓거나, able 혹은 ible로 끝나는 형용사로 짓는다. (ex. Runnable, Iterable)</p>
<p><strong>애너테이션</strong></p>
<p>워낙 다양하게 활용되어 지배적인 규칙이 없이 명사, 동사, 전치사, 형용사가 두루 쓰인다.</p>
<p><strong>메서드</strong></p>
<p>동작을 수행하는 메서드의 이름은 동사나 동사구로 짓는다.
boolean 값을 반환하는 메서드는 보통 is나 드물게 has로 시작하고 명사나 명사구 혹은 형용사로 가능한 아무 단어나 구로 끝나도록 짓는다. (ex. isDigit, isProbablePrime, isEmpty, isEnabled)
반환 타입이 boolean이 아니거나 해당 인스턴스의 속성을 반환하는 메서드의 이름은 보통 명사, 명사구 혹은 get으로 시작하는 동사구로 짓는다. (ex. size, hashCode, getTime)</p>
<p>객체의 타입을 바꿔서 다른 타입의 또 다른 객체를 반환하는 인스턴스 메서드의 이름은 보통 to_Type_ 형태로 짓는다. (ex. toString, toArray)
객체의 내용을 다른 뷰로 보여주는 메서드의 이름은 as_Type_ 형태로 짓는다. (ex. asList)
객체의 값을 기본 타입값으로 반환하는 메서드의 이름은 보통 typeValue 형태로 짓는다. (ex. intValue)
정적 팩터리의 이름은 다양하지만 from, of, valueOf, instance, getInstance, newInstance, get_Type_, new_Type_을 흔히 사용한다. (<a href="https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C1.-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%84%B0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC#%EF%B8%8F-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%84%B0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EC%9D%98-%EB%AA%85%EB%AA%85-%EB%B0%A9%EC%8B%9D">아이템 1</a>)</p>
<p><strong>필드</strong></p>
<p>클래스, 인터페이스, 메서드 이름에 비해 덜 명확하고 덜 중요하다. API 설계를 잘 했다면 필드가 직접 노출될 일이 거의 없기 때문이다.
boolean 타입의 필드 이름은 보통 접근자 메서드에서 앞단어를 뺀 형태다. (ex. initinalized, composite)
다른 타입의 필드라면 명사나 명사구를 사용한다. </p>
<p><strong>지역변수</strong></p>
<p>필드와 비슷하게 지으면 되나, 조금 더 느슨하다.</p>
<br/>


<blockquote>
<h4 id="📌-핵심-정리">📌 핵심 정리</h4>
<p>표준 명명 규칙을 체화하여 자연스럽게 베어 나오도록 하자.
철자 규칙은 직관적이라 모호한 부분이 적은 데 반해, 문법 규칙은 더 복잡하고 느슨하다.
자바 언어 명세[JLS, 6.1]의 말을 인용하자면 &quot;오랫동안 따라온 규칙과 충돌한다면 그 규칙을 맹종해서는 안 된다.&quot; 상식이 이끄는 대로 따르자.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 67. 최적화는 신중히 하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-67.-%EC%B5%9C%EC%A0%81%ED%99%94%EB%8A%94-%EC%8B%A0%EC%A4%91%ED%9E%88-%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-67.-%EC%B5%9C%EC%A0%81%ED%99%94%EB%8A%94-%EC%8B%A0%EC%A4%91%ED%9E%88-%ED%95%98%EB%9D%BC</guid>
            <pubDate>Wed, 08 Mar 2023 02:35:19 GMT</pubDate>
            <description><![CDATA[<p>최적화 이야기를 하기에 앞서, 다음의 격언들을 보자.</p>
<p><em>(맹목적인 어리석음을 포장해) 그 어떤 핑계보다 효율성이라는 이름 아래 행해진 컴퓨팅 죄악이 더 많다(심지어 효율을 높이지도 못하면서)</em> 
- 윌리엄 울프[Wulf72]</p>
<p><em>(전체의 97% 정도인) 자그마한 효율성은 모두 잊자. 섣부른 최적화가 만악의 근원이다.</em> 
- 도널드 크누스[Knuth74]</p>
<p><em>최적화를 할 때는 다음 두 규칙을 따르라.
첫 번째, 하지 마라.
두 번째, (전문가 한정) 아직 하지 마라. 다시 말해, 완전히 명백하고 최적화되지 않은 해법을 찾을 때까지는 하지마라.</em> 
- M.A 잭슨[Jackson75]</p>
<p>이 격언들은 자바가 탄생하기 20년에 나온 것으로 최적화의 어두운 진실을 보여준다.</p>
<p>최적화는 좋은 결과보다는 해로운 결과로 이어지기 쉽다. 빠르지도 않고, 제대로 동작하지도 않으면서, 수정하기 어려운 소프트웨어를 탄생시키는 것이다.</p>
<h3 id="견고한-구조를-희생하지-말자">견고한 구조를 희생하지 말자</h3>
<p>성능 때문에 견고한 구조를 희생하지 말자. 빠른 프로그램보다는 좋은 프로그램을 작성하자.
좋은 프로그램이지만 원하는 성능이 나오지 않는다면 그 아키텍처 자체가 최적화할 수 있는 길을 안내해줄 것이다. 
좋은 프로그램은 정보 은닉 원칙을 따르므로 개별 구성요소의 내부를 독립적으로 설계할 수 있다. 따라서 시스템의 나머지에 영향을 주지 않고도 각 요소를 다시 설계할 수 있다.</p>
<h3 id="설계-단계에서-성능을-염두에-두자">설계 단계에서 성능을 염두에 두자</h3>
<p>그렇다고 프로그램을 완성할 때까지 성능 문제를 무시하라는 뜻이 아니다. 
아키텍처의 결함이 성능을 제한하는 상황이라면 시스템 전체를 다시 작성하지 않고는 해결하기 불가능할 수 있다. (구현상의 문제는 나중에 최적화해서 해결할 수 있지만)</p>
<p>따라서 설계 단계에서 성능을 반드시 염두에 두어야 한다. 설계 시 어떻게 성능을 고려할 수 있을까?</p>
<p>먼저 <strong>성능을 제한하는 설계는 피하자.</strong> 
완성 후 변경하기 가장 어려운 설계 요소는 컴포넌트끼리의 혹은 외부 시스템과의 소통 방식이다. API, 네트워크 프로토콜, 영구 저장용 데이터 포맷 등이 대표적이다.
이러한 설계 요소들은 완성 후에 변경하기 어렵거나 불가능할 수 있다. 또한 동시에 시스템 성능을 심각하게 제한할 수 있다.</p>
<p><strong>API를 설계할 때 성능을 주는 영향을 고려하자.</strong>
public 타입을 가변으로 만들면(내부 데이터를 변경할 수 있게 만들면), 불필요한 방어적 복사를 수없이 유발할 수 있다. 
또한, 컴포지션으로 해결할 수 있음에도 상속 방식으로 설계한 public 클래스는 상위 클래스에 영원히 종속되며 그 성능 제약까지도 물려받게 된다. 
인터페이스가 있는데 굳이 구현 타입을 사용하는 것도 좋지 않다. 특정 구현체에 종속되어, 나중에 더 빠른 구현체가 나오더라도 이용하지 못하게 된다.</p>
<p>신중하게 설계하여 깨끗하고 명확하고 멋진 구조를 갖춘 프로그램을 완성한 다음에야 최적화를 고려해볼 차례가 된다. </p>
<h3 id="성능-측정">성능 측정</h3>
<p><strong>각각의 최적화 시도 전후로 성능을 측정하자.</strong></p>
<p>측정 결과를 보면 시도한 최적화 기법이 성능을 눈에 띄게 높이지 못하거나 더 나빠지는 경우도 많다. 이는 프로그램에서 시간을 잡아먹는 부분을 추측하기 어렵기 때문이다. 느릴거라 짐작한 부분이 사실은 성능에 별다른 영향을 주지 않는 곳이라면 시간만 허비한 꼴이 된다. 일반저긍로 90%의 시간을 단 10%의 코드에서 사용한다는 사실을 기억해두자.</p>
<p>프로파일링 도구(profiling tool)는 최적화 노력을 어디에 집중해야 할지 찾는 데 도움이 된다. 개별 메서드의 소비 시간과 호출 횟수 같은 런타임 정보를 제공하여, 집중할 곳은 물론 알고리즘을 변경해야 한다는 사실을 알려주기도 한다.</p>
<p>JMH (Java Microbenchmark Harness)도 언급해둘 만한 도구다. 자바 코드의 상세한 성능을 알기 쉽게 보여주는 마이크로 벤치마칭 프레임워크다.</p>
<p>최적화 시도 전후의 성능 측정은 성능 모델이 덜 정교한 자바에서 중요성이 크다. 또한 자바의 성능 모델은 구현 시스템, 릴리스, 프로세서마다 차이가 있다. 따라서, 프로그램을 여러 가지 자바 플랫폼이나 여러 하드웨어 플랫폼에서 구동한다면 최적화의 효과를 그 각각에서 측정해야 한다.</p>
<br/>

<blockquote>
<h4 id="📌-핵심-정리">📌 핵심 정리</h4>
<p>빠른 프로그램을 작성하려 안달하지 말자. 좋은 프로그램을 작성하다 보면 성능은 따라오게 마련이다.
하지만 시스템을 설계할 때, 특히 API, 네트워크 프로토콜, 영구 저장용 데이터 포맷을 설계할 때는 성능을 염두에 두어야 한다. 
시스템 구현을 완료했다면 이제 성능을 측정해보라. 충분히 빠르면 그것으로 끝이다. 그렇지 않다면 프로파일러를 사용해 문제의 원인이 되는 지점을 찾아 최적화를 수행하라. 가장 먼저 어떤 알고리즘을 사용했는지 살펴보자. 알고리즘을 잘못 골랐다면 다른 저수준 최적화는 아무리 해봐야 소용이 없다. 만족할 때까지 이 과정을 반복하고, 모든 변경 후에는 성능을 측정하라.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 66. 네이티브 메서드는 신중히 사용하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-66.-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-%EB%A9%94%EC%84%9C%EB%93%9C%EB%8A%94-%EC%8B%A0%EC%A4%91%ED%9E%88-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-66.-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-%EB%A9%94%EC%84%9C%EB%93%9C%EB%8A%94-%EC%8B%A0%EC%A4%91%ED%9E%88-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</guid>
            <pubDate>Wed, 08 Mar 2023 01:54:27 GMT</pubDate>
            <description><![CDATA[<h3 id="네이티브-메서드">네이티브 메서드</h3>
<p>자바 네이티브 인터페이스(Java Native Interface, JNI)는 자바 프로그램이 <strong>네이티브 메서드</strong> 를 호출하는 기술을 말한다.</p>
<p><strong>네이티브 메서드</strong> 란 C나 C++ 같은 네이티브 프로그래밍 언어로 작성한 메서드를 말한다.</p>
<p>네이티브 메서드는 주로 다음과 같은 용도로 사용된다.</p>
<ol>
<li>플랫폼 특화 기능 사용</li>
<li>네이티브 코드로 작성된 기존 라이브러리 (레거시 라이브러리가 그 예다)</li>
<li>성능 개선을 목적으로 성능에 결정적인 영향을 주는 영역만 따로 네이티브 언어로 작성</li>
</ol>
<p>그러나 <strong>네이티브 메서드를 성능을 개선할 목적으로 사용하는 것은 권장하지 않는다.</strong> </p>
<h3 id="네이티브-메서드의-단점">네이티브 메서드의 단점</h3>
<ol>
<li>네이티브 언어가 안전하지 않으므로 네이티브 메서드를 사용하는 애플리케이션도 메모리 훼손 오류로부터 더 이상 안전하지 않다.</li>
<li>네이티브 언어는 자바보다 플랫폼을 많이 타기 때문에 이식성이 낮다.</li>
<li>디버깅이 더 어렵다.</li>
<li>주의하지 않으면 속도가 오히려 느려질 수도 있다.</li>
<li>가비지 컬렉터가 네이티브 메모리는 자동 회수하지 못한다. 심지어 추적조차 할 수 없다.</li>
<li>네이티브 메서드와 자바 코드 사이의 접착 코드(glue code)를 작성해야 한다. 이는 작업하기 귀찮을 뿐만 아니라 가독성도 떨어진다.</li>
</ol>
<br/> 

<blockquote>
<h4 id="📌-핵심-정리">📌 핵심 정리</h4>
<p>네이티브 메서드를 사용하려거든 더 생각해보자.
네이티브 메서드가 성능을 개선해주는 일은 많지 않다.
저수준 자원이나 네이티브 라이브러리를 사용해야만 해서 어쩔 수 없더라도 네이티브 코드는 최소한만 사용하고 철저히 테스트하자.
네이티브 코드 안에 숨은 단 하나의 버그가 애플리케이션 전체를 훼손할 수도 있다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring에서의 Transaction]]></title>
            <link>https://velog.io/@wisdom-one/Transaction-eys4eiwt</link>
            <guid>https://velog.io/@wisdom-one/Transaction-eys4eiwt</guid>
            <pubDate>Tue, 22 Nov 2022 00:20:04 GMT</pubDate>
            <description><![CDATA[<h3 id="트랜잭션-개념">트랜잭션 개념</h3>
<p><a href="https://github.com/jaejlf/CS_Study/blob/main/Database/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98(Transaction)/jihyehann.md">참고</a></p>
<p>트랜잭션이란 데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위를 말하며, ACID 성질을 가지고 있다.</p>
<ul>
<li>Atomicity (원자성): 트랜잭션의 연산은 데이터베이스에 모두 반영되든지 아니면 전혀 반영되지 않는다. </li>
<li>Consistency (일관성): 트랜잭션 작업 처리의 결과가 항상 일관되어야 한다. </li>
<li>Isolation (독립성,격리성): 둘 이상의 트랜잭션이 동시에 병행 실행되는 경우 어느 하나의 트랜잭션 실행 중에 다른 트랜잭션의 연산이 끼어들 수 없다.</li>
<li>Durablility (영속성,지속성): 성공적으로 완료된 트랜잭션의 결과는 (시스템이 고장나더라도) 영구적으로 반영되어야 한다.</li>
</ul>
<hr>
<h3 id="트랜잭션-격리-수준">트랜잭션 격리 수준</h3>
<p><a href="https://github.com/jaejlf/CS_Study/blob/main/Database/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%20%EA%B2%A9%EB%A6%AC%20%EC%88%98%EC%A4%80(Transaction%20Isolation%20Level)/jihyehann.md">참고</a></p>
<p>트랜잭션 격리 수준이란, 동시에 여러 트랜잭션이 처리될 때, 트랜잭션끼리 얼마나 서로 고립되어 있는지를 나타내는 것이다.</p>
<ul>
<li>Read Uncommitted: 커밋되지 않은 데이터를 다른 트랜잭션에서 읽을 수 있다.</li>
<li>Read Committed: 커밋된 데이터만 다른 트랜잭션에서 읽을 수 있다.</li>
<li>Repeatable Read: 선행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지 다른 트랜잭션이 수정/삭제할 수 없다.</li>
<li>Serializable: 선행 트랜잭션이 읽은 데이터를 다른 트랜잭션이 수정/삭제/삽입할 수 없다.</li>
</ul>
<p>아래로 내려갈수록 트랜잭션 간 고립 정도가 높아지며, 성능이 떨어진다.</p>
<hr>
<h3 id="트랜잭션을-병행으로-처리하려고-할-때-발생할-수-있는-문제">트랜잭션을 병행으로 처리하려고 할 때 발생할 수 있는 문제</h3>
<p><a href="https://popo015.tistory.com/40">참고</a></p>
<ul>
<li>갱신 내용 손실 (Lost Update) : 동시에 하나의 데이터가 갱신될 때 하나의 갱신이 누락되는 경우</li>
<li>현황 파악 오류 (Dirty Read) : 하나의 데이터 갱신이 끝나지 않은 시점에서 다른 트랜잭션이 해당 데이터를 조회하는 경우</li>
<li>모순성 (Inconsistency) : 두 트랜잭션이 동시에 실행될 때 데이터베이스가 일관성이 없는 모순된 상태로 남는 문제</li>
<li>연쇄 복귀 (Cascading Rollback) : 두 트랜잭션이 하나의 레코드를 갱신할 때 하나의 트랜잭션이 롤백하면 다른 하나의 트랜잭션 마저 롤백이 되는 문제</li>
</ul>
<p>&rarr; <a href="https://velog.io/@ha0kim/%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B4"><code>동시성 제어 기법</code></a> 을 통해 이러한 문제를 예방할 수 있다.</p>
<hr>
<h3 id="스프링에서-트랜잭션을-다루는-방법">스프링에서 트랜잭션을 다루는 방법</h3>
<p><img src="https://velog.velcdn.com/images/wisdom-one/post/3e5b9048-c717-463e-aef3-d62cee461d37/image.png" alt=""></p>
<ul>
<li>스프링에서는 트랜잭션 추상화 기술을 제공한다.</li>
<li>특정 기술에 종속되지 않는 일관된 방식으로 트랜잭션 적용 가능</li>
<li>PlatformTransactionManager 인터페이스가 트랙잭션 추상화의 핵심  </li>
<li>) 스프링 5.3부터는 JDBC 트랜잭션을 관리할 때 DataSourceTransactionManager 를 상속받아서 약간의 기능을 확장한 JdbcTransactionManager 를 제공한다. 둘의 기능 차이는 크지 않으므로 같은 것으로 이해하면 된다.</li>
</ul>
<h4 id="platformtransactionmanager">PlatformTransactionManager</h4>
<pre><code class="language-java">
  public interface PlatformTransactionManager extends TransactionManager {

    TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
            throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;

}</code></pre>
<ul>
<li>getTransaction: 지정된 propagation behavior에 따라 현재 활성 트랜잭션을 반환하거나 새 트랜잭션을 만든다.</li>
<li>definition: TransactionDefinition 인스턴스(기본값의 경우 null일 수 있음), 전파 동작, 격리 수준, 시간 초과 등을 설명</li>
<li>commit: 해당 상태와 관련하여 주어진 트랜잭션을 커밋한다. 트랜잭션이 프로그래밍 방식으로 롤백 전용으로 표시된 경우 롤백을 수행.</li>
<li>rollback: 지정된 트랜잭션의 롤백을 수행한다.</li>
</ul>
<h4 id="트랜잭션-매니저의-역할">트랜잭션 매니저의 역할</h4>
<ul>
<li>트랜잭션 추상화 : PlatformTransactionManager 인터페이스를 통해 트랜잭션을 추상화한다.</li>
<li>트랜잭션 동기화 :  트랜잭션을 시작하기 위한 Connection 객체를 특별한 저장소에 보관해두고 필요할 때 꺼내쓸 수 있도록 하는 기술. 트랜잭션 동기화 매니저(TransactionSynchronizationManager 추상 클래스)를 제공한다. 트랜잭션 동기화 매니저는 ThreadLocal을 사용해서 커넥션을 동기화해준다. ThreadLocal을 사용하기 때문에 멀티스레드 환경에서도 안전하게 커넥션을 동기화할 수 있다. </li>
</ul>
<p><img src="https://velog.velcdn.com/images/wisdom-one/post/1900087f-2721-445b-858f-3b980052e806/image.png" alt=""></p>
<h4 id="트랜잭션-매니저의-동작-과정">트랜잭션 매니저의 동작 과정</h4>
<ol>
<li>트랜잭션을 시작하려면 커넥션이 필요하다. 트랜잭션 매니저는 데이터소스를 통해 커넥션을 만들고 트랜잭션을 시작한다.</li>
<li>트랜잭션 매니저는 트랜잭션이 시작된 커넥션을 트랜잭션 동기화 매니저에 보관한다.</li>
<li>리포지토리는 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내서 사용한다. (따라서 파라미터로 커넥션을 전달하지 않아도 된다.)</li>
<li>작업이 완료되면 트랜잭션 매니저는 트랜잭션 동기화 매니저에 보관된 커넥션을 통해 트랜잭션을 종료하고, 커넥션도 닫는다.</li>
</ol>
<hr>
<h3 id="transactional-어노테이션"><code>@Transactional</code> 어노테이션</h3>
<p><a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html">공식문서</a></p>
<ul>
<li>선언적 트랜잭션 방식</li>
<li>일련의 작업들을 묶어서 하나의 단위로 처리할 때 사용 (해당 범위 내 메서드가 트랜잭션이 되도록 보장해준다.)</li>
<li>개별 메서드 혹은 클래스의 트랜잭션 속성 설정</li>
<li>PlatformTransactionManager가 관리하는 스레드 바인딩된 트랜잭션에서 작동</li>
<li>현재 실행 스레드 내의 모든 데이터 액세스 작업에 트랜잭션을 노출 </li>
<li>단, 메서드 내에서 새로 시작한 스레드에는 전파되지 않음!</li>
</ul>
<h4 id="transactional을-사용하지-않으면">@Transactional을 사용하지 않으면?</h4>
<p><img src="https://velog.velcdn.com/images/wisdom-one/post/9b0ac4f4-5190-4c43-91bb-4e3e7c0b2ce0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/wisdom-one/post/8cf4dca8-c284-437f-b725-de4eb70258ec/image.png" alt=""></p>
<ul>
<li>기본적으로 JDBC의 트랜잭션은 하나의 Connection Instance를 생성하고 통신하며 종료하는 흐름과 같이 동작하게 된다. 
즉 코드에 존재하는 DAO 로직들은 각각의 트랜잭션 안에서 연산을 진행하게 된다.</li>
</ul>
<br/>

<h4 id="transactional을-사용하면">@Transactional을 사용하면?</h4>
<p><img src="https://velog.velcdn.com/images/wisdom-one/post/a6e11c1f-b127-45cc-bba0-a351dd8112a2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/wisdom-one/post/0614e5e5-776b-4928-ab0b-3a75e0dac7b3/image.png" alt=""></p>
<p><a href="https://lob-dev.tistory.com/36">참고</a></p>
<blockquote>
<p>💡 JPA를 사용한다면 단일 작업에 대해서는 @Transactional을 직접 선언할 필요가 없다.<br>JPA의 구현체를 살펴보면 모든 메서드에 이미 @Transactional이 선언되어 있기 때문에 문제가 발생하면 Rollback 처리해주기 때문이다.<br>따라서, 여러 작업을 하나의 단위로 묶어 Commit or Rollback 처리가 필요할 때 직접 선언해주면 된다!   </p>
</blockquote>
<br/>

<h4 id="transactional-동작-과정">@Transactional 동작 과정</h4>
<p>@Transactional 어노테이션은 <strong>AOP</strong>로 구현되어 있다!</p>
<ul>
<li>클래스나 개별 메소드에 @Transactional이 선언되면 해당 클래스에 트랜잭션이 적용된 프록시 객체가 생성된다.</li>
<li>프록시 객체는 @Transactional이 포함된 메서드가 호출될 경우, 트랜잭션을 시작하고 Commit or Rollback을 수행한다.</li>
<li>CheckedException or 예외가 없을 때는 Commit을 수행하고, UncheckedException이 발생하면 Rollback을 수행한다.</li>
<li>스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용한다.
@Transactional 어노테이션이 포함된 메서드를 호출하여 트랜잭션이 시작될 때 영속성 컨텍스트가 생기고, 메서드가 종료되어 트랜잭션을 커밋할 경우, 영속성 컨텍스트가 flush되면서 해당 내용이 반영되고, 이후에 영속성 컨텍스트 역시 종료된다. <a href="https://kafcamus.tistory.com/30">[참고]</a></li>
</ul>
<p><img src="https://velog.velcdn.com/images/wisdom-one/post/810ba87c-51d3-49dc-8e5d-549d835ce169/image.png" alt=""></p>
<p>이러한 방식으로 영속성 컨텍스트를 관리해 주기 때문에, @Transactional을 쓸 경우 트랜잭션의 원칙을 정확히 지킬 수 있다.</p>
<p>또한, 아래의 원칙 역시 유의해야 한다.</p>
<ul>
<li>만약 같은 트랜잭션 내에서 여러 EntityManager를 쓰더라도, 이는 같은 영속성 컨텍스트를 사용한다.</li>
<li>같은 EntityManager를 쓰더라도, 트랜잭션이 다르면 다른 영속성 컨텍스트를 사용한다.</li>
</ul>
<br/>

<h4 id="test-환경에서의-동작">Test 환경에서의 동작</h4>
<p>테스트 메서드가 트랜잭션으로 감싸지며, 메서드가 종료될 때 자동으로 롤백된다.</p>
<br/>

<h4 id="transactional-옵션">@Transactional 옵션</h4>
<pre><code class="language-java">@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor(&quot;transactionManager&quot;)
    String value() default &quot;&quot;;

    @AliasFor(&quot;value&quot;)
    String transactionManager() default &quot;&quot;;

    String[] label() default {};

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

    String timeoutString() default &quot;&quot;;

    boolean readOnly() default false;

    Class&lt;? extends Throwable&gt;[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class&lt;? extends Throwable&gt;[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}
</code></pre>
<p><strong>readOnly</strong> (기본값 false)</p>
<ul>
<li>해당 트랜잭션이 readOnly 인지 나타내는 flag</li>
<li>readOnly = true로 설정하게 되면, 스프링은 해당 트랜잭션의 FlushMode를 NEVER로 설정한다. 이때 flush가 일어나지 않으므로 비용이 절감되며, 생성/수정/삭제가 일어나지 않으므로 별도의 스냅샷을 만들 필요가 없어 성능상 이점이 생긴다.</li>
<li>데이터를 변경하는 Operation이 없으면 readOnly를 true로 주는 것이 성능상 좋다.</li>
</ul>
<p><strong>propagation</strong> (기본값 Propagation.REQUIRED)</p>
<ul>
<li>트랜잭션의 경계에서 이미 진행중인 트랜잭션이 있거나 없을 때 어떻게 동작할 것인가를 결정</li>
<li>REQUIRED: 현재 트랜잭션이 있는지 확인하고, 있으면 기존 트랜잭션을 사용하고 없으면 새 트랜잭션을 생성한다.</li>
<li>SUPPORTS: 현재 트랜잭션이 있는지 확인하고, 있으면 기존 트랜잭션을 사용하고, 없으면 트랜잭션 없이 실행한다.</li>
<li>MANDATORY: 현재 트랜잭션을 사용하고, 없으면 예외가 발생한다.</li>
<li>REQUIRES_NEW: 새 트랜잭션을 생성한다. 현재 트랜잭션이 존재하는 경우 일시 중단한다.</li>
<li>NOT_SUPPORTED: 트랜잭션 없이 실행한다. 현재 트랜잭션이 존재하는 경우 일시 중단한다.</li>
<li>NEVER: 트랜잭션 없이 실행하고, 현재 트랜잭션이 존재하는 경우 예외가 발생한다.</li>
<li>NESTED: 현재 트랜잭션이 있으면 중첩(자식) 트랜잭션을 시작한다. 현재 트랜잭션이 없다면 REQRUIED처럼 동작한다. NESTED에 의한 중첩 트랜잭션은 먼저 시작된 부모 트랜잭션의 커밋과 롤백에는 영향을 받지만, 자신의 커밋과 롤백은 부모 트랜잭션에게 영향을 주지 않는다.</li>
</ul>
<p><strong>isolation</strong> (기본값 Isolation.DEFAULT)</p>
<ul>
<li>Transaction의 Isolation level. 별도로 정의하지 않으면 DB의 Isolation Level을 따른다.</li>
<li>DEFAULT: RDBMS가 지정한 값으로 설정된다. </li>
<li>READ_UNCOMMITTED</li>
<li>READ_COMMITTED: Postgres와 SQL Server 및 Oracle에서 사용하는 격리 수준.</li>
<li>REPEATABLE_READ: Mysql에서 사용하는 격리 수준.</li>
<li>SERIALIZABLE: 동시 호출을 순차적으로 실행한다(성능이 가장 느리다)</li>
</ul>
<p><strong>timeout</strong> (기본값 -1)</p>
<ul>
<li>지정한 시간 내에 해당 메소드 수행이 완료되지 않을 경우 rollback 수행한다.</li>
<li><code>-1</code> : no time out</li>
</ul>
<p><strong>rollbackFor</strong></p>
<ul>
<li>정의된 Exception에 대해서 rollback을 수행한다.</li>
</ul>
<p><strong>rollbackForClassName</strong></p>
<ul>
<li>정의된 이름의 Exception에 대해서 rollback을 수행한다.</li>
</ul>
<p><strong>noRollBackFor</strong></p>
<ul>
<li>정의된 Exception에 대해서는 rollback을 수행하지 않는다.</li>
</ul>
<p><strong>transactionManager</strong>, <strong>value</strong></p>
<ul>
<li>사용할 트랜잭션 관리자를 지정한다.</li>
</ul>
<br/>


]]></description>
        </item>
        <item>
            <title><![CDATA[[JPA] N+1 문제]]></title>
            <link>https://velog.io/@wisdom-one/N1-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@wisdom-one/N1-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Mon, 21 Nov 2022 08:22:13 GMT</pubDate>
            <description><![CDATA[<h3 id="n1-문제란">N+1 문제란?</h3>
<p>N+1 문제란 1개의 쿼리로 처리되길 기대했지만 N개의 추가 쿼리가 발생하는 현상으로, 처음 실행한 SQL의 결과 수만큼 추가로 SQL이 실행되는 것을 말한다. 이것은 연관관계가 설정되어 있는 엔티티를 JPQL을 통해 조회하는 경우에 발생하는 문제다. 
JPQL을 실행하면 JPA가 JPQL을 분석하여 SQL을 생성해주는데, 이때 연관관계와 즉시 로딩, 지연 로딩에 대해서는 전혀 신경쓰지 않고 엔티티 기준으로만 SQL 쿼리를 생성하게 된다.</p>
<p>즉시 로딩, 지연 로딩 모두에서 N+1 문제가 발생할 수 있다.</p>
<p><strong>즉시 로딩에서의 N+1 문제</strong></p>
<p>Member 엔티티, Order 엔티티가 1:N 양방향 연관관계 설정이 되어있다고 하자.</p>
<pre><code class="language-java">@Entity
@Table(name = &quot;orders&quot;)
public class Order {
    @ManyToOne
    private Member member;
    ...
}

@Entity
public class Member {
    @OneToMany(mappedBy = &quot;member&quot;, fetch = FetchType.EAGER)
    private List&lt;Order&gt; orders = new ArrayList&lt;Order&gt;();
    ...
}</code></pre>
<p>연관관계가 즉시 로딩(fetch = FetchType.EAGER)으로 설정되어 있는 경우, 우선 해당 엔티티를 기준으로 쿼리를 실행한 후에 바로 이어서 연관된 엔티티를 조회하는 쿼리가 추가로 실행된다. </p>
<p>동작 과정</p>
<pre><code class="language-java">memberRepository.findAll()</code></pre>
<ol>
<li>findAll()을 한 순간 select m from Member m 이라는 JPQL 구문이 생성되고 해당 구문을 분석한 <code>select * from member</code> 이라는 SQL이 생성되어 실행된다. </li>
<li>DB의 결과를 받아 member 엔티티의 인스턴스들을 생성한다.</li>
<li>member와 연관되어 있는 order 도 로딩을 해야 한다.</li>
<li>영속성 컨텍스트에서 연관된 order가 있는지 확인한다.</li>
<li>영속성 컨텍스트에 없다면 2에서 만들어진 member 인스턴스들 개수에 맞게 <code>select * from order where member_id = ?</code> 이라는 SQL 구문이 생성된다. <strong>(N+1 발생)</strong></li>
</ol>
<p><strong>지연 로딩에서의 N+1 문제</strong></p>
<pre><code class="language-java">@Entity
@Table(name = &quot;orders&quot;)
public class Order {
    @ManyToOne
    private Member member;
    ...
}

@Entity
public class Member {
    @OneToMany(mappedBy = &quot;member&quot;, fetch = FetchType.LAZY)
    private List&lt;Order&gt; orders = new ArrayList&lt;Order&gt;();
    ...
}</code></pre>
<p>지연로딩의 경우 엔티티 조회 당시에는 N+1 문제가 바로 발생하지는 않지만, 이후에 비즈니스 로직에서 연관된 엔티티를 실제로 사용할 때 추가로 쿼리를 실행하게 된다.</p>
<p>동작 과정</p>
<ol>
<li>findAll()을 한 순간 select m from Member m 이라는 JPQL 구문이 생성되고 해당 구문을 분석한 <code>select * from member</code> 이라는 SQL이 생성되어 실행된다.</li>
<li>DB의 결과를 받아 member 엔티티의 인스턴스들을 생성한다.</li>
<li>코드 중에서 member 의 order 객체를 사용하려고 하는 시점에 영속성 컨텍스트에서 연관된 order가 있는지 확인한다.</li>
<li>영속성 컨텍스트에 없다면 order 객체를 사용하려는 member 인스턴스들 개수에 맞게 <code>select * from order where member_id = ?</code> 이라는 SQL 구문이 생성된다. <strong>(N+1 발생)</strong></li>
</ol>
<hr>
<h3 id="해결-방법">해결 방법</h3>
<p><strong>1. fetch join</strong></p>
<p>페치 조인은 <code>SQL 조인</code> 을 사용해서 연관된 엔티티를 함께 조회하여 N+1 문제가 발생하지 않는다.</p>
<p>JPQL</p>
<pre><code class="language-sql">select m from Member m join fetch m.orders</code></pre>
<p>실행되는 SQL</p>
<pre><code class="language-sql">select m.*, o.* from member m
    inner join orders o on m.id=o.member_id</code></pre>
<p>한계</p>
<ol>
<li>패치 조인 대상에는 별칭을 줄 수 없다.</li>
<li>둘 이상의 컬랙션을 패치할 수 없다.</li>
<li>컬랙션을 패치 조인하면 페이징 API를 사용할 수 없다.</li>
</ol>
<blockquote>
<p>💡 <strong>컬렉션 패치 조인과 페이징</strong></p>
<p>컬렉션을 fetch join하게 되면 <strong>페이징이 불가능</strong>하다. fetch join을 통해 가져온 데이터는 컬렉션의 개수에 따라 row가 증가하기 때문이다. 
페이징을 사용하고 싶은 경우는, row 수에 영향을 미치지 않도록 ToOne 관계만 fetch join 으로 가져오고 컬렉션은 지연 로딩으로 조회하자. 이때 지연 로딩의 성능 최적화를 위해 아래에 나오는 @BatchSize를 사용하자.</p>
</blockquote>
<p><strong>2. 하이버네이트 @BatchSize</strong></p>
<p>org.hibernate.annotations.BatchSize 어노테이션은 연관된 엔티티를 조회할 때 지정한 size만큼 <code>SQL IN 절</code> 을 사용해서 조회한다. 예를 들어, 조회한 회원이 10명이고 size=5라면 2번의 SQL만 추가로 실행된다.</p>
<pre><code class="language-java">@Entity
public class Member {
    @BatchSize(size=5)
    @OneToMany(mappedBy = &quot;member&quot;, fetch = FetchType.EAGER)
    private List&lt;Order&gt; orders = new ArrayList&lt;Order&gt;();

    ...
}</code></pre>
<p>실행되는 SQL</p>
<pre><code class="language-sql">select * from member;

select * from orders where member_id in (?,?,?,?,?);

select * from orders where member_id in (?,?,?,?,?);</code></pre>
<blockquote>
<p>💡 <strong>hibernate.default_batch_fetch_size</strong>
hibernate.default_batch_fetch_size 속성을 사용하면 애플리케이션 전체에 기본으로 <code>@BatchSize</code> 를 적용할 수 있다.
default_batch_fetch_size 의 크기는 적당한 사이즈를 골라야 하는데, <code>100~1000</code> 사이를 선택하는 것을 권장한다. 
대부분의 DB에서 IN절의 최대 개수가 1000개로 제한되어 있기 때문에, 기본적으로 Batch Size의 값을 1000 이하로 설정해야 한다.
application.yml </p>
</blockquote>
<pre><code class="language-yaml">spring:
  jpa:
    properties:
      hibernate:
        default_batch_fetch_size: 1000 </code></pre>
<p><strong>3. 하이버네이트 @Fetch(FetchMode.SUBSELECT)</strong></p>
<p>org.hibernate.annotations.Fetch 어노테이션에 FetchMode를 SUBSELECT로 사용하면 연관된 데이터를 조회할 때 <code>서브 쿼리</code> 를 사용해서 N+1 문제를 해결한다.</p>
<pre><code class="language-java">@Entity
public class Member {
    @Fetch(FetchMode.SUBSELECT)
    @OneToMany(mappedBy = &quot;member&quot;, fetch = FetchType.EAGER)
    private List&lt;Order&gt; orders = new ArrayList&lt;Order&gt;();

    ...
}</code></pre>
<p>즉시 로딩으로 설정하면 조회 시점에, 지연 로딩으로 설정하면 지연 로딩된 엔티티를 사용하는 시점에 추가로 서브 쿼리를 사용하는 SQL이 실행된다.</p>
<p>실행되는 SQL</p>
<pre><code class="language-sql">select * from member;

select * 
from orders o
where o.member_id in (select m.id from member m);</code></pre>
<p><strong>4. @EntityGraph</strong></p>
<p>엔티티 그래프 기능은 <code>SQL 조인</code> 을 통해 엔티티를 조회하는 시점에 연관된 엔티티들을 함께 조회한다. fetch join과 유사하지만 <code>left outer join</code> 만 지원한다는 차이가 있다.</p>
<pre><code class="language-java">public interface MemberRepository extends JpaRepository&lt;Member, Long&gt; {

    @Override
    @EntityGraph(attributePaths = {&quot;orders&quot;}, 
                type = EntityGraphType.LOAD)
    List&lt;Member&gt; findAll();
}</code></pre>
<ul>
<li><p><code>attributePaths</code> : 함께 조회할 엔티티를 추가한다. 복수 가능.</p>
</li>
<li><p><code>type</code> </p>
<ul>
<li><code>EntityGraphType.FETCH</code> (default) : attribute는 EAGER로 패치하고, 나머지 attribute는 LAZY로 패치한다.</li>
<li><code>EntityGraphType.LOAD</code> : 명시한 attribute는 EAGER로 패치하고, 나머지 attribute는 entity에 명시한 fetch type이나 디폴트 FetchType으로 패치한다. (e.g. @OneToMany는 LAZY, @ManyToOne은 EAGER가 디폴트)</li>
</ul>
</li>
</ul>
<p>실행되는 SQL</p>
<pre><code class="language-sql">select m.*, o.* from member m
    left outer join orders o on m.id=o.member_id</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 65. 리플렉션보다는 인터페이스를 사용하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-65.-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EB%B3%B4%EB%8B%A4%EB%8A%94-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-65.-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EB%B3%B4%EB%8B%A4%EB%8A%94-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</guid>
            <pubDate>Fri, 18 Nov 2022 16:52:14 GMT</pubDate>
            <description><![CDATA[<h3 id="리플렉션-reflection-이란">리플렉션 (reflection) 이란?</h3>
<p>리플렉션 기능(java.lang.reflect)을 이용하면 프로그램에서 임의의 클래스에 접근할 수 있다.</p>
<p>Class 객체가 주어지면 그 클래스의 생성자, 메서드, 필드에 해당하는 Constructor, Method, Field 인스턴스를 가져올 수 있다. 또한 이 인스턴스들로는 그 클래스의 멤버 이름, 필드 타입, 메서드 시그니처 등을 가져올 수 있다.</p>
<p>나아가, Constructor, Method, Field 인스턴스를 이용해 각각에 연결된 실제 생성자, 메서드, 필드를 조작할 수도 있다. 
ex) Method.invoke()는 어떤 클래스의 어떤 객체가 가진 어떤 메서드라도 호출할 수 있게 해준다.</p>
<h3 id="단점">단점</h3>
<ul>
<li>컴파일타임 타입 검사가 주는 이점을 하나도 누릴 수 없다. 예외 검사도 마찬가지다. 프로그램이 리플렉션 기능을 써서 존재하지 않는 혹은 접근할 수 없는 메서드를 호출하려 시도하면 런타임 오류가 발생한다.</li>
<li>리플렉션을 이용하면 코드가 지저분하고 장황해진다.</li>
<li>성능이 떨어진다. 리플렉션을 통한 메서드 호출은 일반 메서드 호출보다 훨씬 느리다.</li>
</ul>
<h3 id="사용-예시">사용 예시</h3>
<p>리플렉션은 아주 제한된 형태로만 사용해야 위의 단점을 피하고 이점만 취할 수 있다. 
<strong>리플렉션은 인스턴스 생성에만 쓰고, 이렇게 만든 인스턴스는 인터페이스나 상위 클래스(를 이용할 수 있는 경우)로 참조해 사용하자.</strong></p>
<p>다음은 예제는 리플렉션으로 인스턴스를 생성하는 예제다.</p>
<pre><code class="language-java">public class ReflectiveInstantiation {
    // 리플렉션으로 생성하고 인터페이스로 참조해 활용한다.

    // 명령줄 인수 예시1: java.util.HashSet apple banana
    // 명령줄 인수 예시2: java.util.TreeSet apple banana
    public static void main(String[] args) {
        // 클래스 이름을 Class 객체로 변환
        Class&lt;? extends Set&lt;String&gt;&gt; cl = null;
        try {
            cl = (Class&lt;? extends Set&lt;String&gt;&gt;)  // 비검사 형변환!
                    Class.forName(args[0]);
        } catch (ClassNotFoundException e) {
            fatalError(&quot;클래스를 찾을 수 없습니다.&quot;);
        }

        // 생성자를 얻는다.
        Constructor&lt;? extends Set&lt;String&gt;&gt; cons = null;
        try {
            cons = cl.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            fatalError(&quot;매개변수 없는 생성자를 찾을 수 없습니다.&quot;);
        }

        // 집합의 인스턴스를 만든다.
        Set&lt;String&gt; s = null;
        try {
            s = cons.newInstance();
        } catch (IllegalAccessException e) {
            fatalError(&quot;생성자에 접근할 수 없습니다.&quot;);
        } catch (InstantiationException e) {
            fatalError(&quot;클래스를 인스턴스화할 수 없습니다.&quot;);
        } catch (InvocationTargetException e) {
            fatalError(&quot;생성자가 예외를 던졌습니다: &quot; + e.getCause());
        } catch (ClassCastException e) {
            fatalError(&quot;Set을 구현하지 않은 클래스입니다.&quot;);
        }

        // 생성한 집합을 사용한다.
        s.addAll(Arrays.asList(args).subList(1, args.length));
        System.out.println(s);
    }

    private static void fatalError(String msg) {
        System.err.println(msg);
        System.exit(1);
    }
}</code></pre>
<ul>
<li>Set&lt;String&gt; 인터페이스의 인스턴스를 생성하고, 정확한 클래스는 명령줄의 첫 번째 인수로 확정한다.</li>
<li>두 번째 이후의 인수들은 생성한 집합에 추가하고 모든 원소를 출력한다.</li>
</ul>
<br/>


<blockquote>
<h4 id="📌-핵심-정리">📌 핵심 정리</h4>
<p>리플렉션은 복잡한 특수 시스템을 개발할 때 필요한 강력한 기능이지만, 단점도 많다.
컴파일타임에는 알 수 없는 클래스를 사용하는 프로그램을 작성한다면 리플렉션을 사용해야 할 것이다.
단, 되로록 객체 생성에만 사용하고, 생성한 객체를 이용할 때는 적절한 인터페이스나 컴파일타임에 알 수 있는 상위 클래스로 형변환해 사용해야 한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin에서의 BDD (Behavior Driven Development)]]></title>
            <link>https://velog.io/@wisdom-one/BDD-Behavior-Driven-Development</link>
            <guid>https://velog.io/@wisdom-one/BDD-Behavior-Driven-Development</guid>
            <pubDate>Tue, 15 Nov 2022 13:02:51 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="들어가며">들어가며..</h4>
<p>최근 프로젝트 팀원이 추천해준 영상을 보고 <code>BDD</code> 라는 것을 처음 알게 되었다. 해당 영상에서는 kotest와 mockk 테스팅 툴을 사용하여 TDD와 BDD의 차이점을 잘 설명해주고 있다.
<em>최근 Kotlin 언어를 공부하고, Kotlin + Spring 조합으로 개발하는 것에 관심을 가지고 있다보니 꽤 흥미로웠다.</em></p>
<p>영상 : <a href="https://tv.kakao.com/channel/3693125/cliplink/414004682">https://tv.kakao.com/channel/3693125/cliplink/414004682</a>
예제코드 : <a href="https://github.com/harry-jk/ifkakao-2020-code">https://github.com/harry-jk/ifkakao-2020-code</a></p>
</blockquote>
<h2 id="bdd-behavior-driven-development-란">BDD (Behavior Driven Development) 란</h2>
<ul>
<li>TDD에서 파생된 개발 방법론</li>
<li>개발자와 비개발자간의 협업 과정을 녹여낸 방법</li>
<li><strong>사용자의 행위를 작성</strong>하고 결과를 검증한다.</li>
<li>BDD로 테스트 코드를 작성하면, 설계 역시 행위 중심이 되는 도메인 기반 설계가 된다.</li>
</ul>
<p>TDD 에서 T가 Test 였다면, BDD 에서 B는 Behavior, 즉 행위이다.</p>
<p>일반적으로 테스트 코드를 작성할 때, given-when-then 형식으로 많이 작성하는데 BDD에서는 각각을 주어진 환경, 행위, 기대결과로 서술한다.</p>
<ul>
<li>given : 주어진 환경 (ex. 본인 인증된 사용자가 로그인된 상황에서)</li>
<li>when : 행위 (ex. 검수 정보를 입려갛고 검수 등록 버튼을 누르면)</li>
<li>then : 기대결과 (ex. 등록 결과가 포함된 검수 진행 화면으로 이동한다)</li>
</ul>
<p>BDD는 행위에 초점을 맞춰 테스트를 작성하기 때문에 그 자체로 기획서와 동기화가 되며, 자연스럽게 서비스에 대한 이해도 높아지게 된다는 장점이 있다.</p>
<hr>
<h2 id="tdd와-bdd의-차이">TDD와 BDD의 차이</h2>
<p><img src="https://velog.velcdn.com/images/wisdom-one/post/b3085da5-9a38-4397-9b61-bff9cf06d92d/image.png" alt=""></p>
<p>BDD와 TDD는 상호보완적인 관계이다.</p>
<p>BDD는 TDD에서 확인하기 어려운 유저 시나리오의 흐름을 알 수 있고, TDD는 각 모듈의 기능을 검증할 수 있다. </p>
<p>BDD의 테스트케이스로 시나리오 검증을 하고, 해당 시나리오에서 사용되는 각 모듈들은 TDD 테스트케이스로 검증을 해야 기대하는 테스트 커버리지를 가질 수 있다.</p>
<table>
<thead>
<tr>
<th>&nbsp;</th>
<th>TDD (Test Driven Development)</th>
<th>BDD (Behavior Driven Development)</th>
</tr>
</thead>
<tbody><tr>
<td>테스트 코드의 목적</td>
<td>기능 동작의 검증</td>
<td>서비스 유저 시나리오 동작의 검증</td>
</tr>
<tr>
<td>테스트 코드의 설계중심</td>
<td>제공할 모듈의 기능 중심</td>
<td>서비스 사용자 행위 중심</td>
</tr>
<tr>
<td>테스트 코드 설계 재료</td>
<td>모듈 사양 문서 (개발자가 작성)</td>
<td>서비스 기획서 (서비스 기획자가 작성)</td>
</tr>
<tr>
<td>적합한 프로젝트</td>
<td>모듈/라이브러리 프로젝트</td>
<td>서비스 프로젝트</td>
</tr>
<tr>
<td>장점</td>
<td>설계 단계에서 예외 케이스들을 확인할 수 있다.</td>
<td>설계 단계에서 누락된 기획을 확인할 수 있다.</td>
</tr>
</tbody></table>
<p>BDD의 장점을 유심히 보자!</p>
<p>BDD의 경우, 기획 시나리오의 빈틈(누락)을 테스트케이스 작성 시에 확인할 수 있다는 장점이 있다. 이는 이후에 기획의 변경으로 인한 대대적인 코드 수정을 예방할 수 있도록 해준다.</p>
<hr>
<h2 id="kotest">kotest</h2>
<p>Kotlin을 위한 테스팅 툴로써, BDD 스타일과 TDD 스타일을 모두 지원한다.</p>
<p>BDD : BehaviorSpec, FeatureSpec
TDD : AnnotationSpec, ExpectSpec</p>
<h3 id="behaviorspec">BehaviorSpec</h3>
<p>Given/When/Then 구조를 지원하는 BDD용 스타일</p>
<p>예시</p>
<p><code>And</code> 블록</p>
<ul>
<li>해당 컨텍스트 안에서 하위 분기를 할 수 있다.</li>
<li>즉, 중복된 서술을 하지 않아도 상위에 서술된 동일한 내용을 테스트 결과에 출력할 수 있고, 같은 상위 코드 블록을 사용함으로서 변수도 공유할 수 있다.</li>
</ul>
<pre><code class="language-kotlin">class RegisterEmoticonFeature : BehaviorSpec() {
    ...
    init {
        Given(&quot;본인 인증된 사용자가 로그인된 상황에서&quot;) {
            ...
            When(&quot;검수 정보를 입력란에&quot;) {
                ...
                And(&quot;검수 정보를 입력하고 검수 등록 버튼을 누르면&quot;) {
                    ...
                    Then(&quot;등록 결과가 포함된 검수 진행 목록 화면으로 이동한다&quot;) {
                        ...
                    }
                }

                And(&quot;검수 정보를 입력하지 않고 검수 등록 버튼을 누르면&quot;) {
                    ...
                    Then(&quot;검수 등록 실패 사유가 화면에 표시되어야 한다&quot;) {
                        ....
                    }
                }
            }
        }
    }
}</code></pre>
<h3 id="featurespec">FeatureSpec</h3>
<p>Feature/Scenario 구조를 지원하는 BDD용 스타일<br>시나리오의 행위자를 특정하기 어렵고, 기능에 대해서만 쓰여져있는 기획에서 유용하게 쓸 수 있다.</p>
<pre><code class="language-kotlin">class EmoticonFeature : FeatureSpec() {
    init {
        ...
        feature(&quot;이모티콘 검수 이메일 발송&quot;) {
            ...
            scenario(&quot;&quot;&quot;
                2020-08-05 11:00:00(KST)에 전날 생성된 이모티콘 목록이
                검수자에게 이메일 발송되어야 한다.
            &quot;&quot;&quot;.trimIndent()) {
                ...
            }
        }
    }
}</code></pre>
<h3 id="annotationspec">AnnotationSpec</h3>
<p>JUnit 형태의 Testcase 작성을 하게 해주는 TDD용 스타일  </p>
<pre><code class="language-kotlin">class AccountServiceSpec : AnnotationSpec() {

    @BeforeAll
    fun setupStub() {
        ...
    }

    @AfterAll
    fun clearStub() {
        ...
    }

    @Test
    fun taskAccountIfExistByToken() {
        ...
    }

    @Test
    fun takeNullIfNotExistByToken() {
        ...
    }
}</code></pre>
<h3 id="expectspec">ExpectSpec</h3>
<p>DSL로 Testcase 작성을 하게 해주는 TDD용 스타일  </p>
<pre><code class="language-kotlin">class EmotionServiceSpec : ExpectSpec() {
    ...
    init {
        context(&quot;이모티콘 생성을 할 때&quot;) {
            ...
            expect(&quot;계정과 이모티콘 정보, 이미지가 있으면 이모티콘이 생성된다.&quot;) {
                ...
            }
        }
    }
}</code></pre>
<h3 id="-mockk">+) mockk</h3>
<p>kotest와 함께 사용하기 좋은 라이브러리이다. </p>
<p>kotest를 사용하고 있다면 mockk도 함께 사용해보자!</p>
<p>mockk은 다음과 같은 기능을 제공한다</p>
<ul>
<li>mocking : mock을 생성하여 어떠한 행위를 할 것인지 사전 정의</li>
<li>verify : 해당하는 행위가 원하는대로 시행되었는지 검증</li>
</ul>
<hr>
<h2 id="정리">정리</h2>
<p>정리하자면 BDD는 다음과 같은 큰 장점들을 가지고 있다.</p>
<ul>
<li>기획서를 통한 TestCase 작성으로 작성에 대한 비용 감소</li>
<li>TDD보다도 더 넓은 테스트 커버리지 확보 가능</li>
<li>설계 변경에 따른 리스크 최소화</li>
<li>서비스의 이해도 증가</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템 64. 객체는 인터페이스를 사용해 참조하라]]></title>
            <link>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-64.-%EA%B0%9D%EC%B2%B4%EB%8A%94-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EC%B0%B8%EC%A1%B0%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@wisdom-one/%EC%95%84%EC%9D%B4%ED%85%9C-64.-%EA%B0%9D%EC%B2%B4%EB%8A%94-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EC%B0%B8%EC%A1%B0%ED%95%98%EB%9D%BC</guid>
            <pubDate>Wed, 09 Nov 2022 13:49:07 GMT</pubDate>
            <description><![CDATA[<p><strong>적합한 인터페이스만 있다면 매개변수뿐 아니라 반환값, 변수, 필드를 전부 인터페이스 타입으로 선언하라.</strong></p>
<p>객체의 실제 클래스를 사용해야 할 상황은 &#39;오직&#39; 생성자로 생성할 때뿐이다.</p>
<br/>

<h3 id="인터페이스-사용-장점">인터페이스 사용 장점</h3>
<p>인터페이스를 타입으로 사용하는 습관을 길러두면 프로그램이 훨씬 유연해진다. 
나중에 구현 크래스를 교체하고자 한다면, 그저 새 클래스의 생성자(혹은 다른 정적 팩터리)를 호출해주기만 하면 된다.</p>
<br/>

<h3 id="예시">예시</h3>
<pre><code class="language-java">// 좋은 예
Set&lt;Son&gt; sonSet = new LinkedHashSet&lt;&gt;();

// 나쁜 예
LinkedHashSet&lt;Son&gt; sonSet = new LinkedHashSet&lt;&gt;();</code></pre>
<br/>

<h3 id="인터페이스-대신-클래스-타입을-사용해도-되는-경우">인터페이스 대신 클래스 타입을 사용해도 되는 경우</h3>
<p>적합한 인터페이스가 없다면 당연히 클래스로 참조해야 한다.</p>
<h4 id="1-string과-biginteger-같은-값-클래스">1. String과 BigInteger 같은 값 클래스</h4>
<p>값 클래스를 여러 가지로 구현될 수 있다고 생각하고 설계하는 일은 거의 없다.
값 클래스는 매개변수, 변수, 필드, 반환 타입으로 사용해도 무방하다.</p>
<h4 id="2-클래스-기반으로-작성된-프레임워크가-제공하는-객체들">2. 클래스 기반으로 작성된 프레임워크가 제공하는 객체들</h4>
<p>이런 경우라도 특정 구현 클래스보다는 (보통은 추상 클래스인) 기반 클래스를 사용해 참조하는게 좋다. OutStream 등 java.io 패키지의 여러 클래스가 이 부류에 속한다.</p>
<h4 id="3-인터페이스에는-없는-특별한-메서드를-제공하는-클래스">3. 인터페이스에는 없는 특별한 메서드를 제공하는 클래스</h4>
<p>예시) PriorityQueue 클래스는 Queue 인터페이스에는 없는 comparator 메서드를 제공한다.</p>
<p>이러한 클래스 타입을 직접 사용하는 경우, 클래스 타입에서 제공하는 추가 메서드를 꼭 사용해야 하는 경우로 최소화해야 하며, 절대 남발하지 말아야 한다.</p>
]]></description>
        </item>
    </channel>
</rss>