<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>usnijee_dev_log</title>
        <link>https://velog.io/</link>
        <description>JINSU</description>
        <lastBuildDate>Wed, 15 Jan 2025 10:24:18 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>usnijee_dev_log</title>
            <url>https://velog.velcdn.com/images/usnijee_2/profile/a6b0559f-1167-4534-b023-9bbfdfbc1270/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. usnijee_dev_log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/usnijee_2" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[자료구조] Stack, Queue, Deque]]></title>
            <link>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Stack-Queue-Deque</link>
            <guid>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Stack-Queue-Deque</guid>
            <pubDate>Wed, 15 Jan 2025 10:24:18 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡stack-queue-deque">💡Stack, Queue, Deque</h2>
<p>이번 포스팅에서는 대표적인 선형 자료구조인 <code>Stack</code>, <code>Queue</code> 그리고 <code>Deque</code>에 대해서 알아보자 </p>
<h3 id="stack">Stack</h3>
<ul>
<li><code>후입 선출(LIFO, Last In First Out)</code>의 자료구조 </li>
<li>즉, 나중에 넣은 것이 가장 먼저 나오는 것을 후입선출 자료구조라고 한다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/a275e408-61a3-4a1c-ab61-ea1b722b30f7/image.png" alt="">
<img src="https://velog.velcdn.com/images/usnijee_2/post/1745cdfc-b782-43b1-928f-f8b0b70be7f1/image.png" alt=""></p>
<h4 id="◻️-주의">◻️ 주의!</h4>
<p>JAVA에서 제공하는 <code>Stack</code>은 사용하지 않는 것을 지향한다. <code>Stack</code> 클래스 내부에서 <code>Vector</code>라는 자료 구조를 사용하는데, 이 자료구조는 지금은 사용되지 않고 하위 호환을 위해 존재한다. 대신에 <code>Deque</code>를 사용하는 것을 권장한다 </p>
<h4 id="◻️-stack-활용-예제">◻️ Stack 활용 예제</h4>
<pre><code class="language-java">public class StackMain {

    public static void main(String[] args) {
        Stack&lt;Integer&gt; stack = new Stack&lt;&gt;();

        stack.push(1);
        stack.push(2);
        stack.push(3);
        System.out.println(stack);

        // 다음 꺼낼 요소 확인(꺼내지 않고 단순 조회)
        System.out.println(&quot;stack.peek() = &quot; + stack.peek());

        // 스택 요소 뽑기
        // LIFO 후입 선출
        System.out.println(&quot;stack.pop() = &quot; + stack.pop());
        System.out.println(&quot;stack.pop() = &quot; + stack.pop());
        System.out.println(&quot;stack.pop() = &quot; + stack.pop());
        System.out.println(stack);
    }
}</code></pre>
<h3 id="queue">Queue</h3>
<ul>
<li><code>선입 선출(FIFO, Fist In Fist Out)</code>의 자료구조 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/1c6e671b-9a8d-4434-8388-e768b9cce799/image.png" alt=""></p>
<ul>
<li><code>Queue</code>에 값을 넣는 것을 <code>offer</code>, 값을 꺼내는 것을 <code>poll</code>이라고 한다 </li>
</ul>
<h4 id="◻️-컬렉션의-queue">◻️ 컬렉션의 Queue</h4>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/e3fd2854-ec0a-404d-aa53-13c78ca203a8/image.png" alt=""></p>
<ul>
<li>java의 <code>Collection</code>은 <code>Queue</code>인터페이스를 구현체로 제공한다. </li>
</ul>
<h4 id="◻️-queue-활용-예제">◻️ Queue 활용 예제</h4>
<pre><code class="language-java">public class QueueMain {

    public static void main(String[] args) {
        Queue&lt;Object&gt; queue = new ArrayDeque&lt;&gt;();
//        Queue&lt;Object&gt; linkedlist = new LinkedList&lt;&gt;();

        //데이터 추가
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        System.out.println(queue);

        //다음 꺼낼 데이터 확인(꺼내지 않고 단순 조회만)
        System.out.println(&quot;queue.peek() = &quot; + queue.peek());

        // 데이터 꺼내기
        System.out.println(&quot;queue.poll() = &quot; + queue.poll());
        System.out.println(&quot;queue.poll() = &quot; + queue.poll());
        System.out.println(&quot;queue.poll() = &quot; + queue.poll());
        System.out.println(queue);
    }
}</code></pre>
<h3 id="deque">Deque</h3>
<p><code>Deque</code>는 <code>Double Ended Queue</code>의 약자로, 자료구조의 끝 양쪽에서 데이터를 추가하거나 삭제할 수 있는 자료구조이다. 컬렉션에서는 <code>Queue</code>인터페이스의 구현체로 제공된다 </p>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/76f3a4d1-4b6f-43d7-b809-c71878559a42/image.png" alt=""></p>
<ul>
<li><code>offerFirst()</code> : 앞에 추가한다.</li>
<li><code>offerLast()</code> : 뒤에 추가한다.</li>
<li><code>pollFirst()</code> : 앞에서 꺼낸다.</li>
<li><code>pollLast()</code> : 뒤에서 꺼낸다.</li>
</ul>
<h4 id="◻️-deque-활용-예제">◻️ Deque 활용 예제</h4>
<pre><code class="language-java">public class DequeMain {

    public static void main(String[] args) {
        Deque&lt;Integer&gt; deque = new ArrayDeque&lt;&gt;();
//        Deque&lt;Integer&gt; deque = new LinkedList&lt;&gt;(); 더 느림

        // 데이터 추가
        deque.offerFirst(1);
        System.out.println(deque);
        deque.offerFirst(2);
        System.out.println(deque);
        deque.offerFirst(3);
        System.out.println(deque);
        deque.offerFirst(4);
        System.out.println(deque);

        // 다음 꺼낼 데이터 확인(단순 조회)
        System.out.println(&quot;deque.peekFirst() = &quot; + deque.peekFirst());
        System.out.println(&quot;deque.peekLast() = &quot; + deque.peekLast());

        // 데이터 꺼내기
        System.out.println(&quot;deque.pollFirst() = &quot; + deque.pollFirst());
        System.out.println(&quot;deque.pollFirst() = &quot; + deque.pollFirst());
        System.out.println(&quot;deque.pollLast() = &quot; + deque.pollLast());
        System.out.println(&quot;deque.pollLast() = &quot; + deque.pollLast());
        System.out.println(deque);
    }
}</code></pre>
<h4 id="◻️-deque와-stack-queue">◻️ Deque와 Stack, Queue</h4>
<p><code>Deque</code>는 요소를 저장하는 처음과 끝 즉, 양쪽에서의 데이터 추가와 삭제가 가능하다. 이러한 <code>Deque</code>의 성질로 인해 <code>Deque</code>를 통해 <code>Queue</code>와 <code>Stack</code>을 모두 구현할 수 있다. java의 <code>Deque</code> 인터페이스는 이러한 <code>Deque</code>의 성질을 반영해 <code>Stack</code>의 데이터 입력(<code>push()</code>), 데이터 삭제(<code>pop()</code>) 그리고 <code>Queue</code>의 데이터 입력(<code>offer()</code>), 데이터 삭제(<code>poll()</code>)을 제공한다. </p>
<pre><code class="language-java">// Deque-Stack
import java.util.ArrayDeque;
import java.util.Deque;

/**
 *  Deque 자료구조를 stack으로 사용할 수 있음 이때 단순히 Deque의 offer와 poll이 아닌
 *  stack의 push pop을 그대로 사용할 수 있음
 */
public class DequeStackMain {

    public static void main(String[] args) {
        Deque&lt;Object&gt; deque = new ArrayDeque&lt;&gt;();

        // 데이터 추가
        deque.push(1);
        deque.push(2);
        deque.push(3);
        System.out.println(deque);

        // 다음 꺼낼 데이터 확인(꺼내지 않고 단순 조회만)
        System.out.println(&quot;deque.peek() = &quot; + deque.peek());

        // 데이터 꺼내기
        System.out.println(&quot;deque.pop() = &quot; + deque.pop());
        System.out.println(&quot;deque.pop() = &quot; + deque.pop());
        System.out.println(&quot;deque.pop() = &quot; + deque.pop());
        System.out.println(deque);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] Map 인터페이스 ]]></title>
            <link>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Map-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Map-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Mon, 13 Jan 2025 13:17:39 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡map-인터페이스">💡Map 인터페이스</h2>
<p><code>Map</code>은 <code>키-값</code>쌍을 저장하는 자료구조이다.</p>
<ul>
<li><code>Key</code>는 중복이 불가능하다. <code>Key</code>를 통해 <code>Value</code>를 빠르게 검색이 가능하다</li>
<li><code>Value</code>는 중복이 가능하다.</li>
<li><code>Map</code>은 순서를 허용하지 않는다 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/877f136a-54e5-4061-9bad-d9160f6e213b/image.png" alt=""></p>
<h3 id="map-인터페이스">Map 인터페이스</h3>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/77f3d7b0-893e-4837-a4f2-3dc8d852a449/image.png" alt=""></p>
<h4 id="◻️-hashmap">◻️ HashMap</h4>
<ul>
<li>가장 많이 사용</li>
<li><code>해시 인덱스</code>를 통해 요소를 저장<ul>
<li><code>key</code>값은 해시함수를 통해 <code>해시 인덱스</code>로 변환되고 데이터 저장, 삭제에 사용됨</li>
</ul>
</li>
<li>일반적으로 <code>O(1)</code> 성능을 가짐 (해시 충돌이 많으면 최악의 경우 <code>O(n)</code>)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/025e66f8-5b67-4bdd-899e-502dfb784a74/image.png" alt=""></p>
<ul>
<li>주의점!<ul>
<li><code>HashSet</code>과 동일하게 해시코드로 변형이 되어야 하는 참조형은 반드시 <code>hashCode()</code>와 <code>equals()</code>를 오버라이딩하여 재정의 해주어야 한다. 따라서, <code>HashMap</code>의 <code>Key</code>로 사용되는 객체는 반드시 <code>hashCode()</code> , <code>equals()</code>를 반드시 구현해야 한다 </li>
</ul>
</li>
</ul>
<h4 id="◻️-linkedhashmap">◻️ LinkedHashMap</h4>
<ul>
<li><code>Node</code>를 도입하여 <code>LinkedList</code>를 통해 <code>입력된 요소의 순서를 유지</code></li>
<li>상대적으로 <code>HashMap</code>보다 무거움 </li>
</ul>
<h4 id="◻️-treemap">◻️ TreeMap</h4>
<ul>
<li><code>레드-블랙 트리</code> 기반으로 <code>정렬된 순서</code>를 가짐</li>
<li>모든 key는 <code>Comparator(비교자)</code>에 의해 정렬됨 </li>
<li><code>get</code>, <code>move</code>, <code>remove</code>는 <code>O(logN)</code>의 성능을 가짐</li>
</ul>
<h3 id="set과-map의-관계">Set과 Map의 관계</h3>
<p><code>Map</code>의 특징은 <code>중복</code>을 허용하지 않고, 순서를 보장하지 않는다는 것이다. 이 특성은 <code>Set</code>의 특성과 동일하다. 즉, <code>Map</code>의 <code>Key</code>가 결국 <code>Set</code>과 동일하게 작동다는 것을 의미하고, 이는 결국 <code>Set</code>은 <code>Map</code>에서 <code>value</code>부분만 제거된 것이라고 봐도 무방하다. 따라서 다음과 같은 유의미한 상관관계를 갖는다</p>
<ul>
<li><code>HashSet</code> -&gt; <code>HashMap</code></li>
<li><code>LinkedHashSet</code> -&gt; <code>LinkedHashMap</code></li>
<li><code>TreeSet</code> -&gt; <code>TreeMap</code></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] Set 인터페이스 ]]></title>
            <link>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Set-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Set-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Sun, 12 Jan 2025 11:05:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡set-인터페이스">💡Set 인터페이스</h2>
<p>이전 포스팅에서 <code>Set</code> 자료구조의 특징과 <code>Hash</code> 알고리즘에 대해서 학습하였다. JAVA는 컬렉션을 통해 <code>Set</code> 인터페이스를 제공하고 대표적으로 <code>HashSet</code>,<code>LinkedHashSet</code>,<code>TreeSet</code>이 구현체로 다양한 Set 자료 구조 기능을 제공한다.
<img src="https://velog.velcdn.com/images/usnijee_2/post/37b6e7b6-c374-44d5-8c4e-3fe937959e9d/image.png" alt=""></p>
<ul>
<li><code>HashSet</code> : <code>Hash</code> 알고리즘에 기반한 <code>Set</code> 자료구조</li>
<li><code>LinkedHashSet</code> : <code>HashSet</code>의 데이터 저장 공간을 <code>LinkedList</code>로 구성하여 <code>데이터 입력 순서를 보장</code>하는 <code>Set</code> 자료구조 </li>
<li><code>TreeSet</code> : <code>이진 탐색</code>을 기반으로 <code>데이터를 정렬</code>한 <code>Set</code></li>
</ul>
<h3 id="hashset">HashSet</h3>
<ul>
<li><code>구현</code> : <code>Hash</code> 자료구조를 사용하여 요소 저장</li>
<li><code>순서</code> : 데이터 입력 순서를 보장 x</li>
<li><code>시간복잡도</code> : 주요 연산(추가, 삭제, 검색)은 평균적으로 O(1)</li>
<li><code>용도</code> : 데이터 순서 상관없이 데이터의 유일성이 중요한 경우 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/3a2a9dc2-94c7-4e0f-948b-a947cd93ab21/image.png" alt=""></p>
<h3 id="linkedhashset">LinkedHashSet</h3>
<ul>
<li><code>구현</code> : <code>HashSet</code>에 <code>LinkedList</code>를 추가하여 입력된 요소들의 순서를 보장</li>
<li><code>순서</code> : 데이터 입력에 따른 순서 보장</li>
<li><code>시간복잡도</code> : 주요 연산(추가, 삭제, 검색)은 평균적으로 O(1)</li>
<li><code>용도</code> : 데이터의 유일성과 데이터 입력 순서 모두를 보장해야 하는 경우 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/3aa38beb-4d22-4198-ace2-02f2dc4169bd/image.png" alt=""></p>
<h3 id="treeset">TreeSet</h3>
<ul>
<li><code>구현</code> : <code>TreeSet</code>은 이진 탐색 트리를 개선한 레드-블랙 트리를 내부에서 사용 </li>
<li><code>순서</code> : 요소들은 정렬된 순서로 저장됨. 순서의 기준은 비교자(<code>Comparator</code>)로 변경 가능 </li>
<li><code>시간 복잡도</code> : 주요 연산은 <code>O(log n)</code>의 시간 복잡도를 가짐</li>
<li><code>용도</code> : 데이터를 정렬된 순서로 유지하면서 Set의 특성을 활용해야 하는 경우 </li>
</ul>
<h4 id="◻️-tree-구조">◻️ Tree 구조</h4>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/3454a340-0c84-4466-817f-f893c98aad15/image.png" alt=""> </p>
<ul>
<li>트리구조는 부모 노드와 자식 노드로 구성</li>
<li><code>이진 트리</code> : 자식이 2개까지 올 수 있는 트리</li>
<li><code>이진 탐색 트리</code> : 특정 노드의 왼쪽 자손은 더 작은 값, 오른쪽 자손은 더 큰 값을 가져 <code>정렬</code>이 된 것을 <code>이진 탐색 트리</code>라고 한다 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/e34918f7-b2be-4676-ae34-9a70203100cd/image.png" alt=""></p>
<pre><code class="language-java">class Node {
     Object item;
     Node left;
     Node right;
}</code></pre>
<h4 id="◻️-이진-탐색-트리의-big-o">◻️ 이진 탐색 트리의 Big-O</h4>
<p>위의 Tree 구조에서 사용한 데이터를 보면, 15개의 데이터로 구성되어 있는 것을 확인할 수 있다. 여기서 숫자 <code>35</code>를 검색한다고 가정하면 다음과 같은 과정이 진행된다.</p>
<ul>
<li>1번 : root인 20과 검색 대상인 35를 비교 -&gt; 35가 크기에 오른쪽으로 찾아감</li>
<li>2번 : 40 vs 35 -&gt; 40이 크기에 왼쪽으로 찾아감</li>
<li>3번 : 30 vs 35 -&gt; 35가 크기에 오른쪽으로 찾아감</li>
<li>4번 : 35 vs 35 -&gt; 35를 찾음 </li>
</ul>
<p>15개의 데이터에서 4번의 연산만으로 결과를 얻음. 이는 <code>List</code>의 검색(<code>O(n)</code>) 보단 성능이 좋고 <code>Hash</code>의 검색(<code>O(1)</code>)보다는 느리다. </p>
<p>이진 탐색의 핵심원리는 <code>한번의 연산으로 데이터의 절반을 날릴 수 있다는 점</code>이다. 비교 대상 기준 <code>크다</code> 와 <code>작다</code>의 이분법적 개념으로만 접근하기 때문이다. 예를들어, 검색 대상이 35인 경우 root(20) vs 35 에서 35가 root 보다 크기 때문에 root 보다 작은, 왼쪽에 있는 데이터들은 고려할 필요가 없어진다. </p>
<p>즉, 16개의 데이터 기준 이진 탐색은 <code>16-&gt;8-&gt;4-&gt;2-&gt;1</code> ,4번의 연산만으로 목표에 도달할 수 있다는 의미이고 이를 일반화 하면 n개의 데이터 기준 logN (밑은 2이다)의 연산이 필요 함을 알 수 있다.</p>
<p>따라서, 이진 탐색 트리의 Big-O 는 <code>O(logN)</code> 이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] Hash]]></title>
            <link>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Hash</link>
            <guid>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Hash</guid>
            <pubDate>Wed, 08 Jan 2025 13:54:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡hash-알고리즘">💡Hash 알고리즘</h2>
<h3 id="list와-set">List와 Set</h3>
<ul>
<li><code>List</code> : 순서 유지, 중복 허용, 인덱스 접근 가능의 특징을 갖는 선형 자료구조 (앞에서 많이 언급)</li>
<li><code>Set</code> : 유일한 요소의 컬렉션<ul>
<li>특징<ul>
<li>유일성 : <code>Set</code>의 요소는 중복이 불가능 </li>
<li>순서 미보장 : <code>Set</code>의 요소는 순서가 정해져 있지 않음</li>
</ul>
</li>
<li>용도 : 중복을 허용하지 않고, 요소의 유무만 중요한 경우 사용 
<img src="https://velog.velcdn.com/images/usnijee_2/post/537ae693-433c-4b78-9b19-236c1741f3bc/image.png" alt=""></li>
</ul>
</li>
</ul>
<h4 id="◻️-직접-구현해보는-set">◻️ 직접 구현해보는 Set</h4>
<pre><code class="language-java">public class MyHashSetV0 {

    private int[] elementData = new int[10];
    private int size = 0;

    // O(n)
    public boolean add(int value) {
        if (contains(value)) {
            return false;
        }
        elementData[size] = value;
        size++;
        return true;
    }

    // O(n)
    public boolean contains(int value) {
        for (int data : elementData) {
            if (data == value) {
                return true;
            }
        }
        return false;
    }

    public int size() {
        return size;
    }

    @Override
    public String toString() {
        return &quot;MyHashSetV0{&quot; +
                &quot;elementData=&quot; + Arrays.toString(Arrays.copyOf(elementData,size)) +
                &quot;, size=&quot; + size +
                &#39;}&#39;;
    }
}</code></pre>
<ul>
<li>배열을 활용한 <code>Set</code>을 구현한 결과 <code>Set</code>의 특성상 중복이 허용되면 안되기 때문에 add() 메서드에서 중복되지 않은 대상만 요소로 추가가 가능하도록 contains()를 통해 배열의 모든 요소를 검사하는 과정을 진행한다.</li>
<li>즉, 성능이 O(n)으로 좋지않다. </li>
</ul>
<h3 id="hash-알고리즘-도입">Hash 알고리즘 도입</h3>
<p>배열을 데이터의 저장소로 사용한 <code>Set</code>의 경우를 다시 생각해보자. <code>배열</code>의 경우 인덱스를 통한 조회하는 기능은 O(1)으로 높은 성능을 갖는다. 그렇다면 <code>데이터를 배열의 인덱스로 사용한다면 인덱스 조회와 데이터 검색이 함께 가능하지 않을까??</code></p>
<p>즉, 입력되는 데이터를 동일한 인덱스 공간에 저장하면 조회와 검색이 빨라지지 않을까?</p>
<h4 id="◻️-데이터-값을-배열-인덱스로-사용하는-경우">◻️ 데이터 값을 배열 인덱스로 사용하는 경우</h4>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/7fde06b1-aa07-402c-92ac-eaee70d41b15/image.png" alt=""></p>
<ul>
<li>인덱스 1에는 데이터 1이 들어있고, 인덱스 8에는 데이터 8이 들어있다. 즉, 저장하는 데이터와 인덱스를 완전히 맞추어 인덱스 번호가 데이터가 되고, 데이터가 인덱스 번호가 되었다. </li>
<li><code>하지만 낭비되는 메모리가 너무 많다.</code> 만약에 <code>int</code>의 모든 범위에서 특정 숫자를 <code>Set</code> 자료구조에 추가한다고 가정하면 빈공간으로만 메모리를 거의 다 사용하는 상황이 발생한다. </li>
</ul>
<h4 id="◻️-나머지-연산">◻️ 나머지 연산</h4>
<p>메모리를 절약하면서, 넓은 범위의 값을 사용할 수 없을까? 나머지 연산을 사용하면 가능하다. 예를들어, 배열의 크기를 10으로 고정했다고 가정하자. </p>
<ul>
<li><code>1 % 10 = 1</code></li>
<li><code>2 % 10 = 2</code></li>
<li><code>3 % 10 = 3</code></li>
<li><code>4 % 10 = 4</code></li>
<li><code>5 % 10 = 5</code></li>
<li><code>24 % 10 = 4</code></li>
<li><code>99 % 10 = 9</code>
나머지 연산을 활용하면, 나머지 연산의 결과는 절대로 배열의 크기를 넘지 않고 결국 배열의 크기만큼 배열에 저장할 수 있게 된다. </li>
</ul>
<h4 id="◻️-해시-인덱스">◻️ 해시 인덱스</h4>
<p><code>해시 인덱스(hashIndex)</code> 란 배열의 인덱스로 사용할 수 있도록 원래의 값을 계산한 인덱스를 의미한다.
<img src="https://velog.velcdn.com/images/usnijee_2/post/c2c28fea-2564-4ceb-9ecf-70972c52fd90/image.png" alt=""></p>
<h4 id="◻️-해시-충돌">◻️ 해시 충돌</h4>
<p><code>99</code>와 <code>9</code>를 모두 10으로 나누면 동일한 나머지인 <code>9</code>를 해시 인덱스로 갖게 된다. 이렇게 <code>해시 충돌</code>이란 다른 값을 입력했지만 같은 해시 코드가 나오는 것을 의미한다. 어떻게 해야할까?
<img src="https://velog.velcdn.com/images/usnijee_2/post/a8bfe61f-6773-48d7-b202-e6df4bbe8e10/image.png" alt=""></p>
<ul>
<li>99와 9를 같이 저장하면 된다</li>
<li>배열의 원소를 또다른 배열로 만든다면 동시 저장이 가능하다 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/95bbd901-1422-43be-b797-0bfd1b5d90c1/image.png" alt=""> </p>
<h4 id="◻️-해시-충돌-조회-및-성능">◻️ 해시 충돌 조회 및 성능</h4>
<p><code>해시충돌</code>이 발생한 경우 내부의 데이터를 하나씩 비교해보면 결과를 찾을 수 있다. 위의 예에서 <code>99</code>를 조회한다고 가정하자.
<img src="https://velog.velcdn.com/images/usnijee_2/post/c9bbd5bd-70db-4102-8711-c4980e76b75d/image.png" alt=""></p>
<ul>
<li>9번 인덱스까지 배열의 특성을 통해 조회</li>
<li>9번 인덱스의 내부 배열에서 반복문과 <code>equals()</code>를 통해 배열내의 모든 값을 비교하여 <code>99</code>를 검색 </li>
</ul>
<p>해시 충돌 상황에서의 값 조회 과정을 살펴보니 추가되는 데이터에의해 특정 인덱스에 데이터가 많이 몰리는 경우가 발생할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/9157e983-cfc4-4cef-8bbf-2fbe47eb0b79/image.png" alt=""></p>
<ul>
<li>이 경우 모든 입력 데이터에 대한 <code>해시 인덱스</code>는 <code>9</code>가 된다</li>
<li>따라서 9번 인덱스에 모든 데이터가 저장되고, 특정 데이터를 조회할 때 결국 반복문에 의해 저장된 데이터 수 만큼 비교가 이루어져야 한다</li>
<li>즉, 최악의 경우 <code>O(n)</code>의 성능을 갖게 된다. </li>
</ul>
<h3 id="정리">정리</h3>
<ul>
<li><code>해시 알고리즘</code>에 의한 <code>해시 인덱스</code>를 사용하는 경우 <ul>
<li>데이터 저장<ul>
<li>평균 : O(1)</li>
<li>최악 : O(n)</li>
</ul>
</li>
<li>데이터 조회<ul>
<li>평균 : O(1)</li>
<li>최악 : O(n)</li>
</ul>
</li>
</ul>
</li>
<li>해시 인덱스를 사용하는 방식에서 최악의 경우(해시 충돌에서의 최악 경우)는 거의 발생하지 않고 배열의 크기만 적절하게 잡아주면 대부분 O(1)의 성능을 갖는다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] List와 의존 관계 주입]]></title>
            <link>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-List</link>
            <guid>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-List</guid>
            <pubDate>Mon, 06 Jan 2025 12:54:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡list">💡List</h2>
<p>앞 포스트에서 <code>ArrayList</code>와 <code>LinkedList</code>를 살펴보았다. java에서 제공하는 두 클래스는 <code>List</code>인터페이스의 구현체로 <code>List</code>인터페이스의 핵심 메서드만 살펴보면 다음과 같다 </p>
<pre><code class="language-java">public interface MyList&lt;E&gt; {

    int size();

    void add(E e);

    void add(int index, E e);

    E get(int index);

    E set(int index, E element);

    E remove(int index);

    int indexOf(E o);
}</code></pre>
<h3 id="list-추상화">List 추상화</h3>
<ul>
<li><p><code>List</code>는 순서가 있고 중복을 허용하는 자료구조</p>
</li>
<li><p>리스트 <code>추상화</code></p>
<ul>
<li><code>List</code>를 인터페이스로 추상화하여 <code>ArrayList</code>, <code>LinkedList</code>를 구현체로 구현하면 다형성을 통해 유지보수 측면에서 많은 성능 향상을 구현할 수 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/9b23b908-29fe-4678-8428-af18977bbbb6/image.png" alt=""></p>
</li>
</ul>
<h3 id="의존-관계-주입dependency-injection">의존 관계 주입(Dependency Injection)</h3>
<p><code>ArrayList</code> 자료구조를 통해 많은 데이터 유입을 처리하는 <code>BatchProcessor</code>라는 프로그램이 있다고 가정하자. </p>
<pre><code class="language-java">public class BatchProcessor {
    private final MyArrayList&lt;Integer&gt; list = new MyArrayList&lt;&gt;();

   /**
   * 처리 로직
   **/
}</code></pre>
<ul>
<li><p>이때 앞부분에서의 데이터 유입측면에서는 <code>LinkedList</code>가 <code>ArrayList</code>보다 성능이 높아 데이터 저장공간을 <code>LinkedList</code>로 변환하려고 한다. 즉, <code>BatchProcessor</code>의 직접적인 코드 수정이 필요하다. 이런 경우를 클라이언트 코드인 <code>BatchProcessor</code>가 <code>구체적인 클래스에 의존</code>한다고 표현한다.</p>
</li>
<li><p><code>BatchProcessor</code>가 추상적인 <code>List</code>에 의존하면 어떻게 될까?</p>
</li>
</ul>
<pre><code class="language-java">public class BatchProcessor {

//    private final MyArrayList&lt;Integer&gt; list = new MyArrayList&lt;&gt;();
    private final MyList&lt;Integer&gt; list;

    public BatchProcessor(MyList&lt;Integer&gt; list) {
        this.list = list;
    }

    /**
    * 처리 로직
    **/
}</code></pre>
<ul>
<li>수정된 <code>BatchProcessor</code>를 보면 생성자를 통해 <code>List</code>타입의 참조형 객체를 직접 외부에서 받아오는 것을 확인 할 수 있다. <code>다형성</code>에 의해 생성자로 유입되는 <code>list</code>로 <code>List</code>의 구현체는 모두 가능하다. </li>
<li>즉, <code>BatchProcessor</code>내부에서 상황에 따라 코드의 수정 없이 <code>ArrayList</code> 또는 <code>LinkedList</code>를 외부의 유입을 통해 사용이 가능한 것이다.</li>
<li>위와 같이 추상적인 <code>List</code>와 생성자를 통해 클래스(<code>BatchProcessor</code>)외부에서 <code>의존할 대상</code>이 결정되어서 다형성에 의해 내부로 유입되는 것을 <code>DI(Dependency Injection), 의존성 주입</code>이라고 한다</li>
</ul>
<p>다시 정리 해보자.</p>
<h4 id="◻️-컴파일-타임-의존관계">◻️ 컴파일 타임 의존관계</h4>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/dcf2cddf-05dd-44ca-8cab-6b6b7b1caa67/image.png" alt=""></p>
<ul>
<li>런타임 이전 컴파일 시점에는 <code>클라이언트</code>인 <code>BatchProcessor</code>가 <code>List</code> 인터페이스에만 의존 한다</li>
</ul>
<h4 id="◻️-런타임-의존관계">◻️ 런타임 의존관계</h4>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/3cd559a2-2e6d-4aa7-bb87-3a569ed8dc83/image.png" alt=""></p>
<ul>
<li>코드가 실행되어 런타임 시점에는 생성자를 통해 <code>ArrayList</code> 또는 <code>LinkedList</code> 참조값이 클라이언트인 <code>BatchProcessor</code>에 전달됨</li>
<li>즉, 클라이언트는 의존 관계를 클래스내에서 결정하지 않고 런타임의 객체 생성 시점에 결정한다. <code>OCP 원칙</code>에 의해 클라이언트 코드 변경없이 다형적 참조에 의해 구현체를 성능에 맞게 사용하여 필요한 기능을 사용할 수 있게 된다</li>
</ul>
<h3 id="정리">정리</h3>
<ol>
<li>의존관계를 클래스 내부에서 직접 의존하면 상황에 따라 클라이언트 코드를 매번 변경하는 일이 발생한다</li>
<li><code>의존성 주입</code>을 통해 객체간의 의존을 클래스 외부에서 구현하여 클라이언트 코드의 변경없이 구현이 가능하다 </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] LinkedList]]></title>
            <link>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-LinkedList</link>
            <guid>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-LinkedList</guid>
            <pubDate>Fri, 03 Jan 2025 14:02:12 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡linkedlist">💡LinkedList</h2>
<p>이전 시간에는 <code>ArrayList</code>에 대해 살펴보았다. <code>ArrayList</code>의 단점에 대해 다시 살펴보자.</p>
<h3 id="arraylist의-단점">ArrayList의 단점</h3>
<ul>
<li><p>고정된 크기</p>
<ul>
<li><code>ArrayList</code>는 배열을 통해 데이터를 보관</li>
<li><code>배열</code>은 생성과 동시에 크기가 고정된 연속적인 자료구조 → 데이터의 추가/삭제 여부 예측이 어려워 메모리 낭비 발생</li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/f9d785eb-98b9-4755-8707-85825c83162a/image.png" alt=""></p>
</li>
</ul>
<ul>
<li>데이터의 추가/삭제 성능이 안좋다<ul>
<li><code>add</code>(추가) : 기준 index 이후의 데이터를 전부 오른쪽으로 한 칸씩 이동 후 해당 index 위치의 데이터를 새로운 것으로 대체, <code>O(n)</code></li>
<li><code>remove</code>(제거) : 해당 index 위치 이후의 데이터를 전부 왼쪽으로 한 칸씩 이동, <code>O(n)</code> </li>
</ul>
</li>
</ul>
<h3 id="linkedlist">LinkedList</h3>
<p><code>LinkedList</code>는 <code>Node</code>를 활용하여 낭비되는 메모리 없이 필요한 만큼만 메모리를 확보해서 사용하고, 앞 중간에 데이터를 추가하거나 삭제할 때도 효율적인 자료 구조이다.</p>
<h4 id="◻️-node">◻️ Node</h4>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/c09645ad-0a4e-48b1-ae10-f7170825f4c4/image.png" alt=""></p>
<pre><code class="language-java">public class Node {
    Object item;
    Node next;
}</code></pre>
<ul>
<li><code>Node</code>는 내부에 저장할 데이터인 item과, 다음으로 연결할 노드의 참조값인 next를 가진다</li>
<li>여러 노드가 연결되면 다음과 같다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/f7bab874-6c4f-4e14-bf0a-a43098e52aa8/image.png" alt=""></p>
<h4 id="◻️-직접-구현해보는-linkedlist">◻️ 직접 구현해보는 LinkedList</h4>
<p>제너릭과 중첩 클래스는 따로 정리 할 예정이다</p>
<pre><code class="language-java">public class MyLinkedListV3&lt;E&gt; {

    private Node&lt;E&gt; first;  // 첫번째 노드 
    private int size = 0; // 현재 저장되어 있는 데이터의 양

    // add : 마지막에 데이터 추가
    public void add(E e) {
        Node&lt;E&gt; newNode = new Node(e);
        if (first == null) {
            first = newNode;
        } else {
            Node&lt;E&gt; lastNode = getLastNode(); // O(n)
            lastNode.next = newNode;
        }
        size++;

    }

    private Node&lt;E&gt; getLastNode() {
        Node&lt;E&gt; x = first;
        while (x.next != null) { //
            x = x.next;
        }
        return x;
    }

    // 특정 index 위치에 노드 add
    public void add(int index, E e) {
        Node&lt;E&gt; newNode = new Node&lt;&gt;(e);
        if (index == 0) {
            newNode.next = first;
            first = newNode;
        } else {
            Node&lt;E&gt; prev = getNode(index - 1);
            newNode.next = prev.next;
            prev.next = newNode;
        }
        size ++;
    }

    // 특정 index의 값 변경
    public E set(int index, E element) {
        Node&lt;E&gt; x = getNode(index);
        E oldValue = x.item;
        x.item = element;
        return oldValue;
    }

    // 삭제
    public E remove(int index) {
        Node&lt;E&gt; removeNode = getNode(index);
        E removedItem = removeNode.item;
        if (index == 0) {
            first = removeNode.next;
        } else {
            Node&lt;E&gt; prev = getNode(index - 1);
            prev.next = removeNode.next;
        }
        /**
         *  GC는 메모리 참조값을 기준으로 작동한다.
         *  GC는 더 이상 참조되지 않는 객체를 찾아 제거하기 때문에
         *  제거 대상이 참조되고 있거나 다른 객체를 참조하고 있다면 GC는 이를 삭제하지 않음
         *  따라서, 명시적으로 null을 통해 참조값을 제거해야한다.
         */
        removeNode.item = null;
        removeNode.next = null;
        size--;
        return removedItem;
    }

    // 특정 index의 value값 조회
    public E get(int index) {
        Node&lt;E&gt; node = getNode(index);
        return node.item;
    }

    // 특정 index의 노드 조회 메서드
    private Node&lt;E&gt; getNode(int index) {
        Node&lt;E&gt; x = first;
        for (int i = 0; i &lt; index; i++) {
            x = x.next;
        }
        return x;
    }

    public int indexOf(E o) {
        int index = 0;
        for (Node&lt;E&gt; x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                return index;
            }
            index++;
        }
        return -1;
    }

    public int size() {
        return size;
    }

    @Override
    public String toString() {
        return &quot;MyLinkedListV1{&quot; +
                &quot;first=&quot; + first +
                &quot;, size=&quot; + size +
                &#39;}&#39;;
    }

    // 중첩 클래스를 활용하여 Node를 LinkedList 내부에 설정
    private static class Node&lt;E&gt; {

        E item;
        Node&lt;E&gt; next;

        public Node(E item) {
            this.item = item;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            Node&lt;E&gt; x = this;
            sb.append(&quot;[&quot;);
            while (x != null) {
                sb.append(x.item);
                if (x.next != null) {
                    sb.append(&quot;-&gt;&quot;);
                }
                x = x.next;
            }
            sb.append(&quot;]&quot;);

            return sb.toString();
        }
    }
}</code></pre>
<ul>
<li><code>add(Object o)</code> : o를 item으로 갖는 node를 &#39;순차적으로&#39; 추가</li>
<li><code>add(int index, Object o)</code> : index 위치에 o를 item으로 갖는 node 추가 </li>
<li><code>set(int index, Object o)</code> : index 위치의 노드의 item을 o로 변경</li>
<li><code>get(int index)</code> : index 위치의 노드의 item 반환</li>
<li><code>getNode(int index)</code> : index 위치의 노드 반환 </li>
<li><code>indexOf(Object o)</code> : item을 o로 갖는 노드의 인덱스 반환</li>
</ul>
<h3 id="arraylist-vs-linkedlist">ArrayList vs LinkedList</h3>
<ul>
<li><p><code>공통점</code> : 중복을 허용하고 순서가 있는 자료구조</p>
</li>
<li><p><code>차이점</code> : 성능 차이</p>
<table>
<thead>
<tr>
<th align="left"><strong>기능</strong></th>
<th align="center"><strong>ArrayList</strong></th>
<th align="center"><strong>LinkedList</strong></th>
</tr>
</thead>
<tbody><tr>
<td align="left">인덱스 조회</td>
<td align="center"><code>O(1)</code></td>
<td align="center"><code>O(n)</code></td>
</tr>
<tr>
<td align="left">데이터 검색</td>
<td align="center"><code>O(n)</code></td>
<td align="center"><code>O(n)</code></td>
</tr>
<tr>
<td align="left">앞에 데이터 추가</td>
<td align="center"><code>O(n)</code></td>
<td align="center"><code>O(1)</code></td>
</tr>
<tr>
<td align="left">뒤에 데이터 추가</td>
<td align="center"><code>O(1)</code></td>
<td align="center"><code>O(n)</code></td>
</tr>
<tr>
<td align="left">중간에 데이터 추가</td>
<td align="center"><code>O(n)</code></td>
<td align="center"><code>O(n)</code></td>
</tr>
</tbody></table>
</li>
</ul>
<h3 id="정리">정리</h3>
<ul>
<li>데이터를 조회할 일이 많고, 뒷 부분에 데이터를 빈도있게 추가한다 → <code>ArrayList</code></li>
<li>앞 부분에 데이터를 빈도있게 추가하거나 삭제할 일이 많다 → <code>LinkedList</code></li>
<li>java가 제공하는 <code>collection</code>의 <code>LinkedList</code>는 이중연결리스트이기에 앞/뒤 데이터 추가 삭제 모두 성능이 좋다 </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] ArrayList]]></title>
            <link>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-ArrayList</link>
            <guid>https://velog.io/@usnijee_2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-ArrayList</guid>
            <pubDate>Thu, 02 Jan 2025 13:21:52 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡arraylist">💡ArrayList</h2>
<h3 id="배열">배열</h3>
<p><code>고정된</code> 크기의 <code>연속된</code> 메모리의 공간을 갖는 자료구조
<img src="https://velog.velcdn.com/images/usnijee_2/post/0544b83f-1ea9-436d-8b90-e61f4d32f1a3/image.png" alt=""></p>
<h4 id="◻️-배열의-특징">◻️ 배열의 특징</h4>
<ol>
<li><code>인덱스(index)</code>를 이용하여 빠르게 자료에 접근 가능 (<code>O(1)</code>)</li>
<li>인덱스를 통한 입력(add), 변경(set), 조회(contains)의 경우 한번의 계산으로 자료의 위치에 도달 가능</li>
</ol>
<h4 id="◻️-배열의-index-활용-예시">◻️ 배열의 index 활용 예시</h4>
<ul>
<li><code>arr[0]</code>: x100 + (4byte * 0) : x100</li>
<li><code>arr[1]</code>: x100 + (4byte * 1) : x104</li>
<li><code>arr[2]</code>: x100 + (4byte * 2) : x108</li>
</ul>
<h4 id="◻️-배열의-검색">◻️ 배열의 검색</h4>
<p>배열의 경우 <code>index</code>를 통해 특정 인덱스의 위치까지 빠르게 접근이 가능하지만 배열에 저장되어 있는 <code>데이터</code>를 확인하기 위해서는 데이터를 처음부터 하나하나 비교해야한다(<code>O(n)</code>)</p>
<h4 id="◻️-배열의-데이터-추가">◻️ 배열의 데이터 추가</h4>
<ul>
<li>첫번째 위치에 추가<ul>
<li>기존의 데이터 오른쪽으로 이동 O(n)</li>
<li>추가 데이터 첫번재 인덱스 위치에 접근 후 저장 O(1)</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/4425cf23-6e7d-4b7e-a712-ae3caac19cbd/image.png" alt="">
<img src="https://velog.velcdn.com/images/usnijee_2/post/89ebfa96-5da0-4f46-8b27-5443a328f9c6/image.png" alt=""></p>
<ul>
<li>중간(특정 index)에 추가<ul>
<li>특정 index 이후의 데이터 오른쪽으로 이동 O(n)</li>
<li>특정 index에 접근 후 데이터 저장 O(1)</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/9f40a128-d713-46ed-a41a-274a70bc886d/image.png" alt="">
 <img src="https://velog.velcdn.com/images/usnijee_2/post/9eeb7d7c-f00c-4fad-87fb-6bb8f29cd6c7/image.png" alt=""></p>
<ul>
<li>마지막 위치에 추가<ul>
<li>마지막 index에 접근 후 데이터 저장 O(1)\</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/a6be0d5f-1cc6-4e62-abac-5195aa26b090/image.png" alt=""></p>
<h4 id="◻️-배열의-한계">◻️ 배열의 한계</h4>
<ul>
<li>배열은 생성과 동시에 크기가 고정된다<ul>
<li>배열의 크기가 1000으로 가정하면 배열에 저장되는 데이터가 현저히 작으면 메모리 낭비가 된다. 반대로 배열에 저장되는 데이터가 1000개가 넘어가면 더이상 데이터 추가가 불가능하다  </li>
</ul>
</li>
</ul>
<h3 id="arraylist">ArrayList</h3>
<h4 id="◻️-list-자료구조">◻️ List 자료구조</h4>
<ul>
<li>순서가 있고 중복을 허용하는 자료구조</li>
</ul>
<h4 id="◻️-직접-구현하는-arraylist">◻️ 직접 구현하는 ArrayList</h4>
<pre><code class="language-java">public class ArrayListReview {

    private Object[] elementArray;
    private int size = 0; // 현재 value가 채워져있는 정도 ex. 5크기의 배열에 idx = 3까지 채워져있음 size = 3

    public ArrayListReview(int initialCapacity) {
        this.elementArray = new Object[initialCapacity];
    }

    // size 반환
    public int Size() {
        return size;
    }

    // 데이터 추가
    // 동적 배열을 구현
    public void add(Object o) {
        if (size == elementArray.length) {
            grow();
        }
        elementArray[size] = o;
        size ++;
    }

    // 특정 인덱스 위치에 데이터 추가
    public void add(int index, Object o) {
        if (size == elementArray.length) {
            grow(); // 크기 동적 변환
        }
        shiftRightFrom(index);
        elementArray[index] = o;
        size ++;
    }

    private void shiftRightFrom(int index) {
        for (int i = size; i &gt; index; i--) {
            elementArray[i] = elementArray[i-1];
        }
    }

    private void grow() {
        int oldCapacity = elementArray.length;
        int newCapacity = oldCapacity * 2;

        Object[] newArray = Arrays.copyOf(elementArray, newCapacity);
        elementArray = newArray;
    }

    // 특정 인덱스의 value 반환
    public Object get(int index) {
        return elementArray[index];
    }

    // 특정 인덱스의 value 변경
    public Object set(int index, Object element) {
        Object oldValue = get(index);
        elementArray[index] = element;
        return oldValue;
    }

    // 특정 value의 인덱스 반환
    public int indexOf(Object o) {
        for (int i = 0; i &lt; elementArray.length; i++) {
            if (elementArray[i] == o) {
                return i;
            }
        }
        return -1;
    }

    // 특정 인덱스 value 삭제
    public Object remove(int index) {
        Object oldValue = get(index);
        shiftLeftFrom(index);

        size --;
        elementArray[size] = null; // 자동 안되나?
        return oldValue;
    }

    private void shiftLeftFrom(int index) {
        for (int i = index; i &lt; size - 1; i++) {
            elementArray[i] = elementArray[i + 1];
        }
    }

    // size 크기만큼의 elementData를 복사 후 반환
    public String toString() {
        return Arrays.toString(Arrays.copyOf(elementArray,size)) +
                &quot; size=&quot; + size + &quot;, capacity=&quot; + elementArray.length;
    }
}</code></pre>
<h4 id="◻️-arraylist의-단점">◻️ ArrayList의 단점</h4>
<ul>
<li>데이터 저장공간을 <code>배열</code>을 사용하였기에 고정된 크기에 의한 단점은 여전히 존재</li>
<li>데이터 추가와 삭제의 부분도 마지막 인덱스 위치를 제외하고는 기존의 데이터를 오른쪽 및 왼쪽으로 이동시켜야 하기 때문에 성능이 좋지 못함 <code>O(n)</code></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 열거형 ENUM 2]]></title>
            <link>https://velog.io/@usnijee_2/JAVA-%EC%97%B4%EA%B1%B0%ED%98%95-ENUM-2</link>
            <guid>https://velog.io/@usnijee_2/JAVA-%EC%97%B4%EA%B1%B0%ED%98%95-ENUM-2</guid>
            <pubDate>Wed, 11 Dec 2024 11:59:44 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡열거형-enum-2">💡열거형 ENUM 2</h2>
<p>직전 포스팅에서 고정된 상수 값들의 집합을 정의할 때 <code>String</code>을 사용했을 때의 문제점과 <code>타입 안전 열거형 패턴</code>의 개념을 알아보고 구현을 해보았다. 이번 포스팅에서는 JAVA에서 제공하는 <code>ENUM 열거형 패턴</code>과 <code>열거형 리팩토링</code> 및 <code>열거형이 제공하는 메서드</code>에 대해 알아보자. </p>
<h3 id="열거형-enum-type">열거형 ENUM Type</h3>
<p>JAVA는 <code>타입 안전 열거형 패턴</code>을 쉽게 사용할 수 있도록 <code>ENUM 열거형 타입</code>을 제공한다. 이전 포스팅의 회원 등급과 가격을 입력하면 할인 금액을 계산해주는 예제의 <code>classGrade</code>를 <code>ENUM</code>타입으로 하면 다음과 같다</p>
<pre><code class="language-java">public enum Grade {
    BASIC, GOLD, DIAMOND  
}

/* Enum은 아래와 같다

public class Grade extends Enum {
    public static final Grade BASIC = new Grade();
    public static final Grade GOLD = new Grade();
    public static final Grade DIAMOND = new Grade();

    //private 생성자 추가
    private Grade() {}
}
 */</code></pre>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/36281fda-e6bd-4ed9-a672-6af52835c046/image.png" alt=""></p>
<h4 id="◻️-enum-특징">◻️ ENUM 특징</h4>
<ul>
<li>열거형도 클래스이다.</li>
<li>열거형 <code>ENUM</code>은 자동으로 <code>java.lang.Enum</code>을 상속 받는다 </li>
<li>외부에서 임의로 생성할 수 없다 (private 생성자)</li>
<li>열거형은 <code>인터페이스</code> 구현 가능</li>
<li>열거형에 <code>추상 메서드</code>를 선언하고 구현 가능 </li>
</ul>
<p><code>ENUM</code>은 결국 이전 포스팅의 <code>ClassGrade</code>와 기능이 거의 동일하다고 보면 된다. 다만 개발자가 사용하기 편하게 JAVA에서 직접 제공한다는 점이 차이점이다.</p>
<h4 id="◻️-enum-장점">◻️ ENUM 장점</h4>
<ul>
<li><code>타입 안전성 향상</code> : 사전에 정의된 <code>상수</code>들로만 구성되기에 유효하지 않은 값이 입력되는 경우는 존재하지 않는다. 이런 경우 컴파일 에러가 발생한다 </li>
<li><code>간결성 및 일관성</code> : <code>ENUM</code>을 사용하면 코드의 가독성이 높아지고, <code>타입 안전성 향상</code>에 의해 데이터의 일관성이 보장된다.</li>
<li><code>확장성</code> : 새로운 등급을 추가하고 싶을 때, <code>ENUM</code>에 새로운 상수를 추가하면 끝난다.</li>
</ul>
<h3 id="열거형-주요-메서드">열거형 주요 메서드</h3>
<pre><code class="language-java">public class EnumMethod1 {

    public static void main(String[] args) {

        // values() : 모든 ENUM 반환
        Grade[] values = Grade.values();
        System.out.println(&quot;values = &quot; + Arrays.toString(values)); // values = [BASIC, GOLD, DIAMOND]

        // name() : ENUM 상수 이름 문자열로 반환
        // ordinal() : ENUM 상수의 선언 순서(0부터 시작)를 반환 ,, 사용하지 않을 것을 권장 --&gt; 상수 사이에 새로운 상수가 추가 될 경우 값이 많이 바뀜
        for (Grade value : values) {
            System.out.println(&quot;name = &quot; + value.name() + &quot;, ordinal = &quot; + value.ordinal());
            /**
             * name = BASIC, ordinal = 0
             * name = GOLD, ordinal = 1
             * name = DIAMOND, ordinal = 2
             */
        }
        // valueOf(A) : ENUM 상수의 목록 중 A와 일치하는 ENUM 상수 반환 , 반환 타입은 ENUM 타입
        // A가 ENUM 상수에 없는 대상이라면 IllegalArgumentException에러 발생
        String input = &quot;DIAMOND&quot;;
        Grade gold = Grade.valueOf(input);
        Grade gold2 = Grade.valueOf(&quot;VIP&quot;);
        System.out.println(&quot;gold = &quot; + gold); // gold = DIAMOND
        System.out.println(&quot;gold2 = &quot; + gold2); // 런타임 에러 발생 
    }
}</code></pre>
<ul>
<li><code>values()</code> : 모든 <code>ENUM</code> 상수를 포함하는 배열을 반환</li>
<li><code>valueOf(String name)</code> : 주어진 이름과 일치하는 ENUM 상수를 반환</li>
<li><code>name()</code> : <code>ENUM</code> 상수의 이름을 문자열로 반환하다. </li>
<li><code>ordinal()</code> : <code>ENUM</code> 상수의 선언 순서를 반환한다. </li>
<li><code>toString()</code> : <code>ENUM</code> 상수의 이름을 문자열로 반환 (오버라이딩을 통해 반환 내용 변경 가능)</li>
</ul>
<h3 id="열거형-리팩토링">열거형 리팩토링</h3>
<h4 id="◻️-classgrade-리팩토링">◻️ ClassGrade 리팩토링</h4>
<p><code>BASIC</code>,<code>GOLD</code>,<code>DIAMOND</code>등급에따라 할인율을 적용한 로직을 다시 살펴보자.</p>
<pre><code class="language-java">public class DiscountService {
    public int discount(Grade classGrade, int price) {
        int discountPercent = 0;

        if (classGrade == BASIC) { // classGrade == x001
            discountPercent = 10;
        } else if (classGrade == GOLD) { 
            discountPercent = 20;
        } else if (classGrade == DIAMOND) {
            discountPercent = 30;
        } else {
            System.out.println(&quot;할인X&quot;);
        }
        return price * discountPercent / 100;
    }
}</code></pre>
<p>로직에서는 <code>if-else</code>를 통해 각 등급마다 할인율을 적용하는 것을 알 수 있다. 즉, 결국 등급에 의해 할인율이 결정되는 것이기 때문에 이를 등급 class에서 관리해보자. </p>
<pre><code class="language-java">public class ClassGrade {

    public static final ClassGrade BASIC = new ClassGrade(10);
    public static final ClassGrade GOLD = new ClassGrade(20);
    public static final ClassGrade DIAMOND = new ClassGrade(30);

    private final int discountRate;

    private ClassGrade(int discountRate) {
        this.discountRate = discountRate;
    }

    public int getDiscountRate() {
        return discountRate;
    }
}</code></pre>
<ul>
<li><code>discountRate</code>를 <code>private</code>생성자의 매개변수를 통해 받아 객체 내의 필드에 저장하도록 한다</li>
<li><code>getDiscountRate</code>를 통해 외부에서 <code>할인율</code>을 조회할 수 있는 기능을 추가한다.</li>
</ul>
<p>이렇게 된다면 <code>ClassGrade</code>내에서 사전에 정의한 <code>BASIC</code>,<code>GOLD</code>,<code>DIAMOND</code>등급은 각각 10,20,30의 할인율을 갖게되고 할인율 조회도 가능해진다.</p>
<pre><code class="language-java">ClassGrade.BASIC.getDiscount() // 10</code></pre>
<p><code>ClassGrade</code>에서 등급마다 할인율을 관리하게 된다면 <code>DiscountService</code>로직은 더 이상 <code>if-else</code>의 분기를 통해 할인율을 관리할 필요가 없어진다. </p>
<pre><code class="language-java">public class DiscountService {

    public int discount(ClassGrade classGrade, int price) {
        return price * classGrade.getDiscountRate() / 100;
    }
}</code></pre>
<p>리팩토링을 마치고 <code>DiscountService</code> 로직을 보니 단순한 할인율 계산을 하는 로직만 남았다. <code>ClassGrade</code>에서 각 등급마다 할인율을 갖도록 리팩토링 하였는데 추가로 각 등급마다 할인율을 계산하여 갖고 있게 할 수 없을까? 최종적으로 리팩토링을 진행하면 다음과 같다.</p>
<pre><code class="language-java">public class ClassGrade {

    public static final ClassGrade BASIC = new ClassGrade(10);
    public static final ClassGrade GOLD = new ClassGrade(20);
    public static final ClassGrade DIAMOND = new ClassGrade(30);

    private final int discountRate;

    private ClassGrade(int discountRate) {
        this.discountRate = discountRate;
    }

    public int getDiscountRate() {
        return discountRate;
    }

    public int discount(int price) {
        return price * discountRate / 100;
    }

}</code></pre>
<h4 id="◻️-enum-리팩토링">◻️ ENUM 리팩토링</h4>
<p>위의 <code>ClassGrade</code>와 동일한 방향으로 <code>ENUM</code>을 리팩토링 해보자.</p>
<pre><code class="language-java">public enum Grade {

    BASIC(10), // private static final Grade BASIC = new Grade(10);
    GOLD(20),
    DIAMOND(30);

    private final int discountRate;

    Grade(int discountRate) {
        this.discountRate = discountRate;
    }

    public int getDiscountRate() {
        return discountRate;
    }

    public int discount(int price) {
        return price * discountRate / 100;
    }
}</code></pre>
<p>즉, 더이상 <code>DiscountService</code>로직은 필요 없고 <code>ENUM</code> 열거형에서 고정된 범위의 상수를 설정하고 해당 상수와 괄련된 기능(메서드)를 제공하고 외부에서 이를 조회할 수 있게되었다. </p>
<h3 id="정리">정리</h3>
<p><code>열거형 패턴</code> : <code>타입안정성</code>과 <code>데이터 일관성</code>이 보장 된 고정된 범위의 상수 집합, 데이터 타입</p>
<ul>
<li><p><code>타입 안정성</code></p>
<ul>
<li><code>private</code> 생성자로 인한 외부에서 객체 생성 금지 </li>
<li>열거형 클래스 내에서 미리 정의된 동일 타입의 객체를 상수화하여 고정된 범위의 상수 구현</li>
<li><code>상수</code>와 관련된 기능을 객체지향 관점에서 클래스 내의 필드와 메서드를 통해 구현 가능 </li>
</ul>
</li>
<li><p><code>데이터 일관성</code>
타입 안정성에서 발생한 <code>제약</code>으로인해 데이터의 일관성이 유지 </p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 열거형 ENUM 1]]></title>
            <link>https://velog.io/@usnijee_2/JAVA-%EC%97%B4%EA%B1%B0%ED%98%95-ENUM-1</link>
            <guid>https://velog.io/@usnijee_2/JAVA-%EC%97%B4%EA%B1%B0%ED%98%95-ENUM-1</guid>
            <pubDate>Tue, 10 Dec 2024 14:26:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡열거형-enum-1">💡열거형 ENUM 1</h2>
<p><code>열거형(Enum Type)</code>은 Java에서 <code>고정된 상수 값들의 집합을 정의할 때</code> 사용하는 데이터 타입이다. 열거형을 사용하면 코드의 가독성, 안정성, 유지보수성이 높아진다고 나와있다. 그렇다면 열거형을 사용하지 않았을 때의 문제 즉, 열거형의 필요성을 알아보기위해 열거형이 만들어진 근본적인 이유를 알아야한다. </p>
<h3 id="문자열과-타입-안정성">문자열과 타입 안정성</h3>
<p><code>열거형</code>의 경우 주로 값의 범위가 <code>고정적</code>이고 <code>제한적</code>일 때 사용한다. 이를 <code>열거형</code>이 아닌 문자열을 사용한 예제를 통해 문제점을 살펴보자. </p>
<blockquote>
<p>회원 등급과 가격을 입력하면 할인 금액을 계산해주는 예제</p>
</blockquote>
<pre><code class="language-java">/**
 * 고객은 3등급으로 나누고, 상품 구매시 등급별로 할인을 적용한다. 할인시 소수점 이하는 버린다.
 * BASIC 10% 할인
 * GOLD 20% 할인
 * DIAMOND 30% 할인
 */
public class DiscountService {

    public int discount(String grade, int price) {
        int discountPercent = 0;

        if (grade.equals(&quot;BASIC&quot;)) {
            discountPercent = 10;
        } else if (grade.equals(&quot;GOLD&quot;)) {
            discountPercent = 20;
        } else if (grade.equals(&quot;DIAMOND&quot;)) {
            discountPercent = 30;
        } else {
            System.out.println(grade + &quot;: 할인X&quot;);
        }
        return price * discountPercent / 100;
    }
}

public class StringGradeEx0_1 {

    public static void main(String[] args) {
        int price = 10000;

        DiscountService discountService = new DiscountService();
        int basic = discountService.discount(&quot;BASIC&quot;, price);
        int gold = discountService.discount(&quot;GOLD&quot;, price);
        int diamond = discountService.discount(&quot;DIAMOND&quot;, price);

        System.out.println(&quot;BASIC 등급의 할인 금액: &quot; + basic); 
        System.out.println(&quot;GOLD 등급의 할인 금액: &quot; + gold);
        System.out.println(&quot;DIAMOND 등급의 할인 금액: &quot; + diamond);

        /**
         * 출력 결과
         * BASIC 등급의 할인 금액: 1000
         * GOLD 등급의 할인 금액: 2000
         * DIAMOND 등급의 할인 금액: 3000
         */

    }
}</code></pre>
<p>위 코드는 고객의 등급에 따라 할인 비율을 책정하여 할인된 금액을 반환하는 예제로, 고객의 등급(<code>grade</code>)을 <code>String</code>타입으로 생성하였다. 출력의 결과만 보면 정상적으로 프로그램이 실행되고 문제가 없어보이지만 <code>타입 안정성</code>과 <code>데이터 일관성</code>측면에서 문제점이 존재한다. </p>
<pre><code class="language-java">public class StringGradeEx0_2 {

    public static void main(String[] args) {
        int price = 10000;

        DiscountService discountService = new DiscountService();

        // 존재하지 않는 등급
        int vip = discountService.discount(&quot;VIP&quot;, price);
        System.out.println(&quot;VIP 등급의 할인 금액: &quot; + vip);

        // 오타
        int diamondd = discountService.discount(&quot;DIAMONDD&quot;, price);
        System.out.println(&quot;DIAMONDD 등급의 할인 금액: &quot; + diamondd);

        // 소문자 입력
        int gold = discountService.discount(&quot;gold&quot;, price);
        System.out.println(&quot;gold 등급의 할인 금액: &quot; + gold);

        /**
         * VIP: 할인X
         * VIP 등급의 할인 금액: 0
         * DIAMONDD: 할인X
         * DIAMONDD 등급의 할인 금액: 0
         * gold: 할인X
         * gold 등급의 할인 금액: 0
         */

    }
}</code></pre>
<h4 id="◻️-문제점">◻️ 문제점</h4>
<ul>
<li><p><code>값의 제한 부족</code></p>
<ul>
<li>예제의 목적은 기준 등급인 <code>BASIC</code>,<code>DIAMOND</code>,<code>GOLD</code>에 따른 고객의 할인 비율 계산 및 할인 된 가격 제공이다. 즉, 3개의 등급을 제외한 다른 값은 할인 적용이 불가능해야한다.</li>
<li>존재하지 않는 등급(<code>VIP</code>), 오타(<code>DIAMONDD</code>), 소문자(<code>gold</code>)에 대해서는 <code>제약</code>이 필요하지만 <code>제약</code>이 발생하지 않는다</li>
</ul>
</li>
<li><p><code>컴파일 시 에러 감지 불가</code></p>
<ul>
<li>위의 <code>제약</code>이 발생하지 않는 이유는 <code>String</code>타입에 맞게 코드가 작성되었기 때문이다. 즉, 컴파일 과정에서 에러는 발생하지 않기에 개발자가 런타임에서 디버깅을 직접하여 문제점을 파악해야한다.  </li>
</ul>
</li>
</ul>
<h4 id="◻️-해결-방법">◻️ 해결 방법</h4>
<p>위의 문제를 해결하려면 등급(<code>grade</code>)을 특정 값으로 제한해야 한다. 즉, 등급으로 반드시 <code>BASIC</code>,<code>GOLD</code>,<code>DIAMOND</code>만 적용되도록 해야한다. </p>
<p><code>문자열 상수</code>를 활용해보자.</p>
<pre><code class="language-java">public class StringGrade {
    public static final String BASIC = &quot;BASIC&quot;;
    public static final String GOLD = &quot;GOLD&quot;;
    public static final String DIAMOND = &quot;DIAMOND&quot;;
}

public class DiscountService {

    public int discount(String grade, int price) {
        int discountPercent = 0;

        if (grade.equals(StringGrade.BASIC)) {
            discountPercent = 10;
        } else if (grade.equals(StringGrade.GOLD)) {
            discountPercent = 20;
        } else if (grade.equals(StringGrade.DIAMOND)) {
            discountPercent = 30;
        } else {
            System.out.println(grade + &quot;: 할인X&quot;);
        }

        //10000 * 20
        return price * discountPercent / 100;
    }
}

public class StringGradeEx1_1 {
    public static void main(String[] args) {
        int price = 10000;

        DiscountService discountService = new DiscountService();
        int basic = discountService.discount(StringGrade.BASIC , price);
        int gold = discountService.discount(StringGrade.GOLD, price);
        int diamond = discountService.discount(StringGrade.DIAMOND, price);

        System.out.println(&quot;BASIC 등급의 할인 금액: &quot; + basic);
        System.out.println(&quot;GOLD 등급의 할인 금액: &quot; + gold);
        System.out.println(&quot;DIAMOND 등급의 할인 금액: &quot; + diamond);
    }

}</code></pre>
<p>문자열 상수를 사용하면 가독성이 올라가고, <code>상수</code>형태의 등급을 사용하기 때문에 잘못된 문자열 상수값을 입력할 시 컴파일 에러가 발생하여 오류를 쉽고 빠르게 찾을 수 있다. </p>
<p>하지만, <code>String</code>타입으로 인해 다른 문자열이 입력되었을 때는 여전히 컴파일 과정에서 오류를 찾을 수 없다는 문제점에 직면한다. 또한 만약 코드가 길어지고 복잡하여 여러 개발자가 파트를 나누어 개발을 한다고 가정하자. A라는 개발자가 <code>문자열 상수</code>를 사용하였다면 이를 B라는 개발자는 직접 건네 듣거나, 주석을 달지 않는 이상 알수없다. 즉, 매우 비효율적인 개발 프로세스가 된다는 것이다. </p>
<h4 id="◻️-요약">◻️ 요약</h4>
<p>고정된 범위의 값을 표현할 때 <code>String</code>타입을 사용할 시,</p>
<ul>
<li><code>값의 제한 부족</code>로 인한 타입 안정성 약화 </li>
<li><code>컴파일 시 오류 감지 불가</code>로인한 디버깅 어려움</li>
</ul>
<p>이를 해결하기 위해 고정된 범위를 <code>가독성 높게 표현</code>하며 해당 범위의 이외의 값에 대해 강력한 <code>제약</code>을 제공해야한다</p>
<h3 id="타입-안전-열거형-패턴">타입 안전 열거형 패턴</h3>
<h4 id="◻️-enum을-사용하지-않고-타입-안전-열거형-패턴-구현해보기">◻️ ENUM을 사용하지 않고 타입 안전 열거형 패턴 구현해보기</h4>
<pre><code class="language-java">public class ClassGrade {

    public static final ClassGrade BASIC = new ClassGrade(); // x001
    public static final ClassGrade GOLD = new ClassGrade(); // x002
    public static final ClassGrade DIAMOND = new ClassGrade(); // x003

    //private 생성자 추가
    //private 생성자 --&gt; ClassGrade 외부에서 객체를 생성하여 클래스로 접근하는 것을 방지
    private ClassGrade() {}
}</code></pre>
<p>위의 코드는 <code>ENUM</code>과 거의 같은 코드이다. 두 가지 핵심에 대해 살펴보자.</p>
<p>[1]</p>
<pre><code class="language-java">public static final ClassGrade BASIC = new ClassGrade(); // x001
public static final ClassGrade GOLD = new ClassGrade(); // x002
public static final ClassGrade DIAMOND = new ClassGrade(); // x003</code></pre>
<ul>
<li><code>public</code>을 통해 외부에서 접근이 가능하되, <code>final</code>을 통해 값을 고정시켜 값 변경은 제한</li>
<li>클래스내에서 객체를 생성하여 <code>메모리 참조값</code>을 상수화하여 사용 </li>
</ul>
<p>[2]</p>
<pre><code class="language-java">private ClassGrade() {}</code></pre>
<ul>
<li><code>private</code> 생성자를 통해 클래스 외부에서 객체를 생성하는 것을 방지하여 클래스 내부에서만 객체를 생성하도록 설정<ul>
<li>이를 통해 오직 클래스 내부에서 정의한 <code>상수</code>를 통해 고정된 범위를 표현 가능 </li>
<li>외부에서 객체를 생성시 컴파일 에러 발생 </li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/626e094a-4f8c-4506-9329-c43020bb50cc/image.png" alt=""></p>
<p>위에서 만든 타입 안전 열거형 패턴을 기반으로 할인율을 적용하는 로직을 살펴보자.</p>
<pre><code class="language-java">public class DiscountService {

    public int discount(ClassGrade classGrade, int price) {
        int discountPercent = 0;

        if (classGrade == ClassGrade.BASIC) { // classGrade == x001
            discountPercent = 10;
        } else if (classGrade == ClassGrade.GOLD) {
            discountPercent = 20;
        } else if (classGrade == ClassGrade.DIAMOND) {
            discountPercent = 30;
        } else {
            System.out.println(&quot;할인X&quot;);
        }
        return price * discountPercent / 100;

    }
}

public class StringGradeEx2_1 {

    public static void main(String[] args) {
        int price = 10000;
        DiscountService discountService = new DiscountService();
        int basic = discountService.discount(ClassGrade.BASIC,price); // == discountService.discount(x001, price)
        int gold = discountService.discount(ClassGrade.GOLD,price);
        int diamond = discountService.discount(ClassGrade.DIAMOND,price);

        // 불가능한 경우 -&gt; 컴파일 에러 발생 
        //int errorcase1 = discountService.discount(2,price);
        //int errorcase2 = discountService.discount(new ClassGrade(),price);
        //int errorcase3 = discountService.discount(ClassGrade.VIP,price);

        System.out.println(&quot;BASIC 등급의 할인 금액: &quot; + basic);
        System.out.println(&quot;GOLD 등급의 할인 금액: &quot; + gold);
        System.out.println(&quot;DIAMOND 등급의 할인 금액: &quot; + diamond);
    }</code></pre>
<ul>
<li><code>DiscountService</code> 의 <code>discount</code>메서드를 보면 매개변수로 <code>ClassGrade</code>타입의 객체를 받아 <code>BASIC</code>,<code>GOLD</code>,<code>DIAMOND</code>와 <code>동일성</code>비교를 통해 할인율을 적용한다. 이때 매개변수인 <code>ClassGrade</code>는 외부에서 객체 생성이 불가능함을 확인하였고 <code>main</code>메서드의 불가능한 경우를 살펴보자</li>
<li>불가능한 경우를 살펴보면, <code>ClassGrade</code>에서 사전에 정의한 객체를 제외하고는 전부 매개변수로 사용할 수 없고 이로인해 컴파일 에러가 발생함을 알 수 있다. </li>
<li>이를 통해 잘못된 값을 입력하는 근본적인 문제를 해결하고 데이터의 일관성이 보장된다.</li>
</ul>
<h4 id="◻️-정리">◻️ 정리</h4>
<p>타입 안전 열거형 패턴(Type-Safe Enum Pattern)의 장점</p>
<ol>
<li><p><code>제한된 인스턴스 생성</code></p>
<ul>
<li>클래스는 클래스 내에서 사전에 정의된 인스턴스만 생성하고, 외부에서는 오로지 이 인스턴스들만 사용할 수 있도록하여 미리 정의된 값들만 사용하는 것을 보장</li>
</ul>
</li>
<li><p><code>타입 안정성</code></p>
<ul>
<li>1의 패턴을 통해 잘못된 값이 입력될 경우 컴파일 에러가 발생하여 사전에 방지 가능 </li>
<li><code>ClassGrade</code>의 경우 오로지 <code>BASIC</code>,<code>GOLD</code>,<code>DIAMOND</code>만 사용 가능 </li>
</ul>
</li>
</ol>
<p>단점</p>
<ol>
<li><code>private</code> 생성자 추가에 유의해야함</li>
<li>코드 작성이 길어짐 </li>
</ol>
<p>이 단점을 보완하고 편리하게 사용할 수 있도록 java에서는 <code>열거형(Enum Type)</code>을 제공한다.</p>
<p>다음 포스팅에서는 <code>열거형(Enum Type)</code>과 <code>열거형 리팩토링</code>에 대해 추가 포스팅을 진행할 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 래퍼 클래스]]></title>
            <link>https://velog.io/@usnijee_2/JAVA-%EB%9E%98%ED%8D%BC-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@usnijee_2/JAVA-%EB%9E%98%ED%8D%BC-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Sun, 08 Dec 2024 13:45:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡래퍼-클래스">💡래퍼 클래스</h2>
<h3 id="기본형의-한계">기본형의 한계</h3>
<p>객체 지향 언어인 Java가 제공하는 <code>기본형(Primitive Type)</code>은 객체가 아니기에 두 가지의 한계점이 존재한다. </p>
<h4 id="◻️-객체가-아니다">◻️ 객체가 아니다.</h4>
<ul>
<li><p>객체는 상태(필드)와 기능(메서드)를 통해 객체 지향적 프로그래밍을 장점을 살릴 수 있다.</p>
<pre><code class="language-java">public class MyIntegerMethodMain0 {

  public static void main(String[] args) {
      int value = 10;
      int i1 = compareTo(value,5);
      int i2 = compareTo(value,10);
      int i3 = compareTo(value,20);
      System.out.println(&quot;i1 = &quot; + i1);
      System.out.println(&quot;i2 = &quot; + i2);
      System.out.println(&quot;i3  = &quot; + i3);

  }

  public static int compareTo(int value, int target) {
      if (value &lt; target) {
          return -1;
      } else if (value &gt; target) {
          return 1;
      } else {
          return 0;
      }
  }
}</code></pre>
<p>위의 예제를 보면 <code>compareTo</code>메서드를 통해 <code>value</code>와 다른 정수를 비교하여 비교에 따른 반환값을 반환하여 출력한다. 비교 대상을 보면 <code>value</code>와 다른 정수들이고 <code>value</code>는 계속해서 사용되는 것을 알 수 있다. 만약 <code>value</code>가 기본형이 아닌 참조형이었다면 <code>value</code>객체가 자기 자신과 다른 정수를 비교하는 <code>compareTo</code>메서드를 만드는 것이 객체 지향적이었을 것이다.</p>
<pre><code class="language-java">public class MyInteger {

  private final int value; // int 타입의 기본형 변수를 하나 갖고 있고 클래스화 했기에 유용한 메서드를 만들어서 사용 가능

  public MyInteger(int value) {
      this.value = value;
  }

  public int getValue() {
      return value;
  }

  public int compareTo(int target) {
      if (value &lt; target) {
          return -1;
      } else if (value &gt; target) {
          return 1;
      } else {
          return 0;
      }
  }

  @Override
  public String toString() {
      return String.valueOf(value);
  }
}</code></pre>
<p>위의 <code>MyInteger</code>클래스처럼 기본형을 참조형으로 만들었을 경우 메서드를 통해 자기 자신의 값(<code>value</code>)와 다른 정수를 비교할 수 있다. 즉, 객체 입장에서 문제를 해결할 수 있다. </p>
</li>
</ul>
<h4 id="◻️-null-값을-가질-수-없음">◻️ null 값을 가질 수 없음</h4>
<ul>
<li><p>기본형은 <code>없음</code>의 개념을 표현 할 수 없다. 기본형은 항상 <code>값</code>을 가지기 때문에 <code>없음</code>을 표현할 수 없다. 
다음 예제를 살펴보자</p>
<pre><code class="language-java">public class MyIntegerNullMain0 {

  public static void main(String[] args) {
      int[] intArr = {-1, 0, 1, 2, 3};
      System.out.println(findValue(intArr,-1)); //-1
      System.out.println(findValue(intArr,0)); //0
      System.out.println(findValue(intArr,1)); //1
      System.out.println(findValue(intArr,100)); //-1
  }

  private static int findValue(int[] intArr, int target) {
      for (int value : intArr) {
          if (value == target) {
              return value;
          }
      }
      return -1; // int는 기본형으로 무조건 값이 있어야한다.
  }
}</code></pre>
<p>위의 예제는 <code>findValue</code>메서드를 통해 <code>intArr</code>에 존재하는 정수와 <code>target</code>정수를 비교해서 동일하면 정수를 반환하고 다르면 <code>-1</code>을 반환하는 예제이다. 근데 여기서 문제점이 발생한다. </p>
<pre><code class="language-java">System.out.println(findValue(intArr,-1)); //-1</code></pre>
<p>이 부분을 보면 <code>target</code>이 -1이고 <code>intArr</code>배열은 -1을 원소로 갖고 있다. <code>findValue</code>메서드에 의해 결과가 <code>-1</code>이 반환되었다면 <code>value</code>가 -1이어서 <code>target</code>과 동일하여 반환된 것인지, <code>value</code>가 -1이 아니어서 <code>target</code>과 다르기에 반환된 것인지 어떻게 알까? 기본형이기에 불가능한 것이다. 참조형을 살펴보자.</p>
<pre><code class="language-java">public class MyIntegerNullMain {

  public static void main(String[] args) {
      MyInteger[] intArr = {new MyInteger(-1), new MyInteger(0), new MyInteger(1)};
      System.out.println(findValue(intArr, -1));
      System.out.println(findValue(intArr, 0));
      System.out.println(findValue(intArr, 1));
      System.out.println(findValue(intArr, 100));

  }

  private static MyInteger findValue(MyInteger[] intArr, int target) { // 래퍼 클래스 사용
      for (MyInteger myInteger : intArr) {
          if (myInteger.getValue() == target) {
              return myInteger;
          }
      }
      return null; // 래퍼클래스를 통해 참조형으로 바뀌어 null 사용 가능
  }
}</code></pre>
<p>참조형의 경우 <code>Null</code>을 사용할 수 있기에 조건에 부합하지 않는경우 <code>Null</code>을 통해 표현이 가능하다.     </p>
</li>
</ul>
<h3 id="래퍼-클래스wrapper-class란">래퍼 클래스(Wrapper class)란</h3>
<p>기본형은 객체가 아니기에 &#39;자기 자신&#39;입장에서 특정 기능을 제공 할 수 없다는 것과 값이 &#39;없음&#39;을 표현할 수 없다는 단점을 갖는다. Java는 기본형의 단점을 보완할 수 있는 <code>래퍼 클래스</code>를 제공한다.</p>
<p><code>Wrap</code>을 직역하면 &quot;포장하다&quot;,&quot;감싸다&quot;로 해석할 수 있다. </p>
<p><code>래퍼 클래스</code>는 <code>기본형</code>을 <code>객체</code>로 감싸서 객체의 기능을 갖도록 하는 클래스이다. 즉, 기본형의 객체화라고 보면된다. </p>
<h4 id="◻️-래퍼-클래스-종류">◻️ 래퍼 클래스 종류</h4>
<ul>
<li><code>byte</code> → <code>Byte</code></li>
<li><code>short</code> → <code>Short</code></li>
<li><code>int</code> → <code>Integer</code></li>
<li><code>long</code> → <code>Long</code></li>
<li><code>float</code> → <code>Float</code></li>
<li><code>double</code> → <code>Double</code></li>
<li><code>char</code> → <code>Character</code></li>
<li><code>boolean</code> → <code>Boolean</code></li>
</ul>
<h4 id="◻️-래퍼-클래스-특징">◻️ 래퍼 클래스 특징</h4>
<ol>
<li>불변 객체이다</li>
<li><code>equals</code>를 통해 동등성 비교를 해야한다</li>
</ol>
<h3 id="박싱boxing과-언박싱unboxing">박싱(Boxing)과 언박싱(Unboxing)</h3>
<ul>
<li><p><code>박싱(boxing)</code> : 물건을 박스에 넣는 행위, 기본형을 래퍼 클래스로 변경하는 것</p>
</li>
<li><p><code>언박싱(Unboxing)</code> : 박스의 물건을 꺼내는 행위, 래퍼 클래스를 다시 기본형으로 꺼내는 것</p>
<pre><code class="language-java">public class WrapperClassMain {

  public static void main(String[] args) {
      // 박싱(Boxing)
      Integer newInteger = new Integer(10); // 삭제 예정 -&gt; valueOf() 사용 권장
      Integer integerObj = Integer.valueOf(10); 
      Long longObj = Long.valueOf(100);
      Double doubleObj = Double.valueOf(10.5);

      System.out.println(&quot;newInteger = &quot; + newInteger); // newInteger = 10
      System.out.println(&quot;integerObj = &quot; + integerObj); // integerObj = 10
      System.out.println(&quot;longObj = &quot; + longObj); // longObj = 100
      System.out.println(&quot;doubleObj = &quot; + doubleObj); // doubleObj = 10.5

      // 언박싱(Unboxing)
      System.out.println(&quot;내부 값 읽기&quot;);
      int intValue = integerObj.intValue(); // intValue = 10
      long longValue = longObj.longValue(); // longObj = 100
      System.out.println(&quot;intValue = &quot; + intValue); 
      System.out.println(&quot;longObj = &quot; + longValue);

      System.out.println(&quot;비교&quot;);
      System.out.println(&quot;==: &quot; + (newInteger == integerObj)); // ==: false
      System.out.println(&quot;equals: &quot; + newInteger.equals(integerObj)); // equals: true
  }
}</code></pre>
</li>
<li><p><code>valueOf()</code> - 박싱(Boxing)</p>
<ul>
<li><code>Integer.valueOf(num)</code>는 단순히 num에 해당하는 정수를 박싱하는 기능뿐만아니라 성능 최적화 기능이 있다. </li>
<li><code>-128</code>~<code>128</code> 범위의 <code>Integer</code> 클래스를 미리 생성하고, 해당 범위의 값이 조회되면 미리 생성된 <code>Integer</code> 객체를 반환한다. 마치 <code>문자열 풀</code>과 비슷하게 미리 생성하고 필요시 제공하는 방식이다. </li>
</ul>
</li>
<li><p><code>intValue()</code> - 언박싱(Unboxing)</p>
</li>
<li><p>비교는 <code>equals()</code></p>
<ul>
<li>래퍼 클래스는 객체이기 때문에 동등성 비교를 통해 비교를 해야한다. </li>
</ul>
</li>
</ul>
<h3 id="오토-박싱-오토-언박싱">오토 박싱, 오토 언박싱</h3>
<p>개발자들에 의해 <code>박싱</code>과 <code>언박싱</code>의 사용 빈도가 늘며 변환 과정에 있어서 더 나은 편의를 제공하기 위해 <code>오토 박싱</code>과 <code>오토 언박싱</code>을 지원한다. </p>
<p><code>오토박싱</code>과 <code>오토 언박싱</code>은 <code>valueOf()</code>,<code>intValue()</code>와 같은 메서드를 사용하지 않고 박싱과 언박싱을 진행하는 것이다. 예제를 살펴보자 </p>
<pre><code class="language-java">public class AutoboxingMain2 {

    public static void main(String[] args) {
        //Primitive -&gt; Wrapper ,, boxing
        int value = 7;
//        Integer boxedValue = Integer.valueOf(value);
        Integer boxedValue = value; // 오토 박싱(Auto boxing)

        //Wrapper -&gt; Primitive ,, unboxing
//        int unboxedValue = boxedValue.intValue();
        int unboxedValue = boxedValue; // 오토 언박싱(Auto unboxing)

        System.out.println(&quot;boxedValue = &quot; + boxedValue);
        System.out.println(&quot;unboxedValue = &quot; + unboxedValue;
     }
}</code></pre>
<p>예제를 보면 오토 박싱과 오토 언박싱은 생성된 래퍼 클래스 객체에 일반 &#39;값&#39;을 저장하는 형태로 진행된다. </p>
<pre><code class="language-java">Integer boxedValue = value; // 오토 박싱(Auto boxing)
int unboxedValue = boxedValue; // 오토 언박싱(Auto unboxing)</code></pre>
<p>오토 박싱과 오토 언박싱은 컴파일러가 대신 <code>valueOf</code>,<code>xxxValue()</code>의 코드를 추가해주는 기능이다. </p>
<h3 id="래퍼-클래스의-주요-메서드">래퍼 클래스의 주요 메서드</h3>
<ul>
<li><code>valueOf()</code> : 래퍼 타입 반환, 숫자, 문자열 모두 지원</li>
<li><code>parseInt()</code> : 기본형 타입 반환, 문자열을 기본형으로 변환 </li>
<li><code>compareTo()</code> : 내 값과 인수로 넘어온 값을 비교. 내 값이 크면 1, 같으면 0, 작으면 -1을 반환</li>
<li><code>Integer.sum()</code>,<code>Integer.min()</code>,<code>Integer.max()</code> : <code>static</code>메서드, 간단한 사칙연산 수행 </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] String]]></title>
            <link>https://velog.io/@usnijee_2/JAVA-String</link>
            <guid>https://velog.io/@usnijee_2/JAVA-String</guid>
            <pubDate>Thu, 05 Dec 2024 05:29:07 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡string">💡String</h2>
<h3 id="string-클래스---기본">String 클래스 - 기본</h3>
<h4 id="◻️-문자를-다루는-대표적인-타입--char-string">◻️ 문자를 다루는 대표적인 타입 : char, String</h4>
<p>JAVA에서 문자는 대표적으로 기본형인<code>char</code>와 참조형인<code>String</code> 타입을 사용하여 표현한다. 특징은 다음과 같다.</p>
<ul>
<li><code>char</code> <ul>
<li>기본형</li>
<li>문자 &#39;하나&#39;를 다룰 때 사용</li>
<li>여러 문자를 다루기 위해서는 <code>char[]</code>를 사용</li>
</ul>
</li>
<li><code>String</code><ul>
<li>참조형 (불변 객체)</li>
<li>문자를 다루는 다양한 기능을 메서드를 통해 제공</li>
</ul>
</li>
</ul>
<h4 id="◻️-string-클래스-구조">◻️ String 클래스 구조</h4>
<pre><code class="language-java">public final class String {
    //문자열 보관
     private final char[] value;// 자바 9 이전
     private final byte[] value;// 자바 9 이후

     //여러 메서드
     public String concat(String str) {...}
     public int length() {...}
     ...
}</code></pre>
<p>String은 참조형이기에 클래스의 고유 속성(필드)와 기능(메서드)를 갖는다. (주요 메서드는 포스팅 후반부에 작성할 예정이다) 따라서, 참조형은 변수에 객체의 메모리 참조값이 저장되기에 <code>+</code>와 같은 사칙연산은 불가능하다. 하지만 String은 가능하다. </p>
<pre><code class="language-java">public class StringConcatMain {

     public static void main(String[] args) {
         String a = &quot;hello&quot;;
         String b = &quot; java&quot;;

         String result1 = a.concat(b);
         String result2 = a + b;
         System.out.println(&quot;result1 = &quot; + result1);
         System.out.println(&quot;result2 = &quot; + result2);
     }
}</code></pre>
<p>String의 경우 원칙적으로는 클래스에서 제공하는 <code>concat()</code>이라는 메서드를 통해 문자열을 더해야하한다. 하지만 <code>result2 = a + b</code>에서 a와 b는 문자열임에도 불구하고 <code>+</code>연산을 통해 <code>hello java</code>라는 결과를 출력한다. 그 이유는 String을 너무 자주 사용하기 때문에 java가 편의상 <code>+</code> 연산을 제공하기 때문이다. </p>
<h3 id="string-클래스---비교">String 클래스 - 비교</h3>
<p>String 클래스는 <code>==</code>가 아닌 항상 <code>equals()</code> 비교를 해야만한다. 
동일성(identity)와 동등성(equality)에 대해 복습해보자.</p>
<ul>
<li><code>동일성(Identity)</code>: <code>==</code>연산자를 사용해서 두 객체의 참조가 동일한 객체를 가리키고 있는지 확인</li>
<li><code>동등성(Equality)</code>: <code>equals()</code> 메서드를 사용하여 두 객체가 논리적으로 같은지 확인 </li>
</ul>
<p>그러면 String은 왜 반드시 동등성 비교를 해야만할까? 이를 알아보기 전에 짚고 넘어가야하는 사항이 있다. </p>
<h4 id="◻️-문자열-리터럴-문자열-풀">◻️ 문자열 리터럴, 문자열 풀</h4>
<pre><code class="language-java">String str1 = new String(&quot;hello&quot;);
String str2 = &quot;hello&quot;;</code></pre>
<p>참조형인 클래스는 다른 참조형과 동일하게 <code>new String(문자열)</code>을 통해 객체를 생성하는게 원칙이나, <code>String str2 = &quot;hello&quot;</code>이 가능하다. 어떻게 가능한걸까?<br><img src="https://velog.velcdn.com/images/usnijee_2/post/04fa64c8-cbe7-49e9-b8e5-738a15eacb1a/image.png" alt="">
<code>String</code>은 자주 사용되는 만큼 JAVA에서 제공하는 편의가 많다. 문자열 풀에 대해 알아보자</p>
<ul>
<li>JAVA가 실행되는 시점에 클래스에 문자열 리터럴이 존재하면 문자열 풀에 <code>String</code>인스턴스를 미리 생성한다. 같은 문자열인 경우에는 새로 만들지 않는다</li>
<li><code>String str2 = &quot;hello&quot;;</code>와 같이 문자열 리터럴을 사용하면 문자열 풀에서 미리 생성한 <code>&quot;hello&quot;</code>를 갖고 있는 인스턴스를 찾아 해당 인스턴스의 참조값(x003)을 반환한다. </li>
<li>이때 <code>String str3 = &quot;hello&quot;;</code>는 str2와 동일하게 <code>&quot;hello&quot;</code> 문자열 리터럴 상수를 사용하기 때문에 동일한 참조값이 변수에 저장되어 str2와 str3는 같은 객체를 참조하게 된다. </li>
</ul>
<p>위와 같은 원리로 <code>문자열 풀</code>에 의해 문자를 사용하는 경우 메모리를 효율적으로 사용할 수 있다. </p>
<p>문자열 리터럴과 문자열 풀의 개념을 바탕으로 다시 <code>동일성</code>과 <code>동등성</code>에 대한 예제를 살펴보자 </p>
<pre><code class="language-java">public class StringEqualsMain1 {

    public static void main(String[] args) {
        String str1 = new String(&quot;hello&quot;);
         String str2 = new String(&quot;hello&quot;);
         System.out.println(&quot;new String() == 비교: &quot; + (str1 == str2)); //new String() == 비교: false
         System.out.println(&quot;new String() equals 비교: &quot; + (str1.equals(str2))); //new String() equals 비교: true

         String str3 = &quot;hello&quot;;
         String str4 = &quot;hello&quot;;
         System.out.println(&quot;리터럴 == 비교: &quot; + (str3 == str4)); //리터럴 == 비교: true
         System.out.println(&quot;리터럴 equals 비교: &quot; + (str3.equals(str4))); //리터럴 equals 비교: true
 }
}</code></pre>
<p>위의 예제에서 문자열 리터럴을 사용한 문자열 활용 부분을 보자. </p>
<ul>
<li><p><code>str3</code>와 <code>str4</code>는 동일한 문자열 리터럴인 <code>&quot;hello&quot;</code>을 저장하고 있기에 문자열 풀에 의해 동일한 메모리 참조값을 갖게 된다. </p>
</li>
<li><p>즉, <code>str3</code>와 <code>str4</code>는 동일한 객체를 가리키고 이는 동일성 비교(identity)가 true임을 의미한다. 또한 두 문자열 모두 <code>&quot;hello&quot;</code>로 논리적으로도 동일하기에 동등성 비교(equality)도 true가 된다. </p>
</li>
<li><p><code>str1</code>과 <code>str2</code>는 new를 통해 서로 다른 인스턴스를 생성하였기에 동일성 비교는 false, 동등성 비교는 true가 된다. 
<img src="https://velog.velcdn.com/images/usnijee_2/post/35bc12fc-1057-4461-a636-a823fbe35686/image.png" alt=""></p>
</li>
</ul>
<p>정리하자면, <code>String</code>은 new를 통해 인스턴스를 생성하는 정석적인 방법과 문자열 풀을 사용한 방법을 통해 인스턴스를 생성하고 문자열을 저장한다. 이때 문자열 풀을 사용한 방식은 동일성 비교와 동등성 비교를 구분할 수 없기 때문에 new를 통해 인스턴스를 생성하는 방식에 맞추어 <code>동등성 비교</code>를 해야한다는 결론이 나온다.</p>
<p>즉, <code>String</code>은 <code>equals()</code>를 통한 동등성 비교를 진행해야 한다.</p>
<h3 id="string-클래스---불변객체">String 클래스 - 불변객체</h3>
<p>String은 대표적으로 자주 사용되는 <code>불변객체</code>이다. 따라서, <code>String</code>은 내부의 문자열 값 변경이 불가능하다. (<a href="https://velog.io/@usnijee_2/JAVA-%EB%B6%88%EB%B3%80-%EA%B0%9D%EC%B2%B4">불변 객체 복습</a>을 통해 복습하자!) </p>
<p><code>String</code> 클래스 소스 코드의 일부를 확인해보자.</p>
<pre><code class="language-java">public final class String {
    //문자열 보관
     private final byte[] value;

     //여러 메서드
     public String concat(String str) {...}
     public int length() {...}
     ...
}</code></pre>
<p><code>String</code> 클래스의 문자열 보관 부분은 <code>final</code>키워드를 통해 값이 고정된 것을 확인할 수 있다. 그렇다면 <code>String</code> 클래스의 <code>concat()</code> 메서드를 살펴보자</p>
<pre><code class="language-java">// concat()
public String concat(String str) {
        if (str.isEmpty()) {
            return this;
        }
        return StringConcatHelper.simpleConcat(this, str);
    }

//simpleConcat()
static String simpleConcat(Object first, Object second) {
        String s1 = stringOf(first);
        String s2 = stringOf(second);
        if (s1.isEmpty()) {
            // newly created string required, see JLS 15.18.1
            return new String(s2);
        }
        if (s2.isEmpty()) {
            // newly created string required, see JLS 15.18.1
            return new String(s1);
        }
        // start &quot;mixing&quot; in length and coder or arguments, order is not
        // important
        long indexCoder = mix(initialCoder(), s1);
        indexCoder = mix(indexCoder, s2);
        byte[] buf = newArray(indexCoder);
        // prepend each argument in reverse order, since we prepending
        // from the end of the byte array
        indexCoder = prepend(indexCoder, buf, s2);
        indexCoder = prepend(indexCoder, buf, s1);
        return newString(buf, indexCoder);
    }</code></pre>
<p><code>concat()</code> 메서드를 보면 <code>String</code> 타입의 문자열을 매개변수로 받고 최종적으로 두 문자열을 연결하는 로직을 거쳐서 반환값으로 <code>new String(문자열)</code>형태로 새로운 <code>String</code>타입의 객체를 생성하여 반환하는 것을 알 수 있다. 이는 대표적인 불변 객체에서 나타나는 특징이다. 불변 객체의 경우 기본적으로 객체 내의 속성과 필드가 고정되어 있기 때문에 메서드를 통해 불가피하게 값을 변경해야하는 로직을 구현해야하는 경우 클래스와 동일 타입의 새로운 객체를 통해 이를 실현한다. </p>
<p>그렇다면 <code>String</code>은 왜 불변으로 설계된 것일까? </p>
<h4 id="◻️-string이-불변으로-설계된-이유">◻️ String이 불변으로 설계된 이유</h4>
<pre><code class="language-java">String str3 = &quot;hello&quot;;
String str4 = &quot;hello&quot;;</code></pre>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/ed1c97b2-2d9e-4654-b726-f7ea3b40be10/image.png" alt=""></p>
<p>Java는 <code>문자열 풀</code>을 제공하여 String을 최적화한다. 문자열 풀에 의해 str3와 str4는 문자열풀에 &quot;hello&quot;를 담고 있는 객체의 동일한 메모리 참조값을 저장한다. 만약 <code>String</code>이 가변 객체라면 str3가 참조하는 문자를 변경하면 str4가 참조하는 문자도 같이 변경되는 <code>사이드 이펙트</code>가 발생한다. 따라서 <code>String</code>은 불변 객체로 설계되었다.  </p>
<h3 id="stringbuilder---가변-string">StringBuilder - 가변 String</h3>
<p>불변인 <code>String</code>의 단점에 대해 살펴보자. 두 문자를 더하는 경우를 살펴보자</p>
<pre><code class="language-java">&quot;A&quot; + &quot;B&quot;
String(&quot;A&quot;) + String(&quot;B&quot;)
String(&quot;A&quot;).concat(String(&quot;B&quot;))
new String(&quot;AB&quot;)</code></pre>
<p><code>String</code>은 불변이기에 내부의 값을 변경할 수 없고, 변경해야하는 사항은 새로운 <code>String</code> 타입으로 반환해주어야한다. 그렇다면 더 많은 문자를 연결해야하는 경우는 어떨까?</p>
<pre><code class="language-java">String str = &quot;A&quot; + &quot;B&quot; + &quot;C&quot; + &quot;D&quot;;
String str = String(&quot;A&quot;) + String(&quot;B&quot;) + String(&quot;C&quot;) + String(&quot;D&quot;);
String str = new String(&quot;AB&quot;) + String(&quot;C&quot;) + String(&quot;D&quot;); // 추가1
String str = new String(&quot;ABC&quot;) + String(&quot;D&quot;); // 추가2
String str = new String(&quot;ABCD&quot;); // 추가3</code></pre>
<p>문자를 4개를 더하는데 객체만 3개가 만들어진다. 즉, 문자를 자주 더하는 과정은 많은 <code>String</code> 객체를 만들고 많은 GC(Garbage Collect) 과정을 거치게 되고 그 만큼 CPU, 메모리등의 리소스를 많이 사용해야함을 의미한다. 매우 비효율적이다. 이를 해결하기 위해 Java는 가변 String인 <code>StringBuilder</code>를 제공한다. </p>
<p>예제를 보자.</p>
<pre><code class="language-java">public class StringBuilderMain1 {

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append(&quot;A&quot;); // 문자열에 추가
        sb.append(&quot;B&quot;);
        sb.append(&quot;C&quot;);
        sb.append(&quot;D&quot;);
        System.out.println(&quot;sb = &quot; + sb);

        sb.insert(4, &quot;Java&quot;); // 4번 인덱스에 문자열을 삽입
        System.out.println(&quot;insert = &quot; + sb);

        sb.delete(4,8);
        System.out.println(&quot;delete = &quot; + sb);

        sb.reverse();
        System.out.println(&quot;reverse = &quot; + sb);

        //StringBuilder -&gt; String
        String string = sb.toString();
        System.out.println(&quot;string = &quot; + string);
    }
}</code></pre>
<p><code>StringBuilder</code>의 경우 하나의 객체 생성을 통해 <code>append</code>,<code>insert</code>,<code>delete</code>,<code>reverse</code>와 같은 제공되는 메서드를 통해 추가적인 객체 생성 없이 문자열 변경이 가능하다. 메모리 효율면에서 상당히 훌륭하지만 <code>사이드 이펙트</code>를 조심해야한다. 따라서, <code>StringBuilder</code>는 보통 문자열을 변경하는 동안 사용하고 <code>toString()</code>을 통해 최종적으로 <code>String</code>타입으로 변경하는 것을 권장한다. </p>
<h3 id="string-최적화">String 최적화</h3>
<h4 id="◻️-문자열-리터럴-최적화">◻️ 문자열 리터럴 최적화</h4>
<p>Java는 문자열 리터럴 부분을 직접 더하는 것을 최적화 해준다. </p>
<pre><code class="language-java">//컴파일 전
String helloWorld = &quot;Hello, &quot; + &quot; World!&quot;;
//컴파일 후
String helloWorld = &quot;Hello, World!&quot;;</code></pre>
<h4 id="◻️-string-변수-최적화">◻️ String 변수 최적화</h4>
<p>문자열 변수의 경우 문자열 리터럴과 달리 실행중인 runtime에서 변수에 어떤 문자열이 존재하는 지 알 수 없기에 단순한 <code>+</code>연산으로 문자열을 합칠 수 없다. </p>
<pre><code class="language-java">String result = str1 + str2;
String reuslt = new StringBuilder().append(str1).append(str2).toString();</code></pre>
<p>따라서, <code>String</code>변수 최적화의 경우 내부적으로는 두 번째와 같이 <code>StringBuilder</code>를 사용하여 최적화를 진행한다.</p>
<h4 id="◻️-string-최적화가-어려운-이유">◻️ String 최적화가 어려운 이유</h4>
<p>반복문을 통해 지속적으로 문자열을 더하는 예제를 살펴보자. </p>
<pre><code class="language-java">public class LoopStringMain {

    public static void main(String[] args) {
         long startTime = System.currentTimeMillis();

         String result = &quot;&quot;;
         for (int i = 0; i &lt; 100000; i++) {
             result += &quot;Hello Java &quot;;
         }
         long endTime = System.currentTimeMillis();

         System.out.println(&quot;result = &quot; + result);
         System.out.println(&quot;time = &quot; + (endTime - startTime) + &quot;ms&quot;); // 2490ms
     }
}</code></pre>
<p>위의 예제를 이해하기 쉽게 <code>StringBuilder</code>를 통해 최적화하는 것을 나타내면 다음과 같다</p>
<pre><code class="language-java">String result = &quot;&quot;;
for (int i = 0; i &lt; 100000; i++) {
    result = new StringBuilder().append(result).append(&quot;Hello Java&quot;).toString();
}</code></pre>
<p>코드를 보면 String 최적화가 잘 이루어지는 것처럼 보이지만, 반복문에 의해 한 사이클마다 <code>StringBuilder</code>의 객체가 생성되는 것을 알 수 있다. 즉, 연결될 문자열의 개수(100,000)만큼 객체가 생성되고 GC에의해 제거되고를 반복한다. 이 경우는 컴파일러가 최적화의 범위를 정확히 알 수 없고 결국 최적화가 최적화가 아닌 상황이 되버린다. 어떻게 해야할까?</p>
<p><code>StringBuilder</code>를 직접 사용하자</p>
<pre><code class="language-java">public class LoopStringMain {

    public static void main(String[] args) {
         long startTime = System.currentTimeMillis();

         StringBuilder sb = new StringBuilder(); // StringBuilder 직접 사용하기 
         for (int i = 0; i &lt; 100000; i++) {
             sb.append(&quot;Hello Java&quot;);
         }
        String result = sb.toString();
         long endTime = System.currentTimeMillis();

         System.out.println(&quot;result = &quot; + result);
         System.out.println(&quot;time = &quot; + (endTime - startTime) + &quot;ms&quot;); // 3ms
     }
}</code></pre>
<p>코드처럼 <code>result</code>을 <code>String</code>이 아닌 <code>StringBuilder</code> 타입으로 직접 생성하고 반복문에서 <code>append()</code>메서드를 통해 문자열을 반복문 횟수만큼 추가한다면 하나의 객체만 생성하고 추가가 가능하다. </p>
<h4 id="◻️-정리">◻️ 정리</h4>
<ul>
<li>문자열을 합칠 때는 대부분 <code>String</code> 최적화가 가능하기에 <code>+</code>연산을 사용한다.</li>
</ul>
<p><code>StringBuilder</code>를 직접 사용해야하는 경우</p>
<ul>
<li>반복문에서 문자열 연결을 반복할 때</li>
<li>조건문을 통해 문자열을 동적으로 변경할 때</li>
<li>복잡한 문자열의 특정 부분을 변경해야할 때</li>
<li>대용량 문자열을 다룰 때</li>
</ul>
<h3 id="메서드-체이닝---method-chaining">메서드 체이닝 - Method Chaining</h3>
<p><code>StringBuilder</code>의 <code>append()</code> 메서드의 소스 코드를 살펴보자.</p>
<pre><code class="language-java">@Override
@IntrinsicCandidate
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

public AbstractStringBuilder append(String str) {
    if (str == null) {
        return appendNull();
    }
    int len = str.length();
    ensureCapacityInternal(count + len);
    putStringAt(count, str);
    count += len;
    return this;
}</code></pre>
<p><code>append()</code> 메서드를 살펴보면 최종적으로 <code>this</code> 즉, 자기 자신에 대한 참조값을 반환하는 것을 알 수 있다. 즉, 메서드를 호출할 때마다 <code>append</code>와 관련된 로직을 처리하고 다시 자기 자신에 대한 참조값을 통해 자기 자신을 가리켜 자기 자신 객체의 필드와 멤버에 접근할 수 있다. 
<img src="https://velog.velcdn.com/images/usnijee_2/post/3ef62ddd-a141-4447-9176-be27acebf6bd/image.png" alt="">
<code>StringBuilder</code>의 대부분의 메서드는 반환값으로 자기자신을 반환하고 이러한 기능으로 인해 새로운 변수 선언 없이 메서드를 순서대로 호출할 수 있게 되었다. 이는 코드의 가독성을 높히고 메모리 사용도 효율적이다. 이를 <code>메서드 체이닝</code> 기법이라고 한다.</p>
<pre><code class="language-java">public class StringBuilderMain1_2 {

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        String string = sb.append(&quot;A&quot;).append(&quot;B&quot;).append(&quot;C&quot;).append(&quot;D&quot;)
                 .insert(4, &quot;Java&quot;)
                 .delete(4, 8)
                 .reverse()
                 .toString();

         System.out.println(&quot;string = &quot; + string); // string = DCBA
     }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Intellij 단축키 모음]]></title>
            <link>https://velog.io/@usnijee_2/Intellij-%EB%8B%A8%EC%B6%95%ED%82%A4-%EB%AA%A8%EC%9D%8C</link>
            <guid>https://velog.io/@usnijee_2/Intellij-%EB%8B%A8%EC%B6%95%ED%82%A4-%EB%AA%A8%EC%9D%8C</guid>
            <pubDate>Thu, 05 Dec 2024 02:33:07 GMT</pubDate>
            <description><![CDATA[<p>다음 포스팅은 유용한 Intellj 단축키 모음을 정리한 블로그이다. 
<a href="https://blog.jetbrains.com/ko/2020/03/11/top-15-intellij-idea-shortcuts_ko/">https://blog.jetbrains.com/ko/2020/03/11/top-15-intellij-idea-shortcuts_ko/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 불변 객체]]></title>
            <link>https://velog.io/@usnijee_2/JAVA-%EB%B6%88%EB%B3%80-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@usnijee_2/JAVA-%EB%B6%88%EB%B3%80-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Mon, 02 Dec 2024 12:39:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡불변-객체">💡불변 객체</h2>
<h3 id="기본형과-참조형의-공유">기본형과 참조형의 공유</h3>
<p>자바의 데이터 타입은 크게 <code>기본형(Primitive type)</code>과 <code>참조형(Reference type)</code>으로 나뉜다. </p>
<ul>
<li><p><code>기본형</code></p>
<ul>
<li><p>데이터를 직접 변수에 저장하는 타입</p>
</li>
<li><p>고정된 메모리에 할당 됨</p>
</li>
<li><p>하나의 값을 여러 변수에서 공유가 불가능</p>
</li>
<li><p>ex. byte, int, float, boolean</p>
<pre><code class="language-java">public class PrimitiveMain {
  int a = 10;
  int b = a;

  System.out.println(&quot;a = &quot; + a); // 10
  System.out.println(&quot;b = &quot; + b); // 10, a의 값이 공유된 것이 아닌 복사된 것

  b = 20;
   System.out.println(&quot;a = &quot; + a); // 10
   System.out.println(&quot;b = &quot; + b); // 20</code></pre>
<p>예시 코드를 보면, 기본형으로 선언된 a,b는 데이터가 직접 저장됨을 알 수 있고 공유가 불가능함을 알 수 있다.</p>
</li>
</ul>
</li>
<li><p><code>참조형</code></p>
<ul>
<li><p>객체(인스턴스)의 주소값(참조값)을 저장하는 타입</p>
</li>
<li><p>실제 데이터는 힙 메모리에 저장되며, 변수는 힙 메모리의 주소값을 참조</p>
</li>
<li><p>변수에 메모리 참조값을 저장하기에 참조값을 여러 변수가 공유하여 힙 메모리에 접근이 가능 </p>
</li>
<li><p>ex. Class, Interface, Array, Enum, String</p>
<pre><code class="language-java">public class Address {

  private String value;

   public Address(String value) {
       this.value = value;
   }

   public void setValue(String value) {
       this.value = value;
   }

   public String getValue() {
       return value;
   }

   @Override
   public String toString() {
       return &quot;Address{&quot; +
               &quot;value=&#39;&quot; + value + &#39;\&#39;&#39; +
            &#39;}&#39;;
   }</code></pre>
<pre><code class="language-java">public class RefMain1_1 {
    public static void main(String[] args) {

        Address a = new Address(&quot;서울&quot;); // 참조값을 x001이라고 가정하자
        Address b = a; 
         System.out.println(&quot;a = &quot; + a); // a = Address{value=&#39;서울&#39;}
         System.out.println(&quot;b = &quot; + b); // b = Address{value=&#39;서울&#39;}

         b.setValue(&quot;부산&quot;); //b의 값을 부산으로 변경해야함
         System.out.println(&quot;부산 -&gt; b&quot;);
         System.out.println(&quot;a = &quot; + a); //사이드 이펙트 발생 , a = Address{value=&#39;부산&#39;}
         System.out.println(&quot;b = &quot; + b); // b = Address{value=&#39;부산&#39;}
     }
 }</code></pre>
<p> Address 클래스에 대한 객체를 생성하면 힙 메모리 영역에 객체에 대한 정보가 저장되고 참조값(x001)이 a 변수에 저장된다. 이때 <code>Address b = a;</code>부분에서 참조값이 복사되어 b변수에도 저장되고, 이는 a,b가 모두 같은 객체를 참조하고 있으며 같은 객체를 참조하고 있기에 a,b 모두 객체 내의 동일한 멤버를 조작 가능함을 의미한다. 
 <img src="https://velog.velcdn.com/images/usnijee_2/post/1ce1a4e7-6899-45aa-bfee-820c3e09a4a1/image.png" alt=""> 위의 코드에는 치명적인 문제가 존재한다. <code>b.setValue(&quot;부산&quot;)</code>을 살펴보자. 이 부분은 b를 통해 value의 값을 서울에서 부산으로 바꾸려는 시도이다. b와 a는 동일한 객체를 바라보고 있기에 b의 value만 바뀌는 것이 아닌 a의 value도 같이 바뀌는 문제가 발생하고 이를 사이드 이팩트라고 한다. </p>
</li>
</ul>
</li>
</ul>
<h3 id="공유-참조와-사이드-이팩트">공유 참조와 사이드 이팩트</h3>
<p><code>사이드 이펙트(Side Effect)</code>는 직역하면 &#39;부작용&#39;을 의미한다. 프로그래밍에서 사이드 이팩트는 <code>특정 계산이 의도한 작업 이외의 부수 효과를 발생시키는 것</code>을 의미한다. 프로그래밍에서의 사이드 이팩트는 부정적인 의미이다. 사이드 이팩트가 발생하면 프로그램에서의 특정 부분 변경이 의도치 않은 다른 부분에서의 변경까지 초래하기 때문이다. 이로인해 유지보수와 디버깅에 어려움을 갖게된다.</p>
<p>참조형의 예시코드에서 발생한 사이드 이팩트 부분을 도식화하여 다시 살펴보자.
<img src="https://velog.velcdn.com/images/usnijee_2/post/fedb1773-9f0e-4adf-be0d-c34e1d19fd42/image.png" alt=""></p>
<p>그럼 사이드 이팩트를 어떻게 해결할까? </p>
<p>a와 b가 각각 다른 객체를 가리키게 하면 된다. </p>
<pre><code class="language-java">Address a = new Address(&quot;서울&quot;);
Address b = new Address(&quot;서울&quot;);</code></pre>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/da80969a-b51f-49c1-a573-ac3542398a7b/image.png" alt="">
Address 클래스에 대한 새로운 객체를 하나 더 생성하여 b가 해당 객체를 참조하도록 한다면 value값을 바꾸어도 a의 상태는 변하지 않는다. 정말 간단히 문제를 해결할 수 있다. 하지만...</p>
<p><code>좋은 코드는 &#39;제약&#39;을 통해 개발자의 잘못된 부분을 방지할 수 있는 것이 좋은 코드</code>라고 영한님이 말씀해주신다. 
즉, a와 b의 변수에 각각 다른 객체의 참조값을 저장하면 간단히 해결되지만 이를 강제할 방법은 없다는 것이다. 만약에 엄청나게 긴 코드를 개발하고 있고 개발자가 실수로 <code>Address b = a;</code>를 작성하여 사이드 이펙트가 발생했다고 가정하자. 제약이 발생하지 않아 예외처리가 불가능하다. 과연 이 부분을 지금처럼 가볍게 해결할 수 있을까? 어떻게 해야할까?</p>
<h3 id="불변객체-도입">불변객체 도입</h3>
<p><code>불변 객체(Immutable Object)</code>는 객체의 상태(객체 내부의 멤버 변수, 필드, 값)가 변하지 않는 객체를 의미한다. 
불변 객체를 만드는 것은 간단하다. </p>
<ul>
<li><code>final</code> 키워드를 통해 내부 값을 고정시킨다.</li>
<li><code>setter</code>의 사용을 금지하여 외부에서 객체의 멤버 변수에 접근하지 못하도록 막는다.</li>
</ul>
<p>위의 내용을 토대로 ImmutableAddress 클래스를 만들면 다음과 같다.</p>
<pre><code class="language-java">public class ImmutableAddress {
    private final String value; // final을 통해 value 필드를 고정

    public ImmutableAddress(String value) {
        this.value = value;
     }

     public String getValue() {
         return value;
     }

    @Override
     public String toString() {
         return &quot;Address{&quot; +
                 &quot;value=&#39;&quot; + value + &#39;\&#39;&#39; +
                 &#39;}&#39;;
     }
}</code></pre>
<p>ImmutableAddress의 특징은 오직 생성자를 통해서만 value 필드에 접근하여 값을 저장할 수 있고, 한번 저장된 value는 final 키워드에 의해 변경이 불가능하다는 점이다.      </p>
<h3 id="불변객체에서-값을-변경하려면-어떻게-해야할까">불변객체에서 값을 변경하려면 어떻게 해야할까?</h3>
<p>불변 객체를 사용하지만 그래도 값을 변경 해야하는 메서드가 존재한다면 어떻게 해야할까?
기존의 값에 새로운 값을 더하는 <code>add()</code>가 존재하는 클래스를 만들어보자</p>
<pre><code class="language-java">public class ImmutableObj {

    private final int value;

    public MutableObj(int value) {
        this.value = value
    }

    public int getValue() {
        return value;
    }

    public ImmutableObj add(int addValue) {
        int value = value + addValue;
        return new ImmutableObj(value)
    }
}</code></pre>
<p>ImmutableObj 클래스의 add 메서드를 살펴보자. 메서드의 타입을 ImmutableObj 타입으로 생성하여 기존의 value와 addValue를 더한 값을 매개변수로 하는 새로운 ImmutalbleObj의 객체를 반환한다. 이렇게 메서드를 생성하면 기존의 객체의 변경없이 동일한 타입의 새로운 객체를 반환하여 새로운 결과를 만들 수 있다. </p>
<pre><code class="language-java">ImmutableObj obj1 = new ImmutableObj(10);
ImmutableObj obj2 = obj1.add(20);</code></pre>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/5ed7b18c-db8e-4ec2-9a62-b78631f43333/image.png" alt=""></p>
<h3 id="정리">정리</h3>
<ol>
<li>참조형인 객체의 경우 사이드 이펙트가 발생하여 특정 부분의 변경사항이 의도하지 않은 부분의 변경을 발생시킬수 있다.</li>
<li>사이드 이펙트를 방지하기 위해 <code>불변객체</code>를 사용해야한다.<ul>
<li><code>final</code> 키워드를 통해 필드를 하나의 값으로 고정시키기</li>
<li><code>setValue</code>(setter)의 사용을 금지하여 외부에서 객체의 필드로의 직접 접근을 막기</li>
</ul>
</li>
<li>불변객체 내에서 변경 가능한 곳이 존재하도록 하려면 해당 불변객체 타입의 메서드를 통해 새로운 객체를 반환해야한다.</li>
</ol>
<h4 id="불변객체가-중요한-이유가-뭘까">불변객체가 중요한 이유가 뭘까</h4>
<ul>
<li>자주 사용하는 <code>String</code>, 래퍼 클래스(<code>Integer</code>), <code>LocalDate</code>등 자바가 기본으로 제공하는 수많은 클래스가 불변 객체이기에 불변 객체의 기본 원리를 아는 것이 매우 중요하다. </li>
</ul>
<p>즉, <code>String</code>, 래퍼 클래스, <code>LocalDate</code>(시간관련 클래스)는 모두 위의 불변 객체와 동일한 형태로 설계가 되어 있음을 알 수 있고 왜 해당 클래스들의 특정 메서드의 반환값을 새로운 동일한 불변 객체 타입으로 저장되어야 하는지 알 수 있을 것이다. </p>
<p>다음은 <code>래퍼 클래스</code>에 대해 알아보자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] Object 클래스]]></title>
            <link>https://velog.io/@usnijee_2/JAVA-Object-%ED%81%B4%EB%9E%98%EC%8A%A41</link>
            <guid>https://velog.io/@usnijee_2/JAVA-Object-%ED%81%B4%EB%9E%98%EC%8A%A41</guid>
            <pubDate>Sat, 30 Nov 2024 08:27:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡object-클래스">💡Object 클래스</h2>
<h3 id="javalang-패키지">java.lang 패키지</h3>
<p>java.lang 패키지는 java가 제공하는 가장 기본이 되는 라이브러리로, java를 이루는 가장 기본적인 클래스들의 모음이라고 보면 된다. java.lang 패키지의 대표적인 클래스는 다음과 같다.</p>
<ul>
<li><code>Object</code> : 모든 java 클래스의 부모 클래스</li>
<li><code>String</code> : 문자열 클래스</li>
<li><code>Integer</code>, <code>Long</code>, <code>Double</code> : 래퍼 타입 클래스 (기본형 데이터를 객체화한 것)</li>
<li><code>Class</code> : 클래스의 메타 정보에 관한 클래스</li>
<li><code>System</code> : 시스템과 관련된 기본 기능 제공 </li>
</ul>
<h3 id="object-클래스">Object 클래스</h3>
<p>java에서 항상 최상위 클래스는 <code>Object</code> 클래스이다. 즉, extends로 직접 부모 클래스를 언급하지 않았다는 것은 Object 클래스를 상속 받고 있음을 나타낸다. </p>
<h4 id="묵시적-상속-vs-명시적-상속">묵시적 상속 vs 명시적 상속</h4>
<p>위에서 언급했듯이, 직접 부모 클래스를 언급하지 않았다는 것은 &#39;묵시적 상속&#39;을 의미한다. </p>
<ul>
<li><code>묵시적(Implicit)</code> : 개발자가 코드에 직접 기술하지 않아도 시스템 또는 컴파일러에 의해 자동으로 수행됨을 의미</li>
<li><code>명시적(Explicit)</code> : 개발자가 코드에 직접 기술하여 작동하는 것을 의미 
<img src="https://velog.velcdn.com/images/usnijee_2/post/fd071044-8702-40c7-9df6-9646978ff3ed/image.png" alt=""></li>
</ul>
<h3 id="object-클래스의-기능">Object 클래스의 기능</h3>
<h4 id="공통-기능-제공">공통 기능 제공</h4>
<ul>
<li>객체의 정보 제공, 객체간의 비교(동일성, 동등성), 객체가 어떤 클래스로 만들어졌는지 확인하는 기능은 모든 객체에게서 공통적으로 필요한 기능</li>
<li>위의 기능이 공통 기능이 아닌 사용자 정의 기능이었다면 일관성이 없고 관리도 어려움</li>
<li>Object가 제공하는 공통 기능<ul>
<li><code>toString()</code> : 객체의 정보 제공 메서드</li>
<li><code>equals()</code> : 객체의 같음을 비교하는 메서드 (동등성 비교)</li>
<li><code>getClass()</code> : 객체의 클래스 정보를 제공하는 메서드</li>
<li>이외의 다른 메서드 </li>
</ul>
</li>
</ul>
<h4 id="다형성의-기본-구현">다형성의 기본 구현</h4>
<p>Object는 모든 클래스의 부모 클래스이다. 즉, Object는 다형적 참조에 의해 모든 객체를 참조할 수 있음을 의미한다. 타입이 다른 객체들을 어딘가에 보관해야한다? Object 타입에 보관하면 된다. </p>
<h3 id="object-다형성">Object 다형성</h3>
<p>다음 예시를 보자</p>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/93a32dbb-a2b8-487f-9ff1-b1cda004a42a/image.png" alt=""></p>
<pre><code class="language-java">class Car {
    public void move() {
        System.out.println(&quot;자동차 이동&quot;);
    }
}

class Dog {
    public void sound() {
        System.out.println(&quot;멍멍&quot;);
    }
}

public class ObjectPolyExample1 {

    public static void main(String[] args) {
        Dog dog = new Dog();
        Car car = new Car();

        action(dog);
        action(car);
    }

    private static void action(Object obj) {
        // obj.sound(); // 컴파일 오류 , Object는 sound() 없음
        // obj.move(); // 컴파일 오류, Object는 move() 없음

        //객체에 맞는 다운캐스팅 필요
        if (obj instanceof Dog dog) {
            dog.sound();
        } else if (obj instance of Car car) {
            car.move();
        }
     }
}</code></pre>
<p>위의 코드를 통해 Object 클래스의 장단점에 대해 알아보면 다음과 같다.</p>
<h4 id="ojbect-다형성의-장점">Ojbect 다형성의 장점</h4>
<pre><code class="language-java">private static void action(Object obj) { &#39;&#39;&#39; }</code></pre>
<p>action 메서드의 매개변수는 Object 타입이다. 즉, Object는 모든 객체의 부모이기에 어떤 객체든지 인자로 전달이 가능함을 나타낸다. </p>
<h4 id="object-다형성의-한계">Object 다형성의 한계</h4>
<pre><code class="language-java">action(dog) //main에서 dog 전달
private static void action(Object obj) {
    obj.sound(); //컴파일 오류, Object는 sound()가 없다.
}</code></pre>
<p>action() 메서드 안에서 obj.sound()를 호출하면 당연히 Object 클래스에는 sound()메서드가 없기에 오버라이딩이 불가능하고, sound()를 사용하기 위해서는 다운 캐스팅을 진행해야 한다. 
<img src="https://velog.velcdn.com/images/usnijee_2/post/149e2413-96b6-448a-b872-ce220c9fee9a/image.png" alt=""></p>
<p>정리하면,</p>
<ul>
<li>Object 클래스는 모든 클래스의 부모 클래스이기에 모든 객체를 상대로 다형적 참조가 가능하다</li>
<li>Object 타입을 통해 전달 받은 객체를 호출 하기위해서는 해당 객체로 다운 캐스팅이 필요하다.</li>
</ul>
<p>다형적 참조는 OOP의 핵심중 하나로 제대로 활용하기 위해서는 <code>다형적 참조</code>와 <code>메서드 오버라이딩</code>을 함께 사용해야한다. 물론 Object가 제공하는 toString(), equals(), getClass()와 같은 메서드는 오버라이딩이 가능하나 특정 객체가 갖고 있는 고유한 기능은 Object 클래스에 정의되어 있지 않기에 다운 캐스팅이 수반되어야 하고 이는 Object 클래스의 다형성이 한계가 있음을 보여준다. 그렇다면 Object 클래스는 언제 최대로 활용할 수 있을까?</p>
<h3 id="obejct-배열">Obejct 배열</h3>
<p>Object는 모든 객체를 담을 수 있다. 따라서 Object 타입의 배열은 모든 객체를 담을 수 있는 배열이 된다. </p>
<pre><code class="language-java">public class ObjectPolyExample2 {
    public static void main(String[] args) {
         Dog dog = new Dog();
         Car car = new Car();
         Object object = new Object(); //Object 인스턴스도 만들 수 있다.

         Object[] objects = {dog, car, object};
         size(objects);
     }

     private static void size(Object[] objects) {
         System.out.println(&quot;전달된 객체의 수는: &quot; + objects.length);
     }
}</code></pre>
<p>위의 Object 배열을 메모리 관점에서 보면 다음과 같다. 
<img src="https://velog.velcdn.com/images/usnijee_2/post/99d40479-75ca-4b33-8f1a-cf173d6eb5f1/image.png" alt=""></p>
<h3 id="tostring">toString()</h3>
<p>Object 클래스의 toString()메서드는 객체의 정보를 제공하는 역할을 한다. 기본적으로 패키지를 포함한 객체 이름과 객체 참조값을 제공한다. 실행 결과 예시는 다음과 같다.</p>
<pre><code class="language-java">java.lang.Object@a09ee92 // 패키지명+객체이름+객체 참조값</code></pre>
<p>Object 클래스의 toString()은 위와 같은 결과를 제공하는데, 위와 같이 객체의 참조값이 아닌 사용자 정의에 의해 특정 형태로 객체의 정보를 제공하고 싶다면 모두의 부모인 Object를 상속받아 오버라이딩을 진행하여 toString()을 재정의하면 된다. </p>
<pre><code class="language-java">public class Dog {
    private String dogName;
     private int age;

     public Dog(String dogName, int age) {
         this.dogName = dogName;
         this.age = age;
     }

   @Override
   public String toString() { // toString() 재정의 
      return &quot;Dog{&quot; +
              &quot;dogName=&#39;&quot; + dogName + &#39;\&#39;&#39; +
              &quot;, age=&quot; + age +
              &#39;}&#39;;
     }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/d185e9db-6a85-4c2a-9a38-055f33fb2677/image.png" alt=""></p>
<p>기본적으로 toString()은 객체의 메모리 참조값을 제공하는데, 오버라이딩을 통해 재정의할 경우 다른 정보를 출력할 수 있다. 이런 경우 참조값을 따로 출력하고 싶으면 다음과 같이 진행하면 된다. </p>
<pre><code class="language-java">String refValue = Integer.toHexString(System.identifyHashCode(dog1))
System.out.println(&quot;refValue = &quot; + refValue);  // refValue = 72ea2f77</code></pre>
<h3 id="object와-ocp">Object와 OCP</h3>
<pre><code class="language-java">public class ObjectPrinter {
    public static void print(Object obj) {
         String string = &quot;객체 정보 출력: &quot; + obj.toString();
         System.out.println(string);
     }
}</code></pre>
<h4 id="ocpopen-closed-principle">OCP(Open-Closed Principle)</h4>
<p>OCP는 객체지향 설계 원칙 중 하나로, &quot;클래스는 확장에 열려 있고 변경에는 닫혀 있어야 한다&quot;는 개념을 의미한다.</p>
<ul>
<li><code>Open</code> : 확장에 열려 있다<ul>
<li>새로운 기능이나 요구사항이 추가될 때, 기존 코드를 변경하지 않고도 확장이 가능해야 한다</li>
</ul>
</li>
<li><code>Closed</code> : 수정에 닫혀 있다<ul>
<li>기존 코드는 안정적이어야 하며, 변경하지 않아도 새로운 요구사항을 반영할 수 있도록 설계되어야 한다.</li>
</ul>
</li>
</ul>
<h4 id="object와-ocp-1">Object와 OCP</h4>
<ul>
<li><code>Open</code> : 새로운 클래스를 추가하고, toString()을 오버라이딩하여 기능 확장이 가능하다</li>
<li><code>Closed</code> : 새로운 클래스를 계속해서 추가해도 Object와 toString()을 사용하는 클라이언트 코드인 ObjectPrinter은 변경없이 사용 가능</li>
</ul>
<h3 id="equals---동일성과-동등성">equals() - 동일성과 동등성</h3>
<p>java는 두 객체의 &#39;같음&#39;을 2가지로 표현한다.</p>
<ol>
<li><code>동일성(Identity)</code> : <code>==</code>연산자를 통해 두 객체가 동일한 메모리를 가리키고 있는지 확인 </li>
<li><code>동등성(Equality)</code> : <code>equals()</code> 메서드를 사용하여 두 객체가 논리적으로 동등한지 확인   </li>
</ol>
<pre><code class="language-java">User a = new User(&quot;id-100&quot;) //참조 x001
User b = new User(&quot;id-100&quot;) //참조 x002</code></pre>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/4c1ebe2b-72fa-41c4-9bca-8e843be3aa84/image.png" alt=""></p>
<p>위의 예시의 경우 물리적으로 다른 객체에 존재하지만 논리적으로 동일한 회원 번호를 갖고 있기에 같은 회원으로 볼 수 있다. 따라서 동일성은 false이지만 동등성은 true인 것이다. </p>
<p>예제를 통해 동일성과 동등성에 대해 알아보자 </p>
<pre><code class="language-java">public class UserV1 {

    private String id;
    //constructor
    public UserV1(String id) {
        this.id = id;
     }
}

public class EqualsMainV1 {

    public static void main(String[] args) {
        UserV1 user1 = new UserV1(&quot;id-100&quot;);
         UserV1 user2 = new UserV1(&quot;id-100&quot;);

         System.out.println(&quot;identity = &quot; + (user1 == user2));  //false
         System.out.println(&quot;equality = &quot; + user1.equals(user2)); //false
     }
}</code></pre>
<p>id값이 id-100으로 동일한 user1과 user2의 identity(동일성)과 equality(동등성)을 확인한 것으로, 위에서 언급한 동일성과 동등성 개념에 의하면 true, false가 출력되어야 한다. 하지만 equality(동등성)는 id값이 동일함에도 불구하고 false가 나왔다. 어떻게 된것일까?</p>
<p><code>Object.equals()</code>의 소스 코드를 살펴보자.</p>
<pre><code class="language-java">public boolean equals(Object obj) {
    return (this == obj);
}</code></pre>
<p>소스코드에 의하면, <code>equals</code>는 <code>this==obj</code> 즉, 동일성을 기반으로 작성되어 있음을 확인할 수 있다. 따라서, 서로 다른 객체인 user1과 user2의 참조값은 다르기에 equals()를 사용한 비교도 false가 출력될 수 밖에 없다. </p>
<p><strong>그렇다면 동등성 비교는 어떻게 해야하는 걸까?</strong></p>
<p>동등성(equality) 비교를 논리적으로 생각해보면 동등성 비교는 비교 순간마다 비교의 기준이 달라질 수 있음을 알아야한다. 즉, 동등함의 기준이 id일수도 있고 주민번호일수도 있으며 연락처일수도 있는 것이다. 따라서, 동등성 비교를 해야한다면 <code>Object.equals()</code>를 오버라이딩을 통한 재정의를 해야한다. </p>
<p><code>equals()</code>의 경우 intellij에서 제공하는 <code>Alt+Insert</code>를 통해 제공하는 equals()를 보편적으로 사용한다.</p>
<pre><code class="language-java">public class UserV2 {
    private String id;

     public UserV2(String id) {
         this.id = id;
     }

    //equals() 재정의 
     @Override
     public boolean equals(Object obj) {
         UserV2 user = (UserV2) obj;
         return id.equals(user.id);
     }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 인터페이스]]></title>
            <link>https://velog.io/@usnijee_2/JAVA-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@usnijee_2/JAVA-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Mon, 25 Nov 2024 14:23:17 GMT</pubDate>
            <description><![CDATA[<h2 id="💡인터페이스interface">💡인터페이스(Interface)</h2>
<h3 id="인터페이스란">인터페이스란?</h3>
<p>이전 포스팅에서 &#39;추상 클래스&#39;에 대해 알아보았고, 순수 추상 클래스는 클래스를 이루는 모든 메서드가 추상 메서드인 추상 클래스를 의미한다. 인터페이스는 순수 추상 클래스를 더 편리하게 사용가능한 기능이다.</p>
<h4 id="순수-추상-클래스">순수 추상 클래스</h4>
<pre><code class="language-java">// 순수 추상 클래스
public abstract class AbstractAnimal {
    public abstract void sound();
    public abstract void move();
}</code></pre>
<ul>
<li>인스턴스 생성 불가능</li>
<li>상속시 모든 메서드를 overriding 해야한다</li>
<li>주로 다형성을 위해 사용 </li>
</ul>
<h4 id="인터페이스">인터페이스</h4>
<p>인터페이스는 <span style='background-color:yellow;color:black'>abstract</span>가 아닌 <span style='background-color:yellow;color:black'>interface</span> 키워드를 사용하면 된다. 또한, 인터페이스는 public abstract 키워드의 생략이 가능하고 권장된다.</p>
<pre><code class="language-java">public interface InterfaceAnimal {
    void sound();
    void move();
}</code></pre>
<ul>
<li>인터페이스는 순수 추상 클래스와 같다 </li>
<li>메서드는 모두 추상 메서드인데, public abstract 키워는 생략 가능하고, 권장된다</li>
<li>인터페이스는 다중 상속(구현)이 가능하다 </li>
</ul>
<h4 id="인터페이스와-멤버변수">인터페이스와 멤버변수</h4>
<pre><code class="language-java">public interface InterfaceAnimal {
    // public static final double MY_PI = 3.14;
    double MY_PI = 3.14;
}</code></pre>
<p>인터페이스에서 멤버변수는 &#39;상수&#39;이다. 또한 인터페이스의 상수는 메서드와 동일하게 public static final이 생략이 가능하고 권장된다. </p>
<h4 id="예제">예제</h4>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/d0b234f3-5ba0-47ab-ae9c-7f288510dc01/image.png" alt=""></p>
<pre><code class="language-java">public interface InterfaceAnimal {
    void sound();
    void move();
}

public class Dog implements InterfaceAnimal {
    @Override
    public void sound() {
        System.out.println(&quot;멍멍&quot;);
    }

    @Override
    public void move() {
        System.out.println(&quot;개 이동&quot;);
    }
}

public class Cat implements InterfaceAnimal {
    @Override
    public void sound() {
        System.out.println(&quot;냐옹&quot;);
    }

    @Override
    public void move() {
        System.out.println(&quot;고양이 이동&quot;);
    }
}

public class Cow implements InterfaceAnimal {
    @Override
    public void sound() {
        System.out.println(&quot;음메&quot;);
    }

    @Override
    public void move() {
        System.out.println(&quot;소 이동&quot;);
    }
}
// -----------------------------Main Method----------------------------------

public class InterfaceMain {
    public static void main(String[] args) { 

        // 인터페이스 생성 불가
        // InterfaceAnimal interfaceMain1 = new InterfaceAnimal();

        Cat cat = new Cat();
        Dog dog = new Dog();
        Caw caw = new Caw();

        soundAnimal(cat);
        soundAnimal(dog);
        soundAnimal(caw);

    }

    private static void soundAnimal(InterfaceAnimal animal) {
        System.out.println(&quot;동물 소리 테스트 시작&quot;);
        animal.sound();
        System.out.println(&quot;동물 소리 테스트 종료&quot;);
    }
}</code></pre>
<p>위의 예제를 보면 인터페이스와 순수 추상 클래스의 차이점이 거의 없음을 알수있다. 실제로 메모리 구조상 클래스, 추상 클래스, 인터페이스의 차이는 없다. </p>
<h3 id="인터페이스-왜-사용할까">인터페이스, 왜 사용할까?</h3>
<p>형태만 보면 인터페이스는 abstract키워드만 사용하지 않을 뿐 순수 추상 클래스와 동일한 기능을 한다. 그렇다면 왜 순수 추상 클래스를 사용하지 않고 인터페이스를 사용해야할까?</p>
<ul>
<li><p><span style='background-color:gray;color:white'>제약</span></p>
<ul>
<li>인터페이스는 &#39;반드시 구현해야한다&#39;라는 제약사항이 있다. 즉, 인터페이스안의 메서드는 반드시 순수 추상 메서드 형태이어야하고 자식 클래스는 반드시 해당 메서드를 오버라이딩 해야한다. _순수 추상 클래스와의 차이점은 순수 추상 클래스는 미래 특정 시점에 순수 추상 메서드가 아닌 일반 메서드를 추가하여 일반 추상 클래스로의 전환 가능성이 있다는 점_이다. 인터페이스를 사용하여 이런 점을 원천 방지가 가능하다</li>
</ul>
</li>
<li><p><span style='background-color:gray;color:white'>다중구현</span></p>
<ul>
<li>java에서 클래스 상속은 하나의 부모에게서만 가능하다. 하지만 인터페이스의 경우 자식 입장에서 여러 인터페이스로부터 상속이 가능하다. </li>
</ul>
</li>
</ul>
<h4 id="상속-vs-구현">상속 vs 구현</h4>
<p>부모 클래스의 기능을 자식 클래스가 상속 받을 때, 클래스 상속이라고 표현한다. 하지만 부모 인터페이스의 내용을 자식 클래스가 상속 받을 때는 클래스 &#39;구현&#39;이라고 표현한다. 인터페이스는 모든 메서드가 순수 추상 메서드 즉, 본문이 전혀 없는 메서드이다. 따라서 부모 인터페이스로부터 물려받을 기능은 존재하지 않고 오히려 자식 클래스에서 이를 오버라이딩하여 기능을 &#39;구현&#39;해야한다. 따라서 인터페이스를 &#39;구현&#39;한다고 표현한다. </p>
<h3 id="인터페이스의-다중-구현">인터페이스의 다중 구현</h3>
<h4 id="다이아몬드-문제">다이아몬드 문제</h4>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/74103238-e321-4c85-bd03-183fe41cf579/image.png" alt="">
만약 위의 그림과 같이 AirplaneCar클래스가 Airplane, Car 두 개의 부모 클래스로 부터 다중 상속을 받는다고 가정하자. 두 개의 부모 클래스에는 모두 move() 메서드가 존재하고 이를 자식 클래스에서 사용하려고 한다면 어떤 부모의 메서드를 사용해야할까? 이를 다이아몬드 문제라고 하고, 이러한 이유로 다중 상속은 허용되지 않는다. 하지만 인터페이스의 다중 구현을 통해 이러한 문제를 피하고 구현 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/f954440d-3cf0-4088-8e30-302e878ed756/image.png" alt="">
InterfaceA, InterfaceB는 모두 methodCommon()을 갖는다. 자식 클래스인 Child 입장에서는 두 인터페이스를 구현해야하는데, 상속의 경우 두 부모 클래스의 methodCommon()중 어떤 메서드를 사용할 것인가의 다이아몬드 문제가 발생한다. 하지만 인터페이스의 메서드는 기능을 가지지 않기에 해당 인터페이스를 구현하는 자식 클래스에서 해당 기능을 구현해야한다. 즉, InterfaceA, InterfaceB는 모두 methodCommon()을 제공하지만 최종구현은 Child에서 구현되어야 하며, 오버라이딩에 의해 호출도 Child의 methodCommon()이 우선 호출되기에 다이아몬드 문제가 발생하지 않는다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 추상 클래스]]></title>
            <link>https://velog.io/@usnijee_2/JAVA-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@usnijee_2/JAVA-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Mon, 25 Nov 2024 09:35:01 GMT</pubDate>
            <description><![CDATA[<h2 id="💡추상-클래스abstract-class">💡추상 클래스(Abstract Class)</h2>
<h3 id="추상-클래스란">추상 클래스란?</h3>
<p>추상 클래스는 부모 클래스는 제공하지만 실제로 객체를 생성할 수 없는 클래스를 의미한다. 추상적인 개념을 제공하는 의미로 객체 생성이 불가능하다. </p>
<pre><code class="language-java">public abstract class AbstractAnimal {}</code></pre>
<ul>
<li><span style='background-color:yellow;color:black'>abstract</span> 키워드를 통해 추상 클래스임을 표시할 수 있다.</li>
<li>추상 클래스는 기존 클래스와 같지만, <span style='background-color:yellow;color:black'>new AbstractAnimal();</span> 와 같이 객체를 직접 생성할 수 없다는 제약이 추가된다.</li>
</ul>
<h3 id="추상-메서드">추상 메서드</h3>
<p>부모 클래스를 상속 받는 자식 클래스에서 반드시 오버라이딩 해야하는 메서드를 부모 클래스에 정의할 수 있고, 이를 추상 메서드라고 한다. </p>
<pre><code class="language-java">public abstract void sound();</code></pre>
<ul>
<li>추상 메서드는 추상 클래스와 동일하게, 생성시 <span style='background-color:yellow;color:black'>abstract</span> 키워드를 붙이면 된다.</li>
<li>부모 클래스의 메서드중 하나라도 추상 메서드인 경우 해당 부모는 반드시 추상 클래스이어야 한다.</li>
<li>추상 메서드는 _<strong>자식 클래스가 반드시 오버라이딩 해야하는 대상</strong>_으로 본문(body)가 존재하지 않는 메서드이다. </li>
</ul>
<p>예시를 살펴보자. 부모 클래스 AbstractAnimal과 자식클래스들을 구현해보자.
<img src="https://velog.velcdn.com/images/usnijee_2/post/e31e98f7-dca8-4e38-9486-cd8d49f72ec8/image.png" alt=""></p>
<pre><code class="language-java">public abstract class AbstractAnimal {

    public abstract void sound();  

    public void move() {
        System.out.println(&quot;동물이 움직입니다.&quot;);
    }
}

public class Dog extends AbstractAnimal {
    @Override
    public  void sound() {
        System.out.println(&quot;멍멍&quot;);
    }
}

public class Cat extends AbstractAnimal {
    @Override
    public  void sound() {
        System.out.println(&quot;냐옹&quot;);
    }
}

public class Cow extends AbstractAnimal {
    @Override
    public  void sound() {
        System.out.println(&quot;음메&quot;);
    }
}
// -----------------------main method---------------------------------
public class AbstractMain {
    public static void main(String[] args) {
     //추상클래스 생성 불가
     //AbstractAnimal animal = new AbstractAnimal();

     Dog dog = new Dog();
    Cat cat = new Cat();
     Caw caw = new Caw();

     cat.sound();
     cat.move();

     soundAnimal(cat);
     soundAnimal(dog);
     soundAnimal(caw);
 }

 //동물이 추가 되어도 변하지 않는 코드
 private static void soundAnimal(AbstractAnimal animal) {
     System.out.println(&quot;동물 소리 테스트 시작&quot;);
     animal.sound();
     System.out.println(&quot;동물 소리 테스트 종료&quot;);
 }

}</code></pre>
<p>위의 코드에서 <strong>컴파일 에러가 발생할 수 있는 경우</strong>를 정리해보자.</p>
<ol>
<li>추상 클래스의 인스턴스를 직접 생성할 경우 <pre><code class="language-java">AbstractAnimal animal = new AbstractAnimal();
</code></pre>
</li>
</ol>
<p>//java: poly.ex3.AbstractAnimal is abstract; cannot be instantiated</p>
<pre><code>

2. 추상 메서드를 오버라이딩하지 않은 경우 
```java
public class Dog extends AbstractAnimal {

/*
     @Override
     public void sound() {
         System.out.println(&quot;멍멍&quot;);
     }
*/

}

// java: poly.ex3.Dog is not abstract and does not override abstract method sound()in poly.ex3.AbstractAnimal</code></pre><p>즉, 결론적으로 추상 클래스는 일반 클래스에 &#39;제약&#39;이 추가된 클래스라고 볼 수 있다. 이를 제외하고는 모든 것이 동일하다. 해당 제약으로 인해 실수로 클래스 객체를 직접 생성하거나 sound()를 오버라이딩 하지 않는 문제를 근본적으로 해결이 가능하다고 볼 수 있다. 
<img src="https://velog.velcdn.com/images/usnijee_2/post/b83252fc-5dab-4ad8-b7da-a3efde0f26ca/image.png" alt=""></p>
<h3 id="순수-추상-클래스">순수 추상 클래스</h3>
<p>위의 AbstractAnimal 추상 클래스를 살펴보면, 추상 메서드인 sound()와 일반 메서드인 move()로 이루어져 있음을 알 수 있다. 
순수 추상 클래스란 <strong>모든 메서드가 추상 메서드인 추상 클래스를 의미한다.</strong> </p>
<pre><code class="language-java">public abstract class AbstractAnimal {
    public abstract void sound();
    public abstract void move();
}</code></pre>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/c615b058-31fc-4614-bbf1-c802618a39bc/image.png" alt=""></p>
<h4 id="순수-추상-클래스의-특징">순수 추상 클래스의 특징</h4>
<ul>
<li>인스턴스를 생성 할 수 없다.</li>
<li>상속시 자식은 모든 메서드를 오버라이딩 해야 한다</li>
<li>주로 다형성을 위해 사용된다 </li>
</ul>
<p>순수 추상 클래스는 특별한 로직은 없고 다형성을 위한 껍데기 역할을 한다. 순수 추상 클래스의 모든 메서드는 자식 클래스가 반드시 오버라이딩을 진행해야 하는데 이는 특정 규격을 맞춰 구현하는 과정처럼 느껴진다.</p>
<p>다음 포스팅에서는 순수 추상 클래스를 더 편리하게 사용하기 위한 &#39;인터페이스&#39;에 대해 포스팅 할 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 다형성2]]></title>
            <link>https://velog.io/@usnijee_2/JAVA-%EB%8B%A4%ED%98%95%EC%84%B12-w1lxyj9r</link>
            <guid>https://velog.io/@usnijee_2/JAVA-%EB%8B%A4%ED%98%95%EC%84%B12-w1lxyj9r</guid>
            <pubDate>Sun, 27 Oct 2024 03:28:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 김영한님의 JAVA 강의를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡다형성의-활용">💡다형성의 활용</h2>
<p>앞선 포스팅 <a href="https://velog.io/@usnijee_2/JAVA-%EB%8B%A4%ED%98%95%EC%84%B11">다형성1</a>에서는 다형성의 의미와 핵심기능을 살펴보았다. 이번 포스팅에서는 다형성의 목적과 활용에 대해서 포스팅한다. </p>
<p>다형성의 핵심기능 두 가지 다시 짚고 넘어가보자.</p>
<ol>
<li><strong>다형적 참조</strong> , &quot;부모는 자식을 품을 수 있으나 자식은 부모를 품지 못한다&quot;</li>
<li><strong>메서드 오버라이딩</strong> , &quot;자식 클래스에서 부모 클래스의 매서드를 재정의하여 사용할 수 있으며 해당 메서드는 가장 높은 우선순위를 갖는다&quot;</li>
</ol>
<p>다형성의 필요성을 설명하기 위해 다형성을 사용하지 않은 코드와 다형성을 사용한 코드를 비교해보자.</p>
<pre><code class="language-java">public class Dog {
    public void sound(){
        System.out.println(&quot;멍멍!&quot;);
    }
}

public class Cat {
    public void sound(){
        System.out.println(&quot;냐옹~&quot;);
    }
}

public class Cow {
    public void sound(){
        System.out.println(&quot;음메~~~&quot;);
    }
}

public class AnimalSoundMain {
    public static void main(String[] args) {

        Dog dog = new Dog();
        Cat cat = new Cat();
        Caw caw = new Caw();

        System.out.println(&quot;동물 소리 테스트 시작&quot;);
        dog.sound();
        System.out.println(&quot;동물 소리 테스트 종료&quot;);

        System.out.println(&quot;동물 소리 테스트 시작&quot;);
        cat.sound();
        System.out.println(&quot;동물 소리 테스트 종료&quot;);

        System.out.println(&quot;동물 소리 테스트 시작&quot;);
        caw.sound();
        System.out.println(&quot;동물 소리 테스트 종료&quot;);
    }
}</code></pre>
<p>위의 코드는 개,고양이,소의 울음소리를 출력문으로 구현하기 위해 Dog,Cat,Cow 클래스를 구현한 것이다. main메서드를 보면 클래스에 대한 객체를 생성하고 해당 클래스에 대한 sound 메서드를 순차적으로 호출하여 각 동물에 대한 울음소리를 구현한다.</p>
<p>근데 더 많은 종류의 동물들이 추가되면 어떻게 될까...?</p>
<ul>
<li>해당 동물에 대한 클래스를 형성해야한다</li>
<li>해당 동물들에 대한 객체를 생성해야한다</li>
<li>main 메서드에 동물 클래스가 추가될 때마다 출력문도 추가해야한다</li>
</ul>
<p>즉, 새로운 클래스가 추가될 때마다 중복 코드가 계속해서 늘어난다. 중복 코드를 없애기 위해 두 가지 방법을 사용해보자.</p>
<ol>
<li><p>메서드 생성</p>
<pre><code class="language-java">private static void soundCaw(Cow cow) {

System.out.println(&quot;동물 소리 테스트 시작&quot;);
caw.sound();
System.out.println(&quot;동물 소리 테스트 종료&quot;);
</code></pre>
</li>
</ol>
<p>}</p>
<pre><code>2. for문과 배열 사용
```java
Caw[] cawArr = {cat, dog, caw}; //컴파일 오류 발생!

System.out.println(&quot;동물 소리 테스트 시작&quot;);

for (Caw caw : cawArr) {
    cawArr.sound();
    }</code></pre><p>첫 번째, 메서드를 생성하는 것은 해당 메서드만 호출하면 출력문과 sound()메서드를 호출하는 부분의 중복을 제거 할 수 있다. 하지만 매개변수로 특정 클래스 타입을 받아야 하기에 해당 메서드도 클래스의 개수 만큼 호출해야한다는 단점이 존재한다.</p>
<p>두 번째, for문과 배열을 사용한 부분은 배열을 생성하는 부분에서 배열의 타입에 의해 컴파일 에러가 발생한다.</p>
<p>두 방법 모두 선언 &#39;타입&#39;에 의해 단점이 존재하고 에러가 발생함을 알 수 있다. 이를 해결하기위해 &#39;공통&#39;의 타입이 필요하다. Animal 부모클래스를 만들고 Dog, Cat, Cow 클래스를 상속을 통해 자식 클래스화 해보면 다음과 같다.
<img src="https://velog.velcdn.com/images/usnijee_2/post/152a1579-cecb-4a0d-9e75-30583a930769/image.png" alt=""></p>
<pre><code class="language-java">public class Animal {
    public void sound() {
        System.out.println(&quot;동물 울음 소리&quot;);
    }
}

public class Dog extends Animal    {
    @Override
    public void sound(){
        System.out.println(&quot;멍멍!&quot;);
    }
}

public class Cat extends Animal    {
    @Override
    public void sound(){
        System.out.println(&quot;냐옹~&quot;);
    }
}

public class Cow extends Animal    {
    @Override
    public void sound(){
        System.out.println(&quot;음메~~~&quot;);
    }
}

public class AnimalPolyMain3 {
    public static void main(String[] args) {
        Animal[] animalArr = {new Dog(), new Cat(), new Caw()};
        for (Animal animal : animalArr) {
            soundAnimal(animal);
        }
    }

    //동물이 추가 되어도 변하지 않는 코드
    private static void soundAnimal(Animal animal) {
        System.out.println(&quot;동물 소리 테스트 시작&quot;);
        animal.sound();
        System.out.println(&quot;동물 소리 테스트 종료&quot;);
    }</code></pre>
<p>위 코드의 핵심은 다음과 같다</p>
<pre><code class="language-java">Animal[] animalArr = {new Dog(), new Cat(), new Caw()};
for (Animal animal : animalArr) {
    soundAnimal(animal);
}</code></pre>
<ul>
<li>다형적 참조에 의해 Animal 타입의 변수에 자식 클래스 객체 저장</li>
<li>배열과 for each문을 사용하여 코드 간결화</li>
<li>soundAnimal() 메서드를 for each문을 통해 반복 호출하여 출력문과 오버라이딩 된 메서드를 호출하여 Dog, Cat, Cow의 고유 기능을 사용 즉, 하나의 동물을 받아서 로직을 처리 </li>
</ul>
<h4 id="장점">장점</h4>
<p>위 처럼 다형성을 고려한 코드는 새로운 &#39;동물&#39;을 추가하여도 soundAnimal() 메서드는 변경이 필요없다. 이 부분이 가능한 이유는 동물 마다 (Dog, Cat, Cow) 참조하는 것이 아니라 Animal이라는 부모 클래스만 참조하기 때문에 가능한 것이다. </p>
<h4 id="단점">단점</h4>
<p>위 코드는 다형성을 고려한 유지보수가 용이한 코드이다. 하지만 위 코드에는 두 가지의 문제점이 존재한다.</p>
<p>*<em>1. 부모 클래스 Animal 클래스의 인스턴스를 생성할 문제 *</em></p>
<p>기존의 코드의 문제점은 각 동물 객체를 추가할 때마다 메인 로직을 변경해야 한다는 점이고 이를 해결하기 위해 상속을 도입하여 다형적 참조를 진행하였다. 하지만, Animal 부모 클래스는 오직 다형적 참조을 위해 생성 된 것이지 직접 인스턴스를 생성하여 사용 할 이유도 없고 혼란을 야기한다.</p>
<pre><code class="language-java">  Animal animal = new Animal();</code></pre>
<p>*<em>2. Animal 클래스를 상속 받는 곳에서 sound() 메서드 오버라이딩을 하지 않을 가능성 *</em></p>
<p>Pig라는 새로운 자식 클래스를 추가했다고 가정하자. 근데 개발자가 실수로 오버라이딩 해야하는 메서드를 오버라이딩 하지 않았다면 어떻게 될까? 만약 부모 클래스의 특정 메서드를 오버라이딩하지 않는다면 자식 클래스가 아닌 부모 클래스의 메서드 기능이 호출과 동시에 실행 되어 목적에 맞지 않는 기능을 구현하게 된다. 하지만 진짜 문제점이 이 부분이다. 오류가 발생하지 않는다는 것이다. 당장 Pig 클래스만 보면 큰 문제가 없다고 생각할수도 있으나 코드가 더 복잡해지고 길어진다면 유지보수 측면에서 틀린부분을 고치는 것은 어려울 것이다. 제약을 걸어 줄 수 없을까?</p>
<p>다음 포스팅에서는 다형성의 단점 부분을 보완하는 개념인 추상 클래스와 인터페이스에 대해 포스팅 해보겠다. 끝!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 다형성1]]></title>
            <link>https://velog.io/@usnijee_2/JAVA-%EB%8B%A4%ED%98%95%EC%84%B11</link>
            <guid>https://velog.io/@usnijee_2/JAVA-%EB%8B%A4%ED%98%95%EC%84%B11</guid>
            <pubDate>Tue, 22 Oct 2024 15:14:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>우선, 글을 작성하기 전 이 글의 모든 내용은 <strong>김영한님의 JAVA 강의</strong>를 바탕으로 함을 알립니다.</p>
</blockquote>
<h2 id="💡-다형성polymorphism이란">💡 다형성(Polymorphism)이란?</h2>
<blockquote>
<p>다형성(Polymorphism)은 객체지향 프로그래밍(Object Oriented Programming)의 대표적인 특징 중 하나로 &#39;다양한 형태&#39;를 의미한다. 프로그래밍에서 다형성은 _&#39;<strong>한 객체가 여러 타입의 객체로 취급되는 것</strong>&#39;_을 의미한다. </p>
</blockquote>
<p>다형성은 객체 지향 프로그래밍의 <code>상속</code>과 <code>인터페이스</code>를 기반으로 한다. </p>
<h2 id="💡-다형성의-2가지-핵심이론">💡 다형성의 2가지 핵심이론</h2>
<h3 id="다형적-참조">다형적 참조</h3>
<p>java에서 클래스를 생성하면 클래스의 속성(필드, 메서드)에 접근하기 위해 객체(인스턴스)를 생성해야한다. 객체가 생성되면 힙메모리 영역에 해당 객체의 클래스에 대한 내용이 저장되고, 이때 상속관계가 있다면 부모 클래스와 자식 클래스 모두 하나의 메모리 영역에 저장된다. 우리가 살펴보아할 경우는 &#39;상속&#39;관계가 존재하는 객체이다.</p>
<p>일반적으로, 객체를 생성하면 해당 객체가 생성된 힙메모리영역의 참조값을 해당 클래스 형태의 변수에 저장한다.</p>
<pre><code class="language-java">Parent parent = new Parent(); 
Child child = new Child();</code></pre>
<p>예를들어, new Parent();를 통해 객체를 통해 생성된 메모리의 참조값을 x001이라고 가정하면 다음과 같다고 볼 수 있다.</p>
<pre><code class="language-java">Parent parent = x001;</code></pre>
<p>하지만 java는 다형적 참조에의해 상속관계에 놓인 Parent와 Child 사이에서 부모클래스인 Parent는 Parent와 자식 클래스 Child 모두를 참조할 수 있다. 이 개념이 &#39;다형적 참조&#39;이다. </p>
<pre><code class="language-java">Parent poly = new Child();</code></pre>
<p>즉, 다형적 참조의 핵심은 &quot;부모는 자식을 품을 수 있다&quot;라는 것이다. 이는 &#39;캐스팅&#39;의 개념과 연결된다.</p>
<h4 id="캐스팅">캐스팅</h4>
<p>위에서 언급했듯이, 상속관계가 존재하는 클래스에 대해 객체를 생성하면 메모리 영역에 부모클래스와 자식클래스에 대한 속성이 메모리 영역에 저장된다. 그렇다면 정확히 어떤 클래스를 우선 참조할까? 정답은 ,, 타입에 의존한다는 것이다.</p>
<pre><code class="language-java">Child child = new Child(); // Child 타입 변수 --&gt; Child 클래스를 우선 참조
Parent poly = new Child(); // 다형적 참조 --&gt; Parent 클래스를 우선 참조 </code></pre>
<p>다형적 참조의 경우 부모 타입의 변수에 참조값이 저장되었기에 우선적으로 메모리의 부모 클래스에 대한 속성을 참조하게 되고 직접적으로는 부모 클래스에 대한 속성만 사용이 가능하다. 즉, 다형적 참조에서 객체를 통한 직접적인 자식 클래스에 대한 속성을 사용하는 것은 컴파일 에러를 야기한다. 
<img src="https://velog.velcdn.com/images/usnijee_2/post/8a291891-07b9-48c8-994b-e06bc54cd86a/image.png" alt=""></p>
<p>이럴때 자식 클래스의 속성을 사용하고 싶다면,, 어떻게 해야할까? &#39;다운 캐스팅&#39;</p>
<h4 id="다운-캐스팅과-업-캐스팅">다운 캐스팅과 업 캐스팅</h4>
<p>다운 캐스팅은 말 그대로 부모 -&gt; 자식으로 캐스팅 하는 것이고, 업 캐스팅은 자식 -&gt; 부모로 캐스팅하는 것이다.
일시적 다운 캐스팅은 자식 클래스의 메소드를 호출할 때만 일시적으로 다운 캐스팅 되는 경우를 의미한다. </p>
<pre><code class="language-java">// 다운 캐스팅
Parent poly = new Child();
Child child = (Child) poly; 

// 일시적 다운 캐스팅
Parent poly = new Child();
((Child)poly).childMethod() 

// 업 캐스팅
Child child = new Child();
Parent parent1 = (Parent) child;
Parent parent2 = child;        //  parent2 처럼 업캐스팅은 생략을 권장한다. </code></pre>
<h4 id="다운-캐스팅의-주의점">다운 캐스팅의 주의점</h4>
<p>부모는 자식을 품을 수 있다. 단, 서로 부모-자식 관계가 성립이 되어야 가능하다. 이게 무슨 말일까? 
상속관계의 클래스는 객체 생성시 메모리에 부모, 자식 속성이 모두 저장된다고 했다. 이렇게 상속관계에 의해 메모리에 부모와 자식이 모두 존재할 때 비로소 다운 캐스팅이 가능하다.
<img src="https://velog.velcdn.com/images/usnijee_2/post/4112f099-9222-48fe-af21-730b6440aa1d/image.png" alt=""></p>
<p>다운캐스팅이 불가능한 경우는 다음과 같다.</p>
<pre><code class="language-java">Parent parent = new Parent();
Child child = (Child) parent;</code></pre>
<p>상속관계가 없는 경우에는 메모리상에 부모와 자식이 공존하지 않기에 다운 캐스팅이 불가능하다. 
<img src="https://velog.velcdn.com/images/usnijee_2/post/a3e58d10-642f-4765-acec-b87190681f98/image.png" alt="">
<img src="https://velog.velcdn.com/images/usnijee_2/post/cc971e03-5ab2-44d2-a3a2-82cc1ea6e856/image.png" alt=""></p>
<h3 id="메서드-오버라이딩">메서드 오버라이딩</h3>
<p>다형성의 두 번째 핵심이론은 &#39;메서드 오버라이딩&#39;이다. 메서드 오버라이딩이 뭘까?
Override, &quot;덮어쓰기&quot;라는 의미이다. 직역하면 메서드 오버라이딩은 메서드를 덮어쓰는 것을 의미한다. 즉, 메서드 오버라이딩이란 <strong>상속관계가 존재하는 부모클래스의 특정 메서드를 자식 클래스에서 재정의하여 사용하는 것을 의미</strong>한다. </p>
<pre><code class="language-java">public class Parent {

    public String value = &quot;Parent&quot;;

    public void method() {
        System.out.println(&quot;Parent.method&quot;);
    }
}

public class Child extends Parent {

    public String value = &quot;child&quot;;

    @Override
    public void method() {
        System.out.println(&quot;Child.method&quot;);
    }
}</code></pre>
<p>위의 예제를 보면 Child에서 Parent의 method()를 재정의한 것을 볼 수 있다. 이를 그림으로 도식화하면 다음과 같다.
<img src="https://velog.velcdn.com/images/usnijee_2/post/ea412ed7-4a5a-4052-9bdb-5a0cd6ee2bd5/image.png" alt=""></p>
<p>그러면 여기서 한 가지 의문점이 발생한다. 그냥 굳이 오버라이딩을 하지않고 자식 클래스에 동일한 이름의 메서드를 만들고 해당 메서드의 고유 기능을 정의하면 되는 것 아닐까??</p>
<p>이 부분에서 메서드 오버라이디의 두 가지 특징을 살펴본다. </p>
<h4 id="메서드-오버라이딩의-특징">메서드 오버라이딩의 특징</h4>
<ol>
<li>부모클래스의 메서드를 재정의한다.</li>
<li>오버라이딩 된 메서드는 항상 우선권을 가진다. </li>
</ol>
<p>메서드 오버라이딩의 특징과 다형적 참조를 연결지어보면 다음과 같다. </p>
<pre><code class="language-java">Parent poly = new Child();
poly.method(); </code></pre>
<p>위의 코드를 도식화하면 다음과 같다.
<img src="https://velog.velcdn.com/images/usnijee_2/post/816a82b6-750b-4342-81fd-b1766193ed45/image.png" alt=""></p>
<ul>
<li>다형적 참조에 의해, 우선 메모리상의 부모클래스를 참조한다 </li>
<li>메서드 오버라이딩에 의해 우선순위가 높은 자식 클래스의 method()가 호출된다.</li>
</ul>
<p>즉, 메서드 오버라이딩에 의해 부모 클래스의 고유 기능은 유지하며 여러 자식 클래스에 특화된 동작이 가능해진 것이다. 이는 객체지향 프로그래밍에서 코드의 유지보수를 용이하게 하는 방법 중 하나이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[회고] 우테코 프리코스 지원 후기]]></title>
            <link>https://velog.io/@usnijee_2/%ED%9A%8C%EA%B3%A0-%EC%9A%B0%ED%85%8C%EC%BD%94-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-week1</link>
            <guid>https://velog.io/@usnijee_2/%ED%9A%8C%EA%B3%A0-%EC%9A%B0%ED%85%8C%EC%BD%94-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-week1</guid>
            <pubDate>Mon, 21 Oct 2024 16:11:14 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/usnijee_2/post/8df3828f-b462-46ed-b5d7-75bbeddb6fe8/image.webp" alt=""></p>
<h2 id="우아한테크코스에-지원하다">우아한테크코스에 지원하다</h2>
<p> 올해 2월 자대 공학계열을 졸업하였다. 군대를 전역하고 나는 어떤 길을 걸어가야할까?라는 스스로에게 던진 질문에 대답하기위해 많은 생각을 하였고, 전공자 친구와 함께 처음으로 프로그래밍을 접하였다. 지금 생각해보면 지나간 시간이지만 이 시기가 가장 아쉬웠다.. 프로그래밍을 처음으로 접했고 언어를 배우며 나름 배움에 만족하던 나였기에 더 나아가 프로그래밍을 배워서 &#39;어떤&#39;일을 하는 프로그래머가 되고 싶은지에 대한 생각이 짧았다고 생각한다. 그 후 공부를 심화하기위해 자대의 연구실에 학부연구생으로 들어가게 되었고 약 2년간 연구실 생활과 전공,부전공 수업등을 통해 프로그래밍, 펌웨어, AI등을 접하게 되었다. 정확히 연구실 1년차까지는 나름 연구실 생활에 만족하였고, 전공에 대해서도 거리감이 없었기에 무난히 연구실에서 석사과정을 밟으리라 생각했다. 하지만 3학년 2학기 부전공 수업 AI 프로그래밍과 겨울방학때 연구실내 AI 세미나를 통해 AI를 접하게 되며 진로에 대한 전환점이 생기게 된다. AI와 데이터를 공부하며 데이터 엔지니어링에 관심을 갖게 되었고, 데이터 엔지니어링을 접하니 자연스레 백엔드 개발에도 관심이 가게되었다. 졸업작품을 만들고 학회에 논문을 투고하는 것을 끝으로 연구실을 나왔다. 그리고 졸업을 하였다. 연구실 생활과 펌웨어에 대해서 불만은 없다. 펌웨어 프로그래밍도 나름? 재밌고 목표 직무로 삼을 수 있다고 생각했지만 프로그래머로써 진정 &#39;내가 하고 싶은 분야&#39;가 생기고 말았다.</p>
<p>졸업 후 데이터 엔지니어링 국비지원을 알게되었고 국비지원에 대한 안좋은 시선을 구글에서 많이 보았지만 학원측과 면담 후 &#39;내가 가고싶은 길&#39;이기에 일단 앞으로 나아가 보기로 했다. 6개월이라는 기간동안... 힘들었다. 배움이 힘든것이 아니라 솔직히 국비지원에 대한 소문(?)은 어느정도 맞았다... 안좋은 얘기를 장황하게 할 필요는 없다고 생각하니... 국비지원을 생각한다면 반드시 &#39;강사님의 이력&#39;이 있는 근거가 있는 곳으로 가길 바란다. 뭐가됐든 이수를 하였고, 과정 전부 너무 중요한 내용들이었으나 가장 중요하다고 생각한 백엔드에 대해서 딥하게 학습하지 못하였고 백엔드 엔지니어링에 대한 나만의 갈증이 증폭되었다. 현재 김영한님의 훌륭한! 강의를 들으며 자바를 처음부터 열심히(?) 공부하고 있는 와중에 ,, 우테코 모집을 보게되었다...!</p>
<p>맞다, 우아한테크코스,, 훌륭한 커리큘럼과 유튜브로 가끔 보던 훌륭하신 분들의 테코톡을 보며 대단하다, 멋지다, 나도 해보고 싶다라는 생각이 들던 그 우아한테크코스다. 맞다, 들어가기 힘들다. 이번에 설명회를 유튜브를 통해 보았는데 작년 경쟁률이 거의 40:1? 이었던 것으로 기억한다. 괜찮다. 우아한테크코스를 위해 이력서를 작성하는 과정, 프리코스의 과정 모두 나의 성장에 이바지 할것이다. </p>
<p><strong>프리코스의 목표</strong>는 다음과 같다.</p>
<blockquote>
<ol>
<li>java가 처음이지만 리팩토링을 통해 클린 코드를 작성해볼 것<ol start="2">
<li>디자인 패턴을 학습하고 디자인 패턴을 적용해서 코딩해볼 것 </li>
<li>프리코스 커뮤니티 활동을 활발히 해볼 것</li>
</ol>
</li>
</ol>
</blockquote>
<h2 id="프리코스에-대하여">프리코스에 대하여</h2>
<p>프리코스 첫 번째 미션은 <a href="https://github.com/usnijee/java-calculator-7/tree/usnijee">문자열 덧셈 계산기</a>이다. 자세한 설명은 하이퍼링크로 대체하겠다. 처음부터 막막했다. 요구사항에 맞게 기능을 분기하는 것도 어려웠지만 이를 올바른 패키지명으로 디렉토리 트리를 구성하는 것이 상당히 어려웠다. 이 부분이 안되면 프로그래밍 시작이 안되었기에 결국,, 서칭과 생성형 AI 교수님?의 도움을 조금 받아 디렉토리 트리를 정하였다.(다음부터는 이러지 않겠..) 그리고 초반에 막혔던 부분이 intellij 개발환경설정이었다. 우테코에서 제시한 요구사항에 테스트 build에 대한 내용이 있는데 지속적으로 오류가 발생하였고 원인불명이었다. 이 부분은 따로 포스팅할 예정이나 간단히 말하면 intellij의 경우 <strong>java 파일 디렉토리에 &#39;한글&#39;을 최대한 배제하는 것</strong>이 좋다. 윈도우를 사용하는 나는 자연스레 &#39;바탕화면&#39;을 상위 디렉토리로 하는 디렉토리내에 java파일을 위치하였더니 build가 안되었고 이를 수정하였더니 바로 정상적으로 빌드가 되는것을 확인할 수 있었다. 
 이번 프리코스에서 커뮤니티의 존재를 늦게 알게되어 뒤늦게 합류하였으나 짧은 기간이어도 많은 유익한 점을 알게되었다. 먼저 프리코스 미션과 관련하여 &#39;정규식&#39;표현에 대해 알게되었고 java에서 제공하는 정규식 처리 클래인 Pattern와 Matcher에 대해 알게되어 학습할 수 있어 좋았다.(이 부분도 따로 포스팅 할 예정이다) 커뮤니티에서 다른 지원자분께서 정규식 표현을 정리한 문서를 공유해주셔서 많은 도움이 되었고, 이것이 커뮤니티의 순기능이구나라는 깨달음을 얻게 되었다. 프리코스 미션 이외에도 커뮤니티를 통해 훌륭한 개발 서적에 대해 알게되었고 다양한 개발론에 대해서 토의와 토론을 하시는 분들을 보고 조금의 경외감이 들기도 하였다. 
나도 할 수 있다. 이제 4주간의 프리코스가 남았는데 위에서 작성한 프리코스 목표를 이행하여 한층 더 성장한 사람이 되도록 하겠다. 끝!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] Hadoop 5]]></title>
            <link>https://velog.io/@usnijee_2/TIL-Hadoop-5</link>
            <guid>https://velog.io/@usnijee_2/TIL-Hadoop-5</guid>
            <pubDate>Wed, 15 May 2024 12:10:21 GMT</pubDate>
            <description><![CDATA[<h1 id="apache-sqoop이란">Apache Sqoop이란</h1>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/2af1ffb5-d3b6-4a08-a220-cc8d49b0f9d1/image.png" alt=""></p>
<blockquote>
<p>sqoop은 일반적으로 사용하는 RDBMS(MySQL, Oracle)와 HDFS(Hive, HBase)간 데이터를 전송하기 위해 사용하는 툴이다.</p>
<p>HDFS 저장소를 기준으로 import(RDBMS -&gt; HDFS), export(HDFS -&gt; RDBMS) 기능을 제공하며, MapReduce방식으로 동작한다. </p>
</blockquote>
<h2 id="sqoop-download">Sqoop download</h2>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/dd3d4f7c-7278-4632-8082-920a650e8639/image.png" alt=""></p>
<h2 id="환경변수-및-환경설정">환경변수 및 환경설정</h2>
<h3 id="1-경로-설정">1. 경로 설정</h3>
<ul>
<li>gedit ~/.bashrc</li>
<li>source ~/.bashrc
<img src="https://velog.velcdn.com/images/usnijee_2/post/3d799a6b-4e38-4033-885f-da76818a82cc/image.png" alt=""></li>
</ul>
<h3 id="2-sqoop-환경설정">2. sqoop 환경설정</h3>
<ul>
<li>cp sqoop/conf/sqoop-env-template.sh sqoop-env.sh를 통해 환경설정파일 카피 <ul>
<li>gedit sqoop/conf/sqoop-env.sh
<img src="https://velog.velcdn.com/images/usnijee_2/post/f3401797-18dc-47a1-a4a9-c79587e6e536/image.png" alt=""></li>
</ul>
</li>
</ul>
<h2 id="mysql-db-설정">MySQL DB 설정</h2>
<h3 id="1-mysql-설치">1. MySQL 설치</h3>
<p>mysql 설치 후 필요한 설정 및 진행 단계를 다음 명령어를 통해 진행</p>
<blockquote>
<p>dnf install mysql-server
systemctl start mysqld.service
systemctl enable mysqld
systemctl status mysqld
mysql_secure_installation</p>
</blockquote>
<ul>
<li>mysql user 생성 <blockquote>
<p>CREATE USER &#39;<username>&#39;@&#39;localhost&#39; IDENTIFIED BY &gt;&#39;<password>&#39;;
GRANT ALL PRIVILEGES ON <em>.</em> TO &#39;<username>&#39;@&#39;localhost&#39;;
FLUSH PRIVILEGES;
systemctl restart mysqld</p>
</blockquote>
</li>
</ul>
<h3 id="2-mysql-접속">2. MySQL 접속</h3>
<ul>
<li>Mysql 접속<blockquote>
<p>mysql -u root -p</p>
</blockquote>
</li>
</ul>
<pre><code># 접속 예시 

[sshuser@m-001-example-1fx8-hd ~]$ mysql -u example -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or ㅓ\g.
Your MySQL connection id is 37
Server version: 5.6.35-log MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.

Type &#39;help;&#39; or &#39;\h&#39; for help. Type &#39;\c&#39; to clear the current input statement.

mysql&gt;</code></pre><h3 id="3-mysql-데이터-스키마-생성-및-확인">3. MySQL 데이터 스키마 생성 및 확인</h3>
<blockquote>
<p>create database hadoopguide;</p>
<p>GRANT ALL PRIVILEGES ON hadoopguide.* TO &#39;username&#39;@&#39;localhost&#39; IDENTIFIED BY &#39;password&#39;;
# 권한 부여 : 로컬 사용자가 해당 db를 보고 수정할 수 있도록 권한 부여 </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/82de29d3-8f1b-4851-b2e8-b1ce2dc18428/image.png" alt=""><img src="https://velog.velcdn.com/images/usnijee_2/post/1caacb5a-1afc-4518-b5ef-32fd81eada22/image.png" alt=""></p>
<ol start="3">
<li>sqoop 설정 정리 <blockquote>
<p>Sqoop 1.4.7 환경 설정 후 , </p>
</blockquote>
<ol>
<li>commons-lang-2.6.jar 다운(기존에 있던 commons-lang은 전부 지우거나 파일 다른곳으로 이동)<blockquote>
</blockquote>
</li>
<li>CREATE USER 계정명입력@localhost identified by &#39;비밀번호&#39;;<blockquote>
</blockquote>
</li>
<li>GRANT ALL PRIVILEGES ON 데베명입력.* TO 계정명입력@localhost;<blockquote>
</blockquote>
</li>
<li>flush privileges;<blockquote>
</blockquote>
</li>
<li>SHOW GRANTS FOR 계정명@localhost;</li>
</ol>
</li>
</ol>
<h2 id="sqoop-활용">Sqoop 활용</h2>
<h3 id="1--커넥터-설치-및-압축해제-connectorj">1.  커넥터 설치 및 압축해제 (connector/J)</h3>
<p><em>Sqoop은 관계형 데이터베이스 시스템과 Hadoop의 상호 작용을 용이하게 하기 위해 설계되었는데, 이 때 다양한 데이터베이스 시스템과의 연결은 Sqoop connector를 통해 이루어짐</em></p>
<blockquote>
</blockquote>
<p>wget <a href="https://downloads.mysql.com/archives/get/p/3/file/mysql-connector-java-5.1.46.tar.gz">https://downloads.mysql.com/archives/get/p/3/file/mysql-connector-java-5.1.46.tar.gz</a></p>
<blockquote>
</blockquote>
<p>tar xvfz mysql-connector-java-5.1.46.tar.gz </p>
<blockquote>
<p>error : _java.lang.NoClassDefFoundError: org/apache/commons/lang/StringUtils _ 
해당 오류는 commons-lang 파일이 있어야 해결된다. </p>
</blockquote>
<h3 id="2-commons-lang-파일-다운로드">2. commons-lang 파일 다운로드</h3>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/746396b1-065b-4953-b838-d61623bc4036/image.png" alt="">
<a href="https://commons.apache.org/lang/download_lang.cgi">apache-lang-download</a></p>
<blockquote>
<p>wget <a href="https://dlcdn.apache.org//commons/lang/binaries/commons-lang3-3.14.0-bin.tar.gz">https://dlcdn.apache.org//commons/lang/binaries/commons-lang3-3.14.0-bin.tar.gz</a></p>
</blockquote>
<h3 id="3-sqoop-import">3. Sqoop import</h3>
<blockquote>
<p>Sqoop 명령어 import를 사용하여 특정 데이터베이스, 특정 테이블, 쿼리 수행 결과 등 가져올 수 있다. </p>
</blockquote>
<pre><code># Sqoop import 

sqoop import --connect jdbc:mysql://[마스터 노드 Private IP]:3306/[데이터베이스이름] --username [클러스터 관리자 계정명] --password [클러스터 관리자 패스워드] --table [대상테이블]

ex1.
  sqoop import \
  --connect jdbc:mysql://localhost/hadoopguide \
  --table test2 \
  --target-dir /user/root/test2 \
  --username hadoopguide \
  --P \
  -m 1

ex2.
  #스쿱으로 가져온 데이터의 순서가 틀어지면 임의로 query문을 추가하여 정렬해서 가져온다
  #이때 주의할 점은 , where $CONDITIONS를 반드시 작성해야 error가 발생하지 않는다는 점이다. 해당절을 작성하지 않고 order by를 작성하면 오류가 발생한다. 

  sqoop import 
  --connect jdbc:mysql://localhost/hadoopguide \
  --target-dir widget \
  --username hadoopguide \
  --P  \
  --query &quot;select * from widget where \$CONDITIONS order by id&quot; \
  -m 1
</code></pre><p>실습&gt;</p>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/a2619623-0a12-452e-a727-5e29819dd60c/image.png" alt=""></p>
<pre><code>Warning: /root/sqoop/../hbase does not exist! HBase imports will fail.
Please set $HBASE_HOME to the root of your HBase installation.
Warning: /root/sqoop/../hcatalog does not exist! HCatalog jobs will fail.
Please set $HCAT_HOME to the root of your HCatalog installation.
Warning: /root/sqoop/../accumulo does not exist! Accumulo imports will fail.
Please set $ACCUMULO_HOME to the root of your Accumulo installation.
Warning: /root/sqoop/../zookeeper does not exist! Accumulo imports will fail.
Please set $ZOOKEEPER_HOME to the root of your Zookeeper installation.
2024-03-12 14:04:32,758 INFO sqoop.Sqoop: Running Sqoop version: 1.4.7
Enter password: 
2024-03-12 14:04:36,680 INFO manager.MySQLManager: Preparing to use a MySQL streaming resultset.
2024-03-12 14:04:36,684 INFO tool.CodeGenTool: Beginning code generation
Tue Mar 12 14:04:37 KST 2024 WARN: Establishing SSL connection without server&#39;s identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn&#39;t set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to &#39;false&#39;. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2024-03-12 14:04:38,549 INFO manager.SqlManager: Executing SQL statement: select * from widget where  (1 = 0)  order by id
2024-03-12 14:04:38,567 INFO manager.SqlManager: Executing SQL statement: select * from widget where  (1 = 0)  order by id
2024-03-12 14:04:38,635 INFO manager.SqlManager: Executing SQL statement: select * from widget where  (1 = 0)  order by id
2024-03-12 14:04:38,677 INFO orm.CompilationManager: HADOOP_MAPRED_HOME is /root/hadoop-3.3.6
Note: /tmp/sqoop-root/compile/a1649ef2c28b7580e54d7c867471195d/QueryResult.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
2024-03-12 14:04:44,725 INFO orm.CompilationManager: Writing jar file: /tmp/sqoop-root/compile/a1649ef2c28b7580e54d7c867471195d/QueryResult.jar
2024-03-12 14:04:44,801 INFO mapreduce.ImportJobBase: Beginning query import.
2024-03-12 14:04:44,802 INFO Configuration.deprecation: mapred.job.tracker is deprecated. Instead, use mapreduce.jobtracker.address
2024-03-12 14:04:45,328 INFO Configuration.deprecation: mapred.jar is deprecated. Instead, use mapreduce.job.jar
Tue Mar 12 14:04:45 KST 2024 WARN: Caught while disconnecting...

EXCEPTION STACK TRACE:



** BEGIN NESTED EXCEPTION ** 

javax.net.ssl.SSLException
MESSAGE: closing inbound before receiving peer&#39;s close_notify

STACKTRACE:

javax.net.ssl.SSLException: closing inbound before receiving peer&#39;s close_notify
    at sun.security.ssl.SSLSocketImpl.shutdownInput(SSLSocketImpl.java:740)
    at sun.security.ssl.SSLSocketImpl.shutdownInput(SSLSocketImpl.java:719)
    at com.mysql.jdbc.MysqlIO.quit(MysqlIO.java:2249)
    at com.mysql.jdbc.ConnectionImpl.realClose(ConnectionImpl.java:4221)
    at com.mysql.jdbc.ConnectionImpl.close(ConnectionImpl.java:1464)
    at org.apache.sqoop.manager.GenericJdbcManager.discardConnection(GenericJdbcManager.java:78)
    at org.apache.sqoop.manager.GenericJdbcManager.close(GenericJdbcManager.java:88)
    at org.apache.sqoop.mapreduce.DataDrivenImportJob.configureInputFormat(DataDrivenImportJob.java:335)
    at org.apache.sqoop.mapreduce.ImportJobBase.runImport(ImportJobBase.java:262)
    at org.apache.sqoop.manager.SqlManager.importQuery(SqlManager.java:748)
    at org.apache.sqoop.tool.ImportTool.importTable(ImportTool.java:522)
    at org.apache.sqoop.tool.ImportTool.run(ImportTool.java:628)
    at org.apache.sqoop.Sqoop.run(Sqoop.java:147)
    at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:82)
    at org.apache.sqoop.Sqoop.runSqoop(Sqoop.java:183)
    at org.apache.sqoop.Sqoop.runTool(Sqoop.java:234)
    at org.apache.sqoop.Sqoop.runTool(Sqoop.java:243)
    at org.apache.sqoop.Sqoop.main(Sqoop.java:252)


** END NESTED EXCEPTION **


2024-03-12 14:04:47,676 INFO Configuration.deprecation: mapred.map.tasks is deprecated. Instead, use mapreduce.job.maps
2024-03-12 14:04:48,032 INFO client.DefaultNoHARMFailoverProxyProvider: Connecting to ResourceManager at /0.0.0.0:8032
2024-03-12 14:04:51,141 INFO mapreduce.JobResourceUploader: Disabling Erasure Coding for path: /tmp/hadoop-yarn/staging/root/.staging/job_1710213620221_0002
Tue Mar 12 14:04:57 KST 2024 WARN: Establishing SSL connection without server&#39;s identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn&#39;t set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to &#39;false&#39;. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2024-03-12 14:04:58,150 INFO db.DBInputFormat: Using read commited transaction isolation
2024-03-12 14:04:58,815 INFO mapreduce.JobSubmitter: number of splits:1
2024-03-12 14:04:59,609 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1710213620221_0002
2024-03-12 14:04:59,609 INFO mapreduce.JobSubmitter: Executing with tokens: []
2024-03-12 14:05:00,579 INFO conf.Configuration: resource-types.xml not found
2024-03-12 14:05:00,581 INFO resource.ResourceUtils: Unable to find &#39;resource-types.xml&#39;.
2024-03-12 14:05:01,366 INFO impl.YarnClientImpl: Submitted application application_1710213620221_0002
2024-03-12 14:05:01,491 INFO mapreduce.Job: The url to track the job: http://localhost:8088/proxy/application_1710213620221_0002/
2024-03-12 14:05:01,493 INFO mapreduce.Job: Running job: job_1710213620221_0002
2024-03-12 14:05:27,705 INFO mapreduce.Job: Job job_1710213620221_0002 running in uber mode : false
2024-03-12 14:05:27,724 INFO mapreduce.Job:  map 0% reduce 0%
2024-03-12 14:05:45,264 INFO mapreduce.Job:  map 100% reduce 0%
2024-03-12 14:05:46,355 INFO mapreduce.Job: Job job_1710213620221_0002 completed successfully
2024-03-12 14:05:46,910 INFO mapreduce.Job: Counters: 33
    File System Counters
        FILE: Number of bytes read=0
        FILE: Number of bytes written=283653
        FILE: Number of read operations=0
        FILE: Number of large read operations=0
        FILE: Number of write operations=0
        HDFS: Number of bytes read=87
        HDFS: Number of bytes written=130
        HDFS: Number of read operations=6
        HDFS: Number of large read operations=0
        HDFS: Number of write operations=2
        HDFS: Number of bytes read erasure-coded=0
    Job Counters 
        Launched map tasks=1
        Other local map tasks=1
        Total time spent by all maps in occupied slots (ms)=14246
        Total time spent by all reduces in occupied slots (ms)=0
        Total time spent by all map tasks (ms)=14246
        Total vcore-milliseconds taken by all map tasks=14246
        Total megabyte-milliseconds taken by all map tasks=14587904
    Map-Reduce Framework
        Map input records=3
        Map output records=3
        Input split bytes=87
        Spilled Records=0
        Failed Shuffles=0
        Merged Map outputs=0
        GC time elapsed (ms)=340
        CPU time spent (ms)=2970
        Physical memory (bytes) snapshot=141488128
        Virtual memory (bytes) snapshot=2946174976
        Total committed heap usage (bytes)=28442624
        Peak Map Physical memory (bytes)=141488128
        Peak Map Virtual memory (bytes)=2946174976
    File Input Format Counters 
        Bytes Read=0
    File Output Format Counters 
        Bytes Written=130
2024-03-12 14:05:46,954 INFO mapreduce.ImportJobBase: Transferred 130 bytes in 59.1995 seconds (2.196 bytes/sec)
2024-03-12 14:05:46,978 INFO mapreduce.ImportJobBase: Retrieved 3 records.
</code></pre><p><img src="https://velog.velcdn.com/images/usnijee_2/post/a77998e0-0715-4d71-872f-a68d00b02dbe/image.png" alt=""></p>
<h3 id="4-sqoop-export">4. Sqoop export</h3>
<blockquote>
<p>Sqoop export는 Hadoop 파일 시스템에 저장된 데이터를 외부 데이터베이스나 데이터 웨어하우스로 내보내는 작업을 수행하는 Sqoop 명령어</p>
<p>Sqoop import : 외부 DB의 데이터를 Hadoop 내부로 갖고오기
Sqoop export : Hadoop 내부의 데이터를 외부 DB로 내보내기 </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/usnijee_2/post/58ba0a09-3b08-4a86-947b-904494912694/image.png" alt="">
<em>위 그림은 mysql로 형성한 test 테이블이며 테이블 내용은 사진과 같다. 해당 테이블은 Sqoop import를 통해 /user/root/test/으로 import를 한 상태이다. Hadoop 경로로 들어간 해당 테이블의 내용과 export를 사용하여 다시  mysql의 빈 테이블인 tbl_test 테이블로 갖고오는 작업을 진행하였다.</em></p>
<ul>
<li>export 기본 구조 <pre><code># mysql -u hadoopguide -p를 통해 mysql 접속후 빈 테이블을 하나 형성한다. 이 과정은 설명 생략한다
# Hadoop 내부에 저장되어 있는 /user/root/test/part-m-0000 파일을 MySQL DB로 내보내기

</code></pre></li>
</ul>
<p>sqoop export --connect jdbc:mysql://localhost/hadoopguide --table tbl_target --export-dir /user/root/test/part-m-00000 --username hadoopguide --password password</p>
<pre><code>mysql을 확인해보면 hadoop 내부의 /user/root/test 에 있는 part-m-0000의 내용이 tbl_target에 들어간 것을 알 수 있다. 
![](https://velog.velcdn.com/images/usnijee_2/post/1004e5b8-480b-41cb-b967-068e4b9fde6d/image.png)


</code></pre>]]></description>
        </item>
    </channel>
</rss>