<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dayon.log</title>
        <link>https://velog.io/</link>
        <description>success is within reach, allow yourself time</description>
        <lastBuildDate>Fri, 12 Sep 2025 10:55:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dayon.log</title>
            <url>https://velog.velcdn.com/images/dayon_log/profile/40ac742a-fd2c-488f-9ad4-94acd445beb4/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dayon.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dayon_log" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[HashMap 코테에서 활용하기]]></title>
            <link>https://velog.io/@dayon_log/HashMap-%EC%BD%94%ED%85%8C%EC%97%90%EC%84%9C-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dayon_log/HashMap-%EC%BD%94%ED%85%8C%EC%97%90%EC%84%9C-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 12 Sep 2025 10:55:03 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://school.programmers.co.kr/learn/courses/30/lessons/176963?language=java">https://school.programmers.co.kr/learn/courses/30/lessons/176963?language=java</a></li>
</ul>
<h3 id="📌-문제-요약">📌 문제 요약</h3>
<ul>
<li>사람마다 <strong>이름(name)</strong> 과 <strong>그리움 점수(yearning)</strong> 가 주어진다.</li>
<li>여러 장의 사진(photo)이 있으며, 각 사진에는 여러 사람의 이름이 담겨 있다.</li>
<li>사진 속 등장하는 인물들의 <strong>그리움 점수 합계</strong>를 구해, 각 사진별 점수를 배열에 담아 반환하라.</li>
</ul>
<hr>
<h3 id="📌-입력">📌 입력</h3>
<ul>
<li><code>name</code>: 문자열 배열 (그리워하는 사람 이름)</li>
<li><code>yearning</code>: 정수 배열 (각 사람의 그리움 점수)</li>
<li><code>photo</code>: 2차원 문자열 배열 (사진 속 인물들 이름)</li>
</ul>
<hr>
<h3 id="📌-출력">📌 출력</h3>
<ul>
<li><code>int[]</code> : 각 사진별 추억 점수의 합</li>
</ul>
<hr>
<h3 id="📌-제한사항">📌 제한사항</h3>
<ul>
<li><code>3 ≤ name.length = yearning.length ≤ 100</code></li>
<li><code>1 ≤ yearning[i] ≤ 100</code></li>
<li><code>3 ≤ photo.length ≤ 100</code></li>
<li><code>1 ≤ photo[i].length ≤ 100</code></li>
<li><code>name</code>과 <code>photo[i]</code>의 원소는 중복 없음</li>
</ul>
<h2 id="내가-쓴-코드">내가 쓴 코드</h2>
<pre><code class="language-java">class Solution {
    public int[] solution(String[] name, int[] yearning, String[][] photo) {

        int[] answer = new int[photo.length];

        for(int i = 0 ; i &lt; photo.length ; i++ ) {
            int sum = 0 ; 
            for(int j = 0 ; j &lt; photo[i].length ; j++ ) {
                for(int n = 0 ; n &lt; name.length ; n++ ) {
                    // 이름이 존재한다면 
                    if (photo[i][j].equals(name[n])) {
                        sum += yearning[n] ;
                    }
                }
            }
            answer[i] = sum ; 
        }
        return answer;
    }
}</code></pre>
<h2 id="✅-시간-복잡도--효율성-전략">✅ 시간 복잡도 / 효율성 전략</h2>
<h3 id="1-직접-비교-방식-삼중-for문">1. <strong>직접 비교 방식 (삼중 for문)</strong></h3>
<pre><code class="language-java">for (photo[i][j]) {
    for (name[n]) {
        if (photo[i][j].equals(name[n])) {
            sum += yearning[n];
        }
    }
}</code></pre>
<ul>
<li><p>시간 복잡도:</p>
<ul>
<li><p><code>photo</code> 최대 100개</p>
</li>
<li><p>각 사진 최대 100명</p>
</li>
<li><p><code>name</code> 최대 100명</p>
<p>  → <strong>O(photo × photo[i] × name) = O(100 × 100 × 100) = 1,000,000 (백만 연산)</strong></p>
</li>
</ul>
</li>
<li><p>충분히 실행 가능하지만, 불필요하게 중첩 루프가 깊음</p>
</li>
</ul>
<h3 id="2-hashmap-이용-방식-최적화"><strong>2. HashMap 이용 방식 (최적화)</strong></h3>
<pre><code class="language-java">import java.util.*;

class Solution {
    public int[] solution(String[] name, int[] yearning, String[][] photo) {
        // 이름 → 그리움 점수 매핑
        Map&lt;String, Integer&gt; scoreMap = new HashMap&lt;&gt;();
        for (int i = 0; i &lt; name.length; i++) {
            scoreMap.put(name[i], yearning[i]);
        }

        int[] answer = new int[photo.length];

        for (int i = 0; i &lt; photo.length; i++) {
            int sum = 0;
            for (String person : photo[i]) {
                // 이름이 존재하면 점수 더하기
                sum += scoreMap.getOrDefault(person, 0);
            }
            answer[i] = sum;
        }

        return answer;
    }
}</code></pre>
<ul>
<li><p>시간 복잡도:</p>
<ul>
<li><p><code>Map</code> 생성: O(name.length) = O(100)</p>
</li>
<li><p>사진 순회: O(photo × photo[i]) = O(100 × 100) = 10,000</p>
<p>  → 전체 <strong>O(10,000)</strong></p>
</li>
</ul>
</li>
<li><p><strong>백만 연산 대비 100배 이상 빠름</strong></p>
</li>
<li><p>실무/코테 모두에서 가장 효율적인 풀이 전략.</p>
</li>
</ul>
<p><code>이름 → 그리움 점수</code> 매핑을 <strong>HashMap</strong>에 미리 저장해두고, 조회할 때 <code>O(1)</code>로 가져오는 방식이 좋을 것 같다. </p>
<hr>
<h2 id="🎀-메모">🎀 메모</h2>
<ul>
<li><p>문자열 비교는 equals를 사용한다.</p>
<p>  <code>photo[i][j].equals(name[n])</code></p>
</li>
<li><p>HashMap은 Key→ Value 구조라서 O(1)에 조회 가능</p>
<ul>
<li>1:1 매핑 관계가 필요할때 사용하자</li>
<li>중복 카운트 / 빈도수를 셀때 사용하자 → <code>map.put(x, map.getOrDefault(x, 0) + 1)</code> 패턴 자주 사용</li>
</ul>
</li>
<li><p>HashMap 문법</p>
<ul>
<li><p>값 넣기 : <code>map.put(&quot;may&quot;, 5);</code></p>
</li>
<li><p>값 가져오기 : <code>int score = map.get(&quot;may&quot;);</code></p>
</li>
<li><p>값 업데이트 : <code>map.put(&quot;may&quot;, map.getOrDefault(&quot;may&quot;, 0) + 1);</code></p>
</li>
<li><p>존재 여부 확인 :</p>
<pre><code class="language-java">if (map.containsKey(&quot;may&quot;)) {
  System.out.println(&quot;있음&quot;); 
}</code></pre>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 16937 두 스티커 JAVA]]></title>
            <link>https://velog.io/@dayon_log/%EB%B0%B1%EC%A4%80-16937-%EB%91%90-%EC%8A%A4%ED%8B%B0%EC%BB%A4-JAVA</link>
            <guid>https://velog.io/@dayon_log/%EB%B0%B1%EC%A4%80-16937-%EB%91%90-%EC%8A%A4%ED%8B%B0%EC%BB%A4-JAVA</guid>
            <pubDate>Mon, 03 Mar 2025 14:32:51 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dayon_log/post/fd347bbc-0791-44a5-a878-72ba9f44dc6a/image.jpg" alt=""></p>
<h2 id="📌-문제-탐색하기">📌 문제 탐색하기</h2>
<p>문제 링크 : <a href="https://www.acmicpc.net/problem/16937">https://www.acmicpc.net/problem/16937</a></p>
<ul>
<li><p>H : 모눈종이의 높이 </p>
<ul>
<li>W : 모눈종이의 너비 </li>
</ul>
</li>
<li><p>N : 스티커의 개수 </p>
</li>
<li><p>i 번째 스티커의 크기 : $R_i * C_i$</p>
</li>
<li><p>스티커를 90도 회전시킬 수 있고, 두 스티커는 서로 겹쳐서도 안되고, 모눈 종이를 벗어나서도 안된다</p>
</li>
<li><p>두 스티커를 붙였을 때 최대로 차지하는 넓이를 구해야 함! <strong>핵심!!</strong></p>
</li>
</ul>
<h3 id="알고리즘-선택">알고리즘 선택</h3>
<p>스티커의 조합이 가장 큰 넓이를 가지는 지 전부 탐색과 비교가 필요함 → 브루트포스 알고리즘 사용</p>
<h3 id="가능한-시간복잡도">가능한 시간복잡도</h3>
<p>스티커 조합 생성할때 $O(N^2)$ 시간 복잡도를 가지게 ehla → 각 배치 방법을 확인하는 데 O(1) 시간이 걸리면 문제를 해결할 수 있다. </p>
<hr>
<h2 id="📌-코드-설계하기">📌 코드 설계하기</h2>
<ol>
<li><p>문제의 input을 받는다, 배열에 스티커의 크기를 저장 </p>
</li>
<li><p>모눈종이보다 큰 스티커가 있다면 스티커로 사용할 수 없다. (배열에서 제외)</p>
</li>
<li><p>완전 탐색</p>
<ol>
<li><p>첫번째로 붙일 스티커 선택하기</p>
</li>
<li><p>두번째로 붙일 스티커 선택하기 </p>
<p> 스티커의 너비의 합을 계산후, 최대 넓이보다 크다면 update한다</p>
</li>
</ol>
</li>
</ol>
<hr>
<h2 id="📌-시도-회차-수정-사항">📌 시도 회차 수정 사항</h2>
<h3 id="1회차">1회차</h3>
<ul>
<li><p>90도 회전을 어떻게 설계해야 할지 몰랐다.</p>
<p>  (2,4) 라면 (4,2)가 가능해지는 것 → case 변수를 생성하였다. </p>
</li>
</ul>
<h3 id="2회차">2회차</h3>
<ul>
<li><p>두스티커의 단순 가로와 세로합의 조합으로 스티커가 가능하다, 불가능하다를 판단했음</p>
<pre><code class="language-java">      static boolean canPlace(int r1, int c1, int r2, int c2) {
          return (r1 + r2 &lt;= H &amp;&amp; Math.max(c1, c2) &lt;= W) ||  // 세로
                  (c1 + c2 &lt;= W &amp;&amp; Math.max(r1, r2) &lt;= H);   // 가로
      }</code></pre>
</li>
<li><p>스티커를 한개 배치후 남은 공간에 배치 하는 것으로 변경</p>
</li>
</ul>
<hr>
<h2 id="📌-정답-코드">📌 정답 코드</h2>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    static int H, W, N;
    static int[][] sticker;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), &quot; &quot;);
        H = Integer.parseInt(st.nextToken());
        W = Integer.parseInt(st.nextToken());
        N = Integer.parseInt(br.readLine());

        sticker = new int[N][2];
        for (int i = 0; i &lt; N; i++) {
            st = new StringTokenizer(br.readLine(), &quot; &quot;);
            sticker[i][0] = Integer.parseInt(st.nextToken());
            sticker[i][1] = Integer.parseInt(st.nextToken());
        }
        int maxArea = 0;

        for (int i = 0; i &lt; N; i++) {
            if (sticker[i][0] &gt; H &amp;&amp; sticker[i][1] &gt; W) continue;
            for (int j = i + 1; j &lt; N; j++) {
                if (sticker[j][0] &gt; H &amp;&amp; sticker[j][1] &gt; W) continue;
                maxArea = Math.max(maxArea, getStickerArea(sticker[i], sticker[j]));
            }
        }

        System.out.println(maxArea);
    }

    // 최대 너비 계산
    static int getStickerArea(int[] s1, int[] s2) {
        int max = 0;
        int[][] stickers = {
                {s1[0], s1[1]}, {s1[1], s1[0]}, // 회전을 한 경우도 포함 ![](https://velog.velcdn.com/images/dayon_log/post/bccec05e-d93d-4f68-9e2e-31aafc787811/image.jpg)

                {s2[0], s2[1]}, {s2[1], s2[0]}  
        };

        for (int k = 0; k &lt; 2; k++) {
            for (int l = 2; l &lt; 4; l++) {
                int x1 = stickers[k][0], y1 = stickers[k][1];
                int x2 = stickers[l][0], y2 = stickers[l][1];

                max = getMax(max, x1, y1, x2, y2, x1 * y1, x2 * y2);

                max = getMax(max, y1, x1, y2, x2, x1 * y1, x2 * y2);
            }
        }

        return max;
    }

    private static int getMax(int max, int x1, int y1, int x2, int y2, int i, int i2) {
        if (x1 &lt;= H &amp;&amp; y1 &lt;= W) {
            // 첫 번째 스티커를 붙이고, 남은 아래 부분에 두번째 스티커가 들어가는 경우
            if (x2 &lt;= H &amp;&amp; y2 &lt;= W - y1) {
                max = Math.max(max, i + i2);
            }
            // 첫 번째 스티커를 붙이고, 남은 오른쪽 부분에 두번째 스티커가 들어가는 경우
            if (x2 &lt;= H - x1 &amp;&amp; y2 &lt;= W) {
                max = Math.max(max, i + i2);
            }
        }
        return max;
    }

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[WebFlux - 우아콘2023을 보고 정리한 글 ]]></title>
            <link>https://velog.io/@dayon_log/WebFlux-%EC%9A%B0%EC%95%84%EC%BD%982023%EC%9D%84-%EB%B3%B4%EA%B3%A0-%EC%A0%95%EB%A6%AC%ED%95%9C-%EA%B8%80</link>
            <guid>https://velog.io/@dayon_log/WebFlux-%EC%9A%B0%EC%95%84%EC%BD%982023%EC%9D%84-%EB%B3%B4%EA%B3%A0-%EC%A0%95%EB%A6%AC%ED%95%9C-%EA%B8%80</guid>
            <pubDate>Tue, 10 Sep 2024 08:42:45 GMT</pubDate>
            <description><![CDATA[<p>배민스토어의 WebFlux를 적용한 도입기 우아콘2023을 보고 정리한 글입니다.
 <a href="https://youtu.be/pRpryoQphXQ?si=AYE3DWfrvrAETkG6">https://youtu.be/pRpryoQphXQ?si=AYE3DWfrvrAETkG6</a></p>
<hr>
<blockquote>
<p>Spring WebFlux 는 웹 애플리케이션에 반응형 프로그래밍을 제공한다. 
Reactive 디자인 의 비동기 및 비차단 특성 은 성능과 메모리 사용을 향상시킨다.</p>
</blockquote>
<br>

<h2 id="base">base</h2>
<ul>
<li><p>Spring WebFlux : Spring 5에서 도입된 모듈, 비동기(NonBlocking)와 반응형 프로그래밍을 기반으로한 웹 애플리케이션</p>
</li>
<li><p>반응형 프로그래밍 : 데이터 흐름과 변화를 다루는 프로그래밍 패러다임 ⇒ 비동기 처리가 중요 사항</p>
</li>
<li><p>Non-Blocking 하나의 작업이 끝날때 까지 기다리지 않고 다른 작업을 계속 수행할 수 있게 해준다. 
⇒ 적은 수의 스레드로 많은 요청을 처리할 수 있고, 더 작은 하드웨어 리소스로 애플리케이션 성능을 확장할 수 있다.</p>
</li>
<li><p>함수형 프로그래밍 : 함수를 객체 취급하여, 함수를 인자로 전달하거나 함수→함수로 반환할수 있다.
자바 8에서 람다 표현식의 도임으로 함수형 프로그래밍 지원을 시작</p>
</li>
<li><p>Project Reactor : Spring Web Flux의 기반이 되는 라이브러리, 비동기 데이터 스트림을 관리하는 데 유용한 기능 제공</p>
<ul>
<li>Mono, Flux를 사용해 데이터를 받아오거나 작업이 완료될때 신호(callback)를 보내준다.</li>
</ul>
</li>
</ul>
<pre><code>&lt;br&gt;</code></pre><br>

<h2 id="webflux--패러다임의-변화">WebFlux : 패러다임의 변화</h2>
<p>예) 가게 정보를 가져오는 로직 = Kotlin의 try-catch문을 이용해 로직을 이용해 작성해보자 </p>
<pre><code class="language-jsx">val shop = try {
    shopCacheRepository.findById(shopId)
} catch (e: Throwable) {
    log.error(&quot;Received error on shop cache.&quot;, e)
    null 
}

return shop ?: shopDBRepository.findById(shopId) </code></pre>
<p>→ after : Flux/Mono 의 어떤 연산자를 어떻게 조합해야 할까? </p>
<pre><code class="language-jsx">shopCacheRepository.findById(shopId)
        .doOnError { e -&gt; log.error(&quot;Received error on shop cache.&quot;,e) }
        .onErrorComplete()
        .switchIfEmpty { shopDBRepository.findById(shopId) }</code></pre>
<ul>
<li>어떤 연산자의 어떻게 조합해야 하는지 초점을 맞추게 된다 → 지식이 경험적 지식 ⇒ 높은 러닝 커브</li>
</ul>
<br>
<br>

<h2 id="배민스토어가-그럼에도-webflux를-전면-도입한-이유">배민스토어가 그럼에도, WebFlux를 전면 도입한 이유</h2>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/f095bba4-e9b4-420e-906b-7ea0b4d7fa09/image.png" alt=""></p>
<ul>
<li><p><strong>실시간으로 호출되는 외부 API 수의 증가</strong></p>
</li>
<li><p>Blocking 방식을 사용했을 때 , 통신 중간의 thread를 점유하게 되고, 
Non-Blocking 방식으로 호출하게 되면 Thread를 점유하지 않고 그 시간에 다른 작업에 Thread를 줄 수 있다.</p>
</li>
<li><p>병렬적으로 호출하게 되면 응답시간을 단축할 수 있게 된다.</p>
</li>
</ul>
<blockquote>
<p>⇒ 배민 스토어에서는 전시 서비스에만 도입 하는 것 , 
Batch 성 로직을 사용하는 어드민, 워커에는 도입을 하지 않아 복잡한 로직이 필요없고 단순한 전시 API 단에서 WebFlux를 사용하여 러닝 커브를 상쇄 하고자 함</p>
</blockquote>
<br>

<h2 id="검증을-위한-질문">검증을 위한 질문</h2>
<h3 id="1-spring-webflux의-도입으로-성능이-개선되거나-컴퓨팅-자원을-효율적으로-사용할-수-있게-되었는가">1. Spring WebFlux의 도입으로 성능이 개선되거나, 컴퓨팅 자원을 효율적으로 사용할 수 있게 되었는가?</h3>
<ul>
<li><p>이전보다 더 많은 요청을 받았는데도 빨라졌다.</p>
</li>
<li><p>데이터를 병렬적으로 가져오는 것이 일상이 되었다.</p>
</li>
</ul>
<pre><code class="language-jsx">    Mono.zip (
            productService.getProductDetail(...),
            productService.getProductOption(...),
            promotionService.findActivePromotions(...)
    ). map { (product, shopOptions, promotions) -&gt; ...</code></pre>
<p>but 성능, 효율적인 측면에서 변인이 통제된 자료를 만들지는 못함. 다른 조건을 동일하게 맞춘 상태에서 Spring MVC, Spring WebFlux간의 비교를 하지 못함</p>
<p><br><br></p>
<h3 id="2-project-reactor를-통해-배압을-고려해야하는-비즈니스-로직을-손쉽게-작성-할-수-있었는가">2. Project Reactor를 통해 배압을 고려해야하는 비즈니스 로직을 손쉽게 작성 할 수 있었는가?</h3>
<p>Batch성 로직에서는 프로젝트 리액터의 장점이 잘 살아난다고 판단, 반면 API 로직에서는 ??</p>
<p>들어가기 앞서 배압에 대해 알아보자 </p>
<br>

<p><strong>배압이란?</strong></p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/80b7b996-e6fe-4717-b0da-b5eb8161ce4f/image.png" alt=""></p>
<ul>
<li><p>Reactvice Streams 에서 배압은 스트림 요소의 전송을 조절하는 방법 ,즉 처리량을 조절하는 메커니즘</p>
</li>
<li><p>데이터의 생산 속도와 소비 속도간의 불일치를 관리하기 위해 사용</p>
</li>
</ul>
<br>

<p><strong>Publisher : 데이터의 생산을 담당</strong> (Subscriber가 구독하기전까지 아무런 일도 하지 않음) </p>
<p><strong>Subscriber : 데이터의 소비를 담당</strong> </p>
<p><strong>배압 = Subscriber가 Publisher 에게 데이터 전송 속도를 줄여달라고 신호를 보내는 과정</strong></p>
<br>

<p>흐름제어 -&gt; <strong>Publisher</strong>는 <strong>Subscriber</strong>가 등록되는 시점부터 데이터를 계속 push 한다. </p>
<p>피드백 -&gt; <strong>Subscriber</strong> 데이터가 지나치게 쌓여서 감당할 수 없는 현상을 제거하기 위해 피드백을 보낸다. </p>
<p>이 피드백을 관장하는 요소가 Back-Prussure , 배압이다.</p>
<br>

<pre><code class="language-jsx">Flux&lt;String&gt; dataStream = Flux.range(1, 100)
        .map(i -&gt; {
            // 데이터 처리 로직
            return &quot;Item &quot; + i;
        });

dataStream.onBackpressureBuffer(10) // 버퍼 사이즈 설정
          .subscribe(item -&gt; {
              // 데이터 소비 로직
              System.out.println(&quot;Processing &quot; + item);
          });</code></pre>
<ul>
<li><p><code>onBackpressureBuffer(10)</code>는 데이터가 버퍼에 최대 10개까지 쌓이도록 설정</p>
</li>
<li><p>데이터가 버퍼를 초과하면, <code>Publisher</code>는 추가 데이터를 멈추고, <code>Subscriber</code>가 버퍼에서 데이터를 처리할 때까지 기다리자.</p>
</li>
</ul>
<br>

<p>*<em>API 로직에서 배압이 중요할까?
*</em></p>
<ul>
<li><p>같은 API는 여러번 호출하지 않는다. 다양한 외부 API 들을 통상적으로 1-2번 호출하여 Mono 형태의 결과를 조합한다.</p>
</li>
<li><p>외부 API 에서 필요한 데이터를 스트리밍 형식으로 제공해 주지 않는다.</p>
</li>
<li><p>실시간 호출 API 에서 대량의 데이터를 순차적으로 읽어올 일도 없다.</p>
</li>
</ul>
<br>

<p><strong>오히려 Batch 로직에서 배압이 중요하지 않을까?</strong></p>
<ul>
<li>Batch 서버에서는 Apache Kafka를 통한 이벤트 수신 또는 대량의 상품 데이터를 순차적으로 읽기</li>
</ul>
<pre><code class="language-jsx">fun getProductIds(sellerId: String) Flux&lt;String&gt; {
        return getProducts(serllerId, null)
                .expand { res -&gt; 
                        res.nextCursor.toMono() 
                                .flatMap { cursor -&gt; getProducts(sellerId, cursor) }
                }
                .flatMapIterable { it.items }
                .map { it.productId }
}</code></pre>
<br>

<ul>
<li><p>상품 데이터 전체를 읽는데 커서 기반 페이지네이션으로 읽는 코드</p>
</li>
<li><p>다음 페이지를 언제 가져오는가? 처리량에 따라</p>
</li>
</ul>
<br>


<p><strong>Project Reactor의 장점은 풍부한 연산자로부터</strong></p>
<ul>
<li><p>외부 플랫폼 연동에서 요구되는 사항을 손쉽게 구현 할 수 있다</p>
<ul>
<li><p>1초당 몇 회의 API 호출</p>
</li>
<li><p>1초당 몇 개의 데이터로 호출</p>
</li>
</ul>
</li>
<li><p>큰 chunk 단위를 1초마다 처리하면서 chunk를 작은 chunk를 쪼개서 처리하는 코드</p>
</li>
</ul>
<pre><code class="language-jsx">flux.windowTimeout(
        BIFFER_SIZE * REQUEST_PER_SECONDS,
        Duration.ofSeconds(1),
        /* fairBackpressure = */ true, 
)
        .delayElements(Duration.ofSecond(1))
        .concatMap { window -&gt; 
                window.buffer(BUFFER_SIZE). flatMap { chunk -&gt;
                        importer.import(chunk)
                        }
                    }</code></pre>
<br>


<blockquote>
<p>⇒ 배민 스토어에서는 앞선 내용을 바탕으로 Batch성 로직에서 오히려 프로젝트를 적응하는 것이 배압의 특성을 잘 적용하는 것임을 알게 됨 project reactor 적용으로 방향 수정 </p>
</blockquote>
<blockquote>
<p>⇒ 서비스에 대한 이해도 높아지면서 기술적 이해도도 높아짐</p>
</blockquote>
<p><br><br></p>
<h3 id="3-project-reactor의-높은-러닝-커브가-생산성을-저해하지-않을-만한-수준이었는가">3. Project Reactor의 높은 러닝 커브가 생산성을 저해하지 않을 만한 수준이었는가?</h3>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/5f584607-d88f-4f55-8614-c1266b578e17/image.png" alt=""></p>
<ul>
<li><p>이슈를 파악하는 것도 쉽지 않았다고 합니다. </p>
</li>
<li><p>팀 내에서 지속적으로 스터디 및 노하우를 공유했지만, 잔존하는 이슈들이 많았고, 생산성을 저해하는 부분을 가시화 하는 것은 어려운 일이라고 말씀해주심</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[컬렉션별 데이터 연산의 시간 복잡도 계산 ]]></title>
            <link>https://velog.io/@dayon_log/%EC%BB%AC%EB%A0%89%EC%85%98%EB%B3%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%97%B0%EC%82%B0%EC%9D%98-%EC%8B%9C%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84-%EA%B3%84%EC%82%B0</link>
            <guid>https://velog.io/@dayon_log/%EC%BB%AC%EB%A0%89%EC%85%98%EB%B3%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%97%B0%EC%82%B0%EC%9D%98-%EC%8B%9C%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84-%EA%B3%84%EC%82%B0</guid>
            <pubDate>Wed, 29 May 2024 12:13:20 GMT</pubDate>
            <description><![CDATA[<h3 id="기본-개념">기본 개념</h3>
<ul>
<li>시간복잡도 대표 표현식 위일수록 빠르다.</li>
</ul>
<p>빠른 순서 ↑</p>
<p>상수 시간 $O(1)$</p>
<p>로그시간 $O(log N)$</p>
<p>직선형 시간  $O(N)$</p>
<p>2차 시간 $O(n^2)$</p>
<p>지수 시간 $O(C^n)$</p>
<p>느린 순서 ↓</p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/9d14a954-6778-43a1-8377-af39b43c9a85/image.png" alt=""></p>
<br>

<h2 id="list-자료구조">List 자료구조</h2>
<h3 id="1-arraylist"><strong>1. ArrayList</strong></h3>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/7ca647e6-e472-4d3c-815b-d17818880eb7/image.png" alt=""></p>
<ul>
<li><p><strong><code>ArrayList</code></strong>는 동적 배열을 기반, 연속적인 데이터의 리스트</p>
<pre><code class="language-java">  String[] array = new String[10] ;
  ArrayList&lt;String&gt; list = new ArrayList&lt;&gt; (*initialCapacity:* 10); </code></pre>
</li>
<li><p>데이터는 연속적으로 리스트에 들어있어야 하며, <strong>중간에 빈공간이 있어선 안된다</strong>. → 중간에 삽입/삭제 할때 요소들의 위치를 자동 이동시켜 삽입/삭제 동작이 느리다.</p>
</li>
<li><p>배열을 이용하기에 요소의 인덱스를 통해 <strong>빠르게 직접 접근</strong> 가능</p>
</li>
<li><p>크기가 고정되어 있는 배열과 달리 데이터의 적재량에 따라 <strong>가변적으로 공간을 늘리거나 줄인다</strong>.</p>
</li>
<li><p>객체로 데이터를 다루기 때문에 적은양의 데이터만 쓸 경우 배열보다 사용하는 메모리공간이 더 많다.</p>
</li>
<li><p><strong>삽입</strong></p>
<ul>
<li><p>처음: $O(n)$ / 중간: $O(n)$ / 마지막: $O(1)$ (단, 배열이 꽉 차서 크기를 늘려야 하는 경우 $O(n)$)</p>
</li>
<li><p><code>Object[]</code> 배열을 이용해 요소를 저장한다.</p>
</li>
<li><p>지정된 위치에 요소를 넣을때, 기존의 요소들을 한칸씩 뒤로 미루면서 빈공간을 만들어주어 비효율적</p>
</li>
<li><p>배열 공간이 꽉 찰때마다 배열을 copy하는 방식을 사용해 배열을 늘리는데, 배열 복사 동작 자체의 성능이 좋지 않아 오버헤드 발생</p>
<pre><code class="language-java">  ArrayList&lt;Integer&gt; number = new ArrayList&lt;&gt;();
  number.add(1);
  number.add(3);
  number.add(5);

  // 배열 복사 
  // ArrayList는 내부적으로 Object[] 배열로 저장하기 때문에 형변환이 필요함
  ArrayList&lt;Integer&gt; cloneNumber = (ArrayList&lt;Integer&gt;) number.clone();

  System.out.println(&quot;ArrayList: &quot; + number); // [1, 3, 5]
  System.out.println(&quot;Cloned ArrayList: &quot; + cloneNumber); // [1, 3, 5]</code></pre>
</li>
</ul>
</li>
<li><p><strong>삭제</strong></p>
<ul>
<li>처음: $O(n)$ / 중간: $O(n)$ / 마지막: $O(1)$</li>
<li>삽입과 마찬가지로, 중간에 위치한 요소를 제거할 경우, 빈공간을 채우려 앞으로 이동하게 되는 비용 발생</li>
</ul>
</li>
<li><p><strong>조회</strong>: $O(1)$</p>
</li>
<li><p><strong>검색</strong>: $O(n)$</p>
</li>
</ul>
<br>

<h3 id="2-linkedlist"><strong>2. LinkedList</strong></h3>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/236cfcca-5634-42ce-8333-dc6582b1efdd/image.png" alt=""></p>
<ul>
<li><p><strong><code>LinkedList</code></strong>는 이중 연결 리스트를 기반으로 한, 연속적인 데이터 리스트</p>
<pre><code class="language-java">  LinkedList&lt;String&gt; list = new LinkedList&lt;&gt;();
  list.add(&quot;A&quot;);
  list.add(&quot;B&quot;);
  list.add(&quot;C&quot;);</code></pre>
</li>
<li><p>배열이 아닌, 노드를 연결해 리스트처럼 만든 컬렉션</p>
</li>
<li><p>각 노드는 데이터와 이전 및 다음 노드에 대한 참조를 포함한다.</p>
</li>
<li><p>이중 연결 리스트를 기반으로 하여, 양방향 포인터 구조로 이루어져 있다.</p>
</li>
<li><p><strong>중간 삽입/삭제 시 요소의 위치를 이동시키지 않아도</strong> 되므로 데이터의 중간 삽입, 삭제가 빈번할 경우 빠른 성능 보장한다.</p>
</li>
<li><p>임의의 요소에 대한 접근 성능이 좋지 않다. 
→ 인덱스를 통한 <strong>빠른 직접 접근이 불가능</strong>하여, 특정 요소에 접근하기 위해 처음부터 순차적으로 탐색해야 합니다.</p>
</li>
<li><p>데이터 양에 따라 가변적으로 공간을 늘리거나 줄일 수 있다.</p>
</li>
<li><p><strong>삽입</strong></p>
<ul>
<li><p>처음: O(1) / 중간: O(n) / 마지막: O(1)</p>
</li>
<li><p>처음이나 마지막에 삽입하는 경우, address 부분만 수정하면 되므로, O(1)로 빠르다.</p>
</li>
<li><p>중간에 삽입하는 경우, 삽입 위치를 찾기 위해 순차적으로 탐색해야 하므로 O(n)이 걸린다.</p>
<pre><code class="language-java">  LinkedList&lt;Integer&gt; number = new LinkedList&lt;&gt;();
  number.addFirst(1);
  number.addLast(3);
  number.add(1, 2); // 중간 삽입</code></pre>
</li>
</ul>
</li>
<li><p><strong>삭제</strong></p>
<ul>
<li><p>처음: O(1) / 중간: O(n) / 마지막: O(1)</p>
<pre><code class="language-java">  number.removeFirst();  // 처음 요소 삭제
  number.removeLast();   // 마지막 요소 삭제
  number.remove(0);      // 중간 요소 삭제</code></pre>
</li>
</ul>
</li>
</ul>
<ul>
<li><strong>조회</strong>: O(n)<ul>
<li>특정 인덱스에 있는 요소에 접근하기 위해 처음부터 해당 위치까지 순차적으로 탐색한다.</li>
</ul>
</li>
<li><strong>검색</strong>: O(n)<ul>
<li>특정 요소를 검색하기 위해 처음부터 끝까지 해당 값을 찾을 때까지 순차적으로 탐색한다.</li>
</ul>
</li>
</ul>
<h3 id="추가"><strong>추가</strong></h3>
<blockquote>
<p>ArrayList와 LinkedList는 데이터 삽입이 동일하게 O(1), 배열의 중간 삭제가 O(n)이 걸리는데, 그렇다면 실제 실행속도는 같을까?</p>
</blockquote>
<p>→ ArrayList로 배열 맨뒤에 1억개의 1 삽입 후 , 배열 중간에 1000000번째 인덱스 100개 삭제</p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/24e4c282-a54f-439a-831e-e37ea914c20a/image.png" alt=""></p>
<p>결과  : 삽입 속도: 942ms  / 삭제 속도: 12846ms</p>
<p>→ LinkedList로 배열 맨뒤에 1억개의 1 삽입후, 배열 중간에 1000000번째 인덱스 100개 삭제</p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/1086b9b5-2e22-48a8-aecc-20bf5e767f05/image.png" alt=""></p>
<p>결과 : 삽입 속도: 8314ms / 삭제 속도: 251ms</p>
<p><strong>삽입의 경우</strong> </p>
<ul>
<li>942ms vs 8314ms</li>
<li>시간복잡도는 O(1)로 둘다 동일하지만 이렇게 눈에 띄게 속도 차이가 발생한다.</li>
<li>ArrayList의 경우 추가를 할 때 단순히 배열의 크기를 조정하고 값을 쓰면 되므로 시간이 거의 소요되지 않는다.</li>
<li>LinkedList의 경우 새로운 노드를 선언하고 객체를 생성하고 메모리를 할당해야고 연결하는 등의 비싼 작업이 수행되기 때문에 실제 수행시간에서 차이가 발생하게 된다.</li>
<li>O(1)에서 생략된 상수항의 매우 크다</li>
</ul>
<p><strong>삭제의 경우</strong> </p>
<ul>
<li>12846ms vs 251ms</li>
<li>시간복잡도는 O(n)으로 둘다 동일하다. 하지만, ArrayList 의 경우 중간에서 요소를 삭제할 때, 그 뒤의 모든 요소를 한 칸씩 앞으로 이동해야 한다.이 요소 이동 작업 때문에 삭제에 O(n) 시간이 소요된다.</li>
<li>LinkedList 의 경우, 노드를 삭제할 때, 삭제할 노드를 찾기 위해 평균적으로 O(n) 시간이 소요되는데, 이는 비교적 비용이 적게드는 탐색에 소요되는 시간이고, 실제 삭제 작업 자체는 연결만 바꿔주면 되기에  O(1) 시간이 소요된다.</li>
</ul>
<hr>
<br>

<hr>
<h2 id="map-자료구조">Map 자료구조</h2>
<h3 id="해시함수란">해시함수란?</h3>
<ul>
<li><p>임의 길이의 입력 값을 고정 길이의 암호화된 출력으로 변환해주는 함수</p>
</li>
<li><p>특징</p>
<ol>
<li>어떤 입력 값에도 항상 고정된 길이의 해시값을 출력한다.</li>
<li>입력 값의 아주 일부만 변경되어도 전혀 다른 결과 값을 출력한다.</li>
<li>출력된 결괏값을 통해 입력값을 유추할 수 없다.</li>
</ol>
</li>
<li><p>해시 충돌(Hash Collision)이란?</p>
<ul>
<li><p>입력값이 다르더라도 같은 결과값이 나오는 경우, 해시 충돌이 적은 함수가 좋은 함수</p>
</li>
<li><p>해결방법 1 ) <strong>개방 주소법 (open addressing)</strong> : 해시 테이블 크기는 고정하면서 저장할 위치를 찾는다</p>
</li>
<li><p>해결방법 2 ) <strong>분리연결법 (separate chaining)</strong> : 해시 테이블의 크기를 유연하게 만든다.</p>
<p>   버켓 내에 연결리스트(Linked List)를 할당하여, 버켓에 데이터를 삽입하다가해시 충돌이 발생하면 연결리스트로 데이터들을 연결하는 방</p>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="3-hashmap"><strong>3. HashMap</strong></h3>
<ul>
<li><p><strong><code>HashMap</code></strong>은 HashTable 을 기반으로 한 키-값 쌍 저장소 (각 키는 고유하고, 값은 키와 연결된다)</p>
<blockquote>
<p><code>HashTable</code> : (Key, Value)로 데이터를 저장하는 자바 초기 버전에 나온 레거시 클래스</p>
<p>HashMap은 보조 해시 함수(Additional Hash Function)를 사용하기 때문에 보조 해시 함수를 사용하지 않는 HashTable에 비하여 해시 충돌(hash collision)이 덜 발생할 수 있어 상대으로 성능상 이점이 있다. </p>
<p>HashMap 보다는 느리지만 기본적으로 병렬 프로그래밍을 지원해 동기화가 제공된다. </p>
</blockquote>
</li>
<li><p>데이터를 해시 함수에 따라 저장하기에 빠른 검색이 가능하다</p>
</li>
<li><p>데이터의 순서를 보장하지 않는다.</p>
</li>
</ul>
<br>

<ul>
<li><p><strong>조회 / 삽입 / 삭제 / 검색</strong>: O(1)</p>
</li>
<li><p>키를 해시 함수를 통해 해시 코드로 변환후 해당 위치 저장, 삭제, 조회, 반환</p>
</li>
<li><p>next() : 반복자를 사용해 해시맵의 모든 항목 순회 : O(h/n)</p>
<ul>
<li>h = 해시맵의 내부 버킷의 수 , n은 해시맵의 크기</li>
</ul>
</li>
<li><p><strong>해시 충돌이 발생할 경우</strong></p>
<ul>
<li><p>java 7 이전, separate chaining에서 linked list를 고정적으로 사용해 해결
최악의 경우 = 모든 항목이 하나의 버킷에 모이는 경우 → 최대 O(n)</p>
</li>
<li><p>java 8 부터 데이터 개수가 많아지면 트리를 사용하도록 변경 → 최악의 경우에도 O(log N)</p>
<p>  하나의 해시 버킷에 8개의 키-값 쌍이 모이면 링크드 리스트를 트리로 변경하고, 해당 버킷에 있는 데이터를 삭제하여 개수가 6개에 이르면 다시 링크드 리스트로 변경한다
  (7이 아니라 6인 이유, 1의 차이로 트리와 링크드 리스트로 변경하는 일이 반복되는게 오히려 성능저하) </p>
</li>
</ul>
</li>
</ul>
<br>


<p>예시 코드</p>
<pre><code class="language-java">HashMap&lt;String, Integer&gt; hashMap = new HashMap&lt;&gt;();

// 삽입
hashMap.put(&quot;apple&quot;, 10);
hashMap.put(&quot;banana&quot;, 20);
hashMap.put(&quot;orange&quot;, 15);

// 중복된 키는 새로운 값으로 대체됨
hashMap.put(&quot;apple&quot;, 30);

// 삭제
hashMap.remove(&quot;banana&quot;);

// 조회
for (String key : hashMap.keySet()) {
     System.out.println(key + &quot;: &quot; + hashMap.get(key));
}

// 검색
boolean containsApple = hashMap.containsKey(&quot;apple&quot;);
System.out.println(&quot;Contains key &#39;apple&#39;: &quot; + containsApple);</code></pre>
<p>위의 코드에서, HashMap은 키-값 쌍을 저장하는 데 사용되며, 키를 통해 값에 접근할 수 있다. </p>
<p>삽입, 삭제, 조회, 검색 모두 상수 시간(O(1))에 이뤄진다.</p>
<br>

<h3 id="4-treemap"><strong>4. TreeMap</strong></h3>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/11300c7b-c066-4f55-a99e-6671c1395171/image.png" alt=""></p>
<ul>
<li><p><strong><code>TreeMap</code></strong>은 이진 검색 트리를 기반으로 한 키-값 쌍 저장소로, 키를 정렬된 순서로 유지합니다.</p>
</li>
<li><p>TreeMap은 SortedMap 인터페이스를 구현하고있어 Key값을 기준으로 오름차순 정렬</p>
</li>
<li><p>키와 값을 저장함과 동시에 정렬까지 하기에 일반 Map보다 추가, 삭제에 시간이 오래 걸린다.</p>
</li>
<li><p>정렬된 순서로 저장되기에 빠른 검색(범위 검색)이 가능하다</p>
</li>
<li><p>정렬되는 순서 : 숫자 → 대문자 → 소문자 → 한글</p>
</li>
<li><p>사용되는 자료구조 : <strong>Red-Black Tree</strong></p>
<ul>
<li>이진 탐색 트리가 데이터가 한쪽으로 치우쳐 들어올 경우 depth 가 커져, 매우 비효율적인 성능을 나타내는데, 이를 보완하기 위해 등장한 트리의 높이가 최소화된, 균형 이진 탐색 트리</li>
<li>O(log N)의 시간복잡도 보장</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/730615f6-38fc-42af-a6ad-772bd8e257b1/image.png" alt=""></p>
<ul>
<li>삽입, 삭제, 조회, 검색 연산은 모두 Red-Black Tree의 특성에 따라 O(log N)의 시간복잡도를 갖는다.</li>
<li>트리의 높이에 비례하는 시간이 소요되므로, 트리가 균형적인 상태를 유지할수록 연산의 속도가 빨라진다.</li>
</ul>
<br>

<pre><code class="language-java">import java.util.*;

public class TreeMapExample {
    public static void main(String[] args) {
        // TreeMap 객체 생성
        TreeMap&lt;Integer, String&gt; treeMap = new TreeMap&lt;&gt;();

        // 키-값 쌍 추가
        treeMap.put(3, &quot;Three&quot;);
        treeMap.put(1, &quot;One&quot;);
        treeMap.put(4, &quot;Four&quot;);
        treeMap.put(2, &quot;Two&quot;);
        treeMap.put(5, &quot;Five&quot;);

        // TreeMap 출력 (정렬된 순서로 출력됨)
        System.out.println(&quot;TreeMap: &quot; + treeMap);

        // 조회 
        System.out.println(&quot;Value for key 3: &quot; + treeMap.get(3));

        // 키로 정렬된 순서대로 반복문을 통해 출력
        System.out.println(&quot;Keys in ascending order:&quot;);
        for (Map.Entry&lt;Integer, String&gt; entry : treeMap.entrySet()) {
            System.out.println(entry.getKey() + &quot;: &quot; + entry.getValue());
        }

        // TreeMap을 역순으로 정렬된 순서대로 출력
        NavigableMap&lt;Integer, String&gt; descendingMap = treeMap.descendingMap();
        System.out.println(&quot;Keys in descending order:&quot;);
        for (Map.Entry&lt;Integer, String&gt; entry : descendingMap.entrySet()) {
            System.out.println(entry.getKey() + &quot;: &quot; + entry.getValue());
        }
    }
}</code></pre>
<p>출력</p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/062c415a-b61f-474c-b229-cd96e578c095/image.png" alt=""></p>
<br>

<hr>
<h2 id="set-자료구조">Set 자료구조</h2>
<p>데이터의 중복을 허용하지 않고, 순서를 유지하지 않는 데이터 집합 리스트</p>
<p>순서 자체가 없어 인덱스로 객체를 검색해서 가져오는 get() 메서드도 존재하지 않는다. </p>
<p>중복 저장이 불가능하여 null값도 하나만 저장할 수 있다. </p>
<h3 id="5-hashset---수정중"><strong>5. HashSet - 수정중</strong></h3>
<ul>
<li><p><strong><code>HashSet</code></strong>은 해시 테이블을 기반으로, 중복 요소를 허용하지 않는다.</p>
</li>
<li><p>저장한 순서가 보장되지 않는다. (순서를 유지해야한다면 LinkedHashSet을 사용하기)</p>
</li>
<li><p>배열과 연결노드를 결합한 자료구조의 형태</p>
</li>
<li><p>가장 빠른 임의 검색 접근 속도를 가진다.</p>
</li>
<li><p><strong>삽입 / 삭제 / 검색</strong>: O(1)</p>
<ul>
<li>이상적인 경우(해시 함수 충돌이 적은 경우)에는 상수 시간(O(1))에 삽입이 이루어집니다.</li>
</ul>
</li>
<li><p><strong>조회</strong>: N/A (랜덤 접근이 불가능함)</p>
</li>
<li><p>next() : 해시 테이블의 버킷을 순회하면서 요소를 하나씩 반환 : O(h/n)</p>
<ul>
<li>HashSet 인스턴스의 크기(요소 수) 와 인스턴스의 용량(버킷 수)의 합계에 비례하는 시간이 필요</li>
</ul>
</li>
<li><p><strong>삽입</strong>  : add()</p>
<pre><code class="language-java">  HashSet&lt;String&gt; animals = new HashSet&lt;&gt;()
      animals.add(&quot;tiger&quot;);
      animals.add(&quot;lion&quot;);
      animals.add(&quot;fox&quot;);</code></pre>
</li>
<li><p><strong>삭제 : remove()</strong></p>
<pre><code class="language-java">  animals.remove(&quot;lion&quot;);</code></pre>
</li>
<li><p><strong>검색 : contains()</strong></p>
<pre><code class="language-java">  boolean containsTiger = animals.contains(&quot;tiger&quot;);
  System.out.println(&quot;Contains &#39;tiger&#39;: &quot; + containsTiger);</code></pre>
</li>
</ul>
<br>


<h3 id="6-treeset">6. TreeSet</h3>
<ul>
<li><p><strong><code>TreeSet</code></strong>은 이진 검색 트리를 기반으로 하며, 정렬된 순서로 요소를 저장한다</p>
</li>
<li><p>데이터를 정렬하여 저장한다.</p>
</li>
<li><p>정렬, 검색, 범위 검색에 높은 성능을 뽐낸다.</p>
</li>
<li><p><strong>삽입/ 삭제 / 조회 / 검색</strong>: O(log n)</p>
</li>
</ul>
<p>HashSet과 TreeSet은 각각 해시 테이블과 이진 검색 트리를 기반으로 하기 때문에, 연산 복잡도가 각각 O(1)과 O(log n)입니다. 하지만 실제 실행 시간은 해당 자료구조의 내부 동작 및 구현 방식, 그리고 입력 데이터의 속성에 따라 달라질 수 있습니다.</p>
<pre><code class="language-java">import java.util.TreeSet;

public class TreeSetExample {
    public static void main(String[] args) {
        TreeSet&lt;Integer&gt; treeSet = new TreeSet&lt;&gt;();

        // 삽입
        treeSet.add(5);
        treeSet.add(3);
        treeSet.add(7);
        treeSet.add(1);

        // 삭제
        treeSet.remove(3);

        // 조회
        for (int num : treeSet) {
            System.out.println(num);
        }

        // 검색
        boolean containsFive = treeSet.contains(5);
        System.out.println(&quot;Contains 5: &quot; + containsFive);
    }
}
</code></pre>
<hr>
<br>

<h2 id="결론"><strong>결론</strong></h2>
<p>자바의 컬렉션 프레임워크는 다양한 자료구조를 제공하고, 각 자료구조를 요구되는 상황에 적절하게 사용하는것이 중요하다. </p>
<ul>
<li>ArrayList<ul>
<li>인덱스를 통한 빠른 접근이 필요한 경우</li>
<li>순차적인 추가/삭제 제일 빠르고, 요소의 추가/삭제에는 좋지 않음</li>
</ul>
</li>
<li>Linked List<ul>
<li>빈번한 삽입과 삭제가 필요한 경우 적합</li>
<li>임의의 요소에 대한 접근성이 좋지 않음</li>
</ul>
</li>
<li>HashMap / HashSet<ul>
<li>빠른 검색과 중복 요소 제거가 필요한 경우</li>
<li>검색에 최고 성능</li>
</ul>
</li>
<li>TreeMap / TreeSet<ul>
<li>정렬된 데이터를 유지해야할 때</li>
<li>검색( 특히 범위 검색) 에 적합</li>
</ul>
</li>
</ul>
<br>

<hr>
<p>참고 </p>
<ul>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html">https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html</a></li>
<li><a href="https://d2.naver.com/helloworld/831311">https://d2.naver.com/helloworld/831311</a></li>
<li><a href="https://inpa.tistory.com/entry/JAVA-%E2%98%95-ArrayList-%EA%B5%AC%EC%A1%B0-%EC%82%AC%EC%9A%A9%EB%B2%95">https://inpa.tistory.com/entry/JAVA-☕-ArrayList-구조-사용법</a></li>
<li><a href="https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-Collections-Framework-%EC%A2%85%EB%A5%98-%EC%B4%9D%EC%A0%95%EB%A6%AC#hashmap_%ED%81%B4%EB%9E%98%EC%8A%A4">https://inpa.tistory.com/entry/JCF-🧱-Collections-Framework-종류-총정리#hashmap_클래스</a></li>
<li><a href="https://velog.io/@dlzlqlzl/Java-%EC%9E%90%EB%B0%94-%EC%BB%AC%EB%A0%89%EC%85%98-%EC%8B%9C%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84-%EB%B9%84%EA%B5%90-%EC%A0%95%EB%A6%AC">https://velog.io/@dlzlqlzl/Java-자바-컬렉션-시간-복잡도-비교-정리</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[PostgreSQL과 MySql의 차이]]></title>
            <link>https://velog.io/@dayon_log/PostgreSQL%EA%B3%BC-MySql%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@dayon_log/PostgreSQL%EA%B3%BC-MySql%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Fri, 24 May 2024 15:47:28 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dayon_log/post/a3dc2c7c-4e40-4c69-a5f0-55f2761738ce/image.webp" alt=""></p>
<p>DB를 선택을 할때에는 어떤 프로젝트를 진행하는지, 요구사항에 맞춰 선택하고는 한다. 이때, 프로젝트를 진행할때 대표적으로 사용되는 MySQL과 PostgreSQL의 차이를 알아보자. </p>
<h3 id="postgresql과-mysql의-공통점">PostgreSQL과 MySQL의 공통점</h3>
<ul>
<li>SQL 언어를 인터페이스로 사용하여 데이터를 읽고 편집 가능</li>
<li>데이터 백업, 복제, 엑세스 제어 기능이 내장되어 있음</li>
<li>무료 오픈소스, 대규모의 커뮤니티 지원</li>
</ul>
<h2 id="mysql">MySQL</h2>
<h3 id="소개">소개</h3>
<ul>
<li>오라클에서 관리되는, 가장 많이 사용되는 관계형 데이터베이스 관리 시스템(RDBMS)</li>
<li>주로 웹 애플리케이션에 대해 가볍고 효율적이며 안정적인 상태를 유지할 수 있다.</li>
<li>대규모의 데이터 셋을 빠르고 효율적으로 처리하기 때문에 대량의 데이터를 저장하고, 엑세스 해야 하는 기업에서 인기가 많다.<ul>
<li>Facebook, Netflix, Github  및 대형 기업들은 MySQL을 사용하고 있다.</li>
</ul>
</li>
</ul>
<h3 id="장점-및-사용해야-하는-경우">장점 및 사용해야 하는 경우</h3>
<ul>
<li><p>표준 SQL을 완전히 준수하지 않는 대신 속도와 안정성을 위해 디자인 되었다.</p>
<ul>
<li><p>InnoDB 및 NDB 클러스터 스토리지 엔진에서만 ACID를 준수한다.</p>
<blockquote>
<p>ACID = 트랜젝션의 원칙(원자성, 일관성, 고립성, 지속성) 을 통해 예상치 못한 오류가 발생한 후에도 데이터베이스를  유효한 상태로 유지하는 데이터 베이스 속성</p>
</blockquote>
</li>
<li><p>속도를 우선순위로 지정해, 매우 빠른 속도를 보여준다</p>
</li>
</ul>
</li>
<li><p>MySQL을 사용하는 애플리케이션은 별도의 데몬 프로세스를 통해 데이터베이스에 엑세스 한다.</p>
</li>
<li><p>MVCC(다중 버전 동시성 제어)를 제공한다.</p>
</li>
<li><p>읽기 전용 명령을 관리하는데 선호된다.</p>
</li>
<li><p>보안 수준이 높은 데이터베이스.</p>
<ul>
<li>MySQL은 설치 시 암호 보안 수준을 설정할 수 있고 루트 사용자의 암호를 정의하며 익명 계정을 제거하고 모든 사용자가 접근할 수 있는 기본 테스트 데이터베이스를 젝거해 보안 향상에 도움이 되는 스크립트와 함께 설치</li>
</ul>
</li>
</ul>
<h2 id="postgresql">PostgreSQL</h2>
<h3 id="개요">개요</h3>
<ul>
<li>오픈소스 객체 관계형 데이터베이스 관리 시스템(ORDBMS) : 관계형과 객체 지향 기능을 결합한 하이브리드 데이터베이스 시스템</li>
<li>“카탈로그 기반” 운영 방식, 다른 DBMS보다 훨씬 더 많은 기능을 갖추었다.</li>
<li>ACID 준수, 높은 동시 트랜잭션을 달성하고 있다.</li>
<li>사용자 정의 가능, 뛰어난 확장성과 유연성으로 다양하고, 복잡한 데이터 유형을 관리하는데 적합하다.</li>
</ul>
<h3 id="장점-및-사용해야-하는-경우-1">장점 및 사용해야 하는 경우</h3>
<ul>
<li>읽기-쓰기 작업, 대규모 데이터 세트 및 복잡한 퀀리를 관리하는 경우 선호된다,</li>
<li>읽기 전용 작업에서는 선호되지 않는다.</li>
<li>초대형 데이터베이스를 관리해야 할때</li>
<li>엄격한 SQL 표준 준수로 잘 알려져 있다.<ul>
<li>최고수준의 ACID 규정 준수</li>
</ul>
</li>
</ul>
<h2 id="차이점">차이점</h2>
<ul>
<li>MySQL은 PostgreSQL 보다 기능이 적지만, 그 덕분에 읽기 전용 쿼리에서 더 가볍고 안정적이며 빠른 처리 속도를 유지한다.<ul>
<li>Spotify, IMDB, Apple, Cisco..</li>
</ul>
</li>
<li>MySQL은 대소문자를 구분하지 않지만, PostgreSQL은 대소문자를 구분한다.</li>
<li>MySQL에서 IF 및 IFNULL 문을 사용해도 아무런 문제가 되지 않는다.  PostgreSQL에서는 IF 및 IFNULL 문은 사용할 수 없습니다. 대신 CASE 문을 사용한다.</li>
<li>PostgreSQL 경우 문자 집합과 문자열을 UTF-8을 허용하지 않는다. 변환할 필요가 없다.</li>
<li>인덱싱 방법</li>
<li>MySQL 인덱스  :<ul>
<li>INDEX, FULLTEXT, PRIMARY KEY, UNIQUE 등 B-tree에 저장된 인덱스</li>
<li>공간 데이터 형식에서 찾을 수 있는 인덱스 등 R-tree에 저장된 인덱스</li>
<li>FULLTEXT 인덱스 사용 시 해시 인덱스 및 역 리스트</li>
</ul>
</li>
<li>PostgrSQL 인덱스 :<ul>
<li>해시 인덱스 및 B-tree 인덱스</li>
<li>테이블 일부의 정보만 정리하는 부분 인덱스</li>
<li>열 값과 반대로 수식 함수의 결과로 인덱스를 만드는 수식 인덱스</li>
</ul>
</li>
</ul>
<table>
<thead>
<tr>
<th>기능, 특징</th>
<th>MySQL</th>
<th>PostgreSQL</th>
</tr>
</thead>
<tbody><tr>
<td></td>
<td>RDBMS (관계형 데이터베이스 관리 시스템)</td>
<td>ORDBMS (객체 관계형 데이터베이스 관리 시스템)</td>
</tr>
<tr>
<td>ACID 준수</td>
<td>대부분의 엔진은 ACID 규정을 준수하지만, MyISAM은 지원하지 않는다.</td>
<td>완벽 지원</td>
</tr>
<tr>
<td>크로스플랫폼</td>
<td>대응</td>
<td>UNIX 기반 시스템에 최적</td>
</tr>
<tr>
<td>외부키</td>
<td>MyISAM은 지원하지 않는다.</td>
<td>완벽 지원</td>
</tr>
<tr>
<td>저장 프로시저</td>
<td>지원 O</td>
<td>PL/pgSQL 언어로 고급화</td>
</tr>
<tr>
<td>웹 애플리케이션</td>
<td>속도와 안정성으로 널리 인정</td>
<td>복잡한 사용 사례에서 인기</td>
</tr>
<tr>
<td>트리거</td>
<td>지원 O</td>
<td>다른 언어로도 작성할 수 있는 유연성</td>
</tr>
<tr>
<td>뷰</td>
<td>지원 O</td>
<td>구체화된 뷰 제공(비용, 연산량 많음)</td>
</tr>
</tbody></table>
<p>출시 당시 두 DB는 차이점이 컸지만 새로운 버전이 출시될때마다 데이터베이스의 시스템의 기능과 성능간의 격차는 줄어들고 있다.</p>
<p>최근에 MySQL은 PostgreSQL에서만 제공하던 JSON과의 호환, 다중 버전 동시성 제어, 윈도우 함수, 지리정보 시스템과 공간 참조 시스템(GIS , SRS) 등의 기능을 제공하기 시작</p>
<p>또한 PostgreSQL은 MySQL에만 있던 기능을 제공하기 시작. 테이블을 더 작고 관리하기 쉬운 테이블로 세분화하는 선언적 파티셔닝, 논리적 복제, 준동기 복제 등 </p>
<h2 id="결론">결론</h2>
<ul>
<li>설정과 관리가 쉽고, 빠르고, 안정적, 이해도가 높은 간단한 데이터베이스 = MySQL</li>
<li>복잡한 쿼리와 대규모 데이터베이스를 처리할 수 있는 풍부한 기능을 갖춘 데이터베이스 = PostgreSQL</li>
</ul>
<hr>
<p>참고</p>
<ul>
<li><a href="https://aws.amazon.com/ko/compare/the-difference-between-mysql-vs-postgresql/">https://aws.amazon.com/ko/compare/the-difference-between-mysql-vs-postgresql/</a></li>
<li><a href="https://f-lab.kr/insight/mysql-vs-postgresql">https://f-lab.kr/insight/mysql-vs-postgresql</a></li>
<li><a href="https://smoh.tistory.com/m/369">https://smoh.tistory.com/m/369</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java - Lamda 개념을 정리해보자 ]]></title>
            <link>https://velog.io/@dayon_log/Java-Lamda-%EA%B0%9C%EB%85%90%EC%9D%84-%EC%A0%95%EB%A6%AC%ED%95%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@dayon_log/Java-Lamda-%EA%B0%9C%EB%85%90%EC%9D%84-%EC%A0%95%EB%A6%AC%ED%95%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Mon, 20 May 2024 14:28:17 GMT</pubDate>
            <description><![CDATA[<h2 id="람다식-lambda-expression">람다식 (Lambda Expression)</h2>
<ul>
<li><p>자바 8에서 도입됨</p>
</li>
<li><p>함수(메서드)를 하나의 식으로 표현하는 방법</p>
</li>
<li><p>익명함수 (anonymous function)</p>
</li>
<li><p>람다식으로 표현하면 메서드의 이름과 반환값을 생략할 수 있고, 이를 변수에 넣어 자바 코드가 매우 간결해진다.</p>
</li>
<li><p>메서드</p>
</li>
</ul>
<pre><code class="language-java">int max(int a, int b) {
        return a &gt; b ? a : b ;
}</code></pre>
<ul>
<li>람다식</li>
</ul>
<pre><code class="language-java">(a, b) -&gt; a &gt; b ? a : b </code></pre>
<br>


<h2 id="람다식-작성하기">람다식 작성하기</h2>
<ol>
<li><p>메서드의 이름과 반환타입을 제거하고, “→” 를 블록{} 앞에 추가한다</p>
<pre><code class="language-java"> ~~int max~~(int a, int b) -&gt; {
         return a &gt; b ? a : b ;
 }</code></pre>
</li>
<li><p>반환값이 있는 경우 식이나 값만 적고, return문 생략 가능 (끝에 “;” 를 안붙인다) </p>
<pre><code class="language-java"> (int a, int b) -&gt; a &gt; b ? a : b </code></pre>
</li>
<li><p>매개변수의 타입이 추론 가능하면 생략 가능 </p>
<pre><code class="language-java"> (a, b) -&gt; a &gt; b ? a : b </code></pre>
</li>
</ol>
<br>    

<h3 id="람다식-작성시-주의-해야-할-사항">람다식 작성시 주의 해야 할 사항</h3>
<ol>
<li>매개변수가 하나인 경우, 괄호() 생략가능 - 타입이 없을 때만 </li>
</ol>
<pre><code class="language-java">( a ) -&gt; a * a    
(int a) -&gt; a * a </code></pre>
<pre><code class="language-java">a -&gt; a * a      // OK
int a -&gt; a * a  // 에러 </code></pre>
<ol start="2">
<li><p>블록 안의 문장이 하나 뿐일 때, 괄호{} 생략 가능  (끝에 “;” 를 안붙인다) </p>
<p> 단 하나뿐인 문장이 return문이면 괄호{} 생략 불가 </p>
<pre><code class="language-java"> (int i) -&gt; {
         System.out.println(i);
 }</code></pre>
<pre><code class="language-java"> (int i) -&gt; System.out.println(i)</code></pre>
</li>
</ol>
<br>

<h3 id="익명-화살표-함수-→">익명 화살표 함수 <code>→</code></h3>
<ul>
<li><p>자바스크립트의 익명 화살표 함수 자체가 람다 함수의 일종</p>
</li>
<li><p>변수에 함수를 담을때, 자바는 강타입 언어이기에 반드시 함수에 대한 타입을 선언해야 한다.</p>
</li>
<li><p>하지만 자바에 8가지 타입밖에 없어, 마땅한 자료형이 없고, 인터페이스를 익명 구현 객체 타입으로서 함수를 해당 인터페이스 타입으로 받을수 있게 설계한것</p>
<ul>
<li><p>Primitive Type(원시타입) : 실제 데이터 값을 저장한다 
(int, long, double, float, boolean, byte, short, char)</p>
</li>
<li><p>Reference Type(참조 타입) : 객체를 참조하는 타입, 메모리 번지값을 통해 객체를 참조
Integer, Long, Double, Float, Boolean, Byte, Short, Char</p>
</li>
<li><p>자바는 오토박싱, 언박싱이 잘 되어 있어, 원시 타입과 참조타입을 코드 상에 같이 사용해도 큰 문제는 발생하지 않는다.</p>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="람다식은-익명-함수가-아니라-익명-객체이다">람다식은 익명 함수가 아니라 익명 객체이다.</h3>
<ul>
<li>인터페이스를 익명 클래스로 구현한 익명 구현 객체</li>
<li>자바는 메소드를 단독으로 선언할 수 없다.</li>
</ul>
<pre><code class="language-java">(a, b) -&gt; a &gt; b ? a : b 

// 오른쪽과 동일한 코드 </code></pre>
<pre><code class="language-java">new Object() {
        int max(int a , int b) {
                return a &gt; b ? a : b ;
        }
}</code></pre>
<p>→ 익명 클래스, 익명 객체 : 객체의 선언과 생성을 동시에 한다. </p>
<ul>
<li><p>람다식(익명 객체)을 다루기 위한 참조변수가 필요하다. 참조 변수의 타입은 ?</p>
<pre><code class="language-java">  Object obj = new Object() {
          int max(int a , int b) {
                  return a &gt; b ? a : b ;
          }
  }</code></pre>
<pre><code class="language-java">  타입? obj = (a, b) -&gt; a &gt; b ? a : b ; </code></pre>
<p>  ⇒ <strong>함수형 인터페이스 타입</strong> </p>
</li>
</ul>
<h3 id="함수형-인터페이스">함수형 인터페이스</h3>
<ul>
<li><strong>딱 하나의 추상 메소드가 선언된 인터페이스</strong>를 말한다.</li>
<li>람다식은 함수형 인터페이스 안에 정의된 하나의 추상 메소드 선언을 짧게 표현한 것</li>
</ul>
<pre><code class="language-java">// 함수형 인터페이스가 될 수 있다.
interface OkMax {
    int max(int x, int y);
}

// 함수형 인터페이스가 될수 없다.
interface NotCalculate {
    int max(int x, int y);
    int min(int x, int y);
}

// 구성요소가 많아도 결국 추상 메서드는 한개이기 때문에 함수형 인터페이스이다.
interface OkMax {
    int max(int x, int y);

    final boolean isNumber = true; // final 상수
    default void print() {};        // default 메서드
    static void print2() {};        // static 메서드
}
</code></pre>
<h3 id="functionalinterface">@FunctionalInterface</h3>
<p>인터페이스 선언시 <code>@FunctionalInterface</code> 을 사용하여, 함수형 인터페이스임을 명시적으로 선언한다. </p>
<p>이 어노테이션을 사용하면 컴파일러가 하나의 추상 메소드만 가지는지 검증하여 잘못된 사용을 방지할 수 있다. </p>
<pre><code class="language-java">@FunctionalInterface
interface MyFunctionalInterface {
    void execute();
}</code></pre>
<h3 id="람다식의-타입-추론">람다식의 타입 추론</h3>
<p>컴파일러 스스로 람다 함수 식을 보고 타입을 유추할 수 있다. </p>
<pre><code class="language-java">// 함수형 인터페이스
interface OkMax {
    int max(int x, int y);
}

// 람다식을 사용한 예
OkMax lambda = (a, b) -&gt; a &gt; b ? a : b;</code></pre>
<ul>
<li><p>람다식을 받는 메소드의 매개변수 타입을 보고, 함수형 인터페이스의 정의문을 찾아 추상 메소드의 형태를 본뒤에 추상메소드에 정의된 타입에 따라 람다 함수식의 타입을 자동으로 판별한다.</p>
</li>
<li><p>대부분의 함수형 인터페이스를 이용할때 제네릭(Generics)를 사용하게 되는데, 컴파일러가 타입을 추론하는데, 필요한 타입 정보 대부분을 제네릭에서 판별하여 얻는다.</p>
</li>
</ul>
<p>아래 Collectors.toList() 의 정의문을 보면, 람다함수의 매개변수 타입은 제네릭T 타입이 들어있는 걸 볼수 있다. </p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/05c162b3-3274-44e3-8802-16a0d65747a7/image.png" alt=""></p>
<p>toList() 메소드 </p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/4487af81-e524-4e67-b323-f31b997a0f30/image.png" alt=""></p>
<h3 id="표준-함수형-인터페이스">표준 함수형 인터페이스</h3>
<table>
<thead>
<tr>
<th>함수형 인터페이스</th>
<th>매개변수</th>
<th>메서드</th>
<th>반환값</th>
</tr>
</thead>
<tbody><tr>
<td>Runnable</td>
<td>X</td>
<td>void run()</td>
<td>X</td>
</tr>
<tr>
<td>Supplier<T></td>
<td>X</td>
<td>T get()</td>
<td>O <T></td>
</tr>
<tr>
<td>Consumer<T></td>
<td>O <T></td>
<td>void accept(T t)</td>
<td>X</td>
</tr>
<tr>
<td>Function&lt;T, R&gt;</td>
<td>O <T></td>
<td>R apply(T t)</td>
<td>O (R)</td>
</tr>
<tr>
<td>Predicate<T></td>
<td>O <T></td>
<td>boolean test(T t)</td>
<td>O (boolean)</td>
</tr>
</tbody></table>
<br>


<h2 id="람다식과-메서드-참조">람다식과 메서드 참조</h2>
<ul>
<li>람다식에서 파라미터의 중복을 피하기 위해 사용한다.</li>
</ul>
<ul>
<li>**  람다식의 메소드 참조 문법을 사용하기 위해서는 다음의 3가지 조건을 만족해야 한다. **</li>
</ul>
<ol>
<li><p>함수형 인터페이스의 매개 변수 타입 == 메소드의 매개변수 타입</p>
</li>
<li><p>함수형 인터페이스의 매개 변수 개수 == 메소드의 매개변수 개수</p>
</li>
<li><p>함수형 인터페이스의 반환 타입 == 메소드의 반환 타입 </p>
<br>

</li>
</ol>
<h3 id="메소드-참조-종류">메소드 참조 종류</h3>
<ol>
<li><p>정적 메서드 참조</p>
<pre><code class="language-java"> // 기존 람다식
 Function&lt;String, Integer&gt; stringToInteger = (s) -&gt; Integer.parseInt(s);

 // 정적 메소드 참조
 Function&lt;String, Integer&gt; stringToInteger = Integer::parseInt;</code></pre>
</li>
<li><p>인스턴스 메서드 참조</p>
<pre><code class="language-java"> // 기존 람다식
 BiFunction&lt;String, String, Boolean&gt; stringEquals = (s1, s2) -&gt; s1.equals(s2);

 // 인스턴스 메소드 참조
 BiFunction&lt;String, String, Boolean&gt; stringEquals = String::equals;</code></pre>
</li>
<li><p>매개변수의 메서드 참조 </p>
<pre><code class="language-java"> Function&lt;String, Integer&gt; size;

 // 기존 람다 식 
 size = (String s1) -&gt;  s1.length();

 // 매개변수의 메서드 참조 
 size = String::length;</code></pre>
</li>
<li><p>생성자 참조</p>
<pre><code class="language-java"> // 기존 람다식
 Supplier&lt;ArrayList&lt;String&gt;&gt; listSupplier = () -&gt; new ArrayList&lt;&gt;();

 // 생성자 참조
 Supplier&lt;ArrayList&lt;String&gt;&gt; listSupplier = ArrayList::new;</code></pre>
</li>
</ol>
<br>


<hr>
<p>참고</p>
<ul>
<li><a href="https://youtu.be/4ZtKiSvZNu4?si=2oPzMtEUADRxk_Zn">https://youtu.be/4ZtKiSvZNu4?si=2oPzMtEUADRxk_Zn</a></li>
<li><a href="https://inpa.tistory.com/entry/%E2%98%95-Lambda-Expression#functionalinterface">https://inpa.tistory.com/entry/☕-Lambda-Expression#functionalinterface</a></li>
<li><a href="https://inpa.tistory.com/entry/JAVA8-%E2%98%95-%EB%9E%8C%EB%8B%A4%EC%8B%9D%EC%9D%84-%EB%8D%94-%EC%A7%A7%EA%B2%8C-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%B0%B8%EC%A1%B0Method-Reference#%EC%A0%95%EC%A0%81_%EB%A9%94%EC%86%8C%EB%93%9C_%EC%B0%B8%EC%A1%B0">https://inpa.tistory.com/entry/JAVA8-☕-람다식을-더-짧게-메소드-참조Method-Reference#정적_메소드_참조</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Collection 과 Stream]]></title>
            <link>https://velog.io/@dayon_log/Collection-%EA%B3%BC-Stream</link>
            <guid>https://velog.io/@dayon_log/Collection-%EA%B3%BC-Stream</guid>
            <pubDate>Wed, 15 May 2024 14:05:27 GMT</pubDate>
            <description><![CDATA[<h1 id="collection이란">Collection이란?</h1>
<ul>
<li><p><strong>Collection</strong></p>
<ul>
<li><p>데이터를 수집하기 위한 집합</p>
</li>
<li><p>집계된 데이터를 저장, 검색, 조작, 전달하는 데 사용</p>
<br>


</li>
</ul>
</li>
</ul>
<ul>
<li><strong>Collection Framework</strong><ul>
<li>데이터를 수집하여 저장하기 위해 사용할 수 있도록 정의해 놓은 클래스들을 표준화한 설계</li>
<li>데이터 그룹을 다루고 표현하기 위한 단일화 된 구조</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/967fdf3e-f720-4cb3-b5a1-076225b878b0/image.png" alt=""></p>
<br>

<ul>
<li>Collection에는 List, Map, Set, Stack, Queue 등이 있다.</li>
<li>컬렉션은 각 인터페이스와 클래스들이 계층 구조로 이뤄져있다.</li>
</ul>
<br>

<table>
<thead>
<tr>
<th>인터페이스</th>
<th>구현 클래스</th>
<th>자료구조의 특징</th>
</tr>
</thead>
<tbody><tr>
<td>Set</td>
<td>HashSet, TreeSet</td>
<td>순서를 유지하지 않는 데이터의 집합으로 데이터의 중복을 허용하지 않는다.</td>
</tr>
<tr>
<td>List</td>
<td>LinkedList, Vector, ArrayList</td>
<td>순서가 있는 데이터의 집합으로 데이터의 중복을 허용한다.</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Queue</td>
<td>LinkedList, PriorityQueue</td>
<td>먼저 들어간 자료가 먼저 나오는 구조 FIFO(First In FIrst Out)</td>
</tr>
<tr>
<td>Map</td>
<td>Hashtable, HashMap, TreeMap</td>
<td>키(Key), 값(Value)의 쌍으로 이루어진 데이터의 집합으로, 순서는 유지되지 않으며 키(Key)의 중복을 허용하지 않으나 값(Value)의 중복은 허용한다.</td>
</tr>
</tbody></table>
<br>


<h2 id="왜-collection을-사용하는-것일까">왜 Collection을 사용하는 것일까?</h2>
<ul>
<li>자료 구조를 자바로 반영해둔 프레임워크로 제공을 하기에 자료구조 알고리즘을 고민하고, 직접 구현하지 않아도 된다.</li>
<li>데이터 구조와 알고리즘에 대해 고성능, 고품질 구현을 제공한다.</li>
<li>배열과 달리 크기를 유연하게 변경 가능하고, 삽입 삭제가 편리하다. 따라서 객체의 수를 상황에 맞게 조절할 수 있기에 공간의 효율성이 높아진다.</li>
</ul>
<p>JAVA 의 컬렉션 프레임워크라면 대표적으로 List, Set, Map으로 알고 있는데, 위 사진을 보면 알수 있듯 List와 Set은 컬렉션이라는 인터페이스를 상속받지만 Map은 아니다. </p>
<br>

<h2 id="map은-왜-없는가-">Map은 왜 없는가 ?</h2>
<ul>
<li>JAVA의 공식문서에 따르면 “맵은 컬렉션이 아니고, 컬렉션도 맵이 아니다” 라고 한다.</li>
<li>Map이 Collection 이라면 Map의 구조를 봤을 때 합리적인 답은 &#39;key-value 쌍&#39;이 Map의 Element가 될 것이다. 하지만 이건 Map의 매우 제한적인 추상화를 제공하게 된다.</li>
<li>주어진 Key에 매핑 되는 Value를 찾아 올 수 없고, Key만으로 key-value 쌍을 지울 수도 없다.</li>
<li>억지로 Map과 Collection을 연관관계로 묶는다면 부자연스러운 인터페이스가 만들어지기에 제공하지 않는다.</li>
</ul>
<br>

<hr>
<br>



<h1 id="stream-이란">Stream 이란?</h1>
<p>Stream은 데이터의 흐름이다.</p>
<ul>
<li><p>데이터 소스에서 데이터를 읽고, 처리하고, 출력하는 연산을 수행하는데 사용된다.</p>
</li>
<li><p>컬렉션, 배열 또는 I/O 자원 등 다양한 소스에서 데이터를 다루는 일관된 방법을 제공한다.</p>
</li>
<li><p>자바 8에서 추가한 스트림(<em><code>Streams</code></em>)은 람다를 활용할 수 있는 기술 중 하나.</p>
</li>
<li><p>자바 8 이전에는 배열 또는 컬렉션 인스턴스를 다루는 방법은 <code>for</code> 또는 <code>foreach</code> 문을 돌면서 요소 하나씩을 꺼내서 다루는 방법을 사용했다.</p>
</li>
</ul>
<br>

<h3 id="장점">장점</h3>
<ul>
<li>람다를 이용해 코드의 양을 줄이고 간결한 표현이 가능하다. ⇒ 컬렉션을 함수형으로 처리할 수 있다</li>
<li>병렬처리가 가능하다.</li>
</ul>
<br>

<h3 id="순서">순서</h3>
<ol>
<li><strong>생성하기 : Stream 인스턴스 생성.</strong></li>
<li><strong>가공하기 : 필터링(<em>filtering</em>) 및 맵핑(<em>mapping</em>) 등 원하는 결과를 만들어가는 중간 작업(<em>intermediate operations</em>).</strong></li>
<li><strong>결과 만들기 : 최종적으로 결과를 만들어내는 작업 (terminal operations)</strong><ul>
<li>Calculating - 최소, 최대, 합, 평듄 등 기본형 타입 계산 가능</li>
<li>Reduction - 스트림에 있는 여러 요소의 총합 , accumulator, identity, combiner</li>
<li>Collecting - 리스트로 반환(Collectors.toList()), 하나의 스트링으로 이어붙이기 (Collectors.joining()), Collectors.averageingInt(), Collectors.summingInt() …</li>
</ul>
</li>
</ol>
<br>

<h3 id="특징">특징</h3>
<ol>
<li><p><strong>선언형 프로그래밍 방식</strong>: 코드를 작성할 때 데이터를 어떻게 처리할지에 대해 명시할 수 있으며, 구체적인 구현 방법은 숨겨짐.</p>
<p> → 간결하고 가독성있는 코드 </p>
</li>
<li><p><strong>내부 반복(internal iteration)</strong>: 내부 반복을 사용하여 요소를 처리</p>
</li>
<li><p><strong>지연 연산(lazy evaluation)</strong>:  </p>
<p> 스트림은 중간 연산(intermediate operations)과 최종 연산(terminal operations)으로 구성. </p>
<p> 중간 연산은 스트림을 리턴하며, 최종 연산은 스트림을 사용해 결과를 리턴합니다. </p>
<p> 이때, 중간 연산은 지연 연산이 적용되어 최종 연산이 호출되기 전까지 실제로 처리가 이루어지지 않는다. </p>
<blockquote>
<p>지연연산 : 결과값이 필요할때까지 계산을 늦추는 기법</p>
</blockquote>
</li>
</ol>
<br>

<hr>
<br>

<h1 id="코드에서-찾아보기">코드에서 찾아보기</h1>
<p><strong>상품 목록 불러오는 API</strong> </p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/db580bb6-0dc5-4a89-8dc1-015a2cb722e8/image.png" alt=""></p>
<h3 id="1-생성하기---stream-인스턴스-생성">1. <strong>생성하기 :  Stream 인스턴스 생성.</strong></h3>
<ul>
<li><p>컬렉션의 경우 Interface에 있는 defalut 메소드 <code>stream()</code> 을 이용해 Stream 인스턴스를 생성하였다.</p>
</li>
<li><p>⇒ Stream 파이프라인의 시작</p>
</li>
</ul>
<h3 id="2-가공하기--필터링filtering-및-맵핑mapping-등-원하는-결과를-만들어가는-중간-작업intermediate-operations">2. <strong>가공하기 : 필터링(<em>filtering</em>) 및 맵핑(<em>mapping</em>) 등 원하는 결과를 만들어가는 중간 작업(<em>intermediate operations</em>).</strong></h3>
<ul>
<li>map() 메서드는 스트림 내의 요소들을 특정 값으로 변환해준다. (이때, 람다를 인자로 받는다)</li>
</ul>
<pre><code class="language-json"> &lt;R&gt; Stream&lt;R&gt; map(Function&lt;? super T, ? extends R&gt; mapper);</code></pre>
<ul>
<li><p><code>.map(productMapper::fromListEntity)</code></p>
<pre><code>  요소 내 들어있는 Products 개체의 정보들을 꺼내온다. </code></pre></li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/5ccdc5ec-456d-4413-b13a-56581953946e/image.png" alt=""></p>
<p>  fromListEntity ⇒ 각 <code>Product</code> 객체는 <code>ProductPreViewDto</code> 객체로 매핑한다. </p>
<h3 id="3-결과-만들기--최종적으로-결과를-만들어내는-작업">3. <strong>결과 만들기 : 최종적으로 결과를 만들어내는 작업</strong></h3>
<p><code>.collect(Collectors.toList())</code> :</p>
<ul>
<li><p><code>collect()</code> 메서드는 스트림의 요소를 수집하여 새로운 컬렉션으로 반환</p>
</li>
<li><p><code>Collectors.toList()</code> 스트림의 모든 요소를 리스트에 추가하고, 그 결과로 리스트를 반환</p>
</li>
</ul>
<br>

<hr>
<br>

<p>각 페이지의 상품 목록을 처리하기 위해 Java의 <strong><code>List</code></strong>를 사용</p>
<h2 id="tolist">toList()</h2>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/a384c36d-a0e7-4b5c-aa6e-f908ce6ef5a5/image.png" alt=""></p>
<br>

<p><strong>⇒ 스트림에서 요소를 수집하여 리스트로 변환하는 <code>toList()</code> 컬렉터</strong></p>
<ul>
<li>입력 요소를 발생 순서대로 새 List에 누적하는 Collector를 반환한다.</li>
<li>toList() 메서드 : List의 type, 변경 가능성, 직렬화 가능성, 스레드 안전성에 대해 보장을 하지 않는다.</li>
<li><strong>알</strong> <strong>수 있는 것</strong><ol>
<li><code>ArrayList::new</code>로 새로운 ArrayList 인스턴스를 생성</li>
<li><code>List.add</code> 메서드를 사용하여 요소를 리스트에 추가</li>
<li><code>addAll</code> 메서드를 사용해 병렬 처리 시 실행된 각 부분 결과를 모두 합쳐 하나의 리스트로 생성 </li>
</ol>
</li>
</ul>
<hr>
<h3 id="정리">정리</h3>
<p><code>productPage</code>에서 가져온  <code>Product</code> 객체들을 <code>ProductPreViewDto</code> 객체로 매핑하고, 이를 리스트로 수집하여 반환합니다. </p>
<hr>
<br>

<h3 id="참고-문헌">참고 문헌</h3>
<ul>
<li>공식문서 : <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html">https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html</a></li>
<li><a href="https://docs.oracle.com/javase/tutorial/collections/intro/index.html">https://docs.oracle.com/javase/tutorial/collections/intro/index.html</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/designfaq.html#a14">https://docs.oracle.com/javase/8/docs/technotes/guides/collections/designfaq.html#a14</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[이펙티브 자바] item2 : 생성자에 매개변수가 많다면 빌더를 고려하라 ]]></title>
            <link>https://velog.io/@dayon_log/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-item2-%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EA%B0%80-%EB%A7%8E%EB%8B%A4%EB%A9%B4-%EB%B9%8C%EB%8D%94%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@dayon_log/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-item2-%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EA%B0%80-%EB%A7%8E%EB%8B%A4%EB%A9%B4-%EB%B9%8C%EB%8D%94%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</guid>
            <pubDate>Mon, 13 May 2024 10:43:29 GMT</pubDate>
            <description><![CDATA[<h3 id="책-소개">책 소개</h3>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/6f0d36c1-5595-4f25-8dc7-0fd7c2a9f778/image.png" alt=""></p>
<p>[ 이펙티브 자바 3/E ] <a href="https://www.yes24.com/Product/Goods/65551284">https://www.yes24.com/Product/Goods/65551284</a></p>
<hr>
<h2 id="item2--생성자에-매개변수가-많다면-빌더를-고려하라">item2 : 생성자에 매개변수가 많다면 빌더를 고려하라</h2>
<hr>
<p>정적 팩터리 메서드와 생성자는 선택적 매개변수가 많을때 적절한 대응이 어렵다. 
이러한 케이스에서는 같은 방법을 사용하였다. </p>
<ul>
<li>점층적 생성자 패턴 (telescoping constructor pattern)</li>
<li>자바 빈즈 패턴</li>
<li>빌더 패턴</li>
</ul>
<h2 id="1-점층적-생성자-패턴">1. 점층적 생성자 패턴</h2>
<p>필수 매개변수 : 인스턴스 생성에 꼭 필요한 변수</p>
<p>선택 매개변수 : 인스턴스 생성에 있어도 되고 없어도 되는 변수</p>
<blockquote>
<p><strong>점층적 생성자 패턴</strong> </p>
</blockquote>
<ul>
<li>필수 매개 변수만 받는 생성자</li>
<li>필수 매개변수 + 선택 매개변수 1개를 받는 생성자</li>
<li>필수 매개변수 +  선택 매개변수 2개를 받는 생성자 </li>
<li>…  </li>
<li>&quot;선택 매개변수를 전부 다 받는 생성자까지 점층적으로 늘려가는 방식&quot;<blockquote>
</blockquote>
</li>
</ul>
<br>


<pre><code class="language-java">public class NutritionFacts {
    private final int servingSize;  // (mL, 1회 제공량)     필수
    private final int servings;     // (회, 총 n회 제공량)   필수
    private final int calories;     // (1회 제공량당)        선택
    private final int fat;          // (g/1회 제공량)       선택
    private final int sodium;       // (mg/1회 제공량)      선택
    private final int carbohydrate; // (g/1회 제공량)       선택

    public NutritionFacts(int servingSize, int servings) {
        this(servingSize, servings, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories) {
        this(servingSize, servings, calories, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat) {
        this(servingSize, servings, calories, fat, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
        this(servingSize, servings, calories, fat, sodium, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
        this.servingSize  = servingSize;
        this.servings     = servings;
        this.calories     = calories;
        this.fat          = fat;
        this.sodium       = sodium;
        this.carbohydrate = carbohydrate;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola =
                new NutritionFacts(240, 8, 100, 0, 35, 27);
    }

}</code></pre>
<p>이 클래스의 인스턴스를 만들려면 원하는 매개변수를 모두 포함한 생성자 중에서 가장 짧은 것을 호출하면 된다. → <code>NutritionFacts(240, 8, 100, 0, 35, 27)</code></p>
<br>

<h3 id="단점">단점</h3>
<p>매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다. </p>
<p>클라이언트가 실수로 매개변수의 순서를 바꿔 건네줘도 컴파일러는 알아차리지 못하고, 런타임에 엉뚱한 동작을 수행할 수 있다. </p>
<p><br><br></p>
<h2 id="2-자바-빈즈-패턴">2. 자바 빈즈 패턴</h2>
<p>자바 빈즈 패턴 (JavaBeans Pattern) : 매개변수가 없는 생성자로 객체를 만든 후, setter로 원하는 매개변수의 값을 설정하는 방식이다.</p>
<pre><code class="language-java">public class NutritionFacts {    
        private int servingSize  = -1; // 필수; 기본값 없음
    private int servings     = -1; // 필수; 기본값 없음
    private int calories     = 0;
    private int fat          = 0;
    private int sodium       = 0;
    private int carbohydrate = 0;

    public NutritionFacts() { }
    // Setters
    public void setServingSize(int val)  { servingSize = val; }
    public void setServings(int val)     { servings = val; }
    public void setCalories(int val)     { calories = val; }
    public void setFat(int val)          { fat = val; }
    public void setSodium(int val)       { sodium = val; }
    public void setCarbohydrate(int val) { carbohydrate = val; }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts();
        cocaCola.setServingSize(240);
        cocaCola.setServings(8);
        cocaCola.setCalories(100);
        cocaCola.setSodium(35);
        cocaCola.setCarbohydrate(27);
    }
}</code></pre>
<p>⇒ 코드가 길지만, 인스턴스를 만들기 더 쉽고, 더 읽기 쉬운 코드가 되었다. </p>
<h3 id="단점-1">단점</h3>
<ul>
<li>객체 하나를 만들려면 메서드 여러개를 호출해야한다</li>
<li>객체가 완전히 완성되기 전까지는 일관성(consistency)이 무너진 상태에 놓인다<ul>
<li>점층적 생성자 패턴에서는 매개변수들이 유효한지를 생성자에서만 확인하면 일관성을 유지할 수 있었지만 그 장치가 사라져 디버깅이 어렵다.</li>
</ul>
</li>
<li>클래스를 불변으로 만들수 없어, 스레드 안정성을 얻기 위해서는 프로그래머의 추가 작업이 필요하다.</li>
</ul>
<h2 id="3-빌더-패턴">3. 빌더 패턴</h2>
<p>빌더 패턴 : 점층적 생성자 패턴의 안전성과 자바 빈즈 패턴의 가독성을 겸비한 패턴</p>
<ol>
<li>클라이언트는 필요한 객체를 직접 만드는 대신, 필수 매개변수만으로 생성자를 호출해 빌더 객체를 얻는다</li>
<li>빌더 객체가 제공하는 Setter 메서드들로 원하는 선택 매개변수를 설정한다. </li>
<li>매개변수가 없는 build 매서드를 호출해 필요한 객체(불변 객체)를 얻는다. </li>
</ol>
<p>빌더는 생성한 클래스 안에 정적 멤버 클래스로 만들어 두는 것이 보통이다. </p>
<pre><code class="language-java">public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // 필수 매개변수
        private final int servingSize;
        private final int servings;

        // 선택 매개변수 - 기본값으로 초기화한다.
        private int calories      = 0;
        private int fat           = 0;
        private int sodium        = 0;
        private int carbohydrate  = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val)
            { calories = val;      return this; }

        public Builder fat(int val)
            { fat = val;           return this; }

        public Builder sodium(int val)
            { sodium = val;        return this; }

        public Builder carbohydrate(int val)
            { carbohydrate = val;  return this; }

        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}</code></pre>
<p>⇒ 위 클래스는 불변이며, 각 setter 메서드는 자신을 반환하기 때문에 연쇄적으로 호출할 수 있다. 이를 플루언트 API(fluent API) 혹은 메서드 연쇄(method chaining)라 한다.</p>
<pre><code class="language-java">public static void main(String[] args) {

        NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
                .calories(100).sodium(35).carbohydrate(27).build();

    }</code></pre>
<p>위의 클라이언트 코드는 쓰기 쉽고, 읽기 쉽다. </p>
<p>빌더 패턴은 명명된 선택적 매개변수를 흉내낸것이다. </p>
<p><br><br></p>
<h2 id="필더-패턴의-활용">필더 패턴의 활용</h2>
<p>빌더 패턴은 계층적으로 설계된 클래스와 함께 쓰기 좋다. </p>
<p>추상 클래스는 추상 빌더를, 구체 클래스는 구체 빌더를 갖게 한다. </p>
<p>아래 코드는 피자의 다양한 종류를 표현하는 계층 구조의 루트에 놓인 추상 클래스다. </p>
<pre><code class="language-java">public abstract class Pizza {
    public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
    final Set&lt;Topping&gt; toppings;

    abstract static class Builder&lt;T extends Builder&lt;T&gt;&gt; {
    // T로 표현되는 타입이 Builder를 사용 가능해야 하므로, Builder를 구현한 클래스만 타입으로 사용해라
        EnumSet&lt;Topping&gt; toppings = EnumSet.noneOf(Topping.class);
        public T addTopping(Topping topping) {
            toppings.add(Objects.requireNonNull(topping));
            return self();
        }

        abstract Pizza build();

        // 하위 클래스는 이 메서드를 재정의(overriding)하여
        // &quot;this&quot;를 반환하도록 해야 한다.
        protected abstract T self();
    }

    Pizza(Builder&lt;?&gt; builder) {
        toppings = builder.toppings.clone(); // 아이템 50 참조
    }
}</code></pre>
<ul>
<li>Pizza.Builder 클래스는 재귀적 타입 한정을 이용하는 제네릭 타입</li>
<li>이 클래스에 self()를 더해 하위 클래스는 형변환 하지 않고 메서드 연쇄를 지원할 수 있다.</li>
</ul>
<br>

<h3 id="뉴욕-피자">뉴욕 피자</h3>
<ul>
<li>크기 (size)를 매개변수를 필수로 받는다.</li>
</ul>
<pre><code class="language-java">public class NyPizza extends Pizza {
    public enum Size { SMALL, MEDIUM, LARGE }
    private final Size size;

    public static class Builder extends Pizza.Builder&lt;Builder&gt; {
        private final Size size;

        public Builder(Size size) {
            this.size = Objects.requireNonNull(size);
        }

        @Override public NyPizza build() {
            return new NyPizza(this);
        }

        @Override protected Builder self() { return this; }
    }

    private NyPizza(Builder builder) {
        super(builder);
        size = builder.size;
    }
}</code></pre>
<br>


<h3 id="칼조네-피자">칼조네 피자</h3>
<ul>
<li>소스를 안에 넣을지 선택(sauceInside)하는 매개변수를 필수로 받는다.</li>
</ul>
<pre><code class="language-java">public class Calzone extends Pizza {
    private final boolean sauceInside;

    public static class Builder extends Pizza.Builder&lt;Builder&gt; {
        private boolean sauceInside = false ;  // 기본값

        public Builder sauceInside() {
            sauceInside = true ; 
            return this ; 
        }

        @Override public Calzone build() {
            return new Calzone(this);
        }

        @Override protected Builder self() { return this; }
    }

    private Calzone(Builder builder) {
        super(builder);
        sauceInside = builder.sauceInside;
    }
}</code></pre>
<p>각 하위 클래스의 빌더가 정의한 build() 메서드는 해당하는 구체 하위 클래스를 반환하도록 선언한다. </p>
<p>NyPizza의 메서드(하위 클래스)가 Pizza의 메서드(상위 클래스)가 정의한 반환 타입이 아닌, 그 하위 타입을 반환하는 기능을 <strong>공변 반환 타이핑이라 한다.</strong> </p>
<p>이 기능을 통해 클라이언트는 형 변환을 신경 쓰지 않고 빌더를 사용할 수 있게 된다.</p>
<p>빌버 패턴은 매우 유연하다. 빌더하나로 여러 객체를 순회하면 만들수도 있고, 빌더에 넘기는 매개변수에 따라 다른 객체를 만들수도 있다. </p>
<p>하지만 빌더부터 만들어야하기에  성능이 민감한 상황에서는 문제가 될수 있다. </p>
<p>점층적 생성자 패턴보다 코드가 장황하여 매개변수가 3-4개 이상 으로 많을 경우 사용하는 것이 좋다. </p>
<br>

<hr>
<h2 id="정리-">정리 !</h2>
<p>생성자나 정적 팩터리가 처리해야할 매개변수가 많다면 빌더 패턴을 선택하라</p>
<p>빌더는 정측정 생성자보다 클라이언트의 코드를 읽고 쓰기 간결하고, 자바 빈즈보다 안전하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[이펙티브 자바] - item6 : 불필요한 객체 생성을 피하라]]></title>
            <link>https://velog.io/@dayon_log/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-item6-%EB%B6%88%ED%95%84%EC%9A%94%ED%95%9C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1%EC%9D%84-%ED%94%BC%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@dayon_log/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-item6-%EB%B6%88%ED%95%84%EC%9A%94%ED%95%9C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1%EC%9D%84-%ED%94%BC%ED%95%98%EB%9D%BC</guid>
            <pubDate>Wed, 08 May 2024 14:43:26 GMT</pubDate>
            <description><![CDATA[<p>책 소개</p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/d7b19448-1b39-497f-8e55-2c93fc24394f/image.png" alt=""></p>
<p>[ 이펙티브 자바 3/E ] <a href="https://www.yes24.com/Product/Goods/65551284">https://www.yes24.com/Product/Goods/65551284</a></p>
<h2 id="item6--불필요한-객체-생성을-피하라">item6 : 불필요한 객체 생성을 피하라</h2>
<h3 id="똑같은-기능의-객체를-매번-생성하기보다-객체-하나를-재사용하는-편이-나을-때가-많다">똑같은 기능의 객체를 매번 생성하기보다 객체 하나를 재사용하는 편이 나을 때가 많다.</h3>
<pre><code class="language-java">String s = new String(&quot;bikini);  // (1) 따라 하지 말 것 ! 

String s = &quot;bikini&quot;;  // (2) </code></pre>
<ul>
<li><p>(1)의 문장은 실행될 때마다 String 인스턴스를 새로 만든다. → 쓸모 없는 행위
문장이 반복문이나 빈번히 호출되는 메서드 안에 있다면 쓸데없는 String 인스턴스가 수백만개 만들어질 수 있다.</p>
</li>
<li><p>(2) 코드는 새로운 인스턴스를 매번 만드는 대신, 하나의 String 인스턴스를 사용한다. 같은 가상 머신(JVM) 안에서 이와 똑같은 문자열 리터럴을 사용하는 모든 코드가 같은 객체를 재사용함이 보장된다.</p>
</li>
</ul>
<br>

<p>생성자  대신 정적 팩터리 매서드 를 제공하는 불변 클래스에서는 정적 팩터리 메서드를 사용해 불필요한 객체 생성을 피할 수 있다. </p>
<p>예) <code>Boolean(String)</code> 생성자 보다 <code>Boolean.valueOf(String)</code> 팩터리 메서드를 사용하는 것이 좋다. </p>
<pre><code class="language-java">Boolean true1 = new Boolean(&quot;true&quot;);
Boolean true2 = new Boolean(&quot;true&quot;);
System.out.println(true1 == true2); // false, 서로 다른 객체</code></pre>
<ul>
<li><code>new Boolean(&quot;true&quot;)</code>는 true1과 true2가 각각 새로운 Boolean 객체를 참조하게 한다.</li>
</ul>
<br>

<pre><code class="language-java">Boolean true1 = Boolean.valueOf(&quot;true&quot;);
Boolean true2 = Boolean.valueOf(&quot;true&quot;);
System.out.println(true1 == true2); // true, 같은 객체 재사용</code></pre>
<ul>
<li><code>Boolean.valueOf(&quot;true&quot;)</code>는 필요에 따라 기존 객체를 재사용한다.</li>
</ul>
<h2 id="생성-비용이-비싼-객체">생성 비용이 비싼 객체</h2>
<ul>
<li>생성 비용이 비싼 객체가 반복해서 필요하다면 캐싱하여 사용하는 것이 좋지만, 객체가 비싼 객체인지 매번 확인 할수 없다.</li>
</ul>
<h3 id="정규-표현식을-활용한-예제">정규 표현식을 활용한 예제</h3>
<pre><code class="language-java">static boolean isRomanNumeral(String s) {
    return s.matches(&quot;^(?=.)M*(C[MD]|D?C{0,3})&quot; 
                                        + &quot;(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$&quot;);
}</code></pre>
<p>→ <code>**String.matches**</code> <strong>메서드는 정규표현식으로 문자열 형태를 확인하는 가장 쉬운 방법이지만, 성능이 중요한 상황에서 반복해서 사용하기엔 적합하지 않는다.</strong> </p>
<ul>
<li>이 메서드가 내부에서 만드는 <code>Pattern</code> 인스턴스는 한번 쓰고 버려져 바로 가비지 컬렉션 대상이 된다.</li>
<li><code>java.util.rege.Pattern</code> 클래스는 <strong>정규식을 기반으로 문자열에 대한 검증을 수행한다.</strong></li>
<li><code>Pattern</code>은 입력받은 정규표현식에 해당하는 유한상태 머신(finite state machine)을 만들기 때문에 인스턴스 생성비용이 높다. 
( → 유한상태머신을 쉽게 말하면 장치나 모델이 가질 수 있는 유한개의 상태를 정의하고, 조건에 맞는 이벤트가 발생되면 해당 상태로 변경되는 방식으로 동작하는 것을 유한상태머신이라 한다.)</li>
</ul>
<br>

<h3 id="성능-개선하기">성능 개선하기</h3>
<p>정규표현식을 표현하는 (불변인) Pattern 인스턴스를 클래스 초기화 과정에서 직접 생성해 캐싱해두고, 나중에 isRomanNumeral 메서드가 호출될때 마다 재사용한다. </p>
<pre><code class="language-java">public class RomanNumerals {
    private static final Pattern ROMAN = Pattern.compile(
                &quot;^(?=.)M*(C[MD]|D?C{0,3})&quot; 
                + &quot;(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$&quot;);

    static boolean isRomanNumeral(String s) {
        return ROMAN.matcher(s).matches();
    }
}</code></pre>
<p>위와 같은 개선을 통해 성능이 좋아지고, 코드가 더 명확해졌다. </p>
<p>Pattern 인스턴스를 static final 필드로 끄집어내고, 이름을 지어주어 코드의 의미가 잘 드러난다. </p>
<p>but! </p>
<ul>
<li>만약 개선된 방식의 클래스가 초기화된 후 이 메서드를 한 번도 호출하지 않는다면, ROMAN 필드는 쓸데없이 초기화 된 꼴이다.</li>
<li><code>isRomanNumeral</code> 메서드가 처음 호출될 때 필드를 초기화하는 지연초기화(Lazy initialization)로 불필요한 초기화를 없앨수도 있지만, 지연 초기화는 코드를 복잡하게 만드는데, 성능은 크게 개선되지 않을 때가 많아 추천하지 않는다.</li>
</ul>
<h2 id="객체가-불변이라도">객체가 불변이라도?</h2>
<ul>
<li>객체가 불변이라면 재사용해도 안전함이 명백하다.</li>
<li>하지만, 반대되는 상황을 생각해보자 ⇒ 어뎁터</li>
<li>어댑터는 실제 작업은 뒷단 객체에 위임하고, 자신은 제2의 인스턴스 역할을 해주는 객체다. 어댑터는 뒷단 객체만 관리하면 된다. 즉, 뒷단 객체 외에는 관리할 상태가 없으므로 뒷단 객체 하나당 어댑터 하나씩만 만들면 충분하다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/788f4c10-b587-42ca-8349-9a25a8164830/image.png" alt=""></p>
<blockquote>
<p><strong>Adapter Pattern</strong></p>
<ul>
<li>Adaptee 를 감싸고, Target Interface 만을 클라이언트에게 드러낸다.</li>
<li>Target Interface 를 구현하여 클라이언트가 예상하는 인터페이스가 되도록 Adaptee 의 인터페이스를 간접적으로 변경한다.</li>
<li>Adaptee 가 기대하는 방식으로 클라이언트의 요청을 간접적으로 변경한다.</li>
<li>호환되지 않는 우리의 인터페이스와 Adaptee 를 함께 사용할 수 있다.</li>
</ul>
</blockquote>
<h2 id="오토박싱auto-boxing으로-인한-성능-저하">오토박싱<strong>(auto boxing)</strong>으로 인한 성능 저하</h2>
<ul>
<li>프로그래머가 기본 타입(primitive)과 박싱된 기본 타입을 섞어 쓸 때 자동으로 상호 변환해주는 기술</li>
<li><strong>기본 타입과 그에 대응하는 박싱된 기본 타입의 구분을 흐릿하게 해주지만, 완전히 없애주진 못한다</strong></li>
<li>모든 양의 정수의 총합을 구하는 메서드</li>
</ul>
<pre><code class="language-java">private static long sum() {
    **Long** sum = 0L;  // 박싱된 타입 (Long)
    for (long i = 0; i &lt;= Integer.MAX_VALUE; i++) {
        sum += i;  // 오토박싱 발생
    }
    return sum;
}</code></pre>
<ul>
<li>반복문이 실행될 때마다 새로운 Long 객체가 생성되어 성능 저하가 발생한다.</li>
</ul>
<p>→ sum 변수를 Long 에서 기본 타입인 long으로 변경하면, 오토박싱이 발생하지 않으므로 성능이 향상된다.</p>
<p>저자의 컴퓨터에서는 6.3초에서 0.59초로 빨라졌다고 한다. </p>
<blockquote>
<p><strong>박싱된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토 박싱이 숨어들지 않도록 주의해야한다.</strong></p>
</blockquote>
<br>

<ul>
<li>이번 아이템을 &quot;객체 생성은 비싸니 피해야 한다&quot; 로 오해하면 안된다.</li>
<li>요즘의 JVM에서는 별다른 일을 하지 않은 작은 객체를 생성하고 회수하는 일이 크게 부담되지 않는다.</li>
<li>프로그램의 명확성, 간결성, 기능을 위해서 객체를 추가로 생성하는 것이라면 일반적으로 좋은 일이다.</li>
</ul>
<br>

<hr>
<p><a href="https://www.tutorialspoint.com/design_pattern/adapter_pattern.htm">https://www.tutorialspoint.com/design_pattern/adapter_pattern.htm</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[이펙티브 자바] - item1 : 생성자 대신 정적 팩터리 메서드를 고려하라 ]]></title>
            <link>https://velog.io/@dayon_log/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-item1-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%84%B0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@dayon_log/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-item1-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%84%B0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</guid>
            <pubDate>Wed, 08 May 2024 02:30:32 GMT</pubDate>
            <description><![CDATA[<h3 id="책-소개">책 소개</h3>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/7aaa8f0d-4805-4f6a-91fb-752b18b4b4c9/image.png" alt=""></p>
<p>[ 이펙티브 자바 3/E ] <a href="https://www.yes24.com/Product/Goods/65551284">https://www.yes24.com/Product/Goods/65551284</a></p>
<br>

<hr>
<h2 id="2장-객체-생성과-파괴">2장. 객체 생성과 파괴</h2>
<p>2장에서는 다음을 알아본다.</p>
<ul>
<li><p>객체를 만들어야 할때와 만들지 말아야 할때를 구분하는 법</p>
</li>
<li><p>올바른 객체 생성 방법과 불필요한 생성을 피하는 방법</p>
</li>
<li><p>객체가 제때 파괴됨을 보장하고 파괴 전에 수행해야 할 정리 작업을 관리하는 요령</p>
</li>
</ul>
<br> 

<h2 id="item1--생성자-대신-정적-팩터리-메서드를-고려하라">item1 : 생성자 대신 정적 팩터리 메서드를 고려하라</h2>
<ul>
<li>클라이언트가 클래스의 인스턴스를 얻는 정통적인 수단 = public 생성자 </li>
</ul>
<h3 id="생성자"><strong>생성자</strong></h3>
<ul>
<li>클래스로부터 객체를 생성할 때의 호출하는 메소드</li>
</ul>
<h3 id="정적-팩터리-메서드-static-factory-method"><strong>정적 팩터리 메서드 (static factory method)</strong></h3>
<ul>
<li>객체 생성의 역할을 하는 클래스 메서드</li>
</ul>
<br> 

<p><strong>정적 팩터리 메서드 사용한 boolean boxed class</strong> </p>
<pre><code>public static Boolean valueOf(boolean b) {
        return b ? Boolean.TRUE : Boolean.FALSE;
}</code></pre><br> 


<p>클래스는 클라이언트에 public 생성자 대신 정적 팩터리 메서드를 제공할 수 있다. </p>
<h3 id="정적-팩터리-메서드의-장점"><strong>정적 팩터리 메서드의 장점</strong></h3>
<p><strong>1. 이름을 가질 수 있다.</strong></p>
<ul>
<li><p>생성자를 사용한 경우, 매개변수와 생성자 자체 이름만으로는 반환될 객체의 특성을 제대로 설명하지 못한다.</p>
</li>
<li><p>정적 팩터리 메서드는 이름을 잘 지어 반환될 객체의 특성을 쉽게 묘사할 수 있다. 
예 ) <code>BigInteger.probablePrime</code></p>
</li>
</ul>
<br> 

<p><strong>2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.</strong></p>
<ul>
<li><p>불변 클래스는 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체 생성을 피할 수 있다. </p>
</li>
<li><p><code>Boolean.valueOf(boolean)</code> 메서드는 객체를 아예 생성하지 않는다. </p>
</li>
<li><p>인스턴스 통제 클래스 : 반복되는 요청에 같은 객체를 반환, 정적 팩터리 방식의 클래스는 인스턴스를 통제 할수 있다. 
  =&gt; 인스턴스를 통제하면, 클래스를 싱글턴, 인스턴스화 불가 등으로 만들 수 있다. </p>
</li>
</ul>
<br> 

<p><strong>3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.</strong></p>
<ul>
<li><p>반환할 객체의 클래스(해당 객체의 하위 클래스)를 자유롭게 선택 가능하다. =&gt; 유연성</p>
</li>
<li><p>이런 유연성을 이용해, 구현 클래스를 공개하지 않고, 객체 반환이 가능해 API를 작게 유지할 수 있다. </p>
</li>
<li><p>45개의 유틸리티 구현체를 제공하는 java.util.Collections 클래스 또한 정적 팩터리 메서드를 사용한다.</p>
</li>
</ul>
<br> 

<p><strong>4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.</strong></p>
<ul>
<li>반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관 없다</li>
</ul>
<ul>
<li><p>EnumSet 클래스의 경우 매개변수의 원소 갯수에 따라 서로 다른 구현체 반환한다.<br>(<code>RegularEnumSet</code> - 64개 이하, <code>JumboEnumSet</code> - 65개 이상 )</p>
</li>
<li><p>다음 릴리스에서는 또 다른 클래스의 객체를 반환해도 된다. </p>
</li>
</ul>
<br> 

<p><strong>5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.</strong></p>
<ul>
<li><p>이런 유연함은 서비스 제공자 프레임워크의 근간이 된다. 
(제공자 = 서비스의 구현체, 클라이언트를 구현체로부터 분리) </p>
</li>
<li><p>서비스 제공자 프레임워크의 3개 핵심 컴포넌트 </p>
<ul>
<li><p>서비스 인터페이스 : 구현체의 동작을 정의</p>
</li>
<li><p>제공자 등록 API : 제공자가 구현체를 등록할 때 사용</p>
</li>
<li><p>서비스 접근 API : 클라이언트가 서비스의 인스턴스를 얻을 때 사용 =&gt; 유연한 정적 팩터리의 실체 </p>
</li>
<li><p>더불어, 서비스 제공자 인터페이스 : 서비스 인터페이스의 인스턴스를 생성하는 팩터리 객체 설명 </p>
</li>
</ul>
</li>
<li><p>JDBC 의 경우, connection이 서비스 인터페이스 역할을 , DriverManger.registerDriver가 제공자 등록 API , DriverManger.getConnection이 서비스 접근 API 역할, Driver가 서비스 제공자 인터페이스 역할을 수행 </p>
</li>
</ul>
<p><br> <br> </p>
<h3 id="정적-팩터리-메서드의-단점">정적 팩터리 메서드의 단점</h3>
<p><strong>1. 상속을 하려면 public, protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.</strong></p>
<ul>
<li><p>컬렉션 프레임워크 유틸리티 구현 클래스는 상속이 불가하다. </p>
</li>
<li><p>오히려 이 제약은 상속보다 컴포지션을 사요ㅇ하도록 유도하고, 불변타입으로 만들려면 제약을 지키라는 장점 </p>
<br> 

</li>
</ul>
<p><strong>2. 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.</strong></p>
<ul>
<li><p>생성자와 같이 API 명세에 정확히 드러나지 않는다. </p>
</li>
<li><p>사용자는 여러 정적 팩토리 메서드가 존재하는 경우, 클래스를 인스턴스화할 수 있는 여러 방법을 스스로 학습해야 사용가능하다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[이펙티브 자바 - 소개]]></title>
            <link>https://velog.io/@dayon_log/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94</link>
            <guid>https://velog.io/@dayon_log/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94</guid>
            <pubDate>Tue, 07 May 2024 16:49:59 GMT</pubDate>
            <description><![CDATA[<h3 id="책-소개">책 소개</h3>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/0c777418-ef75-4c0a-95a8-56dbc8f60c19/image.png" alt=""></p>
<p>[ 이펙티브 자바 3/E ] <a href="https://www.yes24.com/Product/Goods/65551284">https://www.yes24.com/Product/Goods/65551284</a></p>
<h3 id="목차">목차</h3>
<p><strong>1장 들어가기</strong></p>
<p><strong>2장 객체 생성과 파괴</strong>
아이템 1. 생성자 대신 정적 팩터리 메서드를 고려하라
아이템 2. 생성자에 매개변수가 많다면 빌더를 고려하라
아이템 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라
아이템 4. 인스턴스화를 막으려거든 private 생성자를 사용하라
아이템 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
아이템 6. 불필요한 객체 생성을 피하라
아이템 7. 다 쓴 객체 참조를 해제하라
아이템 8. finalizer와 cleaner 사용을 피하라
아이템 9. try-finally보다는 try-with-resources를 사용하라</p>
<p><strong>3장 모든 객체의 공통 메서드</strong>
아이템 10. equals는 일반 규약을 지켜 재정의하라
아이템 11. equals를 재정의하려거든 hashCode도 재정의하라
아이템 12. toString을 항상 재정의하라
아이템 13. clone 재정의는 주의해서 진행하라
아이템 14. Comparable을 구현할지 고려하라</p>
<p><strong>4장 클래스와 인터페이스</strong>
아이템 15. 클래스와 멤버의 접근 권한을 최소화하라
아이템 16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라
아이템 17. 변경 가능성을 최소화하라
아이템 18. 상속보다는 컴포지션을 사용하라
아이템 19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라
아이템 20. 추상 클래스보다는 인터페이스를 우선하라
아이템 21. 인터페이스는 구현하는 쪽을 생각해 설계하라
아이템 22. 인터페이스는 타입을 정의하는 용도로만 사용하라
아이템 23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라
아이템 24. 멤버 클래스는 되도록 static으로 만들라
아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라</p>
<p><strong>5장 제네릭</strong>
아이템 26. 로 타입은 사용하지 말라
아이템 27. 비검사 경고를 제거하라
아이템 28. 배열보다는 리스트를 사용하라
아이템 29. 이왕이면 제네릭 타입으로 만들라
아이템 30. 이왕이면 제네릭 메서드로 만들라
아이템 31. 한정적 와일드카드를 사용해 API 유연성을 높이라
아이템 32. 제네릭과 가변인수를 함께 쓸 때는 신중하라
아이템 33. 타입 안전 이종 컨테이너를 고려하라</p>
<p><strong>6장 열거 타입과 애너테이션</strong>
아이템 34. int 상수 대신 열거 타입을 사용하라
아이템 35. ordinal 메서드 대신 인스턴스 필드를 사용하라
아이템 36. 비트 필드 대신 EnumSet을 사용하라
아이템 37. ordinal 인덱싱 대신 EnumMap을 사용하라
아이템 38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라
아이템 39. 명명 패턴보다 애너테이션을 사용하라
아이템 40. @Override 애너테이션을 일관되게 사용하라
아이템 41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라</p>
<p><strong>7장 람다와 스트림</strong>
아이템 42. 익명 클래스보다는 람다를 사용하라
아이템 43. 람다보다는 메서드 참조를 사용하라
아이템 44. 표준 함수형 인터페이스를 사용하라
아이템 45. 스트림은 주의해서 사용하라
아이템 46. 스트림에서는 부작용 없는 함수를 사용하라
아이템 47. 반환 타입으로는 스트림보다 컬렉션이 낫다
아이템 48. 스트림 병렬화는 주의해서 적용하라</p>
<p><strong>8장 메서드</strong>
아이템 49. 매개변수가 유효한지 검사하라
아이템 50. 적시에 방어적 복사본을 만들라
아이템 51. 메서드 시그니처를 신중히 설계하라
아이템 52. 다중정의는 신중히 사용하라
아이템 53. 가변인수는 신중히 사용하라
아이템 54. null이 아닌, 빈 컬렉션이나 배열을 반환하라
아이템 55. 옵셔널 반환은 신중히 하라
아이템 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라</p>
<p><strong>9장 일반적인 프로그래밍 원칙</strong>
아이템 57. 지역변수의 범위를 최소화하라
아이템 58. 전통적인 for 문보다는 for-each 문을 사용하라
아이템 59. 라이브러리를 익히고 사용하라
아이템 60. 정확한 답이 필요하다면 float와 double은 피하라
아이템 61. 박싱된 기본 타입보다는 기본 타입을 사용하라
아이템 62. 다른 타입이 적절하다면 문자열 사용을 피하라
아이템 63. 문자열 연결은 느리니 주의하라
아이템 64. 객체는 인터페이스를 사용해 참조하라
아이템 65. 리플렉션보다는 인터페이스를 사용하라
아이템 66. 네이티브 메서드는 신중히 사용하라
아이템 67. 최적화는 신중히 하라
아이템 68. 일반적으로 통용되는 명명 규칙을 따르라</p>
<p><strong>10장 예외</strong>
아이템 69. 예외는 진짜 예외 상황에만 사용하라
아이템 70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라
아이템 71. 필요 없는 검사 예외 사용은 피하라
아이템 72. 표준 예외를 사용하라
아이템 73. 추상화 수준에 맞는 예외를 던지라
아이템 74. 메서드가 던지는 모든 예외를 문서화하라
아이템 75. 예외의 상세 메시지에 실패 관련 정보를 담으라
아이템 76. 가능한 한 실패 원자적으로 만들라
아이템 77. 예외를 무시하지 말라</p>
<p><strong>11장 동시성</strong>
아이템 78. 공유 중인 가변 데이터는 동기화해 사용하라
아이템 79. 과도한 동기화는 피하라
아이템 80. 스레드보다는 실행자, 태스크, 스트림을 애용하라
아이템 81. wait와 notify보다는 동시성 유틸리티를 애용하라
아이템 82. 스레드 안전성 수준을 문서화하라
아이템 83. 지연 초기화는 신중히 사용하라
아이템 84. 프로그램의 동작을 스레드 스케줄러에 기대지 말라</p>
<p><strong>12장 직렬화</strong>
아이템 85. 자바 직렬화의 대안을 찾으라
아이템 86. Serializable을 구현할지는 신중히 결정하라
아이템 87. 커스텀 직렬화 형태를 고려해보라
아이템 88. readObject 메서드는 방어적으로 작성하라
아이템 89. 인스턴스 수를 통제해야 한다면 readResolve보다는 열거 타입을 사용하라
아이템 90. 직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라</p>
<br> 

<h3 id="이-책을-통해-얻고자-하는것">이 책을 통해 얻고자 하는것</h3>
<ul>
<li>자바를 제대로 알고 백엔드 개발자로서 더 깊이있는 성장을 하는것을 목표로 한다. </li>
<li>5월 7일 부터 5월 31일까지 기간을 잡고 진행한다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데이터 과학] 기계 학습 ]]></title>
            <link>https://velog.io/@dayon_log/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B3%BC%ED%95%99-%EA%B8%B0%EA%B3%84-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@dayon_log/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B3%BC%ED%95%99-%EA%B8%B0%EA%B3%84-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Sun, 05 May 2024 15:17:12 GMT</pubDate>
            <description><![CDATA[<h1 id="11장---기계-학습">11장 :  기계 학습</h1>
<h2 id="111-모델링">11.1 모델링</h2>
<ul>
<li>개념 : 다양한 변수 간의 수학적 (혹은 확률적) 관계를 표현한 것</li>
<li>예시<ul>
<li>SNS 상에서 사용자수, 사용자당 광고수익 → 차후 몇년 간의 연간 수익</li>
<li>요리책에서 먹는 사람의 수, 배고픈 정도 → 재료의 양</li>
<li>미국 경기지표, 미국 주식 지표 → 한국 주가 지수</li>
</ul>
</li>
</ul>
<h2 id="112-기계-학습">11.2 기계 학습</h2>
<ul>
<li><p>정의 : 데이터를 통해 모델을 만들고 사용하는 것</p>
</li>
<li><p>예측 모델링, 데이터 마이닝이라고도 불린다.</p>
</li>
<li><p>보통 주어진 데이터로 모델을 구축하고, 새로운 데이터에 만들어진 모델을 적용하면 다음과 같이 다양한 것을 예측해볼수 있다</p>
<ul>
<li>이메일이 스팸 메일인지 아닌지 예측</li>
<li>신용카드 사기 예측</li>
<li>쇼핑 고객이 클릭할 확률이 높은 광고 예측</li>
<li>슈퍼볼에서 우승할 미국 축구팀 예측</li>
</ul>
</li>
</ul>
<p><strong>&lt;종류&gt;</strong></p>
<ul>
<li>지도 학습 : 학습에 사용될 데이터에 답(종속 변수 값)이 포함되어있는 학습</li>
<li>비지도 학습 : 답(종속 변수값) 이 없는 데이터를 활용해 학습</li>
<li>준지도 학습 : 답(종속 변수값) 이 일부에만 있는 데이터를 활용</li>
<li>온라인 학습 : 새로 들어오는 데이터를 통해 모델을 끊임 없이 조정</li>
<li>강화 학습 : 연속된 예측 뒤 모델이 얼마나 잘 예측 했는지 파악 후 모델을 조정</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/904f6fe5-019c-4a2e-9bd2-521bcd52f4a2/image.png" alt=""></p>
<h2 id="113-overfitting-과-underfitting">11.3 Overfitting 과 Underfitting</h2>
<ul>
<li><p><strong>Overfitting (오버피팅)</strong></p>
<ul>
<li>기계 학습의 일반적인 문제점</li>
<li>만들어진 데이터모델이 학습데이터에서는 좋지만 새로운 데이터에서는 좋지 않은 경우</li>
<li>원인 : 학습 데이터의 잡음이나 불필요한 데이터를 모델에 학습 반영</li>
<li>문제점 : 새로운 데이터에 대한 예측의 부적확성</li>
<li>예시 : 옆 그림의 9차 함수 (점선)</li>
</ul>
</li>
<li><p><strong>Underfitting (언더피팅)</strong></p>
<ul>
<li>만들어진 데이터 모델의 성능이 학습 데이터에서도 좋지 않은 경우</li>
<li>원인 : 부적합한 모델 선택, 모델을 변경해야 함</li>
<li>문제점 : 학습 데이터도 부정확한 예측</li>
<li>예시 : 아래 그림의 상수 함수 (가로 점선)</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/41b7c8e9-0686-4753-ba3f-a3a9b7dbf547/image.png" alt=""></p>
<h3 id="적합-모델을-찾기-위한-실험-설계">적합 모델을 찾기 위한 실험 설계</h3>
<ul>
<li><p>적합 모델 찾기 : 학습과 평가 (테스팅) → 학습 데이터와 테스트 데이터의 분할</p>
</li>
<li><p>모델 실험 설계</p>
<ul>
<li><p>hold-out 기법</p>
<p>  전체 데이터에서 7:3 또는 8:2 등 비율로 훈련 데이터와 테스트 데이터로 나눈다</p>
<p>  훈련 데이터(training set)는 모델을 개발하기 위해, 테스트 데이터(test set)는 모델을 평가하기 위해 사용 </p>
</li>
<li><p>k-묶음 교차검증</p>
<p>  전체 데이터를 k-묶음으로 나눈 후 (k-1)개 묶음 </p>
</li>
<li><p>hold-out + validation 기법</p>
<p>  검증 데이터 셋을 추가하여, 훈련 데이터에 다시 hold-out 기법을 적용한 방법 </p>
<p>  검증 데이터셋은 훈련 데이터로 개발된 여러 모델의 성능을 비교하는데 사용되며, 선정된 최선의 모델의 성능은 테스트 데이터셋을 이용하여 평가 </p>
</li>
</ul>
</li>
</ul>
<pre><code>### hold-out 방식으로 데이터 분할

```python
import random
from typing import TypeVar, List, Tuple

X = TypeVar(&#39;X&#39;)

def split_data(data: List[X], prob: float) -&gt; Tuple[List[X], List[X]]:
    # 데이터를 섞지 않고 나누는 함수
    idx = int(len(data) * prob)  # 데이터를 나누는 인덱스 계산
    return data[:idx], data[idx:]  # 데이터를 나누어 반환

data = [n for n in range(1000)]  # 0부터 999까지의 숫자로 이루어진 데이터 생성
train, test = split_data(data, 0.75)  # 데이터를 나눔

# 반환된 데이터의 길이가 올바른지 확인
assert len(train) == 750
assert len(test) == 250

# 반환된 데이터가 원본 데이터를 포함하고 있는지 확인
assert sorted(train + test) == data
```

### 독립(x), 종속(y) 형태의 데이터 분할

학습 데이터, 평가 데이터에 x와 y가 제대로 쌍을 이뤄서 들어갈수 있도록 한다. 

```python
Y = TypeVar(&#39;Y&#39;)    # 출력 변수를 표현하기 위한 일반적인 타입 

def train_test_split(xs: List[X],                     
                    ys: List[Y],                     
                    test_pct: float) -&gt; Tuple[List[X], List[X], List[Y], List[Y]]:    

    # Generate the indices and split them.    
    idxs = [i for i in range(len(xs))]    
    train_idxs, test_idxs = split_data(idxs, 1 - test_pct)    

    return ([xs[i] for i in train_idxs],  # x_train            
            [xs[i] for i in test_idxs],   # x_test            
            [ys[i] for i in train_idxs],  # y_train            
            [ys[i] for i in test_idxs])   # y_test

xs = [x for x in range(20)]    # xs are 1 ... 1000
ys = [2 * x for x in xs]       # each y_i is twice 
x_train, x_test, y_train, y_test = train_test_split(xs, ys, 0.25)

print(xs, ys)
print(x_train, x_test)
print(y_train, y_test)

# 비율이 맞는지 확인
assert len(x_train) == len(y_train) == 750
assert len(x_train) == len(y_test) == 250

# 대응되는 데이터들이 잘 짝지어졌는지 확인
assert all(y == 2 * x for x, y in zip(x_train, y_train))
assert all(y == 2 * x for x, y in zip(x_test, y_test))

# 모델 학습 하고, 성능 평가 
model = SomeKindOfModel()
x_train, x_test, y_train, y_test = train_test_split(xs, ys, 0.33)
model.train(x_train, y_train)
performance = model.test(x_test, y_test)
```

[sklearn.model_selection.**train_test_split**(**arrays*, *test_size=None*, *train_size=None*, *random_state=None*, *shuffle=True*, *stratify=None*)](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html)

- Split arrays or matrices into random train and test subsets
- Param

    `test_size, train_size: *float (0.0~1.) or int, default=None*`

- Returns

    `splitting*list, length=2 * len(arrays)*`</code></pre><h2 id="114-정확도"><strong>11.4 정확도</strong></h2>
<p><strong>모델의 분류와 정답</strong></p>
<ul>
<li>True Positive(TP) : 실제 Positive인 정답을 Positive라고 예측 (정답)</li>
<li>False Positive(FP) : 실제 Negative인 정답을 Positive라고 예측 (오답)</li>
<li>False Negative(FN) : 실제 Positive인 정답을 Negative라고 예측 (오답)</li>
<li>True Negative(TN) : 실제 Negative인 정답을 Negative라고 예측 (정답)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/b729ab74-4c5c-42b5-a725-9cd46479cf35/image.png" alt=""></p>
<p><strong>정확도 (accuracy)</strong></p>
<ul>
<li><p>accuracy = (tp + tn) / (tp + fp + fn + tn)</p>
</li>
<li><p>높게 나와도 의미가 없을 수 있음</p>
</li>
<li><p>정밀도(Precision) : Positive 라고 분류한 것 중에서 실제 Positive 인 것의 비율</p>
</li>
<li><p>재현율(Recall) : 실제 Positive 인 것 중에서 모델이 Positive라고 예측한 것의 비율</p>
</li>
<li><p>특이도(Specificity) : 실제 Negative 중 맞춘 Negative(TN)의 비율</p>
</li>
<li><p>위양성율(FRP : False Positive Rate) : 실제 Negative 중 틀린 Positive(FP)의 비율</p>
</li>
<li><p>위음성율(FNR : False Negative Rate) : 실제 Positive 중 틀린 Negative(FN)의 비율</p>
</li>
<li><p>환자 적용</p>
<ul>
<li>실제 확자를 환자로 판단하는 비율 : Recall, Sensitvity, HitRate</li>
<li>실제 환자를 비환자로 판단하는 비율 : FNR</li>
<li>비환자(건강한 사람)을 환자로 판단하는 비율 : FPR</li>
<li>비환자(건강한 사람)을 비환자로 판단하는 비율 : Specifity</li>
</ul>
</li>
<li><p>정밀도와 재현율의 트레이드 오프 관계</p>
<ul>
<li>정밀도를 높이려면 가급적 FP가 작아야 한다 → 가급적 negative로 예측해야 함</li>
<li>재현율을 높이려면 가급적 FN이 작아야한다 → 가급적 positive로 예측해야 함</li>
</ul>
</li>
<li><p>조화 평균</p>
<ul>
<li><p>주어진 수들의 역수와 산술평균의 역수</p>
</li>
<li><p>정밀도와 재현율의 조화 평균</p>
<p>  <img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/47b72aee-d070-4c59-9f55-b1c9aef910ff/0ee1cbb8-b2cc-4769-a77b-6d135b5ecdd3/Untitled.png" alt="Untitled"></p>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 - 객체 지향 프로그래밍 ]]></title>
            <link>https://velog.io/@dayon_log/%EC%9E%90%EB%B0%94-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@dayon_log/%EC%9E%90%EB%B0%94-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Wed, 01 May 2024 14:31:17 GMT</pubDate>
            <description><![CDATA[<p><strong>자바는 객체 지향 프로그래밍 언어이다.</strong> </p>
<p>모든 요소들이 객체로 표현되고, 객체 지향 개념의 특징인 <strong>추상화, 캡슐화, 상속, 다형성</strong> 이 적용된 언어이다.</p>
<p>이러한 객체 지향적 프로그램을 설계를 하면, 각각의 클래스, 메소드들이 각자 독립적인 역할을 가지기 때문에 코드 변경을 최소화하고 유지 보수를 하는데 유리하다. 코드의 재사용으로 반복적인 코드를 최소화하고 간결하게 표현할 수 있는것이다. </p>
<h2 id="객체object란">객체(object)란?</h2>
<p>객체는 객체지향 프로그래밍의 가장 기본적인 단위,  “모든 실재하는 대상”</p>
<p>객체는 책상, 의자, 시계 뿐만아니라 눈에 보이지 않는 논리, 철학, 개념들도 포함될 수 있다. </p>
<p>우리가 보고 느끼고 인지할 수 있는 그 모든 것을 의미한다. </p>
<p>객체 지향 프로그래밍에서는 객체를 추상화 시켜 속성(state)과 기능(behavior)으로 분류한 후에 이것을 다시 각각 변수(variable)와 함수(function)로 정의한다. </p>
<h2 id="객체-지향-프로그래밍의-특징-4가지">객체 지향 프로그래밍의 특징 4가지</h2>
<ol>
<li><p><code>추상화(Abstraction)</code> : 객체에서 공통된 속성과 행위를 추출하여 정의하는 것</p>
<ul>
<li><p>자바에서는 추상 클래스(abstract class)와 인터페이스(interface)로 구현할 수 있다.</p>
<pre><code class="language-java">public interface Vehicle {
      public abstract void start() 
      void moveForward();
      void moveBackward();
}

# 이동수단이라는 상위 클래스에서는 전진과 후진이라는 공통적인 기능을 추출해 선언하였다. </code></pre>
</li>
<li><p>인터페이스는 추상 메서드나 상수를 통해 객체의 역할만을 정의해두고, 실제적인 구현은 해당 인터페이스를 구현하는 각각의 객체에서 하도록 설계한다.</p>
</li>
<li><p>역할과 구현의 분리 ⇒ <strong>객체 지향 프로그래밍에서 유연한 프로그램을 설계하는데 핵심적인 부분</strong></p>
<blockquote>
<p>추상클래스 : 확장자의 역할 /  상속을 받아서 기능을 이용하고 확장시킨다. 
인터페이스 : 명세서의 역할 / 함수 껍데기 - 함수의 구현을 강제해주어, 구현 객체의 같은 동작을 보장한다.</p>
</blockquote>
</li>
</ul>
</li>
<li><p><code>상속 (Inheritance)</code> : 기존의 클래스를 재활용하여 새로운 클래스를 작성하는 자바의 문법 요소 </p>
<ul>
<li>상속은 상위 클래스로부터 확장된 여러개의 하위 클래스들이 모두 상위 클래스의 속성과 기능들을 간편하게 사용할 수 있도록 한다.</li>
<li>반복해서 정의할 필요없이 딱 한번만 정의해두고 재사용할 수 있어, 코드를 최소화하고 공유하는 속성과 기능에 간편하게 접근하여 사용할 수 있도록 한다.</li>
<li><strong>메서드 오버라이딩</strong> : 상위 클래스의 기능과 속성을 그대로 사용할 수도 있지만, 각각의 클래스의 맥락에 맞게 내용을 재정의 할 수도 있다.</li>
<li>위의 인터페이스를 사용하는 구현에 비해 추상화의 정도가 낮다. 모든 구체적인 내용을 정의해두고, 하위 클래스에서는 단순히 가져다가 재사용 할 수 있다.</li>
</ul>
</li>
<li><p><code>다형성 (Polymorphism)</code> : 어떤 객체의 속성이나 기능이 상황에 따라 여러가지 형태를 가질 수 있는 성질 </p>
<ul>
<li><p>한 타입의 참조 변수를 통해 여러 타입의 객체를 참조할 수 있도록 만든것</p>
</li>
<li><p>상위 클래스 타입의 참조변수로 하위 클래스의 객체를 참조할 수 있도록 하는 것</p>
</li>
<li><p>메서드 오버라이딩, 메서드 오버로딩</p>
</li>
<li><p>다형성을 활용하면 여러 종류의 객체를 배열로 다루는 일이 가능해진다.</p>
<pre><code class="language-java">  public class Main {
      public static void main(String[] args) {
          // 상위 클래스 타입의 객체 배열 선언
          Vehicle vehicles[] = new Vehicle[2];
          vehicles[0] = new Car();
          vehicles[1] = new MotorBike();

          for (Vehicle vehicle : vehicles) {
              System.out.println(vehicle.getClass()); // 각각의 클래스를 확인하는 메서드
          }
      }
  }

  // 클래스 정의
  class Car {}
  class MotorBike {}</code></pre>
</li>
<li><p>하나의 타입만으로 여러 가지 타입의 객체를 참조할 수 있다.</p>
</li>
</ul>
</li>
<li><p><code>캡슐화 (Encapsulation)</code> : 서로 연관되어 있는 속성과 기능들을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것을 말한다. </p>
<ul>
<li>캡슐화를 하는 이유<ul>
<li>데이터 보호 : 외부로부터 클래스에 정의된 속성과 기능들을 보호</li>
<li>데이터 은닉 : 내부의 동작을 감추고 외부에는 필요한 부분만 노출</li>
</ul>
</li>
<li>데이터 보호와 데이터 은닉으로 각 객체 고유의 독립성과 책임 영역을 안전하게 지키고자하는 목적</li>
</ul>
</li>
</ol>
<h2 id="정보-은닉과-캡슐화의-차이점">정보 은닉과 캡슐화의 차이점</h2>
<ul>
<li>정보은닉 : 데이터 구현이 외부 모듈에 알려지지 않아야 한다고 설명하며 단어 생성</li>
<li>캡슐화 : 원치 않는 수정을 방지하기 위해 클래스의 기본 데이터에 대한 엑세스를 줄이는 방법 설명을 위해 단어 고안</li>
</ul>
<p>초반에는 단어가 동의어로 사용되어있지만, 2001년 paul roger가 정보를 숨기지 않고 캡슐화 할 수 있다고 주장. <strong>캡슐화는 클래스를 사용하여 외부 코드를 손상시키지 않고 클래스에 기능을 도입</strong></p>
<pre><code class="language-java">class BookEncapsulation {
    public String author;
    public int isbn;
    public BookEncapsulation(String author, int isbn) {
        this.author = author;
        this.isbn = isbn;
    }
    public String getBookDetails() {
        return &quot;author name: &quot; + author + &quot; ISBN: &quot; + isbn;
    }
}</code></pre>
<p>→  <em>id</em> 를 도입하여 <em>BookEncapsulation</em> 클래스를 수정</p>
<pre><code class="language-java">// ...
public int id = 1;
public BookEncapsulation(String author, int isbn) {
    this.author = author;
    this.isbn = isbn;
}
public String getBookDetails() {
    return &quot;author id: &quot; + id + &quot; author name: &quot; + author + &quot; ISBN: &quot; + isbn;
} 
// ...</code></pre>
<p>→ 이러한 내부 변경 사항은 클라이언트 코드를 손상시키지 않는다.  이는 캡슐화를 강력하고 모듈화시켜준다</p>
<p>public으로 모든 엑세스 수정자를 사용할수 있는 문제점 발생 </p>
<p>하지만, 정보은닉 개념을 적용하면 캡슐화를 더욱 엄격하게 만들수 있다. </p>
<ul>
<li><strong>정보 은닉은 클래스 데이터의 직접적인 수정을 방지하는 것을 목표로 하는</strong> 프로그래밍 원칙</li>
</ul>
<pre><code class="language-java">class BookInformationHiding {
    private String author;  // private 액세스 수정자로 변경 
    private int isbn;
    private int id = 1;

    public BookInformationHiding(String author, int isbn) {
        setAuthor(author);
        setIsbn(isbn);
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
    public int getIsbn() {
        return isbn;
    }
    public void setIsbn(int isbn) {
        this.isbn = isbn;
    }
    public String getBookDetails() {
        return &quot;author id: &quot; + id + &quot; author name: &quot; + author + &quot; ISBN: &quot; + isbn;
    }
}</code></pre>
<p>→ private 액세스 수정자로 변경 외부 클래스의 필드 액세스 및 변경을 제한한다. </p>
<p>→ 필드에 엑세스하고 수정하는 방법을 설정하기 위해 getter와 setter를 만든다. </p>
<table>
<thead>
<tr>
<th>정보 은닉</th>
<th>캡슐화</th>
</tr>
</thead>
<tbody><tr>
<td>구현 세부 사항과 의도하지 않은 데이터 수정을 숨기는 설계 원칙</td>
<td>데이터를 작동하는 기능과 함께 묶는 객체 지향 원리</td>
</tr>
<tr>
<td>private 액세스 수정자를 엄격하게 사용합니다.</td>
<td>액세스 한정자에 엄격하지 않으며 public , private 또는 protected 액세스 한정자를 사용할 수 있다.</td>
</tr>
<tr>
<td>defensive 프로그래밍을 달성하는 데 도움이 됩니다.</td>
<td>정보 은닉을 달성하기 위한 방법론</td>
</tr>
</tbody></table>
<p>결론 : 클래스를 캡슐화할때 정보 은닉의 원칙을 적용해야 한다. 하지만 두 단어는 동일어가 아니다! </p>
<hr>
<p>출처</p>
<ol>
<li><a href="https://www.codestates.com/blog/content/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%8A%B9%EC%A7%95">https://www.codestates.com/blog/content/객체-지향-프로그래밍-특징</a></li>
<li><a href="https://www.baeldung.com/java-information-hiding-vs-encapsulation">https://www.baeldung.com/java-information-hiding-vs-encapsulation</a></li>
</ol>
<hr>
<p>더 알아볼것 - 의존관계 주입 (객체 간 높은 결합도를 해결하는 것)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JVM - Garbage Collector]]></title>
            <link>https://velog.io/@dayon_log/JVM-Garbage-Collector</link>
            <guid>https://velog.io/@dayon_log/JVM-Garbage-Collector</guid>
            <pubDate>Tue, 16 Apr 2024 17:24:00 GMT</pubDate>
            <description><![CDATA[<p>Garbage Collection이 JVM에서 어떻게 작동하는지, 서로 다른 가비지 컬렉터들의 특징과 성능 차이에 대해 알아보자 </p>
<h1 id="1-garbage-collection-이란">1. Garbage Collection 이란?</h1>
<h3 id="jvm-java-virtual-machine">JVM (Java Virtual Machine)</h3>
<ul>
<li>JVM is a program that looks like a machine to the programs written to execute in it.
JVM은 그 안에서 실행되도록 작성된 프로그램에게 기계처럼 보이게 하는 프로그램이다.</li>
<li>운영체제 메모리 영역에 접근하여 메모리를 관리하는 프로그램</li>
</ul>
<pre><code>![사진 출처 : https://mirinae312.github.io/develop/2018/06/04/jvm_gc.html](https://velog.velcdn.com/images/dayon_log/post/39af3672-e607-424b-998c-4fdb4ea0743c/image.png)

&lt;br&gt;</code></pre><h3 id="메모리-관리의-필요성">메모리 관리의 필요성</h3>
<p>모든 프로그램은 실행을 위해 메모리를 할당받아 사용하며, 사용이 끝난 메모리는 시스템에 반환되어야 한다.</p>
<ul>
<li><p><code>C, C++</code> 등 가비지 컬렉션이 없는 언어는 할당했던 메모리를 반환하지 않는다.</p>
<ul>
<li><p>시스템의 모든 RAM을 사용하고 프로그램과 컴퓨터가 머물 수도 있다.</p>
</li>
<li><p>메모리가 해제된 후 포인터를 통해 버퍼를 읽고쓰려 시도할때 무작위의 결과가 발생할 수 있다.(Dangling Pointer)</p>
</li>
<li><p>버퍼 오버런, 문자열 조작이 발생 할 수 있다.</p>
<p>⇒ 이러한 이유로 로그래머가 수동으로 메모리 할당과 해제를 일일이 해줘야 했었다.</p>
</li>
</ul>
</li>
<li><p>파이썬, 자바스크립트, Go 언어 등많은 프로그래밍 언어에서 가비지 컬렉션이 기본으로 내장</p>
  <br>

</li>
</ul>
<h3 id="java의-garbage-collector">Java의 Garbage Collector</h3>
<ul>
<li><p>JVM Heap 영역에서 사용하지 않는 객체를 <strong>삭제하는 프로세스</strong></p>
</li>
<li><p>Garbage collection was invented to simplify manual memory mangagement.</p>
<p>  <strong>동적으로 할당된 메모리</strong> 영역 중 사용하지 않는 영역을 탐지하여 해제하는 기능 </p>
<ul>
<li>Heap ⇒  동적으로 할당한 메모리 영역 
모든 Object 타입의 데이터가 할당, Heap 영역의 Object를 가리키는 참조 변수가 Stack에 할당</li>
<li>Stack ⇒ 정적으로 할당한 메모리 영역, Heap영역에서 생성된 Object 의 주소만 참조</li>
</ul>
</li>
<li><p>Java에서는 메모리 할당 해제 프로세스가 가비지 컬렉터에 의해 자동으로 처리된다.</p>
  <br>

</li>
</ul>
<h3 id="stop-the-world">stop-the-world</h3>
<ul>
<li><p>GC를 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것</p>
</li>
<li><p>stop-the-world가 발생하면 GC를 실행하는 쓰레드를 제외한 <strong>나머지 쓰레드는 모두 작업을 멈춘다.</strong></p>
</li>
<li><p>GC작업을 완료한 이후에 중단했던 작업을 다시 시작</p>
</li>
<li><p>자동으로 처리해준다 해도 메모리가 언제 해제되는지 정확하게 알 수 없어 제어하기 힘들며, 가비지 컬렉션(GC)이 동작하는 동안에는 다른 동작을 멈추기 때문에 오버헤드가 발생되는 문제점</p>
</li>
<li><p>어떠한 Garbage Collector 알고리즘을 사용하더라도 stop-the-world는 발생한다.
⇒ GC 튜닝 = stop-the-world의 시간을 줄이는 것</p>
<blockquote>
<p>&quot;메모리가 5배 더 많다면 성숙한 비복사 공간이 있는 아펠(Appel)식 세대별 컬렉터의 성능은 도달 가능성 기반의 명시적 메모리 관리의 성능과 대등하다. 메모리가 3배 더 많다면 컬렉터의 실행 성능은 명시적 메모리 관리에 비해 평균 17% 느리다. 메모리가 2배인 경우 가비지 컬렉션의 성능은 70% 가까이 하락한다. 물리적 메모리가 부족하면 페이징으로 인해 가비지 컬렉션의 속도는 명시적 메모리 관리에 비해 훨씬 더 느리게 된다.</p>
<p><a href="https://people.cs.umass.edu/~emery/pubs/gcvsmalloc.pdf">매튜 허츠와 에머리 D. 버거의 2005년 논문</a></p>
<p>!<a href="http://linkback.itworld.co.kr/images/onebyone.gif?action_id=e8e4beee07c904b8d7dde7c56153d87">http://linkback.itworld.co.kr/images/onebyone.gif?action_id=e8e4beee07c904b8d7dde7c56153d87</a></p>
<p>!<a href="http://linkback.itworld.co.kr/images/onebyone.gif?action_id=bf7c84ffcaf5b2a97163d6a70229b07">http://linkback.itworld.co.kr/images/onebyone.gif?action_id=bf7c84ffcaf5b2a97163d6a70229b07</a></p>
</blockquote>
</li>
</ul>
<br>

<h1 id="2-garbage-collection-동작-방식">2. Garbage Collection 동작 방식</h1>
<h3 id="garbage-collection-대상">Garbage Collection 대상</h3>
<p>가비지 컬렉션(Garbage Collection)은 어떤 Object를 Garbage로 판단해서 스스로 지워버릴까?</p>
<p>= 메서드가 끝나는 등의 특정 이벤트로 Heap영역 객체의 메모리 주소를 가진 참조 변수가 삭제가 되면 <strong>Heap 영역에서 어디서도 참조하고 있지 않은 객체</strong>들이 발생 ⇒ 이 Object들을 주기적으로 제거 </p>
<br>

<h3 id="mark-and-sweep---청소-방식">Mark and Sweep - 청소 방식</h3>
<ol>
<li><strong>Mark 과정 - (1)</strong> : Garbage Collector가 Stack의 모든 변수를 스캔하면서 각각 어떤 객체를 참고하고 있는지 찾아서 마킹한다. </li>
<li><strong>Mark 과정 - (2)</strong> : Reachable Object가 참조하고 있는 객체도 찾아서 마킹한다. </li>
<li><strong>Sweep 과정</strong> : 마킹되지 않은 객체를 Heap 에서 제거한다. </li>
<li><strong>Compact 과정</strong> : Sweep 후에 분산된 객체들을 Heap 의 시작 주소로 모아 메모리가 할당됭 부분과 그렇지 않은 부분으로 압축한다.(종류에 따라 하지 않는 경우도 O)</li>
</ol>
<br>

<h3 id="garbage-collection-동작-과정">Garbage Collection 동작 과정</h3>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/fbd1c902-598d-4dcd-a3e1-e72c3f6b5342/image.png" alt=""></p>
<p> JVM의 Heap 영역은 동적으로 레퍼런스 데이터가 저장되는 공간으로서, GC에 대상이 되는 공간이다. </p>
<p>하나의 Heap 영역을 세부적으로 쪼개 객체의 생존기간을 세밀하게 제어 </p>
<ul>
<li><p><strong>Heap 영역 설계 전제 &#39;weak generational hypothesis’</strong></p>
<ul>
<li>대부분의 객체는 금방 접근 불가능 상태(Unreachable)가 된다.</li>
<li>오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다.</li>
</ul>
</li>
<li><p>즉 객체는 대부분 일회성이고, 메모리에 오래 남아있는 경우는 드물다</p>
</li>
<li><p>Heap 영역 구분</p>
<ul>
<li><code>Young Generation</code> : 새로운 객체들이 할당되는 영역</li>
<li><code>Old Generation</code> : Young Generation 오래동안 살아남은 객체들이 존재하는 영역</li>
</ul>
</li>
</ul>
<ul>
<li><p>Young Generation에서의 GC</p>
<ul>
<li>메모리 상의 객체를 찾아 제거하는데 적은 시간이 걸림 ⇒ Young 영역에 대한 GC를 <strong>Minor GC</strong>라 부름</li>
<li>많은 객체가 Young 영역에서 생성되었다가 사라진다.</li>
<li>Eden : 새로 생성된 객체 위치 → GC 이후 살아남은 객체 Survivor 영역으로 이동</li>
<li>Survivor 1 / 2 : 최소 1번의 GC 에서 살아남은 객체가 존재 , 둘중 하나는 꼭 비어있어야 한다.</li>
</ul>
</li>
<li><p>Old  Generation에서의 GC</p>
<ul>
<li>Young 영역 보다 크게 할당 되고, 영역의 크기가 큰 만큼 오랜시간이 걸린다. (10배 이상)
⇒ Stop-The-World 문제 발생</li>
<li>Old 영역에 대한 GC를 <strong>Major GC</strong> 또는 <strong>Full GC</strong> 라고 부른다.</li>
<li>Survivor 에서 객체의 age(살아남은 횟수)가 임계값에 도달하면 Old Generation으로 이동</li>
</ul>
</li>
</ul>
<br>

<h1 id="3-주요-garbage-collection-알고리즘">3. 주요 Garbage Collection 알고리즘</h1>
<h3 id="serial-gc">Serial GC</h3>
<ul>
<li>적은 메모리와 CPU 코어 수 가 적을때 사용하기 위해 개발된 가장 단순한 GC</li>
<li>가장 Stop-The-World 시간이 길다.</li>
<li>Young 영역에서는 Mark-Sweep 사용하고, Old 영역에서는 Mark-Sweep-Compact을 사용한다.</li>
</ul>
<br>

<h3 id="parallel-gc">Parallel GC</h3>
<ul>
<li>Java 8 default GC</li>
<li>Serial GC와 알고리즘은 동일하지만, Young영역의 Minor GC 멀티 쓰레드로 수행</li>
</ul>
<br>

<h3 id="parallel-old-gc">Parallel Old GC</h3>
<ul>
<li>Parallel GC 를 개선해 Old 영역도 멀티 쓰레드로 수행</li>
<li>GC 방식을 Mark-Summary-Compact 방식으로 이용</li>
</ul>
<br>

<h3 id="g1-gc-garbage-first">G1 GC (Garbage First)</h3>
<ul>
<li>CMS GS(GC 복잡한 과적으로 수행되어 메모리 파편화 문제 발생한 알고리즘 - java14에서부터 사용 중지)</li>
<li>CMS GS 대체제로 등장 → java 9+ 버전의 default GC</li>
<li>목적 = 큰 Heap 메모리에서 짧은 GC 시간 보장</li>
<li>Heap 영역을 Young/Old 로 구분하지 않고, Region 이라는 개념 도입</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/857eb019-49ed-4076-b2f1-dc8bf410399f/image.png" alt=""></p>
<ul>
<li>Region (2048개의 Region)<ul>
<li><strong>Humongous</strong> : Region 크기의 50%를 초과하는 큰 객체를 저장하기 위한 공간이며, 이 Region 에서는 GC 동작이 최적으로 동작하지 않는다.</li>
<li><strong>Available/Unused</strong> : 아직 사용되지 않은 Region을 의미한다.</li>
<li>각 heap 영역들은 고정이 아닌 동적으로 부여</li>
</ul>
</li>
<li>순차적 이동 X , 더 효율적이라고 생각하는 위치로 객체를 재할당 시킨다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/634b9f9d-cac8-403f-b0a2-a5174cbf4f14/image.png" alt=""></p>
<br>


<h3 id="shenandoah-gc">Shenandoah GC</h3>
<ul>
<li>Redhat에서 개발 GC</li>
<li>G1 GC의 pause dltb goruf</li>
<li>Heap 사이즈에 영향을 받지 않고, 일정한 pause 시간이 소요가 되는것이 특징</li>
</ul>
<br>

<hr>
<h3 id="참고한-자료">참고한 자료</h3>
<ol>
<li><a href="https://www.ibm.com/docs/ko/sdk-java-technology/8?topic=introduction-java-virtual-machine">https://www.ibm.com/docs/ko/sdk-java-technology/8?topic=introduction-java-virtual-machine</a></li>
<li><a href="https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html">https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html</a></li>
<li><a href="https://youtu.be/vZRmCbl871I?si=uWo59nmT_92hBy1k">https://youtu.be/vZRmCbl871I?si=uWo59nmT_92hBy1k</a></li>
<li><a href="https://www.youtube.com/watch?v=Fe3TVCEJhzo&amp;list=WL&amp;index=4">https://www.youtube.com/watch?v=Fe3TVCEJhzo&amp;list=WL&amp;index=4</a></li>
<li><a href="https://d2.naver.com/helloworld/1329">https://d2.naver.com/helloworld/1329</a></li>
<li><a href="https://www.itworld.co.kr/news/276625">https://www.itworld.co.kr/news/276625</a></li>
<li><a href="https://mirinae312.github.io/develop/2018/06/04/jvm_gc.html">https://mirinae312.github.io/develop/2018/06/04/jvm_gc.html</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[synchronized 와  ReentrantLock]]></title>
            <link>https://velog.io/@dayon_log/synchronized-%EC%99%80-ReentrantLock</link>
            <guid>https://velog.io/@dayon_log/synchronized-%EC%99%80-ReentrantLock</guid>
            <pubDate>Wed, 10 Apr 2024 08:42:56 GMT</pubDate>
            <description><![CDATA[<h2 id="👍-주제-선정-이유">👍 주제 선정 이유</h2>
<ul>
<li><p>Java21 은 Virtual Thread에서 carrier thread의 pinning 이슈를 유발해 성능을 저하시키는 synchronized 사용을 지양하고 ReentrantLock 으로 사용을 권장함
<a href="https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html#GUID-704A716D-0662-4BC7-8C7F-66EE74B1EDAD">https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html</a></p>
</li>
<li><p><a href="https://velog.io/@dayon_log/JDK-21-Virtual-Thread">이전에 작성한 JDK-21 포스트 </a></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/14e0307c-88ae-4515-9579-ec7c81b92132/image.png" alt=""></p>
<ul>
<li>synchronized 와  ReentrantLock 이 무엇이고, 뭐가 다른지 알아보자.</li>
</ul>
<br>

<hr>
<h2 id="🧶-thread">🧶 Thread</h2>
<ul>
<li><p><strong>프로세스 내에서 실행되는 여러 흐름의 단위</strong></p>
</li>
<li><p>같은 자원을 공유할 수 있기 때문에 동시에 여러 가지 일을 같은 자원을 두고 수행할 수 있고, 이는 여러 스레드가 동시에 하나의 자원을 공유하고 있기 때문에 자원 공유에서 <strong>동시성 이슈</strong>가 발생한다.</p>
<p>⇒ 동시성 문제를 해결하기 위한 여러 방법중 <strong>Lock</strong> 에 대해 알아보자 </p>
</li>
<li><p>자바에서 멀티 스레드 환경에서 thread-safe 확보를 위해 <code>synchronized</code> 혹은 <code>ReentrantLock</code> 를 사용하는 방법이 대표적</p>
</li>
</ul>
<p><br><br></p>
<h2 id="1️⃣-synchronized">1️⃣ synchronized</h2>
<ul>
<li>한 번에 하나의 쓰레드만 객체에 접근할 수 있도록 <strong>객체에 Lock을 걸어서 데이터의 일관성을 유지</strong>하는 것</li>
<li>메서드 <code>synchronized</code>는 호출될 때 자동으로 Lock 작업을 수행한다. Lock 작업이 완료될때까지 내용은</li>
</ul>
<h3 id="모니터를-사용하여-구현되는-동기화"><strong>모니터를 사용하여 구현되는 동기화</strong></h3>
<p><code>synchronized</code> 는 모니터와 &quot;대기 및 알림&quot; 또는 &quot;신호 및 계속&quot; 메커니즘과 연결된 대기 세트를 사용하여 수행된다. </p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/ee9dc787-7cb7-4e76-ac2b-79a6fbdd13df/image.png" alt=""></p>
<blockquote>
<p><strong>Java Monitor</strong></p>
</blockquote>
<ul>
<li>여러 스레드가 객체로 동시에 객체로 접근하는 것을 막는다.</li>
<li>자바의 각 개체는 스레드를 잠그거나 잠금 해제할 수 있는 모니터와 연결되어 있다.</li>
<li><strong>한 번에 하나의 Thread만 모니터에 대한 Lock을 보유할 수 있다</strong>.  해당 모니터를 잠그려고 시도하는 다른 스레드는 해당 모니터에 대한 잠금을 얻을 수 있을 때까지 차단된다.</li>
</ul>
<br>



<h3 id="사용법">사용법</h3>
<p>*<em>1) 메서드에 Lock을 걸 때  *</em></p>
<pre><code class="language-jsx">class Test {
    int count;
    synchronized void bump() {     
        count++;
    }
    static int classCount;
    static synchronized void classBump() {
        classCount++;
    }
}</code></pre>
<p>*<em>2) 특정 변수에 Lock을 걸 때 *</em></p>
<pre><code class="language-jsx">class BumpTest {
    int count;
    void bump() {
        synchronized (this) { count++; }  
    }
    static int classCount;
    static void classBump() {
        try {
            synchronized (Class.forName(&quot;BumpTest&quot;)) {
                classCount++;
            }
        } catch (ClassNotFoundException e) {}
    }
}</code></pre>
<ul>
<li><p>단, 변수에 lock을 걸기 위해선 해당 변수는 객체여야 한다. int, long과 같은 기본형 타입에는 lock을 걸 수 없다.</p>
</li>
<li><p>wait() : 객체의 lock을 풀고 Thread를 해당 객체의 waiting pool 에 넣는다.</p>
</li>
<li><p>notify() : waiting pool 에서 대기중인 Thread 중 하나를 깨운다.</p>
</li>
<li><p>notifyAll() : waiting pool 에서 대기중인 모든 Thread를 깨운다.</p>
<p>  synchronized  로직은 JDK 내에 내장 되어 있고, C++ 언어로 구현이 되어있다. <a href="https://github.com/openjdk/jdk/blob/master/src/hotspot/share/runtime/synchronizer.cpp">[오픈소스]</a></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/4cf90d9e-b66a-4e46-96b2-ec0631c02c26/image.png" alt=""></p>
<p>⭐️ notify()를 실행할때, 대기 중인 스레드(waitSet) 중에서 어떤 스레드가 선택되는지 보장할 수 없다. </p>
<p><strong>⇒ 어느 부분이 Lock인지 알수 없다, 암시적 Lock</strong> </p>
<p><br><br></p>
<h2 id="2️⃣-reentrantlock">2️⃣ ReentrantLock</h2>
<ul>
<li>JAVA로 작성되어 JDK 1.5 부터 등장한 <code>java.util.concurrent</code> 패키지에 포함되어있다.</li>
<li><code>synchronized</code> 과 동일한 의미와 기본동작을 갖지만 <strong>명시적</strong>으로 Lock 객체를 생성하며 스레드의 재진입성을 지원하는 더 확장된 Lock 이다.</li>
<li>해당 Lock의 범위를 메서드 내부에서 한정하기 어렵거나, 동시에 여러 Lock을 사용하고 싶을 때 사용한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/19c81709-cece-487c-84d5-948ef87e4860/image.png" alt="ReentrantLock이 많은 기능들을 제공함을 알 수 있다."></p>
<p>ReentrantLock이 많은 기능들을 제공함을 알 수 있다. </p>
<ul>
<li><p>CAS(Comapre And Swap)으로 동시에 하나의 스레드만이 잠금을 획득할 수 있도록 상태값을 관리하여 스레드 안전을 보장합니다.</p>
</li>
<li><p>경쟁에 실패해 대기중인 스레드는 작업이 일시 중단된 후  FIFO 대기열에 저장이 된다.</p>
<ul>
<li>synchronized의 경우 Thread간의 lock을 획득하는 순서를 보장해주지 않았다.(unFair) , ReentrantLock은 Fair, unFair 모두 지원한다.</li>
<li>Fair Lock을 선택하면 대기열에 들어간 스레드 순으로 Lock 리소스에 접근</li>
</ul>
</li>
<li><p>동일한 스레드에 의해 최대 20억번 까지의 재귀적 잠금을 지원한다.</p>
</li>
<li><p>직접적으로 Lock 객체를 생성하여 사용할 수 있습니다.</p>
</li>
</ul>
<pre><code>```java
public class ReentrantLock
extends Object
implements Lock, Serializable
```</code></pre><ul>
<li><p>잠금 및 잠금 해제는 모두 수동으로 처리할 수 있다.</p>
<ul>
<li><p>lock() 메서드를 사용할 경우 다른 스레드가 해당 lock() 메서드 시작점에 접근하지 못하고 대기하게 된다.</p>
</li>
<li><p>unlock() 메서드를 실행해야 다른 메서드가 lock을 획득할 수 있게 된다.</p>
</li>
</ul>
</li>
<li><p>잠금 시간 제한을 설정할 수 있다.  만료가 되면 해당 스레드를 건너뛰기 때문에 <code>synchronized</code> 에서 발생할 수 있는 교착 상태를 방지할 수 있다.</p>
</li>
</ul>
<h3 id="사용법-1">사용법</h3>
<pre><code class="language-jsx"> class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }</code></pre>
<p><br><br></p>
<hr>
<h3 id="정리">정리</h3>
<ul>
<li>코드는 더 길어지지만 JDK21 의 Virtual Thread를 사용하거나, Lock의 상세한 부분을 설정해주고 싶다면 ReentrantLock 을 사용하자</li>
</ul>
<br>

<h3 id="앞으로-해볼-것">앞으로 해볼 것</h3>
<ul>
<li>현재 진행중인 프로젝트에서 조회수 count하는 메서드에서 동시성 이슈를 전혀 고려하지 않았다! 수정할 것</li>
<li>Thread-safe 한 방식으로 Lock이 외에도 Volatile, Immutable Instance, ConcurrentHashMap 에 대해 공부해 볼 것</li>
</ul>
<br>

<h3 id="참고한-사이트"><strong>참고한 사이트</strong></h3>
<ol>
<li><a href="https://www.baeldung.com/java-synchronize-static-variable-different-threads">https://www.baeldung.com/java-synchronize-static-variable-different-threads</a></li>
<li><a href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.3.6">https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.3.6</a></li>
<li><a href="https://stackoverflow.com/questions/3362303/whats-a-monitor-in-java">https://stackoverflow.com/questions/3362303/whats-a-monitor-in-java</a></li>
<li><a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.1">https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.1</a></li>
<li>남궁성의 자바의 정석 : <a href="https://github.com/castello/javajungsuk_basic">https://github.com/castello/javajungsuk_basic</a> [Chapter 13 : Thread ] </li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html">https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html</a></li>
<li><a href="https://medium.com/javarevisited/synchronized-vs-reentrantlock-how-to-choose-cfb5306080e7">https://medium.com/javarevisited/synchronized-vs-reentrantlock-how-to-choose-cfb5306080e7</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html">https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[JDK 21 Virtual Thread]]></title>
            <link>https://velog.io/@dayon_log/JDK-21-Virtual-Thread</link>
            <guid>https://velog.io/@dayon_log/JDK-21-Virtual-Thread</guid>
            <pubDate>Wed, 20 Mar 2024 04:12:31 GMT</pubDate>
            <description><![CDATA[<h3 id="jdk-21">JDK 21</h3>
<p><strong>2023.09.19</strong> JDK 21 LTS</p>
<p>2023.10.04 Gradle 8.4 </p>
<p>2023.11.17 Spring 6.1</p>
<p>2023.11.23 Spring boot 3.2</p>
<p>2023.12.07 Jetbrain Intellij </p>
<br>

<h3 id="사전-정의">사전 정의</h3>
<p>프로세스(Process) :</p>
<blockquote>
<p>메모리에 올라와 <strong>실행되고 있는 프로그램의 인스턴스,</strong> 운영체제로부터 시스템 자원을 할당받는 <strong>작업의 단위</strong></p>
</blockquote>
<p>스레드(Thread) :</p>
<blockquote>
<p>프로세스가 할당받은 자원을 이용하는 <strong>실행 흐름의 단위</strong></p>
</blockquote>
<br>

<h2 id="virtual-thread가상-스레드-란">Virtual Thread(가상 스레드) 란?</h2>
<ul>
<li>JDK 21 에 추가된 <strong>경량 스레드</strong></li>
<li>OS 스레드를 그대로 사용하지 않고 JVM 내부 스케줄링을 통해서 수십만~수백만개의 스레드를 동시에 사용할 수 있게 한다.</li>
</ul>
<br>

<h2 id="virtual-thread-배경">Virtual Thread 배경</h2>
<h3 id="전통적인-java의-thread">전통적인 JAVA의 Thread</h3>
<ul>
<li>Java의 Thread는 OS Thread를 Wrapping한 것 = <strong>Platform Thread</strong></li>
<li>Java 애플리케이션에서 Thread를 사용하면 실제로는 OS Thread를 사용한 것</li>
<li>OS Thread는 생성 갯수가 제한적이고, 생성하고 유지하는 비용이 비싸다</li>
<li>이 때문에 애플리케이션에는 <strong>플랫폼 스레드</strong>를 효율적으로 사용하기 위해 <strong>Thread Pool</strong>을 사용했다
(<strong>Thread Pool</strong> : 작업 처리에 사용되는 스레드를 제한된 개수로 정해두고 작업 queue에 들어오는 작업들을 하나씩 스레드가 맡아 처리하는 것)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/2896ab7d-8d51-4e43-b3a7-fcfc980e621b/image.png" alt=""></p>
 <br>


<h3 id="throughput-의-한계">Throughput 의 한계</h3>
<ul>
<li>스프링부트와 같은 기본적인 Web Request 처리방식은 <strong>Thread Per Request</strong> (하나의 요청 / 하나의 스레드)</li>
<li>처리량을 높이려면 스레드 증가 필요, but 스레드를 무한정 늘릴 수 없다. (OS 스레드 제약)</li>
<li>따라서 애플리케이션의 처리량(throughput)은 스레드 풀에서 감당할 수 있는 범위를 넘을 수 없다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/f6c9c113-6c5d-448a-a80a-75698928e7e5/image.png" alt=""></p>
<h3 id="blocking-io">Blocking I/O</h3>
<ul>
<li><p>Thread에서 I/O 작업을 처리할때 Blocking이 일어난다.</p>
</li>
<li><p>Thread는 I/O 작업이 마칠때까지 다른 요청을 처리하지 못하고 기다려야 한다.</p>
</li>
<li><p>작업을 처리하는 시간보다 대기하는 시간이 길어져 Thread를 효율적으로 사용하지 못한다.</p>
<p>  ⇒ Non-Blocking 방식의 Reactive Programming이 발전</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/f86df040-50a8-4e47-924b-19f74400fced/image.png" alt="">
<img src="https://velog.velcdn.com/images/dayon_log/post/3c44df31-cb45-45ae-9c78-b8b28bdfc88d/image.png" alt=""></p>
<h3 id="reactive-programming">Reactive Programming</h3>
<ul>
<li>처리량을 높이기 위한 방법으로 비동기 방식의 Reactive 프로그래밍이 발전</li>
<li>Webflux 방식을 도입해 스레드를 대기하지 않고 다른 작업 처리 가능</li>
<li>but 코드를 작성하고, 이해하는 비용이 높다 (Mono, Flux)</li>
<li>Reactive 하게 동작하는 별도의 라이브러리 지원을 필요로 한다.</li>
<li>JPA를 사용할 수 없고, R2DBC를 사용한다.</li>
</ul>
<br>

<h3 id="java-platform-design">Java Platform Design</h3>
<ul>
<li>자바 플랫폼은 ‘<strong>스레드 중심</strong>’으로 구성되어있다.</li>
<li>스레드 호출 스택은 ThreadLocal 을 사용해 데이터와 컨텍스트를 연결하도록 설계되어있다.</li>
<li>Exception Stack Trace, Debugger, Profiling 모두 스레드 기반</li>
<li>Reactive 작업을 할때에는 사용자의 요청이 여러 스레드를 거쳐 처리되는데, 컨텍스트를 확인이 어려워져 디버깅이 어려움</li>
</ul>
<p><br><br></p>
<h2 id="해결하고자-하는-문제">해결하고자 하는 문제</h2>
<ol>
<li><p><strong>Application의 높은 처리량(throughput) 확보</strong></p>
<ul>
<li><p>Blocking 발생시 내부 스케줄링을 통해 다른 가상 스레드가 이어서 작업을 처리 할 수 있도록 한다.</p>
<p>  ⇒ Reactive Programming과 동일하게 플랫폼 스레드의 리소스 낭비를 방지 </p>
</li>
</ul>
</li>
<li><p><strong>자바 플랫폼의 디자인과 조화를 이루는 코드 생성</strong> </p>
<ul>
<li>기존 스레드 구조 그대로 사용</li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/1cfd6422-8c32-45b7-ac7e-4ea59fd92ec6/image.png" alt=""></p>
<p>  기존의 스레드를 그대로 상속하고 있다. </p>
<p>  ⇒ 디버깅, 프로파일링등 기존의 도구도 그대로 사용할 수 있도록 한다. </p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/4f336113-640d-4315-a373-4f12eabb653a/image.png" alt=""></p>
<h4 id="-가상-스레드-"><code>[ 가상 스레드 ]</code></h4>
<p>Spring Web MVC 스타일로 코드를 작성하더라도 내부에서 가상 스레드가 기존의 플랫폼 스레드를 직접 사용하는 방식보다  Reactive programming 처럼 효율적으로 스케줄링하여 처리량을 높일 수 있다</p>
<br>



<p><strong>🩷 Project Loom ?</strong> 
경량의 스레드를 Java에 추가하기 위해서 가상 스레드를 비롯한 여러가지 기능들을 개발하는 프로젝트
5년동안 Virtual Thread를 개발하기 위해 노력을 했다고 함
기존 스레드의 사용성을 해치지 않면서도 가상 스레드를 내놓았다는 점에서 좋게 평가함 </p>
<br>

<h2 id="구조">구조</h2>
<h3 id="platform-thread-전통적인-thread-사용방식">Platform Thread (전통적인 Thread 사용방식)</h3>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/b4ffcdd1-b097-4d12-b4f8-0e0a774d2c2e/image.png" alt=""></p>
<ul>
<li>Platform Thread는 실제로는 OS 스레드를 사용하는 것</li>
<li>애플리케이션이 Thread Pool 안에 플랫폼 스레드를 OS Thread 와 1:1 매핑을 하는 구조</li>
</ul>
<br>



<h3 id="virtual-thread">Virtual Thread</h3>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/f55e4315-6f70-47cb-a6b5-37088da43efe/image.png" alt=""></p>
<ul>
<li><p>Virtual Thread 의 경우 Carrier Thread (Platform Thread 와 유사, 가상 스레드를 실제 OS와 연결해줌) 앞에 Virtual Thread 들이 따로 존재한다.</p>
</li>
<li><p>Carrier Thread 와 1:1 매핑되는 구조이지만 , 애플리케이션은 Thread Pool 없이 사용하고, JVM 자체적으로 가상 스레드를 OS 스레드와 연결하는 스케줄링한다.</p>
</li>
<li><p>가장 큰 차이점은 자체적인 Virtual Thread Scheduling</p>
<p>  기존의 Thread는 Blocking 발생시 Carrier Thread(Platform Thread)가 기다리기만 했었다.
  하지만, Blocking 발생시 버추얼 스레드는 <strong>Unmount</strong>(연결을 끊고) → 다른 버추얼 스레드가 해당 캐리어 스레드와 <strong>Mount</strong>(연결)을 한다. </p>
</li>
</ul>
<br>


<h2 id="사용법">사용법</h2>
<h3 id="샘플-코드">샘플 코드</h3>
<pre><code class="language-java">import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) throws Exception {
        run();
    }

    public static void run() throws Exception {

         // Virtual Thread 방법 1 : Thread.startVirtualThread() 빌더 사용 
         Thread.startVirtualThread(() -&gt; {    
            System.out.println(&quot;Hello Virtual Thread&quot;);
         });

         // Virtual Thread 방법 2 : Thread.ofVirtual() 빌더 사용
         Runnable runnable = () -&gt; System.out.println(&quot;Hi Virtual Thread&quot;);
         Thread virtualThread1 = Thread.ofVirtual().start(runnable);

         // Virtual Thread 이름 지정
         Thread.Builder builder = Thread.ofVirtual().name(&quot;JVM-Thread&quot;);
         Thread virtualThread2 = builder.start(runnable);

         // 스레드가 Virtual Thread인지 확인하여 출력
         System.out.println(&quot;Thread is Virtual? &quot; + virtualThread2.isVirtual()); 

         // ExecutorService 사용
         try (ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i &lt;3; i++) {
                executorService.submit(runnable);
            }
         }
    }
}
</code></pre>
<h3 id="springboot-32-mvc-적용법">SpringBoot 3.2 (MVC) 적용법</h3>
<pre><code class="language-yaml"># application.yml
spring:
    threads: 
        virtual:
            enabled: true</code></pre>
<p>내부에서 발생하는 Tomcat, WAS 에 대한 처리를 Virtual Thread에서 감당하도록 처리해준다. </p>
<br>



<h2 id="사용하는-자원-비교">사용하는 자원 비교</h2>
<table>
<thead>
<tr>
<th></th>
<th>Platform Thread</th>
<th>Virtual Thread</th>
</tr>
</thead>
<tbody><tr>
<td>Stack size</td>
<td>~ 2MB (OS 별 차이 있음)</td>
<td>~10KB</td>
</tr>
<tr>
<td>생성시간</td>
<td>~ 1ms</td>
<td>~ 1µs</td>
</tr>
<tr>
<td>Context Swiching cost</td>
<td>~100 µs</td>
<td>~10 µs</td>
</tr>
</tbody></table>
<p>Thread는 기본적으로 최대 2MB의 스택 메모리 사이즈를 가지기 때문에, 컨텍스트 스위칭 시 메모리 이동량이 크다. 또, 생성을 위해선 커널과 통신하여 스케줄링해야 하므로, 시스템 콜을 이용하기 때문에 생성 비용도 적지 않습니다.</p>
<p>하지만 Virtual Thread는 JVM에 의해 생성되기 때문에 시스템 콜과 같은 커널 영역의 호출이 적고, 메모리 크기가 일반 스레드의 1%에 불과합니다. 따라서 Thread에 비해 컨텍스트 스위칭 비용이 적다.</p>
<br>



<h3 id="테스트"><strong>&lt; 테스트 &gt;</strong></h3>
<pre><code class="language-java">public String ioBound() {
        requestSleep().block(); //Thread.sleep(300) API 호출
        requestSleep().block();
        requestSleep().block();

    return &quot;ok&quot;;
}

public Integer cpuBound() {
        IntStream.range(0, 300000000).reduce(0, Integer::sum);
        IntStream.range(0, 300000000).reduce(0, Integer::sum);
        return IntStream.range(0, 300000000).reduce(0, Integer::sum);
}</code></pre>
<p>사전 조건 : 테스트하기 위해 애플리케이션 스펙을 최소 사양으로 두고, 256MB의 힙 사이즈를 사용하도록 설정</p>
<p>테스트는 300ms를 sleep하는 API를 3번 호출하는 Request I/O Bound 작업, 0~300000000(3억)까지 합을 3번 계산하는 CPU Bound 작업으로 진행</p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/e1e63e49-dc9b-421d-b26b-0f152ad09524/image.png" alt=""></p>
<p>I/O Bound 작업→ <strong>Virtual Thread의 성능은 Thread 모델에 비해 약 51% 이상 향상</strong>되었다</p>
<p>CPU Bound 작업 → 일반 스레드 모델이 성능상 우위를 보였다. Virtual Thread가 Switching 되지 않는 경우에는 Platform Thread 사용 비용뿐만 아니라 Virtual Thread 생성 및 스케줄링 비용까지 포함되어 성능 낭비가 발생되기 때문</p>
<br>



<h3 id="유의사항-1">유의사항 1</h3>
<p>Platform Thread → Virtual Thread (x) </p>
<p>Task → Virtual Thread (0)</p>
<p><strong>개별 Task 를 Virtual Thread로 할당하는 것에 이점이 있다.</strong> </p>
<h3 id="유의사항-2">유의사항 2</h3>
<p><strong>Thread Local 사용한다면 메모리 사용 증가를 주의 해야한다.</strong> </p>
<p>Virtual Thread는 수시로 생성되고, 소멸되며 스위칭이 된다. </p>
<p>Platform Thread Pool을 사용할때 공유를 위해 ThreadLocal을 사용하던 관습 (X) </p>
<p>Virtual Thread는 수십~수백만개까지 늘어날 수 있기에 내부에서 ThreadLocal을 남발해서 사용하면 메모리를 점유하게 되어 메모리 사용이 늘어난다. </p>
<h3 id="유의사항-3">유의사항 3</h3>
<p>** Pinning issue**</p>
<p>synchronized 사용시 Virtual Thread에 연결된 Carrier Thread가 Blocking이 될 수 있다  </p>
<p>Virtual thread 내에서 synchronized 나 parallelStream 혹은 네이티브 메서드를 쓰면 Virtual Thread가 Carrier Thread에 고정되어 언마운트 할 수 없게 되는 문제 발생한다. </p>
<p><img src="https://velog.velcdn.com/images/dayon_log/post/7c5601f2-429c-45ea-abfe-126c9f372ce0/image.png" alt=""></p>
<p>⇒ Spring은 synchronized를 ReentrantLock으로 마이그레이션 하는 방향</p>
<br>



<h2 id="정리">정리</h2>
<ol>
<li><p>Virtual Thread 는 기존의 Platform Thread를 대체하는것이 목적이 아니다. 
도입을 한다고 해서 무조건 처리량이 높아지지 않는다. 필요에 따라 적절히 사용해야 한다</p>
<ul>
<li>I/O Blocking 이 발생하는 경우 Virtual Thread 더 좋은 처리량을 보여준다</li>
<li>CPU Bound 작업에는 적합하지 않다.</li>
</ul>
</li>
<li><p>Spring MVC 기반 Web API 제공시 편리하게 사용할 수 있다</p>
<ul>
<li>높은 Throughput을 위해 Webflux을 고려중이라면 대안이 될수 있다</li>
</ul>
</li>
<li><p>Virtual Thread는 기다림에 대한 개선, 그리고 플랫폼 디자인과의 조화 </p>
<ul>
<li>Virtual Thread 그 자체로 동시성을 완전히 개선했다고 보기 어렵다</li>
</ul>
</li>
</ol>
<hr>
<h2 id="짧은-소감">짧은 소감</h2>
<ul>
<li>아직 자바 21 버전을 사용해보지 못했는데 이번기회에 Virtual Thread에 대해 자세히 알게되었고, 다음에 사용한다면 이부분을 생각하며 코드를 짜봐야겠다.</li>
<li>관련된 Virtual Thread vs Kotlin Coroutine 부분도 더 알아보고 싶다.</li>
</ul>
<hr>
<h2 id="참고">참고</h2>
<ol>
<li><a href="https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4">https://inpa.tistory.com/entry/👩‍💻-프로세스-⚔️-쓰레드-차이</a></li>
<li><a href="https://tech.kakao.com/2023/12/22/techmeet-virtualthread/">https://tech.kakao.com/2023/12/22/techmeet-virtualthread/</a></li>
<li><a href="https://findstar.pe.kr/2023/04/17/java-virtual-threads-1/">https://findstar.pe.kr/2023/04/17/java-virtual-threads-1/</a></li>
<li><a href="https://techblog.woowahan.com/15398/">https://techblog.woowahan.com/15398/</a></li>
<li><a href="https://www.baeldung.com/spring-6-virtual-threads">https://www.baeldung.com/spring-6-virtual-threads</a></li>
<li><a href="https://velog.io/@eastperson/synchronized%EC%99%80-ReentrantLock-%EA%B7%B8%EB%A6%AC%EA%B3%A0-JDK-21-Virtual-Thread">https://velog.io/@eastperson/synchronized와-ReentrantLock-그리고-JDK-21-Virtual-Thread</a></li>
<li><a href="https://d2.naver.com/helloworld/1203723">https://d2.naver.com/helloworld/1203723</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[클린코드 (1) - service와 impl 분리]]></title>
            <link>https://velog.io/@dayon_log/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-1-service%EC%99%80-impl-%EB%B6%84%EB%A6%AC</link>
            <guid>https://velog.io/@dayon_log/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-1-service%EC%99%80-impl-%EB%B6%84%EB%A6%AC</guid>
            <pubDate>Tue, 05 Mar 2024 16:31:11 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dayon_log/post/c0102880-40ba-44c1-b24f-536d08a6a6da/image.jpeg" alt=""></p>
<br>

<p>이전까지는 왜 굳이 service를 interface와 impl로 분리하는지 몰랐고, 그냥 팀원이 하니까 무지성으로 따라했고, 혼자 프로젝트를 할때에는 하나의 인터페이스와 하나의 구현클래스 만 쓰기때문에 무의미하다 생각하여 분리하지 않았다. </p>
<br>

<h3 id="인터페이스와-구현-클래스를-분리하는-이유">인터페이스와 구현 클래스를 분리하는 이유</h3>
<p>인터페이스와 구현체 클래스를 분리함으로써 구현체를 독립적으로 확장할 수 있으며, 구현체 클래스를 변경하거나 확장해도 이를 사용하는 클라이언트의 코드에는 영향을 주지 않는다. </p>
<p>추상화를 통한 구현 방식은 객체 지향의 특징 중 하나인 다형성과 객체지향의 다섯가지 원칙 중 하나인 OCP 원칙을 가장 잘 실현해주는 설계 방식이라고 할 수 있다. </p>
<blockquote>
<p><strong>개방-폐쇄 원칙</strong>(OCP, Open-Closed Principle) :  &#39;소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다&#39;는 프로그래밍 원칙</p>
</blockquote>
<p>확장성을 보장하기 위해서는 개발자는 언제든지 구현 로직이 변경될 수 있음을 염두에 두어야 하며, 분리된 구조를 적용하면 하나의 인터페이스에 대해 여러 개의 독립된 구현체를 사용할 수 있다는 장점을 갖는다. 이는 모듈 개발과 배포 단계에서 각각 다른 로직을 구성할 수 있음을 의미한다. 또한 이는 구현체와 인터페이스를 명확히 구분함으로써 유연성을 확보한다.</p>
<br>

<h3 id="mvc-패턴에서-service-란">MVC 패턴에서 Service 란?</h3>
<p>MVC 패턴에서는 View가 자신이 요청할 Controller만 알고 있으면 되며, Controller는 화면에서 넘어오는 매개변수들을 이용해 Service 객체를 호출하는 역할을 수행한다. 그렇기에 Service는 어떤 컨트롤러에서 호출되더라도 필요한 매개변수만 받아들여 자신의 비즈니스 로직을 처리할 수 있다. 이는 모듈화를 통해 재사용 가능한 클래스 파일을 만들어내며, View가 변경되더라도 Service는 그대로 유지되어 재사용할 수 있는 장점을 가진다. </p>
<p>더불어, Service는 순수한 자바 객체로 구성되어 있어서 웹 기반 뿐만 아니라 추후에는 네이티브 앱으로의 전환이 이루어져도 View에 종속적인 코드가 없기 때문에 그대로 재사용이 가능합니다. 새로운 요청사항이 발생하더라도 기존 소스를 수정하는 것이 아니라 기존 service 인터페이스를 구현한 다른 클래스를 만들어 해당 객체를 사용하도록 하는 방식으로 확장성을 확보할 수 있습니다. </p>
<blockquote>
<p><strong>Model</strong>: 애플리케이션의 데이터와 비즈니스 로직을 담당. 데이터베이스와 상호작용하거나 데이터를 처리하는 등의 역할을 수행</p>
<p><strong>View</strong>: 사용자에게 데이터를 표시하고 사용자의 입력을 받는 역할. 주로 사용자 인터페이스(UI)를 담당</p>
<p><strong>Controller</strong>: 사용자의 입력을 받아 해당하는 작업을 수행하고 모델의 데이터 또는 뷰를 업데이트하는 역할</p>
</blockquote>
<br>

<h3 id="사실-service-코드만-interface로-분리를-하는-것이-아니였다">사실 Service 코드만 interface로 분리를 하는 것이 아니였다.</h3>
<p>Entity의 경우 이미 DTO가 존재하여 데이터 전송 객체와의 매핑이 되어있고, 컨트롤러는 사실상 디스패처 서블릿을 통해 요청을 처리하므로 직접 작성하지 않지만 분리되어 있다. </p>
<p>Repository는 데이터 JPA를 활용하고 있어서 데이터 액세스 레이어의 인터페이스 작업이 필요하지 않았다. </p>
<br>

<h3 id="마지막으로">마지막으로,</h3>
<p>프로젝트에서 코드를 분리하면 코드 구조가 복잡해지고, 복잡해진 구조 만큼 코드를 분석하고 확인하는 과정에서 인터페이스를 거쳐 구현체들을 확인해야 하는 번거로움이 생긴다. 하지만 Service, ServiceImpl 패턴으로 설계를 해야하는 이유는 인터페이스와 구현체를 분리함으로써 구현체를 독립적으로 확장할 수 있으며, 구현체 클래스를 변경하거나 확장해도 영향이 최소화된다. 이는 JAVA 언어를 쓰는 가장 큰 목적인 객체지향의 특징을 잘 실현해주는 설계방식이다. </p>
<br>


<hr>
<p>참고한 글</p>
<p><a href="https://see-one.tistory.com/1">https://see-one.tistory.com/1</a></p>
<p><a href="https://wildeveloperetrain.tistory.com/49">https://wildeveloperetrain.tistory.com/49</a></p>
<p><a href="https://multifrontgarden.tistory.com/97">https://multifrontgarden.tistory.com/97</a></p>
<p><a href="https://velog.io/@ghkdwp018/service%EB%A5%BC-interface%EC%99%80-impl%EB%A1%9C-%EB%B6%84%EB%A6%AC%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0">https://velog.io/@ghkdwp018/service를-interface와-impl로-분리하는-이유</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JWT]]></title>
            <link>https://velog.io/@dayon_log/JWT</link>
            <guid>https://velog.io/@dayon_log/JWT</guid>
            <pubDate>Mon, 12 Feb 2024 13:55:04 GMT</pubDate>
            <description><![CDATA[<p>로그인 및 로그아웃은 웹 애플리케이션에서 핵심적인 기능으로, 사용자의 인증(Authentication)과 권한 관리를 담당한다. </p>
<p>이를 구현하기 위해서는 사용자의 신원을 확인하고 권한을 부여하는 인증(Authentication)과 사용자가 요청한 자원에 대한 접근 권한을 확인하는 인가(Authorization)이 필요하며,  JWT(JSON Web Token)는 이러한 인증과 권한 관리를 효과적으로 처리하기 위한 방법 중 하나이다. </p>
<h3 id="jwt의-구조">JWT의 구조</h3>
<p>JWT는 세 부분으로 구성되어 있다
: Header, Payload, Signature.</p>
<h4 id="header">Header</h4>
<p>Header에는 토큰의 유형과 사용된 알고리즘 등에 대한 메타데이터가 포함된다. 주로 이러한 정보는 JSON 형식으로 인코딩되어 있다.</p>
<h4 id="payload">Payload</h4>
<p>Payload에는 토큰에 담길 정보와 토큰의 발행 및 만료 시간이 포함되며, 중요한 정보를 포함할 때에는 주의가 필요하며, 사용자를 식별할 수 있는 고유한 식별자를 사용하는 것이 좋다. </p>
<h4 id="signature">Signature</h4>
<p>Signature는 토큰의 무결성을 확인하기 위해 사용한다. Header와 Payload를 인코딩하고, 서버에서 사용하는 비밀 키를 사용하여 서명한다. </p>
<h3 id="jwt를-사용하는-이유">JWT를 사용하는 이유</h3>
<ul>
<li><p>확장성: JSON 형식을 사용하므로, 다양한 환경에서 쉽게 사용할 수 있다.</p>
</li>
<li><p>보안성: 서명된 토큰을 사용하여 인증 및 권한 부여를 수행하므로, 보안성이 높다. </p>
</li>
<li><p>상태 없음(Stateless): 서버는 클라이언트의 상태를 유지할 필요가 없어서 확장성이 용이하다.</p>
</li>
</ul>
<br>


<h3 id="spring-security를-활용한-로그인-구현">Spring Security를 활용한 로그인 구현</h3>
<p>Spring Security는 사용자의 인증(Authentication)과 권한 부여(Authorization)를 위한 프레임워크.
JWT와 조합하여 로그인을 구현하는 방법은 다음과 같다:</p>
<h4 id="로그인-요청-처리">로그인 요청 처리</h4>
<p>로그인 요청이 들어오면 사용자의 인증을 수행합니다. 이를 위해 Spring Security의 AuthenticationManager를 사용하여 사용자를 인증한다</p>
<h4 id="jwt-발급">JWT 발급</h4>
<p>인증에 성공한 경우, 사용자에게 JWT를 발급하여 클라이언트에게 전달한다. JWT는 사용자의 인증 정보를 포함하고, 서버에서 발행된 서명이 포함되어 있다. </p>
<h3 id="jwt를-사용하여-인가-및-보안을-관리">JWT를 사용하여 인가 및 보안을 관리</h3>
<p>각 요청에 포함된 JWT를 검증하여 사용자의 권한을 확인하고, 보안적인 접근을 보장할 수 있다</p>
<pre><code>public TokenProvider(CustomUserDetailsService customUserDetailsService,
                     @Value(&quot;${jwt.secret}&quot;) String secret) {
    this.customUserDetailsService = customUserDetailsService;
    this.secret = secret;
    this.accessExpirationTime = 3 * 60 * 60 * 1000L;       // 3 hours
    this.refreshExpirationTime = 15 * 24 * 60 * 60 * 1000L;  // 15 days

}


    @Override
public void afterPropertiesSet() throws Exception {
    // jwt 원시 키를 디코딩하고 jwt 서명하는데 사용
    byte[] keyBytes = Decoders.BASE64.decode(secret);
    this.key = Keys.hmacShaKeyFor(keyBytes);
}


// Access Token 생성 메서드 

    public String createAccessToken(String uid) {

    Claims claims = Jwts.claims()
            .setSubject(uid);
    claims.put(&quot;token_type&quot;, &quot;accessToken&quot;);

    Date expirationTime = getExpirationTime(accessExpirationTime);

    String accessToken = Jwts.builder()
            .setClaims(claims)
            .setExpiration(expirationTime)
            .signWith(key, SignatureAlgorithm.HS256)
            .compact();

    return accessToken;

}



    // 토큰에서 정보 추출
public Authentication getAuthentication(String token) {

    String uid = parseClaims(token).getSubject();
    UserDetails userDetails = customUserDetailsService.loadUserByUsername(uid);

    return new UsernamePasswordAuthenticationToken(userDetails, token);
}


// Access Token 검증
public boolean validateAccessToken(String accessToken) {
    try {
        Claims claims = parseClaims(accessToken);

        // 토큰 타입 확인
        String tokenType = (String) claims.get(&quot;token_type&quot;);
        if (!&quot;accessToken&quot;.equals(tokenType)) {
            return false;
        }

        // 만료 날짜 확인
        return !claims.getExpiration().before(new Date());
    } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
        log.warn(&quot;잘못된 JWT 서명입니다.&quot;, e);
    } catch (ExpiredJwtException e) {
        log.warn(&quot;만료된 JWT입니다.&quot;, e);
    } catch (UnsupportedJwtException e) {
        log.warn(&quot;지원되지 않는 JWT입니다.&quot;, e);
    } catch (IllegalArgumentException e) {
        log.warn(&quot;잘못된 JWT입니다.&quot;, e);
    }

    return false;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Spring JDBC 와 JPA]]></title>
            <link>https://velog.io/@dayon_log/Spring-JDBC-%EC%99%80-JPA</link>
            <guid>https://velog.io/@dayon_log/Spring-JDBC-%EC%99%80-JPA</guid>
            <pubDate>Mon, 05 Feb 2024 13:43:12 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dayon_log/post/3d68e7e0-b43d-4f8e-9918-4f14b99b26cf/image.png" alt=""></p>
<h2 id="jdbc-정의">JDBC 정의</h2>
<p>JDBC(Java Database Connectivity)
JDBC는 자바 프로그램에서 데이터베이스에 접속하고 SQL 쿼리를 실행하기 위한 자바 API. JDBC는 데이터베이스 종류에 상관없이 표준화된 방식으로 자바 애플리케이션이 데이터베이스와 상호 작용할 수 있도록 한다. </p>
<br>

<h2 id="jpa의-정의">JPA의 정의</h2>
<p>JPA(Java Persistence API)</p>
<p>JPA는 자바에서 객체-관계 매핑을 위한 API로, 객체 지향 언어인 자바와 관계형 데이터베이스 간의 매핑을 지원한다.  JPA는 데이터베이스 테이블과 자바 객체 사이의 매핑을 관리하고, 객체 지향적인 방식으로 데이터베이스를 다룰 수 있도록 도와준다. </p>
<p> 공식문서를 번역해서 인용해보았다. </p>
<p>스프링 데이터 JPA는 데이터 액세스 레이어의 구현을 실제로 필요한 양으로 줄여줌으로써 크게 개선하고자 합니다. 개발자는 다양한 기술을 사용하여 리포지토리 인터페이스를 작성하고, 스프링이 자동으로 연결해주는데. 심지어 사용자 지정 검색기나 예제를 통한 쿼리도 사용할 수 있고, 스프링이 쿼리를 작성해준다. </p>
<br>

<h2 id="jdbc와-jpa의-차이점">JDBC와 JPA의 차이점</h2>
<h4 id="저수준low-level-대-고수준high-level-접근">저수준(Low-level) 대 고수준(High-level) 접근</h4>
<p>JDBC는 데이터베이스와의 접속 및 쿼리 실행에 대한 저수준 API를 제공하며, 개발자가 직접 SQL 쿼리를 작성하고 데이터베이스와의 연결을 관리해야 합니다.
JPA는 객체 지향적인 관점에서 데이터베이스를 다룰 수 있도록 고수준의 API를 제공하며, 객체와 데이터베이스 간의 매핑을 자동으로 처리합니다.</p>
<h4 id="ormobject-relational-mapping의-사용-여부">ORM(Object-Relational Mapping)의 사용 여부</h4>
<p>JDBC는 직접 SQL을 작성하여 데이터베이스와 상호 작용하므로 ORM을 사용하지 않습니다.
JPA는 ORM의 대표적인 기술이며, 객체와 관계형 데이터베이스 간의 매핑을 자동화하여 개발자가 직접 SQL을 작성하는 수고를 덜어줍니다.</p>
<br>

<h2 id="활용-방법">활용 방법</h2>
<h4 id="jdbc-활용">JDBC 활용</h4>
<p>JDBC를 사용하여 데이터베이스에 연결하고 SQL 쿼리를 실행한다. 
Connection, Statement, ResultSet 등의 인터페이스를 활용하여 데이터베이스와의 상호 작용을 구현한다. 
주로 복잡한 쿼리나 특정 데이터베이스 벤더에 특화된 기능을 사용해야 할 때 JDBC를 활용한다. </p>
<br>

<h4 id="jpa-활용">JPA 활용</h4>
<p>JPA를 사용하여 객체와 관계형 데이터베이스 간의 매핑을 설정
엔티티 클래스를 정의하고 JPA의 어노테이션을 사용하여 객체와 테이블 간의 매핑을 설정
EntityManager를 통해 영속성 컨텍스트를 관리하고, JPQL(Query Language)을 사용하여 객체 지향적인 쿼리를 작성
주로 객체 지향적인 관점에서 데이터베이스를 다뤄야 할 때 JPA를 활용하며, 코드의 유지보수성과 생산성을 높일 수 있게 된다. </p>
<br>

<p>JDBC는 저수준의 데이터베이스 접근을 위한 API로, 개발자가 직접 SQL을 작성하고 데이터베이스와의 연결을 관리하지만 JPA는 고수준의 ORM 기술로, 객체와 관계형 데이터베이스 간의 매핑을 자동화하여 개발자가 객체 지향적으로 데이터베이스를 다룰 수 있도록 도와준다. </p>
<br>

<hr>
<p>참고한 영상 - 10분 테코톡 
<a href="https://youtu.be/Ppqc3qN75EE?si=2PXQuBtB4ZqBDFo">https://youtu.be/Ppqc3qN75EE?si=2PXQuBtB4ZqBDFo</a>_</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA 스프링부트 ]]></title>
            <link>https://velog.io/@dayon_log/JPA-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8</link>
            <guid>https://velog.io/@dayon_log/JPA-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8</guid>
            <pubDate>Mon, 22 Jan 2024 13:46:50 GMT</pubDate>
            <description><![CDATA[<p>JPA(Java Persistence API)는 자바 진영의 ORM 기술에 대한 API 표준 명세이다. </p>
<p>객체와 데이터베이스간의 관계를 편리하게 이어준다. </p>
<h3 id="spring-data-jpa">Spring Data JPA</h3>
<p>Spring Data JPA는 스프링에서 제공하는 데이터 액세스 기술 중 하나로, 객체 관계 매핑(Object-Relational Mapping, ORM)을 지원한다. </p>
<p>JPA(Java Persistence API)의 표준을 따르며, 데이터베이스와 자바 객체 간의 매핑을 간소화하고 일관성 있게 처리한다. </p>
<h4 id="주요기능">주요기능</h4>
<ul>
<li><p>엔티티 매핑: 자바 객체와 데이터베이스 테이블 간의 매핑을 어노테이션을 통해 간편하게 설정할 수 있다. </p>
</li>
<li><p>CRUD 작업: 데이터베이스의 Create, Read, Update, Delete 작업을 자동으로 지원하여 개발자가 반복적인 코드를 최소화한다. </p>
</li>
<li><p>Query Methods: 메서드 이름을 통한 쿼리 생성을 지원하여 간편한 데이터 검색이 가능하다. </p>
</li>
</ul>
<br>

<h3 id="jparepository-인터페이스">JpaRepository 인터페이스</h3>
<p>JpaRepository 인터페이스를 확장함으로써 기본적인 CRUD 작업을 자동으로 제공받을 수 있다.</p>
<p>추가적으로 정의한 메서드 이름에 따라 쿼리가 자동으로 생성되어 사용자 정의 쿼리를 최소화할 수 있다.</p>
<br>

<h3 id="jpa-의-장점">JPA 의 장점</h3>
<ul>
<li><p>생산성 향상
스프링 부트의 자동 설정과 스타터 패키지, Spring Data JPA의 간소화된 데이터 액세스 작업으로 개발 생산성이 향상된다.</p>
</li>
<li><p>일관된 코드
JPA를 통한 객체 관계 매핑과 Spring Data JPA의 통합으로 일관된 코드 작성이 가능하며, 유지보수가 용이하다.</p>
</li>
<li><p>데이터베이스 독립성
데이터베이스에 대한 세부적인 설정을 하지 않아도 되며, 다양한 데이터베이스를 쉽게 변경할 수 있다.</p>
</li>
</ul>
<p>스프링 부트의 간편한 설정과 Spring Data JPA의 객체 관계 매핑을 통해 개발자들은 복잡한 데이터 액세스 작업을 간소화하고 일관된 코드를 작성할 수 있다. 빠른 개발 주기와 유지보수의 편의성을 제공하여 현대적인 웹 애플리케이션 개발이 가능하다. </p>
<br>

<h3 id="spring-data-jpa의-활용">Spring Data JPA의 활용</h3>
<h4 id="1-repository-인터페이스">1. Repository 인터페이스</h4>
<p>Spring Data JPA에서 데이터 액세스 레이어를 추상화한 Repository 인터페이스를 제공한다. JpaRepository 인터페이스를 확장함으로써 기본적인 CRUD 작업을 자동으로 수행할 수 있다.</p>
<pre><code>public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
    List&lt;User&gt; findByLastName(String lastName);
}</code></pre><p>UserRepository는 JpaRepository를 상속받아 자동으로 CRUD 메서드를 제공받으면서, 추가적으로 메서드 이름을 통한 쿼리(findByLastName)를 정의한다. </p>
<br>

<h4 id="2-쿼리-어노테이션과-named-쿼리">2. 쿼리 어노테이션과 Named 쿼리</h4>
<p>Spring Data JPA는 @Query 어노테이션을 통해 개발자가 직접 JPQL(Querydsl)이나 네이티브 쿼리를 작성한다.</p>
<pre><code>@Query(&quot;SELECT u FROM User u WHERE u.email = :email&quot;)
User findByEmail(@Param(&quot;email&quot;) String email);</code></pre><p>@NamedQuery를 통해 이름 있는 쿼리를 정의하여 사용한다.</p>
<pre><code>@Entity
@NamedQuery(name = &quot;User.findByFirstName&quot;, query = &quot;SELECT u FROM User u WHERE u.firstName = ?1&quot;)
public class User {
    //...
}</code></pre><h4 id="3쓰기-지연-과-dirty-checking">3.쓰기 지연 과 Dirty Checking</h4>
<p>JPA는 쓰기 지연(write-behind)과 더티 체킹(dirty checking)을 통해 효율적인 데이터베이스 변경을 지원한다. </p>
<p>변경 감지 메커니즘을 활용하여 객체 상태의 변화를 추적하고, 트랜잭션 커밋 시점에 변경된 부분만 데이터베이스에 반영한다.</p>
<pre><code>// 변경된 필드만 지정
user.setLastName(&quot;NewLastName&quot;);

// 트랜잭션 커밋 시점에 변경된 필드만 데이터베이스에 반영</code></pre><h4 id="4auditing">4.Auditing</h4>
<p>Spring Data JPA는 감사(Auditing)를 지원하여 엔티티의 생성일, 수정일, 생성자, 수정자 등의 정보를 자동으로 관리할 수 있다. </p>
<p>@CreatedDate, @LastModifiedDate, @CreatedBy, @LastModifiedBy 어노테이션을 통해 이러한 정보를 엔티티에 추가할 수 있다.</p>
<pre><code>@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;

    //...
}
</code></pre><h4 id="5-paging-과-정렬">5. paging 과 정렬</h4>
<p>Spring Data JPA는 페이징과 정렬을 지원하여 대량의 데이터를 효율적으로 처리할 수 있다. </p>
<p>Pageable 인터페이스를 통해 페이징 및 정렬 옵션을 적용할 수 있다.</p>
<pre><code>// 페이징 및 정렬 적용
Page&lt;User&gt; findByLastName(String lastName, Pageable pageable);</code></pre><p><br><br><br></p>
]]></description>
        </item>
    </channel>
</rss>