<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발 정리 모음집</title>
    <link>https://lightworld.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 10 May 2026 02:19:00 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>주다애</managingEditor>
    <image>
      <title>개발 정리 모음집</title>
      <url>https://tistory1.daumcdn.net/tistory/5678565/attach/ab37f7ec80cb499db3f43b8aa22c7946</url>
      <link>https://lightworld.tistory.com</link>
    </image>
    <item>
      <title>이진 탐색 - Upper Bound와 Lower Bound</title>
      <link>https://lightworld.tistory.com/71</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;이진 탐색&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이진 탐색은 정렬된 상태의 데이터에서 원하는 값을 찾아내는 알고리즘이다. &lt;br /&gt;데이터의 중앙값과 찾고자 하는 값을 비교한다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이진 탐색은 탐색 대상이 매번 절반으로 줄어들기 때문에 시간 복잡도는 O(logN)이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진 탐색의 동작 원리는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;배열이나 리스트를 정렬한다.&lt;/li&gt;
&lt;li&gt;배열이나 리스트의 중간 값을 선택한다.&lt;/li&gt;
&lt;li&gt;중간 값이 목표 값보다 크면 왼쪽 절반을 탐색하고, 중간 값이 목표 값보다 크면 오른쪽 절반을 탐색한다.&lt;/li&gt;
&lt;li&gt;중간 값과 목표 값이 같을 때까지 탐색한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정렬된 배열(arr) : [1, 3, 5, 7, 9, 11, 13]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;목표 값 : 9&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;먼저 left는 0, right는 6로 시작한다.(시작 인덱스0, 가장 끝 인덱스 6)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1737728841381&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class BinarySearchMain {
    public static void main(String[] args) {
        int[] arr = {1, 3, 5, 7, 9, 11, 13};
        int v = 7;
        int ans = binarySearch(arr, v);
        System.out.println(ans);
    }

    private static int binarySearch(int[] arr, int v) {
        int left = 0;
        int right = arr.length - 1;

        while (left &amp;lt;= right) {
            int mid = (left + right) / 2;

            if (arr[mid] &amp;gt; v) {
                right = mid - 1;
            }
            else if (arr[mid] &amp;lt; v){
                left = mid + 1;
            }
            else return mid;
        }

        return -1;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;80&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFWNsl/btsL0zpEWIH/ASynKnceDkRtyfBbMmkmIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFWNsl/btsL0zpEWIH/ASynKnceDkRtyfBbMmkmIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFWNsl/btsL0zpEWIH/ASynKnceDkRtyfBbMmkmIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFWNsl%2FbtsL0zpEWIH%2FASynKnceDkRtyfBbMmkmIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;402&quot; height=&quot;80&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;80&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;left = 0, right = 6이므로 mid = 3이다. arr[mid]는 arr[3] = 7이다. 목표 값인 9는 7보다 크므로 mid를 중심으로 오른쪽 절반을 다시 탐색한다. 즉 left 값을 mid + 1로 해준다.&lt;/li&gt;
&lt;li&gt;left = 4, right = 6이므로 mid = 5이다. arr[mid]는 arr[5] = 9이다. 목표 값이 9와 동일하므로 else문에 들어와서 mid를 리턴해준다.&lt;/li&gt;
&lt;li&gt;만약 left &amp;lt;= right일 때까지, 즉, 시작점이 끝점보다 작거나 같을 때까지 탐색했을 때 원하는 값의 인덱스를 찾지 못하면 -1을 리턴해준다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Upper Bound와 Lower Bound&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이진 탐색의 방식으로 상한선과 하한선 방식이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Upper Bound는 상한선 방식&lt;/b&gt;으로 주어진 값보다 &lt;b&gt;큰 값이 처음으로 등장하는 위치&lt;/b&gt;를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Lower Bound는 하한선 방식&lt;/b&gt;으로 주어진 값보다 &lt;b&gt;크거나 같은 값이 처음으로 등장하는 위치&lt;/b&gt;를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[1, 3, 3, 5, 7]의 배열 / 목표 값 3의 upper bound는 인덱스 3이다.(값은 5)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[1, 3, 3, 5, 7]의 배열 / 목표 값 3의 lower bound는 인덱스 1이다.(값은 맨 처음 나오는 3)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드로 구현해보자&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #080808;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;package BinarySearch;

public class UpperBound {
    public static void main(String[] args) {
       // 이분탐색은 기본적으로 정렬되어 있어야 함
       int[] arr = {1, 2, 3, 4, 5, 6, 6};
       System.out.println(&quot;upper bound : &quot; + upperBound(arr, 5));
       System.out.println(&quot;lower bound : &quot; + lowerBound(arr, 5));
    }

    // 특정 값을 초과하는 가장 처음에 나오는 위치
    // 상한선
    static  int upperBound(int[] arr, int target) {
       int left = 0;
       int right = arr.length;

       while (left &amp;lt; right) {
          int mid = (left + right) / 2;
          if (arr[mid] &amp;lt;= target) {
             left = mid + 1;
          }
          else {
             right = mid;
          }
       }
       return left;
    }

    // 특정 값 이상인 첫번째 위치(인덱스)
    // 하한선
    static int lowerBound(int[] arr, int target) {
       int left = 0;
       int right = arr.length;

       while (left &amp;lt; right) {
          int mid = (left + right) / 2;
          if (arr[mid] &amp;lt; target) {
             left = mid + 1;
          }
          else {
             right = mid;
          }
       }
       return left;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wKSwv/btsL1F29cSP/zAjBQcLeFA1Em3UDIoU1k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wKSwv/btsL1F29cSP/zAjBQcLeFA1Em3UDIoU1k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wKSwv/btsL1F29cSP/zAjBQcLeFA1Em3UDIoU1k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwKSwv%2FbtsL1F29cSP%2FzAjBQcLeFA1Em3UDIoU1k1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;220&quot; height=&quot;57&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;57&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target 값이 5이면 upper bound는 5보다 큰 값인 6이 맨 처음 나오는 인덱스인 5를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target 값이 5이면 lower bound는 5보다 크거나 같은 값인 5가 처음 나오는 인덱스인 4를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 가지를 짚고 넘어가볼 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;둘 다 &lt;b&gt;right 값을 arr.length - 1이 아닌 arr.length로 하고 있다.&lt;/b&gt; 만약 upperBound(7)과 lowerBound(7)를 호출하게 되면 주어진 배열 크기만큼 리턴되어야 한다.(upper bound는 목표 값보다 큰 첫 번째 위치를 가리키므로 배열 끝 이후를 가리키게 되고, lower bound는 목표 값이 처음 나타나는 위치를 넘어서 배열 끝을 가리킨다.) 즉, 값이 없는 경우에도 올바를 결과를 리턴한다.&lt;/li&gt;
&lt;li&gt;이 때 left 값을 반환하는 이유는 탐색 과정에서 left가 최종적으로 특정 조건을 만족하는 지점을 가리키기 때문이다.그래서 최종적으로 left는 조건을 만족하는 최초의 위치를 가리키게 된다. 이진 탐색 과정에서 left는 항상 조건을 만족할 가능성이 있는 최소 인덱스를 유지한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>lowerBound</category>
      <category>upperBound</category>
      <category>이진탐색</category>
      <author>주다애</author>
      <guid isPermaLink="true">https://lightworld.tistory.com/71</guid>
      <comments>https://lightworld.tistory.com/71#entry71comment</comments>
      <pubDate>Sat, 25 Jan 2025 00:09:05 +0900</pubDate>
    </item>
    <item>
      <title>error-css파일 net::ERR_ABORTED 404 (Not Found)</title>
      <link>https://lightworld.tistory.com/70</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;상&lt;span style=&quot;color: #000000;&quot;&gt;황 : thymeleaf로 개발 중 html파일과 css파일을 분리한 후 발생한 에러&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;html 파일에 css를 넣으니 html 파일이 너무 길어졌다. 그래서 css파일을 분리했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;196&quot; data-origin-height=&quot;129&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tF5KV/btsLAfdkSsy/K1aDgmbHvTIXCrISxs16z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tF5KV/btsLAfdkSsy/K1aDgmbHvTIXCrISxs16z1/img.png&quot; data-alt=&quot;static/css 밑에 css 파일이 들어간다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tF5KV/btsLAfdkSsy/K1aDgmbHvTIXCrISxs16z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtF5KV%2FbtsLAfdkSsy%2FK1aDgmbHvTIXCrISxs16z1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;248&quot; height=&quot;163&quot; data-origin-width=&quot;196&quot; data-origin-height=&quot;129&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;static/css 밑에 css 파일이 들어간다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;공통적으로 resources 폴더 밑에 존재하고 html 파일은 templates 폴더 밑에, css 파일은 static/css/ 폴더 밑에 위치하는 구조이다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 html 파일에서 css 파일을 읽어올 수 있도록 하기 위해 다음과 같이 설정 해주었다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1735317618283&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;/static/css/subscription.css&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;근데 서버를 실행하면 html 파일은 인식해도 css 파일은 인식을 못했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  수정 전 코드&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1735317548903&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;/static/css/subscription.css&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  수정 후 코드&lt;/p&gt;
&lt;pre id=&quot;code_1735317662080&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;/css/subscription.css&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인 : css 파일의 경로 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring boot는 &lt;b&gt;정적 리소스 파일&lt;/b&gt;(css, js, 이미지)을 제공할 때 static, public, resources, META-INF/resources 디렉토리의 파일을 &lt;b&gt;웹 루트 경로(/)&lt;/b&gt; 아래에 자동으로 매핑한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[&lt;b&gt;참고&lt;/b&gt; : &lt;a href=&quot;https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot&lt;/a&gt;]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, src/main/resources/static 폴더를 정적 리소스의 루트 경로로 설정하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그래서 /static/css 폴더 밑에 위치한 css 파일은 /static/css가 아니라 /css 로 매핑 해주어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(css/style.css가 아니라 /css/style.css 라고 설정 해주어야 한다!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정 후 코드로 설정하면 정상적으로 css 파일 경로를 불러와서 html 화면에 적용된다!&lt;/p&gt;</description>
      <category>오곡(Ogok)</category>
      <category>CSS</category>
      <category>Thymeleaf</category>
      <author>주다애</author>
      <guid isPermaLink="true">https://lightworld.tistory.com/70</guid>
      <comments>https://lightworld.tistory.com/70#entry70comment</comments>
      <pubDate>Sat, 28 Dec 2024 01:53:08 +0900</pubDate>
    </item>
    <item>
      <title>error-thymeleaf 적용 시 whitelabel error page가 나와요.</title>
      <link>https://lightworld.tistory.com/69</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;상황 : spring 프로젝트 시에 thymeleaf로 화면을 구성하고 서버를 올렸다. &lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 그리고 localhost:8080/{내 html 파일 이름}.html을 하니 whitelabel error page를 마주하게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해결&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일단 확인할 것은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;build.gradle&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1735263689096&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;applicaton.yml&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1735263638667&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Thymeleaf 설정
spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    cache: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개 모두 적절히 설정이 되었다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음을 확인하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MvcConfiguration.class&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1735263818335&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class MvcConfiguration implements WebMvcConfigurer {

	@Override
	public void addResourceHandlers(final ResourceHandlerRegistry registry) {
		registry.addResourceHandler(&quot;/**&quot;)
			.addResourceLocations(&quot;classpath:/templates/&quot;, &quot;classpath:/static/&quot;)
			.setCacheControl(CacheControl.maxAge(10, TimeUnit.MINUTES));
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;214&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3sGlE/btsLyTVqoJM/PIK0pOx83vDrAdjiALFlp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3sGlE/btsLyTVqoJM/PIK0pOx83vDrAdjiALFlp0/img.png&quot; data-alt=&quot;나는 templates 폴더 밑에 html을 작성했다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3sGlE/btsLyTVqoJM/PIK0pOx83vDrAdjiALFlp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3sGlE%2FbtsLyTVqoJM%2FPIK0pOx83vDrAdjiALFlp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;255&quot; height=&quot;117&quot; data-origin-width=&quot;214&quot; data-origin-height=&quot;98&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;나는 templates 폴더 밑에 html을 작성했다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 설정 클래스를 만들어주어야 하는 이유는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;설정하기 전에는 static 폴더의 html 파일을 불러온다.&lt;/li&gt;
&lt;li&gt;그래서 templates 폴더의 html 파일을 불러오게 만들어야 한다.&lt;b&gt;&lt;i&gt;(addResourceLocations(&quot;classPath:/templates/&quot;)&lt;/i&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해주면 서버가 올라가고 정상적으로 html 파일에 접근하게 되어 화면이 나오게 된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 자료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devzzi.tistory.com/16&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://devzzi.tistory.com/16&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735264054127&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Spring Boot] thymeleaf 사용하기 (1) - thymeleaf 초기 설정하기 |  templetes 폴더에서 정적 파일 불러오기 | &quot; data-og-description=&quot;[Spring] thymeleaf 사용하기 (1) thymeleaf 기초 설정 - templetes 폴더에서 정적 파일 불러오기 -View Mapping Controller 생성 Thymeleaf 란? - spring 기반 웹 애플리케이션의 view에서 html, xml, javascript, css, text 처리 후 &quot; data-og-host=&quot;devzzi.tistory.com&quot; data-og-source-url=&quot;https://devzzi.tistory.com/16&quot; data-og-url=&quot;https://devzzi.tistory.com/16&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/RnjaO/hyXSrLNhDe/jtkqQK6mO3kflkNC85Twtk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bgOeGY/hyXSDlbmjg/knRuS4yB358LM4OG6RvTNk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/btqP9R/hyXSptFGUd/eZM0E2vGDFy34xmvnvDGo1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://devzzi.tistory.com/16&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devzzi.tistory.com/16&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/RnjaO/hyXSrLNhDe/jtkqQK6mO3kflkNC85Twtk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bgOeGY/hyXSDlbmjg/knRuS4yB358LM4OG6RvTNk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/btqP9R/hyXSptFGUd/eZM0E2vGDFy34xmvnvDGo1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Spring Boot] thymeleaf 사용하기 (1) - thymeleaf 초기 설정하기 | templetes 폴더에서 정적 파일 불러오기 |&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[Spring] thymeleaf 사용하기 (1) thymeleaf 기초 설정 - templetes 폴더에서 정적 파일 불러오기 -View Mapping Controller 생성 Thymeleaf 란? - spring 기반 웹 애플리케이션의 view에서 html, xml, javascript, css, text 처리 후&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devzzi.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>오곡(Ogok)</category>
      <author>주다애</author>
      <guid isPermaLink="true">https://lightworld.tistory.com/69</guid>
      <comments>https://lightworld.tistory.com/69#entry69comment</comments>
      <pubDate>Fri, 27 Dec 2024 10:47:54 +0900</pubDate>
    </item>
    <item>
      <title>@Component만 사용하면 안되나요?</title>
      <link>https://lightworld.tistory.com/68</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;약간의 사견이 들어있는 글입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  @Component&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;@Component 어노테이션은 Spring Context에 Bean(빈)을 추가하는 방식이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스프링 컨텍스트에 빈을 추가하는 것은 스프링이 관리해야 하는 객체를 인식하게 하는 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 스프링 컨텍스트에 빈을 추가해야 스프링이 객체를 볼 수 있게된다.(그 후 객체 간의 의존성 설정이라던지 메소드 호출이라던지 다양한 기능을 수행할 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 방법 중 하나가 &lt;b&gt;@Component&lt;/b&gt; 어노테이션을 사용하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 과정을 설명하면&lt;/p&gt;
&lt;pre id=&quot;code_1733970727103&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
@Getter
@Setter
public class Parrot {
    private String name;
} // Parrot.class&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1733971927770&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@ComponentScan(basePackages = &quot;com.spring.study.component&quot;)
public class ProjectConfig {
} // ProjectConfig.class&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1733971940174&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ComponentMain {
    public static void main(String[] args) {
        var context = new AnnotationConfigApplicationContext(ProjectConfig.class);
        Parrot p = context.getBean(Parrot.class);
        p.setName(&quot;앵무새&quot;);
        System.out.println(p);
        System.out.println(p.getName());
    }
} // ComponentMain.class&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;빈으로 만들 클래스에 @Component 어노테이션을 추가한다.&lt;/li&gt;
&lt;li&gt;@ComponentScan 어노테이션으로 스프링이 @Component 어노테이션이 추가된 클래스를 찾을 수 있게 한다. 이 때 basePackages의 속성으로 해당 빈(Parrot)이 위치한 정확한 패키지 명을 작성해주어야 한다.&lt;/li&gt;
&lt;li&gt;Main 클래스에서 스프링 컨텍스트가 빈을 생성하고 추가하는 것을 확인한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2tDGO/btsLf0GIzSY/mjtILDDZaPwC69KhfMFEg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2tDGO/btsLf0GIzSY/mjtILDDZaPwC69KhfMFEg1/img.png&quot; data-alt=&quot;빈이 제대로 등록되었음을 알 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2tDGO/btsLf0GIzSY/mjtILDDZaPwC69KhfMFEg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2tDGO%2FbtsLf0GIzSY%2FmjtILDDZaPwC69KhfMFEg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;433&quot; height=&quot;93&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;빈이 제대로 등록되었음을 알 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Component 어노테이션은 가장 일반적인 형태의 어노테이션으로 특정 역할에 종속되지 않는 일반적인 스프링 빈을 생성할 때 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;다른 어노테이션은 없나?&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;@Service, @Repository, @Controller와 같은 어노테이션을 통해 스프링 빈을 등록할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@Service&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Serivce 어노테이션은 서비스 레이어에 해당하는 클래스에서 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;이 클래스는 비즈니스 로직을 처리하는 서비스이다.&quot;라는 의미를 담고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@Repository&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Repository 어노테이션은 데이터 접근 계층 클래스에서 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;이 클래스는 데이터베이스와의 상호작용을 담당한다.&quot;라는 의미를 담고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@Controller&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Controller 어노테이션은 웹 컨트롤러 클래스에서 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;이 클래스는 HTTP 요청과 응답을 처리한다.&quot;라는 의미를 담고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring MVC에서 모델 객체를 만들어서 데이터를 담고 뷰를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자도 그렇고 다양한 Spring 프로젝트를 보면 위의 세 개의 어노테이션을 거의 사용하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;@Component만 사용하면 안되나요?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 질문으로 돌아가서 스프링 빈으로 등록하기 위해 @Component 어노테이션만 사용하면 안되나요?에 대한 대답을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러지 말자..!&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;020&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/020.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/020.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1️⃣ 의미의 명확성 부족&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;모든 빈에 @Component 어노테이션을 붙이면 클래스의 역할을 파악하기 힘들다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 클래스의 역할을 표현하는 다른 어노테이션(@Service 등등)이 있음에도 사용하지 않는 것은 좋은 선택은 아닌 듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2️⃣ 특화된 기능 부족&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;@Service, @Repository, @Controller 에는 각각 고유한 기능이 포함되어 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;@Repository로 예를 들어 보자&lt;/b&gt;&lt;br /&gt;@Repository는 데이터 계층에서 발생하는 예외 처리에 특화되어 있다.&lt;br /&gt;PersistenceExceptionTranslationPostProcessor는 예외를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;DataAccessException&lt;/b&gt;으로 변환한다.&lt;br /&gt;그럼 스프링은 데이터 접근 계층의 예외를 일관된 스프링의 데이터 접근 예외(DataAccessException)으로 처리할 수 있다.&lt;br /&gt;해당 어노테이션을 사용하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;PersistenceExceptionTranslationPostProcessor&lt;/b&gt;가 자동으로적용된다.&lt;br /&gt;&lt;br /&gt;즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;데이터베이스에 종속적이지 않은&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;기술 독립적이고 일관된 예외 처리가 가능해지는 것이다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;@Controller로 예를 들어보자&lt;br /&gt;&lt;/b&gt;Spring 5 및 Spring Boot 2.x 이전에는 &lt;i&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@Component + @RequestMapping&lt;/span&gt;&lt;/b&gt; &lt;/i&gt;조합으로 웹 요청 처리 핸들러로 해당 클래스를 등록할 수 있었다.&lt;br /&gt;즉, @Controller 없이도 HTTP 요청 처리가 가능했다.(기본적으로 @Controller가 @Component를 확장한 구조였기 때문)&lt;br /&gt;&lt;br /&gt;그런데 &lt;b&gt;Spring 6(Spring Boot 3.x) 이후부터는 HTTP 요청 처리를 위한 핸들러로 등록할 때 반드시 @Controller나 @RestController를 사용해야 한다.&lt;/b&gt; 그렇지 않으면 HTTP 요청을 제대로 처리할 수 없다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1733978614384&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
		implements MatchableHandlerMapping, EmbeddedValueResolverAware {
    ...
    @Override
    protected boolean isHandler(Class&amp;lt;?&amp;gt; beanType) {
		// 컨트롤러 애너테이션인지 확인	
        return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);
    }
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3️⃣ 유지 보수의 어려움&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;클래스가 많아질수록 @Component만 사용하면 클래스 역할을 구분하기 어려워 유지 보수의 어려움이 생긴다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의미적 명확성 부족과도 일맥상통하는 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 구조가 복잡해질수록 @Component만 사용하면 클래스의 역할을 파악하기 어려워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유틸리티 클래스나 특정 기능과 관련되지 않은 일반적인 구성 요소를 스프링 빈으로 등록할 때 @Component 어노테이션을 사용하자&lt;/p&gt;</description>
      <category>spring</category>
      <category>component</category>
      <category>Spring</category>
      <author>주다애</author>
      <guid isPermaLink="true">https://lightworld.tistory.com/68</guid>
      <comments>https://lightworld.tistory.com/68#entry68comment</comments>
      <pubDate>Thu, 12 Dec 2024 13:48:23 +0900</pubDate>
    </item>
    <item>
      <title>동일성과 동등성</title>
      <link>https://lightworld.tistory.com/67</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt; &amp;zwj;♂️ 동일성(Identity)&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;동일성은 두 객체가 메모리 상에서 같은 객체인지 비교하는 개념이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;동일성&lt;/b&gt;은 &lt;b&gt;두 객체가 메모리에서 동일한 위치를 참조&lt;/b&gt;하는지 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;==&quot;&lt;/b&gt; &lt;b&gt;연산자&lt;/b&gt;를 사용하여 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &quot;==&quot; 연산자는 객체의 참조(레퍼런스)를 비교한다. 객체의 값이 같아도 항상 true를 반환하는 것은 아닌 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 기본 타입에 대해서는 &quot;==&quot; 연산자를 사용하면 값을 비교한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733192661201&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Coffee coffee1 = new Coffee(100);
    Coffee coffee2 = new Coffee(100);
    System.out.println(coffee1 == coffee2); // false
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 Coffee 객체는 같은 값(100)을 가지지만 다른 객체이기 때문에(참조 값이 다르므로) 메모리 상에서 다른 위치를 참조할 것이고 즉 동일성 비교에서는 false가 반환된다.&lt;/p&gt;
&lt;pre id=&quot;code_1733193015101&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Coffee coffee1 = new Coffee(100);
    Coffee coffee2 = new Coffee(100);
    Coffee coffee3 = coffee1;

    System.out.println(coffee1 == coffee3); // true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와는 다르게 coffee3 객체는 coffee1과 같은 참조 값를 가진다. 그래서 동일성 비교를 하면 같다고 의미하는 true가 반환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; &amp;zwj;♀️ 동등성(Equality)&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;동등성은 객체의 내용(값)을 비교하는 개념이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;동등성&lt;/b&gt;은 &lt;b&gt;객체의 값이 같은지&lt;/b&gt;를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;equals()&lt;/b&gt; 메소드를 사용하여 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 객체의 값을 equals() 메소드를 사용해 비교했을 때 내용이 같으면 true, 다르면 false를 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733193349328&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Coffee coffee1 = new Coffee(100);
    Coffee coffee2 = new Coffee(100);
    Coffee coffee3 = coffee1;

    System.out.println(coffee1.equals(coffee2)); // false. 왜일까?
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방금 전의 설명과는 다르게 coffee1과 coffee2를 동등 비교 했을 때 false가 나왔다. 두 객체의 내용이 같은데 왜그럴까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;문자열(String)이 아닌 다른 객체를 비교할 때 동등 비교를 하려면 &lt;b&gt;equals() 메소드를 오버라이딩&lt;/b&gt; 해서 사용해야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 Coffee 객체의 동등 비교를 하려면 Coffee 클래스에서 equals() 메소드를 오버라이딩해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지 않으면 Object 클래스의 equals() 메소드를 사용하기 때문에 비교할 대상이 객체인 경우 객체 주소를 이용해서 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 객체를 비교하는 경우 &quot;==&quot; 연산자와 차이가 없다는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1733193650756&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Coffee {
    private int price;

    public Coffee(int price) {
        this.price = price;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        Coffee coffee = (Coffee)o;
        return price == coffee.price;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(price);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 객체의 값을 비교하기 위해 equals()메소드를 오버라이딩했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(hashCode() 메소드도 함께 오버라이딩 했는데 이건 다음 포스팅에서 알아보겠다 )&lt;/p&gt;
&lt;pre id=&quot;code_1733193888451&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Coffee coffee1 = new Coffee(100);
    Coffee coffee2 = new Coffee(100);
    Coffee coffee3 = coffee1;

    System.out.println(coffee1.equals(coffee2)); // true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이렇게 true가 반환된다! 우리가 원하는대로 객체의 값을 비교하는 동등 비교를 한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  문자열(String)의 객체 비교&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;문자열(String) 클래스는 equals() 메소드가 오버라이딩 되어 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 우리는 &lt;b&gt;문자열(String)이 아닌 다른 객체를 비교할 때 동등 비교를 하려면&amp;nbsp;equals() 메소드를 오버라이딩&amp;nbsp;해서 사용해야 한다. &lt;/b&gt;라고 이야기 했다.&lt;/p&gt;
&lt;pre id=&quot;code_1733194365406&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    return (anObject instanceof String aString)
            &amp;amp;&amp;amp; (!COMPACT_STRINGS || this.coder == aString.coder)
            &amp;amp;&amp;amp; StringLatin1.equals(value, aString.value);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;String 클래스 내부에 이렇게 equals() 메소드가 재정의 되어있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 우리가 오버라이딩을 해주지 않아도 .equals() 비교를 하면 동등 비교가 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 String은 객체인데 &quot;==&quot; 비교를 했을 때 두 값이 같으면 true를 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733194511862&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    String str1 = &quot;Hello World!&quot;;
    String str2 = &quot;Hello World!&quot;;
    System.out.println(str1 == str2); // true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 &lt;b&gt;문자열 리터럴&lt;/b&gt;은 &lt;b&gt;문자열 상수 풀(String Constant Pool)&lt;/b&gt;에서 관리된다. 그래서 동일한 리터럴은 재사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 동일한 문자열 리터럴에 대해서 &quot;==&quot; 비교를 하면 같다고 판단하고 true를 반환하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 new 키워드를 사용해서 문자열을 생성하면 매번 다른 객체를 생성한다. 그래서 같은 문자열이라도 서로 다른 객체가 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1733194707284&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    String str1 = &quot;Hello World!&quot;;
    String str2 = &quot;Hello World!&quot;;
    String str3 = new String(&quot;Hello World!&quot;);
    String str4 = new String(&quot;Hello World!&quot;);

    System.out.println(str1 == str3); // false
    System.out.println(str4 == str3); // false
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 str3과 str4는 서로 다른 객체이므로 &quot;==&quot; 비교를 하면 false가 반환되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, 우리는 문자열 비교를 할 때 항상 equals() 메소드를 사용하는 것이 동등성 비교를 위해 좋은 선택이다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>java</category>
      <category>Java</category>
      <category>객체</category>
      <category>동등성</category>
      <category>동일성</category>
      <author>주다애</author>
      <guid isPermaLink="true">https://lightworld.tistory.com/67</guid>
      <comments>https://lightworld.tistory.com/67#entry67comment</comments>
      <pubDate>Tue, 3 Dec 2024 12:00:26 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] ID 생성 전략</title>
      <link>https://lightworld.tistory.com/66</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;  JPA의 ID 생성 전략&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;JPA는 직접 할당과 자동 할당을 통해 ID를 생성한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 직접 할당 방식은 &lt;b&gt;@Id&lt;/b&gt; 어노테이션을 사용한다. 이 때 다른 어노테이션은 함께 사용하지 않고 기본키를 할당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스가 아니라 애플리케이션에서 기본 키를 직접 할당한다.&lt;/p&gt;
&lt;pre id=&quot;code_1732843543491&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Id
@Column(name = &quot;ID&quot;)
private String id;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 키 직접 할당 전략은 &lt;b&gt;em.persist()&lt;/b&gt;로 엔티티를저장하기 전에 기본 키를 직접 할당한다.&lt;/p&gt;
&lt;pre id=&quot;code_1732847055907&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Book book = new Book();
book.setId(&quot;id1&quot;); // 기본 키 직접 할당
em.persist();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 기본키를 직접 할당하지 않고 데이터베이스에서 생성하주는 값을 사용하려면 어떻게 매핑해야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스마다 기본 키를 생성하는 방식이 다르기 때문에 해당 문제를 해결하기 쉽지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA는 데이터베이스 기본 키 전략을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;자동 할당&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;JPA에서 제공하는 데이터베이스 기본 키 생성 전략은 IDENTITY, SEQUENCE, TABLE, AUTO 전략이 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 키 자동 할당은 &lt;b&gt;&lt;b&gt;@Id&lt;/b&gt;&lt;/b&gt;와 &lt;b&gt;@GeneratedValue&lt;/b&gt; 어노테이션을 함께 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나씩 살펴보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1️⃣ IDENTITY 전략&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;IDENTITY 전략은 기본 키 생성을 데이터베이스에 위임하는 전략이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 MySQL, PostgreSQL. SQL Server, DB2에서 사용하는 전략이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL에서는 &lt;b&gt;AUTO_INCREMENT&lt;/b&gt; 기능을 사용해서 기본 키 생성을 MySQL에게 넘긴다.&lt;/p&gt;
&lt;pre id=&quot;code_1732847415106&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE BOOK (
	ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY;
   	TITLE VARCHAR(255)
 );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 작성하면 ID 값을 직접 작성해주지 않아도 값이 저장될 때 자동으로 순서대로 ID값을 작성해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1732847576412&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 속성 값을 &lt;b&gt;GenerationType.IDENTITY&lt;/b&gt;로 지정해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 JPA는 기본 키 값을 얻어오기 위해서 데이터베이스를 추가로 조회한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IDENTITY 전략에서 중요한 점은 &lt;b&gt;데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있다는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 엔티티에 식별자 값을 할당하기 위해서 JPA가 데이터베이스를 추가로 조회해야 한다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔티티가 영속 상태가 되려면 식별자는 필수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 방금 설명했듯 IDENTITY 전략에서는 데이터베이스에 엔티티가 저장되어야 식별자를 구할 수 있어서 &lt;b&gt;em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 IDENTITY 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2️⃣ SEQUENCE 전략&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;SEQUENCE 전략은 유일한 값을 순서대로 생성하는 시퀀스를 사용해서 기본 키를 생성하는 전략이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 시퀀스란 유일한 값을 자동으로 생성하는 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1732847999997&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE BOOK (
    ID BIGINT NOT NULL PRIMARY KEY,
    TITLE VARCHAR(255)
)

// 시퀀스 생성
CREATE SEQUENCE BOOK_SEQ START WITH 1 INCREMENT BY 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 시퀀스를 직접 생성한 후 엔티티와 매핑해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1732848067307&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@SequenceGenerator(
        name = &quot;BOOK_SEQ_GENERATOR&quot;,
        sequenceName = &quot;BOOK_SEQ&quot;, // 매핑할 데이터베이스 시퀀스 이름
        initialValue = 1,
        allocationSize = 1
)
public class BOOK {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
    generator = &quot;BOOK_SEQ_GENERATOR&quot;)
    private Long id;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@SequenceGenerator&lt;/b&gt;를 사용해서 시퀀스 생성기를 등록하면 JPA는 이 시퀀스 생성기를 실제 데이터베이스의 시퀀스와 매핑한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;속성 값을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;GenerationType.SEQUENCE&lt;/b&gt;로 지정해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;  IDENTITY 전략과 SEQUENCE 전략의 차이&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼핏보면 비슷해 보이지만 차이가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 &lt;b&gt;시퀀스를 사용해서 식별자를 조회&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;b&gt;조회한 식별자를 엔티티에 할당한 후&lt;/b&gt; 엔티티를 영속성 컨텍스트에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 트랜잭션을 커밋해서 플러시가 일어나면 &lt;b&gt;엔티티를 데이터베이스에 저장한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IDENTITY 전략은 엔티티를 &lt;b&gt;데이터베이스에 저장한 후&lt;/b&gt; 식별자를 조회해서 &lt;b&gt;엔티티에 할당했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3️⃣ TABLE 전략&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;TABLE 전략은 키 생성 전용 테이블을 만들어 그곳에 이름과 값으로 사용할 칼럼을 만들어 시퀀스를 흉내내는 전략이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블을 사용하기 때문에 모든 데이터베이스에 적용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1732860609582&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE MY_SEQUENCES (
    SEQUENCE_NAME VARCHAR(255) NOT NULL,
    NEXT_VAL BIGINT
    PRIMARY KEY (SEQUENCE_NAME)
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 직접 MY_SEQUENCES라는 시퀀스 역할을 하는 테이블을 만들어서 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1732860819864&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@TableGenerator(
        name = &quot;BOOK_SEQ_GENERATOR&quot;,
        table = &quot;MY_SEQUENCE&quot;,
        pkColumnValue = &quot;BOOK_SEQ&quot;,
        initialValue = 1,
        allocationSize = 50
)
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE,
    generator = &quot;BOOK_SEQ_GENERATOR&quot;)
    private Long id;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@TableGenerator&lt;/b&gt; 를 사용해서 테이블 키 생성기를 등록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 테이블 키 생성기(BOOK_SEQ_GENERATOR)와 위에서 생성한 MY_SEQUENCES 테이블을 키 생성용 테이블로 매핑했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;그리고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;속성 값을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;GenerationType.TABLE&lt;/b&gt;로 지정해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TABLE 전략은 따로 시퀀스 역할을 하는 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작 방식은 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 TABLE 전략은 값을 조회하면서 SELECT 쿼리를 사용하고 + UPDATE 쿼리를 통해 다음 값으로 증가시키므로 SEQUENCE 전략보다 데이터베이스와 한 번 더 조회하는 단점이 있다. 하지만 데이터베이스 종류를 타지 않기 때문에 그 점은 장점으로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4️⃣ AUTO 전략&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;AUTO 전략은 데이터베이스 방언에 따라 IDENTITY, SEQUECNE, TABLE 전략 중 하나를 자동으로 선택하는 전략이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 종류에 따라 기본 키 자동 생성 전략이 가능한 방언도 있고 그렇지 않은 방언도 있었지만 AUTO 전략은 데이터베이스 방언에 따라 전략 중 하나를 자동으로 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 장점을 가지고 있다.(개발 초기 단계에 특히 편하다.)&lt;/p&gt;
&lt;pre id=&quot;code_1732861431786&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이렇게 속성 값을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;GenerationType.AUTO&lt;/b&gt;로 지정해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기본 키 매핑 전략 정리&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로 엔티티를 영속 상태로 만드려면 식별자 값이 반드시 있어야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;직접 할당 : em.persist()를 호출하기 전에 애플리케이션에서 직접 식별자 값을 할당한다.&lt;/li&gt;
&lt;li&gt;IDENTITY : 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후에 영속성 컨텍스트에 저장한다. 데이터베이스에 엔티티 저장이 선행되어야 한다.&lt;/li&gt;
&lt;li&gt;SEQUENCE : 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다. 그 후 엔티티를 데이터베이스에 저장한다.&lt;/li&gt;
&lt;li&gt;TABLE : 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 자동 생성 전략을 사용해서 식별자를 선택해도 되고, 자연 키(NATURAL KEY)를 기본 키로 선택할 수도 있다.(주민번호, 이메일, 전화번호 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 &lt;b&gt;기본 키&lt;/b&gt;는 &lt;b&gt;NULL이 아니고 + 유일해야 하고 + 변하지 않아야&lt;/b&gt; 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고 자료&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;도서 : 자바 ORM 표준 JPA 프로그래밍(김영한 지음)&lt;/span&gt;&lt;/p&gt;</description>
      <category>springboot</category>
      <category>id생성전략</category>
      <category>JPA</category>
      <category>기본키</category>
      <author>주다애</author>
      <guid isPermaLink="true">https://lightworld.tistory.com/66</guid>
      <comments>https://lightworld.tistory.com/66#entry66comment</comments>
      <pubDate>Fri, 29 Nov 2024 15:30:36 +0900</pubDate>
    </item>
    <item>
      <title>얕은 복사와 깊은 복사</title>
      <link>https://lightworld.tistory.com/65</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;  자바에서의 객체 복사&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;자바에서의 객체 복사는 얕은 복사와 깊은 복사가 존재한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘의 차이를 알아보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 예제는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732675377611&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Objects;

class Book {

	private String name; // 책 이름
	private Author author; // 저자

	public Book(String name, Author author) {
		this.name = name;
		this.author = author;
	}

	public Book shallowCopy() { // 얕은 복사
		return new Book(this.name, this.author);
	}

	public Book deepCopy() { // 깊은 복사
		Author copiedAuthor = new Author(this.author.getName());
		return new Book(this.name, copiedAuthor);
	}

	public void changeAuthor(String name) { // 저자 이름 변경
		author.setName(name);
	}

	@Override
	public String toString() {
		return &quot;Book name : &quot; + name + &quot;, &quot; + author;
	}

	static class Author {

		private String name; // 저자 이름

		public Author(String name) {
			this.name = name;
		}

		public String getName() { // 저자 이름 반환
			return name;
		}

		public void setName(String name) { // 저자 이름 변경
			this.name = name;
		}

		@Override
		public String toString() {
			return &quot;Author : &quot; + name;
		}
	}

	@Override
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;
		Book book = (Book)o;
		return Objects.equals(name, book.name) &amp;amp;&amp;amp; Objects.equals(author, book.author);
	}

	@Override
	public int hashCode() {
		return Objects.hash(name, author);
	}

	public static void main(String[] args) {
		Author author1 = new Author(&quot;조슈아 블로크&quot;);
		Book book1 = new Book(&quot;이펙티브 자바&quot;, author1);

		// 얕은 복사 후 변경
		Book shallowCopyBook = book1.shallowCopy();
		shallowCopyBook.changeAuthor(&quot;Joshua Bloch&quot;);

		// 얕은 복사 결과 출력
		System.out.println(&quot;After shallow copy and change:&quot;);
		System.out.println(&quot;Original book1: &quot; + book1);
		System.out.println(&quot;Shallow copied book: &quot; + shallowCopyBook);

		System.out.println(&quot;book1's hashcode: &quot; + book1.hashCode());
		System.out.println(&quot;shallowCopyBook's hashcode: &quot; + shallowCopyBook.hashCode());
		System.out.println(&quot;book1's author's hashcode: &quot; + book1.author.hashCode());
		System.out.println(&quot;shallowCopyBook's  author's hashcode: &quot; + shallowCopyBook.author.hashCode() + &quot;\n&quot;);

		Author author2 = new Author(&quot;마틴 파울러&quot;);
		Book book2 = new Book(&quot;리팩터링&quot;, author2);

		// 깊은 복사 후 변경
		Book deepCopyBook = book2.deepCopy();
		deepCopyBook.changeAuthor(&quot;Martin Fowler&quot;);

		// 깊은 복사 결과 출력
		System.out.println(&quot;After deep copy and change:&quot;);
		System.out.println(&quot;Original book2: &quot; + book2);
		System.out.println(&quot;Deep copied book: &quot; + deepCopyBook);

		System.out.println(&quot;book2's hashcode: &quot; + book2.hashCode());
		System.out.println(&quot;deepCopyBook's hashcode: &quot; + deepCopyBook.hashCode());
		System.out.println(&quot;book2's author's hashcode: &quot; + book2.author.hashCode());
		System.out.println(&quot;deepCopyBook's  author's hashcode: &quot; + deepCopyBook.author.hashCode());
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금은 복잡해 보이지만 프린트 문이 길어서 그렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나씩 살펴보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;얕은 복사&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;자바의 얕은 복사는 복사 대상 객체의 참조(주소값)만 복사한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 원본과 복사본이 같은 힙(Heap)의 데이터를 바라보는 것이다.(객체를 생성하면 힙 영역에 객체의 값이 올라간다.)&lt;/p&gt;
&lt;pre id=&quot;code_1732676108353&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Book shallowCopy() { // 얕은 복사
    return new Book(this.name, this.author);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Book 객체는 new를 통해 새로 만들어 주었지만 Author 객체는 기존의 값과 같은 참조를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;shallowCopy()&lt;/b&gt;를 사용해서 얕은 복사를 실행하면&lt;/p&gt;
&lt;pre id=&quot;code_1732676204180&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Author author1 = new Author(&quot;조슈아 블로크&quot;);
Book book1 = new Book(&quot;이펙티브 자바&quot;, author1);

// 얕은 복사 후 변경
Book shallowCopyBook = book1.shallowCopy();
shallowCopyBook.changeAuthor(&quot;Joshua Bloch&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;book1 객체와 shallowCopyBook 객체가 같은 Author 참조값을 가지게되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;shallowCopyBook에서 changeAuthor() 메소드를 통해 저자 이름을 바꾸게 되면 book1 객체에도 영향을 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;둘은 같은 Author 객체를 공유하기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;결과 값을 출력하면&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ruLxz/btsKWUgQeWC/ksPlqwgfTiIgMaFCkIoQPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ruLxz/btsKWUgQeWC/ksPlqwgfTiIgMaFCkIoQPk/img.png&quot; data-alt=&quot;둘의 저자 이름이 모두 같은 값으로 변경됐다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ruLxz/btsKWUgQeWC/ksPlqwgfTiIgMaFCkIoQPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FruLxz%2FbtsKWUgQeWC%2FksPlqwgfTiIgMaFCkIoQPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;669&quot; height=&quot;78&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;둘의 저자 이름이 모두 같은 값으로 변경됐다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Author 값이 둘 다 변경된 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;equals()와 hashCode() 메소드를 오버라이딩해서 해시 코드 값이 같은지도 살펴보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;50&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4hKD1/btsKY0mcV6D/QergufjalSefKF0trGDFX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4hKD1/btsKY0mcV6D/QergufjalSefKF0trGDFX0/img.png&quot; data-alt=&quot;같은 해시 코드를 가진다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4hKD1/btsKY0mcV6D/QergufjalSefKF0trGDFX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4hKD1%2FbtsKY0mcV6D%2FQergufjalSefKF0trGDFX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;484&quot; height=&quot;50&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;50&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;같은 해시 코드를 가진다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘의 Author 객체는 같은 해시 코드 값을 가짐을 알 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일성 비교를 해도&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #080808;&quot;&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;System.out.println(book1.author == shallowCopyBook.author);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;53&quot; data-origin-height=&quot;30&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxU1Qh/btsKX5aI7dr/roKqTbwHnfSPWIVQMikXMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxU1Qh/btsKX5aI7dr/roKqTbwHnfSPWIVQMikXMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxU1Qh/btsKX5aI7dr/roKqTbwHnfSPWIVQMikXMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxU1Qh%2FbtsKX5aI7dr%2FroKqTbwHnfSPWIVQMikXMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;53&quot; height=&quot;30&quot; data-origin-width=&quot;53&quot; data-origin-height=&quot;30&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같음을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;깊은 복사&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;자바의 깊은 복사는 원본이 참조하고 있는 힙의 데이터까지 복사한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 원본과 복사본이 서로 다른 객체를 참조하므로 원본의 변경이 복사본의 변경에 영향을 주지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1732681300729&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Book deepCopy() { // 깊은 복사
    Author copiedAuthor = new Author(this.author.getName());
    return new Book(this.name, copiedAuthor);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;deepCopy()&lt;/b&gt; 메소드를 살펴보면 new를 통해 새로운 Author 객체(copiedAuthor)를 생성하고 그 후 new를 통해 새로운 Book 객체를 리턴해주고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&amp;nbsp;&lt;b&gt; &lt;b&gt;deepCopy()&lt;/b&gt;&lt;/b&gt; 를 통해 deepCopyBook 객체를 만들고 changeAuthor() 메소드를 통해 저자 이름을 바꾸게 되면 book2 객체에 영향을 주지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애초에 각각 다른 Author 주소값을 가리키고 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 값을 출력하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cq0kL6/btsKYqsjUqa/voKeP1w0E1p3DQcDHqAHK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cq0kL6/btsKYqsjUqa/voKeP1w0E1p3DQcDHqAHK1/img.png&quot; data-alt=&quot;저자 이름이 복사본만 변경됐다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cq0kL6/btsKYqsjUqa/voKeP1w0E1p3DQcDHqAHK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcq0kL6%2FbtsKYqsjUqa%2FvoKeP1w0E1p3DQcDHqAHK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;611&quot; height=&quot;85&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;저자 이름이 복사본만 변경됐다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 변경을 한 복사본의 저자 이름만 변경된 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;equals()와 hashCode() 메소드를 오버라이딩해서 해시 코드 값이 다른지도 살펴보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;51&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qaWI2/btsKY4bfjqw/hGxWz4sHk6opMNOJikzdE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qaWI2/btsKY4bfjqw/hGxWz4sHk6opMNOJikzdE0/img.png&quot; data-alt=&quot;다른 해시 코드를 가진다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qaWI2/btsKY4bfjqw/hGxWz4sHk6opMNOJikzdE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqaWI2%2FbtsKY4bfjqw%2FhGxWz4sHk6opMNOJikzdE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;452&quot; height=&quot;51&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;51&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다른 해시 코드를 가진다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘의 Author 객체는 다른 해시 코드 값을 가짐을 알 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일성 비교를 해도 당연히 false를 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;clone 메소드&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Object.clone() 메소드는 인스턴스 객체의 복제를 위한 메소드로 해당 인스턴스를 복제해서 새로운 인스턴스를 생성하여 그 참조값을 반환한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clone() 메소드를 사용하기 위해서는 Cloneable 인터페이스를 구현해야 한다.(&lt;b&gt;implements Cloneable&lt;/b&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 해당 클래스에서 clone() 메소드를 오버라이딩 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서 Book 클래스가 Cloneable 인터페이스를 구현하고 그 안에 clone() 메소드가 오버라이딩 되어있다고 하면&lt;/p&gt;
&lt;pre id=&quot;code_1732682718919&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 깊은 복사(deep copy)
Book book = new Book();
book.setName(&quot;책책&quot;);
Book copiedBook = (Book) book.clone();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;book 객체와 copiedBook은 다른 힙 영역을 참조한다. 둘은 당연히 다른 해시 코드를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기억해야 할 점은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 clone의 핵심은 원시 타입이 아닌 클래스를 복사할 일이 있을 경우 clone() 메소드를 꼭 오버라이딩 해야한다는 것이다. 그래야 깊은 복사를 수행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;다루는 데이터가 참조 타입인 경우 clone() 메소드를 재정의해야 깊은 복사가 이루어진다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고 자료&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-Object-%ED%81%B4%EB%9E%98%EC%8A%A4-clone-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/JAVA-%E2%98%95-Object-%ED%81%B4%EB%9E%98%EC%8A%A4-clone-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732682426568&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;☕ 자바 clone 메서드 재정의 (얕은 복사 &amp;amp; 깊은 복사)&quot; data-og-description=&quot;clone 메소드 Object.clone() 메소드는 인스턴스 객체의 복제를 위한 메소드로, 해당 인스턴스를 복제하여 새로운 인스턴스를 생성해 그 참조값을 반환한다. 이러한 clone() 메소드를 사용하기 위해서&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-Object-%ED%81%B4%EB%9E%98%EC%8A%A4-clone-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC&quot; data-og-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-Object-%ED%81%B4%EB%9E%98%EC%8A%A4-clone-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/UFTLI/hyXDlezMTj/Mwl90gryfAWqBijQaoQvn0/img.jpg?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500,https://scrap.kakaocdn.net/dn/dtbYOu/hyXDivq7IM/sFh5yH2CIjehg4ICxA5iik/img.jpg?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500,https://scrap.kakaocdn.net/dn/cPNreo/hyXDk03PbC/pq3N2ACqJNkupVxtu6Yrfk/img.png?width=933&amp;amp;height=410&amp;amp;face=0_0_933_410&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-Object-%ED%81%B4%EB%9E%98%EC%8A%A4-clone-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-Object-%ED%81%B4%EB%9E%98%EC%8A%A4-clone-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/UFTLI/hyXDlezMTj/Mwl90gryfAWqBijQaoQvn0/img.jpg?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500,https://scrap.kakaocdn.net/dn/dtbYOu/hyXDivq7IM/sFh5yH2CIjehg4ICxA5iik/img.jpg?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500,https://scrap.kakaocdn.net/dn/cPNreo/hyXDk03PbC/pq3N2ACqJNkupVxtu6Yrfk/img.png?width=933&amp;amp;height=410&amp;amp;face=0_0_933_410');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;☕ 자바 clone 메서드 재정의 (얕은 복사 &amp;amp; 깊은 복사)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;clone 메소드 Object.clone() 메소드는 인스턴스 객체의 복제를 위한 메소드로, 해당 인스턴스를 복제하여 새로운 인스턴스를 생성해 그 참조값을 반환한다. 이러한 clone() 메소드를 사용하기 위해서&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>java</category>
      <category>깊은복사</category>
      <category>얕은복사</category>
      <category>오블완</category>
      <category>자바</category>
      <category>티스토리챌린지</category>
      <author>주다애</author>
      <guid isPermaLink="true">https://lightworld.tistory.com/65</guid>
      <comments>https://lightworld.tistory.com/65#entry65comment</comments>
      <pubDate>Wed, 27 Nov 2024 13:50:11 +0900</pubDate>
    </item>
    <item>
      <title>@NoargsConstructor(AccessLevel.PROTECTED)</title>
      <link>https://lightworld.tistory.com/64</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;  내 코드&lt;/h4&gt;
&lt;pre id=&quot;code_1732612946718&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder(access = AccessLevel.PRIVATE)
public class Book { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Book 엔티티 코드를 살펴보자. 도서 관련 백엔드 서비스이므로 당연히 Book 엔티티는 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 살펴볼 것은&lt;/p&gt;
&lt;pre id=&quot;code_1732613046139&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@NoArgsConstructor(access = AccessLevel.PROTECTED)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  이 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;왜 사용했나?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 솔직하게 말하면 이 프로젝트 이전 팀 프로젝트에서 팀원 분이 베이스 코드 작성 시에 위처럼 코드를 작성하셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 때는 내가 맡은 부분에만 정신이 팔려서 사실 저 코드 쓰임의 이유를 못 여쭤봤다.(내가 찾아봤어야 하는건데 말이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 좋은 게 좋은거지 라는 안일한 생각으로 버드북 프로젝트에 그대로 가져왔다.&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;039&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/039.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/039.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 왜 &lt;b&gt;@NoArgsConstructor&lt;/b&gt; 어노테이션에 &lt;b&gt;access 레벨을 proteced로&lt;/b&gt; 지정하는걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;@NoArgsConstructor&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;@NoArgsConstructor 어노테이션은 파라미터가 없는 디폴트 생성자를 생성하는 어노테이션이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Book엔티티를 예시로 보면&lt;/p&gt;
&lt;pre id=&quot;code_1732613391378&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Book() {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 생성자를 알아서 만들어준다는 소리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA는 기본 스펙 상 기본 생성자를 요구한다. JPA는 리플렉션을 사용해서 내부적으로 엔티티를 생성하는데 이 때 동적으로 런타임 시점에 기본 생성자를 통해 클래스를 인스턴스화 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 직접 기본 생성자를 작성하든 어노테이션을 통해서 생성하든 해야한다는 소리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;@NoArgsConstructor(access = AccessLevel.PROTECTED)&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;외부에서 무분별한 불완전한 객체 생성을 방지하기 위해 기본 생성자에 접근 제한을 건다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 일단 JPA를 사용하기 위해 기본 생성자가 필요하단 것은 이해했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 왜 접근 제한을 두는걸까? 즉, &lt;b&gt;AccessLevel&lt;/b&gt;을 왜 설정하는걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 기본 생성자로 객체를 생성하는 것을 방지하기 위함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 생성자 생성에 접근 제한을 걸지 않았을 때를 가정해보자&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1732626319122&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Getter
@NoArgsConstructor
public class Book { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 외부에서 기본 생성자를 통해 객체 생성이 자유로워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 아무런 필드 값을 가지지 못한 불완전한 Book 객체가 생성될 수 있고, 이는 비즈니스 로직이나 데이터베이스와의 상호작용에서 예외를 발생시킬 가능성이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 완전한 객체를 만들기 위해서 &lt;b&gt;setter&lt;/b&gt;를 함께 쓴다고 가정해보자.(위의 코드에서 &lt;b&gt;@Setter&lt;/b&gt; 어노테이션을 추가했다고 가정한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;setter는 객체 생성 시 지양하는 방법이다.&amp;nbsp;&lt;/b&gt;setXX()를 사용하면 어디든 객체의 값을 변경할 수 있다. 즉, 객체의 일관성을 유지하기가 어렵고 데이터베이스의 일관성 또한 깨지기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 기본 생성자에 접근 제한을 걸어서 외부에서 무분별한 불완전한 객체 생성을 방지하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;access = AccessLevel.PROTECTED&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;접근 권한을 Protected로 설정하는 이유는 엔티티의 프록시 조회 때문이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시에서 접근 권한을 그냥 Public으로 해주면 안되는 이유를 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 왜 접근 권한이 &lt;b&gt;Protected&lt;/b&gt;인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔티티끼리 연관관계를 맺게되면 &lt;b&gt;지연 로딩&lt;/b&gt;의 경우에 실제 엔티티가 아니라 &lt;b&gt;프록시 객체&lt;/b&gt;를 조회한다.(프록시에 대해선 다음에 알아보자..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암튼 JPA의 구현체는 실제 엔티티의 기본 생성자를 통해 프록시 객체를 생성한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 접근 권한을 private으로 하면 프록시 객체를 생성할 수가 없다. 즉, 지연 로딩 시 값을 조회할 수가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결론은?&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;무분별한 객체 생성을 막고, 프록시 객체 생성을 통한 조회를 위해 기본 생성자 생성 @NoargsConstructor 어노테이션의 접근 제한은 Protected로 하자&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고민할 지점을 알려주신 팀원 분께 감사드린다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고 자료&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@kevin_/%EB%82%B4%EA%B0%80-NoargsConstructor-access-AccessLevel.PROTECTED%EB%A5%BC-%EC%99%9C-%EC%9E%91%EC%84%B1%ED%96%88%EC%9D%84%EA%B9%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@kevin_/%EB%82%B4%EA%B0%80-NoargsConstructor-access-AccessLevel.PROTECTED%EB%A5%BC-%EC%99%9C-%EC%9E%91%EC%84%B1%ED%96%88%EC%9D%84%EA%B9%8C&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732628185330&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;내가 @NoArgsConstructor (access = AccessLevel.PROTECTED)를 작성했던 이유&quot; data-og-description=&quot;그 때 당시에는 Spring은 물론 Java에 대한 개념도 매우 약할 때라 지금도 약하지만레퍼런스의 코드를 가져다 사용하기에 급급했다.당시의 나는 해당 개념에 대한 정확한 이해보다는 기능 구현이 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@kevin_/%EB%82%B4%EA%B0%80-NoargsConstructor-access-AccessLevel.PROTECTED%EB%A5%BC-%EC%99%9C-%EC%9E%91%EC%84%B1%ED%96%88%EC%9D%84%EA%B9%8C&quot; data-og-url=&quot;https://velog.io/@kevin_/내가-NoargsConstructor-access-AccessLevel.PROTECTED를-왜-작성했을까&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/emYzJ7/hyXC8zmI7G/60efUkeQ6f0t7gSlJrDKO1/img.gif?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/ldPMz/hyXDjHFrgJ/n52JaaTCdWfTrhy15WwM4K/img.gif?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/caJSfE/hyXDm5tNTM/hcK4TXG8IMNvieOtYiF0jK/img.png?width=1492&amp;amp;height=448&amp;amp;face=0_0_1492_448&quot;&gt;&lt;a href=&quot;https://velog.io/@kevin_/%EB%82%B4%EA%B0%80-NoargsConstructor-access-AccessLevel.PROTECTED%EB%A5%BC-%EC%99%9C-%EC%9E%91%EC%84%B1%ED%96%88%EC%9D%84%EA%B9%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@kevin_/%EB%82%B4%EA%B0%80-NoargsConstructor-access-AccessLevel.PROTECTED%EB%A5%BC-%EC%99%9C-%EC%9E%91%EC%84%B1%ED%96%88%EC%9D%84%EA%B9%8C&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/emYzJ7/hyXC8zmI7G/60efUkeQ6f0t7gSlJrDKO1/img.gif?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/ldPMz/hyXDjHFrgJ/n52JaaTCdWfTrhy15WwM4K/img.gif?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/caJSfE/hyXDm5tNTM/hcK4TXG8IMNvieOtYiF0jK/img.png?width=1492&amp;amp;height=448&amp;amp;face=0_0_1492_448');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;내가 @NoArgsConstructor (access = AccessLevel.PROTECTED)를 작성했던 이유&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;그 때 당시에는 Spring은 물론 Java에 대한 개념도 매우 약할 때라 지금도 약하지만레퍼런스의 코드를 가져다 사용하기에 급급했다.당시의 나는 해당 개념에 대한 정확한 이해보다는 기능 구현이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://erjuer.tistory.com/106&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://erjuer.tistory.com/106&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732628183504&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[JPA] Entity Class의 @NoargsConstructor (access = AccessLevel.PROTECTED)&quot; data-og-description=&quot;실무에서 JPA를 활용하다보면 Entity 생성시 @NoargsConstructor (access = AccessLevel.PROTECTED) 이라는 Annotation을 붙여서 개발을 하게 된다. 이에 조금 더 정확히 이해하고자 이번 블로그 글로 언급하고자 한&quot; data-og-host=&quot;erjuer.tistory.com&quot; data-og-source-url=&quot;https://erjuer.tistory.com/106&quot; data-og-url=&quot;https://erjuer.tistory.com/106&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QrjGu/hyXC8MUpx2/TmbH5OAwdxgAkAO0lEKZHK/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/bokNHv/hyXDdnbqPz/XAVAurgoEpCLp89l30YAmk/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/kg53z/hyXDbplhMH/9JUyuNW2zzaUbuuWJs2WC0/img.png?width=2014&amp;amp;height=917&amp;amp;face=0_0_2014_917&quot;&gt;&lt;a href=&quot;https://erjuer.tistory.com/106&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://erjuer.tistory.com/106&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QrjGu/hyXC8MUpx2/TmbH5OAwdxgAkAO0lEKZHK/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/bokNHv/hyXDdnbqPz/XAVAurgoEpCLp89l30YAmk/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/kg53z/hyXDbplhMH/9JUyuNW2zzaUbuuWJs2WC0/img.png?width=2014&amp;amp;height=917&amp;amp;face=0_0_2014_917');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[JPA] Entity Class의 @NoargsConstructor (access = AccessLevel.PROTECTED)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실무에서 JPA를 활용하다보면 Entity 생성시 @NoargsConstructor (access = AccessLevel.PROTECTED) 이라는 Annotation을 붙여서 개발을 하게 된다. 이에 조금 더 정확히 이해하고자 이번 블로그 글로 언급하고자 한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;erjuer.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;버드북&quot; href=&quot;https://github.com/jooda00/birdbook-backend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/p&gt;</description>
      <category>버드북(birdbook-backend)</category>
      <category>@NoArgsConstructor</category>
      <category>springboot</category>
      <category>생성자</category>
      <author>주다애</author>
      <guid isPermaLink="true">https://lightworld.tistory.com/64</guid>
      <comments>https://lightworld.tistory.com/64#entry64comment</comments>
      <pubDate>Tue, 26 Nov 2024 22:38:55 +0900</pubDate>
    </item>
    <item>
      <title>Servlet(서블릿) 이란?</title>
      <link>https://lightworld.tistory.com/63</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;  Servlet(서블릿)&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Servlet(서블릿)은 Java 언어를 기반으로 하는 웹 애플리케이션 개발을 위한 기술로, 클라이언트의 요청에 따라 동적으로 서버에서 처리하는 자바 클래스이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿은 동적 웹페이지를 만들 때 사용되는 자바 기반의 웹 애플리케이션 프로그래밍 기술이며 HTTP 프로토콜을 통해 클라이언트의 요청을 받고, 자바 코드를 통해 동적인 웹 페이지를 생성하거나 데이터를 처리해서 응답을 반환해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;서블릿 이전에는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿 이전의 초기 웹은 단순히 &lt;b&gt;정적인 HTML 문서&lt;/b&gt;를 클라이언트에게 제공하는 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 서버에 HTML 파일을 요청하면   서버는 해당 HTML 문서를 그대로 전송한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 웹 페이지를 표시할 수는 있었지만, 사용자 맞춤형 데이터나 &lt;b&gt;동적인 컨텐츠는 제공할 수 없었다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(ex. 사용자가 로그인하면 이름을 표시하거나, 상품 검색 결과를 동적으로 보여줄 수 없었음)&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;007&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/007.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/007.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해서 나온 기술이 &lt;b&gt;CGI(Common Gateway Interface)&lt;/b&gt;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CGI는 웹 서버가 클라이언트의 요청을 받을 때마다 외부 프로그램을 실행해서 &lt;b&gt;동적인 결과를 생성&lt;/b&gt;하고 이를 HTML로 반환했다. 즉, 동적인 컨텐츠 제공이 가능해진 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, CGI의 치명적인 문제가 있는데 바로 &lt;b&gt;각 요청마다 매번 새로운 프로세스를 생성한다&lt;/b&gt;는 것이다.(1요청 1프로세스)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 리소스의 소모가 크고, 성능 이슈를 발생시키고 확장성도 부족했다.&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;016&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/016.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/016.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 나온 것이 바로 &lt;b&gt;서블릿(Servlet)&lt;/b&gt;이라는 기술이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;서블릿의 등장&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 역사를 요약하자면&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;정적인 웹(초기) : 단순 HTML 파일만 제공. A 클라이언트와 B 클라이언트가 보는 화면 같음&lt;/li&gt;
&lt;li&gt;CGI 도입 : 동적인 콘텐츠 제공이 가능하지만 리소스 낭비와 성능 문제 발생(1요청 1프로세스)&lt;/li&gt;
&lt;li&gt;서블릿(Servlet) : Java 기반의 멀티 스레드 기술로 CGI의 문제를 해결하고 동적인 웬 애플리케이션 개발 가능하게 함&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 서블릿이라는 기술이 등장했다고 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 서블릿의 특징을 간단히 알아보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1️⃣ 멀티 스레드 지원&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서블릿은 각 클라이언트의 요청을 새로운 스레드로 처리하여 멀티 스레드를 지원한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CGI와 다르게 별도의 프로세스를 생성하는 것이 아니라 하나의 프로그램에서 여러 스레드가 작동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 프로그램은 하나만 켜져있고, 그 안에서 스레드들이 요청을 처리할 수 있어 성능 이슈를 줄인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2️⃣ Wirte Once, Run Anywhere&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서블릿은 자바 언어로 작성되므로, 플랫폼 독립성을 가진다. 어떤 OS에서든 자바를 지원하는 서버 환경에서 실행 가능하다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3️⃣ 영구적인 서버 프로그램&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서블릿은 웹 애플리케이션이 실행되는 동안 서버에 지속적으로 유지된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청마다 종료되는 CGI와 다르게 웹 애플리케이션이 실행되는 동안 서버에 유지되므로 속도와 성능이 더 뛰어나다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4️⃣ 효율적인 자원 관리&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서블릿은 웹 애플리케이션 시작 시 메모리에 로드되며 요청이 들어오면 해당 서블릿 객체가 재사용된다.(싱글톤으로 관리되는 서블릿 객체)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 특성으로 인해 초기화 비용이 줄어들고 서버 자원을 효율적으로 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;서블릿 동작 방식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 서블릿은 어떻게 동작할까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1492&quot; data-origin-height=&quot;688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpXMmY/btsKVQFiEXm/59NSsD7X5DRYaRsITkjPbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpXMmY/btsKVQFiEXm/59NSsD7X5DRYaRsITkjPbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpXMmY/btsKVQFiEXm/59NSsD7X5DRYaRsITkjPbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpXMmY%2FbtsKVQFiEXm%2F59NSsD7X5DRYaRsITkjPbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;592&quot; height=&quot;273&quot; data-origin-width=&quot;1492&quot; data-origin-height=&quot;688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brJdfh/btsKX3b1Quk/CaOcGZ0ZJUViehkQm9cEkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brJdfh/btsKX3b1Quk/CaOcGZ0ZJUViehkQm9cEkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brJdfh/btsKX3b1Quk/CaOcGZ0ZJUViehkQm9cEkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrJdfh%2FbtsKX3b1Quk%2FCaOcGZ0ZJUViehkQm9cEkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;620&quot; height=&quot;255&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트 측에서 HTTP 요청을 보내면 HTTP 요청이 Servlet Container(서블릿 컨테이너)로 전송된다.&lt;/li&gt;
&lt;li&gt;요청을 전송받은 서블릿 컨테이너는 HttpServletRequest, HttpServletResponse 객체를 생성한다.&lt;/li&gt;
&lt;li&gt;web.xml을 기반으로 사용자가 요청한 URL이 어느 서블릿에 대한 요청인지 찾는다.&lt;/li&gt;
&lt;li&gt;해당 서블릿에서 service 메소드를 호출한 후 클라이언트의 GET, POST 여부에 따라 doGet(), doPost()를 호출한다.&lt;/li&gt;
&lt;li&gt;doGet(), doPost() 메소드는 동적 페이지를 생성하서 HttpServletResponse 객체에 응답을 담는다.(서블릿 컨테이너(WAS)가 객체에 담긴 내용으로 HTTP응답 정보를 생성해서 클라이언트에게 전달한다.)&lt;/li&gt;
&lt;li&gt;응답이 끝난 후 생성한 HttpServletRequest, HttpServletResponse 객체를 소멸한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동작 과정을 코드로 살펴보자&lt;/p&gt;
&lt;pre id=&quot;code_1732595845114&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.IOException;
import java.io.PrintWriter;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet(&quot;/hello&quot;)
public class HelloServlet extends HttpServlet {
	// GET 요청을 처리하는 메서드
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

		// 응답의 Content-Type을 설정
		response.setContentType(&quot;text/html&quot;);

		// 클라이언트에게 데이터를 쓰기 위한 PrintWriter 객체 생성
		PrintWriter out = response.getWriter();

		// 응답으로 반환할 HTML 생성
		out.println(&quot;&amp;lt;html&amp;gt;&quot;);
		out.println(&quot;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Hello Servlet&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&quot;);
		out.println(&quot;&amp;lt;body&amp;gt;&quot;);
		out.println(&quot;&amp;lt;h1&amp;gt;Hello, Servlet!&amp;lt;/h1&amp;gt;&quot;);
		out.println(&quot;&amp;lt;p&amp;gt;This is a simple servlet example.&amp;lt;/p&amp;gt;&quot;);
		out.println(&quot;&amp;lt;/body&amp;gt;&quot;);
		out.println(&quot;&amp;lt;/html&amp;gt;&quot;);

		// PrintWriter 닫기
		out.close();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트의 요청이 GET인 경우의 코드이다. 이 때는 위의 설명과 같이 doGet() 메소드가 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 &lt;b&gt;http://localhost:8080/MyServletApp/hello&amp;nbsp;&lt;/b&gt;요청을 보내면 화면에 &lt;span style=&quot;color: #006dd7;&quot;&gt;&quot;Hello, Servlet!&quot;&lt;/span&gt;이라는 메세지가 출력된 HTML 페이지가 표시된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;서블릿 컨테이너(Servlet Container)&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서블릿 컨테이너(Servlet Container)는 서블릿을 관리하는 컨테이너이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말 그대로 서블릿 컨테이너는 서블릿을 관리하는 컨테이너이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5FHvl/btsKXq6H7V5/x3kqbQdeSg3nXV6vkBRPm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5FHvl/btsKXq6H7V5/x3kqbQdeSg3nXV6vkBRPm1/img.png&quot; data-alt=&quot;서블릿 컨테이너(Servlet Container)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5FHvl/btsKXq6H7V5/x3kqbQdeSg3nXV6vkBRPm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5FHvl%2FbtsKXq6H7V5%2Fx3kqbQdeSg3nXV6vkBRPm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;360&quot; height=&quot;285&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서블릿 컨테이너(Servlet Container)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿을 생성했다고 해서 스스로 작동하는 것이 아니다. 누군가 서블릿을 관리해주어야 하는데 그 역할을 하는 것이 서블릿 컨테이너이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿 컨테이너는 &lt;b&gt;WAS&lt;/b&gt;(Web Application Server)로 클라이언트의 요청을 받고 응답할 수 있도록 웹 서버와 소켓으로 통신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿 컨테이너의 역할을 알아보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1️⃣ 웹 서버와 통신 지원&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서블릿 컨테이너는 서블릿과 웹 서버가 손쉽게 통신할 수 있도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소켓을 만들고 listen, accept 등의 작동을 API로 제공하여 개발자가 서블릿에 구현해야 할 비즈니스 로직만 구현할 수 있게 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2️⃣ 서블릿 생명주기 관리&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서블릿 컨테이너는 서블릿의 탄생과 죽음을 관리한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서블릿의 생명주기&lt;/b&gt;는 크게 3단계로 나눌 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;445&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0cNsd/btsKXGPo60f/glXRKKOEo4T13cU84eNK2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0cNsd/btsKXGPo60f/glXRKKOEo4T13cU84eNK2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0cNsd/btsKXGPo60f/glXRKKOEo4T13cU84eNK2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0cNsd%2FbtsKXGPo60f%2FglXRKKOEo4T13cU84eNK2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;438&quot; height=&quot;272&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;445&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서블릿 생성 및 초기화
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서블릿 컨테이너가 애플리케이션이 처음 실행되거나 서블릿이 처음 요청될 땨 &lt;b&gt;서블릿 객체를 최초 한 번만 생성&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;서블릿 객체가 생성되면 컨테이너가 &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;init()&lt;/b&gt;&lt;/span&gt; 메소드를 호출해서 서블릿 객체를 초기화한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; &lt;b&gt;init()&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메소드는 서블릿이 로드될 때 딱 한 번만 호출된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;서블릿 요청 처리&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;클라이언트의 HTTP 요청을 받을 때마다 &lt;b&gt;service()&lt;/b&gt; 메소드를 호출한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;b&gt;service()&lt;/b&gt; 메소드는 클라이언트의 요청 유형에 따라 &lt;b&gt;doGet(), doPost()&lt;/b&gt; 메소드를 호출한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;요청마다 새로운 스레드가 생성되어 처리한다.&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;즉, 서블릿 객체는 싱글톤으로 관리되지만 &lt;b&gt;요청 / 응답 데이터는 각 요청마다 독립적으로 처리&lt;/b&gt;된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;서블릿 종료&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;서블릿 컨테이너가 애플리케이션을 종료하거나 서블릿을 제거할 때 서블릿 객체를 메모리에서 해제한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;b&gt;destroy()&lt;/b&gt; 메소드를 호출한다. &lt;b&gt;init()&lt;/b&gt; 메소드와 마찬가지로 &lt;b&gt;한 번만 실행된다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;컨테이너가 정상 종료될 때 &lt;b&gt;destroy()&lt;/b&gt; 메소드가 호출된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1732606579556&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Override
public void init() throws ServletException {
    // 초기화 작업
    System.out.println(&quot;Servlet Initialized!&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1732606662505&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
    // GET 요청 처리
    response.getWriter().println(&quot;Handled GET request!&quot;);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
    // POST 요청 처리
    response.getWriter().println(&quot;Handled POST request!&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1732606845165&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Override
public void destroy() {
    // 종료 작업
    System.out.println(&quot;Servlet Destroyed!&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3️⃣ 멀티 스레드 지원 및 관리&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서블릿 컨테이너는 요청이 올 때 마다 새로운 자바 스레드를 생성하고 관리한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명했듯 요청이 올 때 마다 각각 스레드 독립적으로 처리된다. 서버가 멀티 스레드를 생성 및 운영해주기 때문에 스레드 안정성도 보장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4️⃣ 선언적 보안 관리&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서블릿 컨테이너는 개발자가 보안에 관한 내용을 서블릿이나 자바 클래스에 구현하지 않아도 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 서블릿은 클라이언트 요청을 처리하고 결과를 반환하는 자바 웹 프로그래밍 기술로 동적 웹페이지 생성 시 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 서블릿을 관리해주는 것이 서블릿 컨테이너이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고 자료&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jh2021.tistory.com/20&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jh2021.tistory.com/20&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732607275576&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;서블릿(Servlet)이 뭔지 궁금해?&quot; data-og-description=&quot;자바 웹기술 역사는 서블릿 &amp;rarr; JSP &amp;rarr; (서블릿 + JSP) MVC 패턴 &amp;rarr; 스트럿츠, 웹워크 -&amp;gt; 스프링 MVC 요런식으로 발전해왔어요. 요즘은 Spring WebFlux라는 서블릿 기반이 아닌 Web Reactive 기반을 이용하기도 &quot; data-og-host=&quot;jh2021.tistory.com&quot; data-og-source-url=&quot;https://jh2021.tistory.com/20&quot; data-og-url=&quot;https://jh2021.tistory.com/20&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bYDMsz/hyXDesJdQF/UwN0ILISE3oK8UWsdKqjx0/img.png?width=800&amp;amp;height=395&amp;amp;face=0_0_800_395,https://scrap.kakaocdn.net/dn/ekEUpc/hyXDaqjOJ2/Qu9wSIhhKLaRz4kAKqW8pK/img.png?width=800&amp;amp;height=395&amp;amp;face=0_0_800_395,https://scrap.kakaocdn.net/dn/cafQeX/hyXDjU7EQ9/UcoWuk0FuvH74kXq1CSHwk/img.png?width=1466&amp;amp;height=768&amp;amp;face=0_0_1466_768&quot;&gt;&lt;a href=&quot;https://jh2021.tistory.com/20&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jh2021.tistory.com/20&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bYDMsz/hyXDesJdQF/UwN0ILISE3oK8UWsdKqjx0/img.png?width=800&amp;amp;height=395&amp;amp;face=0_0_800_395,https://scrap.kakaocdn.net/dn/ekEUpc/hyXDaqjOJ2/Qu9wSIhhKLaRz4kAKqW8pK/img.png?width=800&amp;amp;height=395&amp;amp;face=0_0_800_395,https://scrap.kakaocdn.net/dn/cafQeX/hyXDjU7EQ9/UcoWuk0FuvH74kXq1CSHwk/img.png?width=1466&amp;amp;height=768&amp;amp;face=0_0_1466_768');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;서블릿(Servlet)이 뭔지 궁금해?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자바 웹기술 역사는 서블릿 &amp;rarr; JSP &amp;rarr; (서블릿 + JSP) MVC 패턴 &amp;rarr; 스트럿츠, 웹워크 -&amp;gt; 스프링 MVC 요런식으로 발전해왔어요. 요즘은 Spring WebFlux라는 서블릿 기반이 아닌 Web Reactive 기반을 이용하기도&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jh2021.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mangkyu.tistory.com/14&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732607281471&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[JSP] 서블릿(Servlet)이란?&quot; data-og-description=&quot;1. Servlet(서블릿) 서블릿을 한 줄로 정의하자면 아래와 같습니다. 클라이언트의 요청을 처리하고, 그 결과를 반환하는 Servlet 클래스의 구현 규칙을 지킨 자바 웹 프로그래밍 기술 간단히 말해서, &quot; data-og-host=&quot;mangkyu.tistory.com&quot; data-og-source-url=&quot;https://mangkyu.tistory.com/14&quot; data-og-url=&quot;https://mangkyu.tistory.com/14&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bx2CzB/hyXDfSz5Vi/uzq8wKIup6zvkgsHcwXWok/img.png?width=800&amp;amp;height=287&amp;amp;face=0_0_800_287,https://scrap.kakaocdn.net/dn/cJAJ8N/hyXDjOejnF/2wkDYowprbaMwZGyQZQ1y1/img.png?width=800&amp;amp;height=287&amp;amp;face=0_0_800_287,https://scrap.kakaocdn.net/dn/nZdbw/hyXDaDKJr2/RgNqvgxYYsnAPRxh4VIr21/img.jpg?width=740&amp;amp;height=330&amp;amp;face=0_0_740_330&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mangkyu.tistory.com/14&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bx2CzB/hyXDfSz5Vi/uzq8wKIup6zvkgsHcwXWok/img.png?width=800&amp;amp;height=287&amp;amp;face=0_0_800_287,https://scrap.kakaocdn.net/dn/cJAJ8N/hyXDjOejnF/2wkDYowprbaMwZGyQZQ1y1/img.png?width=800&amp;amp;height=287&amp;amp;face=0_0_800_287,https://scrap.kakaocdn.net/dn/nZdbw/hyXDaDKJr2/RgNqvgxYYsnAPRxh4VIr21/img.jpg?width=740&amp;amp;height=330&amp;amp;face=0_0_740_330');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[JSP] 서블릿(Servlet)이란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. Servlet(서블릿) 서블릿을 한 줄로 정의하자면 아래와 같습니다. 클라이언트의 요청을 처리하고, 그 결과를 반환하는 Servlet 클래스의 구현 규칙을 지킨 자바 웹 프로그래밍 기술 간단히 말해서,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mangkyu.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>spring</category>
      <category>Java</category>
      <category>Servlet</category>
      <category>Spring</category>
      <category>서블릿</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>주다애</author>
      <guid isPermaLink="true">https://lightworld.tistory.com/63</guid>
      <comments>https://lightworld.tistory.com/63#entry63comment</comments>
      <pubDate>Tue, 26 Nov 2024 16:48:11 +0900</pubDate>
    </item>
    <item>
      <title>[DB] Index란?</title>
      <link>https://lightworld.tistory.com/62</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;  Index(인덱스)&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Index(인덱스)란 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조이다. 사전의 색인과 같다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스를 사용하면 데이터베이스에서 원하는 정보(레코드, 튜플)를 빠르게 조회할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스는 튜플의 주소라고 생각하면 쉽다. (인덱스에 키를 대응시키고 튜플에 값을 대응시켜보자)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 인덱스는 정렬되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인덱스를 이루는 자료구조&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;인덱스를 구현하기 위한 자료구조로 대표적인 것은 B-Tree와 해시 테이블이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1️⃣ B(Balanced)-Tree 인덱스&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;B-Tree는 데이터베이스의 인덱싱 알고리즘 가운데 가장 먼저 도입되고 가장 일반적으로 사용되는 알고리즘으로 트리 구조를 따른다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(트리구조 : 루트 노드, 브랜치 노드, 리프 노드로 구성되는 형태)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JHcpt/btsKT0BhDeN/R3TexaIHVfQowNYmZTxHlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JHcpt/btsKT0BhDeN/R3TexaIHVfQowNYmZTxHlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JHcpt/btsKT0BhDeN/R3TexaIHVfQowNYmZTxHlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJHcpt%2FbtsKT0BhDeN%2FR3TexaIHVfQowNYmZTxHlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;685&quot; height=&quot;379&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트리의 &lt;b&gt;리프 노드&lt;/b&gt;가 실제 데이터 레코드를 찾아가기 위한 주소 값(키 값)을 가지고 있다. 모든 노드들이 데이터를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 인덱스의 키 값은 빠른 탐색을 위해 정렬되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 키 값에 대응되는 레코드는 정렬 되어 있을 수도 있고 아닐 수도 있다.(대부분 RDMS에는 레코드들이 정렬되지 않고 저장된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스는 페이지 단위로 저장된다.&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(페이지?   디스크와 메모리에 대해 데이터를 읽고 쓰는 최소의 단위로 블록이라고도 한다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레코드를 찾는 순서를 간단하게 살펴보면&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;데이터를 따라서 리프노드에 도달하면 인덱스 키에 해당하는 레코드의 PK 값이 저장되어 있다.&lt;/li&gt;
&lt;li&gt;이후 PK를 따라 리프노드에서 실제 레코드를 읽어온다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같다. 즉, 인덱스를 통해서 데이터를 조회하면 인덱스를 통해 PK를 찾고   PK를 통해 레코드를 찾는 2가지 작업이 수행되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 모든 테이블을 뒤져서 레코드를 찾는 것보다 레코드를 찾는 속도가 훨씬 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2️⃣ 해시 테이블 인덱스&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;해시 테이블은 데이터를 Key : Value 쌍으로 저장하는 자료구조로, 빠른 데이터 검색이 필요할 때 유용하다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Azc8l/btsKUHnOeTz/2gPcEKTUbRtVkKKiKtxtb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Azc8l/btsKUHnOeTz/2gPcEKTUbRtVkKKiKtxtb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Azc8l/btsKUHnOeTz/2gPcEKTUbRtVkKKiKtxtb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAzc8l%2FbtsKUHnOeTz%2F2gPcEKTUbRtVkKKiKtxtb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;541&quot; height=&quot;333&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key값을 이용해서 고유한 인덱스를 생성한 후, 그 인덱스에 저장된 값을 읽어오는 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 테이블의 시간 복잡도는 O(1)로 매우 빠른 검색을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레코드를 찾는 순서를 간단하게 살펴보면&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;검색하고자 하는 값을 넘기면&lt;/li&gt;
&lt;li&gt;해시 함수를 거쳐서 찾고자 하는 key 값이 포함된 버킷을 찾아내고&lt;/li&gt;
&lt;li&gt;버킷 안에서 실제 레코드가 저장된 위치를 찾는 순서로 검색이 이루어진다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 트리 내의 여러 노드를 거쳐서 검색하는 B-Tree에 비해서 &lt;b&gt;속도가 빠르다. &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 해시 테이블 자료구조는 &lt;b&gt;동등 비교 검색&lt;/b&gt;에 최적화 되어있다.(DB 인덱스에서 해시 테이블이 사용되는 경우도 적다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 해시 함수의 값이 1이라도 달라지면 기존 해시 값과 완전히 다른 해시 값을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 부등호 연산이 자주 사용되는 데이터베이스 검색에는 적합하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 들어보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  동등 비교인 경우&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;12345&quot;라는 key를 가진 데이터를 해시 테이블에 저장했다고 가정하자&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;해시 함수는 &quot;12345&quot;를 입력받아 특정 인덱스(예: 7번 버킷)으로 매핑한다.&lt;/li&gt;
&lt;li&gt;이제 &quot;12345&quot;를 검색할 대 해시 함수가 다시 같은 결과(7번 버킷)를 생성하므로 데이터는 곧바로 7번 버킷에서 검색된다.&lt;/li&gt;
&lt;li&gt;즉, 해시 값의 일치 여부를 통해 빠르게 동등성을 확인한다.(O(1))&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  부등호 연산인 경우&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;라는 키가 각각 다른 해시 값으로 변환되어 해시 테이블에 저장되어 있다고 가정하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key &quot;1&quot;   해시 값: 12AB34&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key &quot;2&quot;   해시 값: CDEF78&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;key &quot;3&quot;   해시 값:&lt;span&gt; 9A1B2C&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;..&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7; text-align: start;&quot;&gt;여기서 볼 수 있듯 key 값이 &quot;1&quot;에서 &quot;2&quot;로 단 1 증가했음에도 해시 값은 전혀 다른 값으로 변한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7; text-align: start;&quot;&gt;해시 함수는 입력 간의 순서와 크기는 고려하지 않고 고유한 해시 값을 생성하기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이 때 Key &amp;gt; 3 이라는 조건으로 검색한다고 하면&lt;/li&gt;
&lt;li&gt;해시 함수는 key를 고유한 해시 값으로 변환하므로 키의 크기나 순서를 알 수 없다. 또한 key가 정렬되어 있지도 않다.&lt;/li&gt;
&lt;li&gt;즉, 해시 테이블의 모든 데이터를 하나씩 확인해야 한다.(O(N))&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3️⃣ B+-Tree 인덱스&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;B+-Tree 인덱스는 자식 노드가 2개 이상인 B-Tree를 개선한 자료구조이다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7L7k4/btsKV76GYWG/xiKp6EiCiPtFHkTu3rkjl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7L7k4/btsKV76GYWG/xiKp6EiCiPtFHkTu3rkjl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7L7k4/btsKV76GYWG/xiKp6EiCiPtFHkTu3rkjl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7L7k4%2FbtsKV76GYWG%2FxiKp6EiCiPtFHkTu3rkjl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;392&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B+-Tree 인덱스는 &lt;b&gt;리프 노드만 인덱스와 함께 데이터를 가지고 있으며&lt;/b&gt; 다른 노드들은 인덱스만 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;리프 노드들은 LinkedList로 연결&lt;/b&gt;되어 있어서 부등호를 이용한 순차 검색에 용이하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL이 B+-Tree 구조를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인덱스 스캔 방식&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;MySQL의 인덱스 스캔 방식에는 크게 인덱스 레인지 스캔, 인덱스 풀 스캔, 루스 인덱스 스캔이 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1️⃣ 인덱스 레인지 스캔&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;인덱스 레인지 스캔은 검색할 인덱스 &lt;b&gt;범위가 결정된 경우&lt;/b&gt; 사용되며 가장 빠른 스캔 방식이다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1732503931540&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT * FROM EMPLOYEE WHERE NAME BETWEEN &quot;Lemon&quot; AND &quot;Mango&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 쿼리가 날아갔다고 해보자. 그리고 Employee 테이블의 name 칼럼에 인덱스가 걸려있다고 하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리의 실행 순서는&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인덱스 탐색 : 인덱스의 조건을 만족하는 값이 저장된 시작 리프 노드를 찾는다.&lt;/li&gt;
&lt;li&gt;인덱스 스캔 : 시작 리프 노드부터 필요한 만큼 인덱스를 순서대로 읽는다.&lt;/li&gt;
&lt;li&gt;랜덤 I/O : 읽어들인 인덱스와 PK를 사용해서 최종 레코드를 읽는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2️⃣ 인덱스 풀 스캔&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;인덱스 풀 스캔은 처음부터 끝까지 페이지를 이동하며 모든 인덱스를 읽는 스캔 방식이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 인덱스가 A, B, C 순서로 되어있고 쿼리의 조건절이 B 또는 C 칼럼으로 검색되면 이 때 인덱스 풀 스캔이 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 풀 스캔은 인덱스를 사용한다고 하지 않는다. 하지만 모든 데이터 레코드들을 읽는 풀 테이블 스캔보다는 인덱스만 스캔하는 인덱스 풀 스캔이 분명히 성능이 낫기 때문에 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3️⃣ 루스 인덱스 스캔&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;루스 인덱스 스캔은 인덱스를 듬성듬성 읽는 스캔 방식이다.(위의 두 스캔 방식은 타이트 인덱스 스캔으로 분류)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간에 필요하지 않은 인덱스 키 값은 무시하고 넘어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Group by, max(), min() 함수에 대해 최적화 하는 경우에 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고 자료&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/286&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mangkyu.tistory.com/286&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732507299978&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[MySQL] B-Tree로 인덱스(Index)에 대해 쉽고 완벽하게 이해하기&quot; data-og-description=&quot;인덱스를 저장하는 방식(또는 알고리즘)에 따라 B-Tree 인덱스, Hash 인덱스, Fractal 인덱스 등으로 나눌 수 있습니다. 일반적으로 B-Tree 구조가 사용되기 때문에 B-Tree 인덱스를 통해 인덱스의 동작 &quot; data-og-host=&quot;mangkyu.tistory.com&quot; data-og-source-url=&quot;https://mangkyu.tistory.com/286&quot; data-og-url=&quot;https://mangkyu.tistory.com/286&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/box1w4/hyXDbWFs2w/qhOK0kD947F8A8mDBNKG0k/img.png?width=800&amp;amp;height=210&amp;amp;face=0_0_800_210,https://scrap.kakaocdn.net/dn/b7AAJj/hyXDdUt6dg/3vSug1Scu2HlRyYAcoyb00/img.png?width=800&amp;amp;height=210&amp;amp;face=0_0_800_210,https://scrap.kakaocdn.net/dn/bkQEAm/hyXDbCoyyE/Q6LLdFC4QT2KvTl3mAQPx0/img.png?width=1424&amp;amp;height=849&amp;amp;face=0_0_1424_849&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/286&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mangkyu.tistory.com/286&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/box1w4/hyXDbWFs2w/qhOK0kD947F8A8mDBNKG0k/img.png?width=800&amp;amp;height=210&amp;amp;face=0_0_800_210,https://scrap.kakaocdn.net/dn/b7AAJj/hyXDdUt6dg/3vSug1Scu2HlRyYAcoyb00/img.png?width=800&amp;amp;height=210&amp;amp;face=0_0_800_210,https://scrap.kakaocdn.net/dn/bkQEAm/hyXDbCoyyE/Q6LLdFC4QT2KvTl3mAQPx0/img.png?width=1424&amp;amp;height=849&amp;amp;face=0_0_1424_849');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[MySQL] B-Tree로 인덱스(Index)에 대해 쉽고 완벽하게 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;인덱스를 저장하는 방식(또는 알고리즘)에 따라 B-Tree 인덱스, Hash 인덱스, Fractal 인덱스 등으로 나눌 수 있습니다. 일반적으로 B-Tree 구조가 사용되기 때문에 B-Tree 인덱스를 통해 인덱스의 동작&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mangkyu.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/96&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mangkyu.tistory.com/96&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732507310473&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Database] 인덱스(index)란?&quot; data-og-description=&quot;1. 인덱스(Index)란? [ 인덱스(index)란? ] 인덱스란 추가적인 쓰기 작업과 저장 공간을 활용하여 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조이다. 만약 우리가 책에서 원하는 내&quot; data-og-host=&quot;mangkyu.tistory.com&quot; data-og-source-url=&quot;https://mangkyu.tistory.com/96&quot; data-og-url=&quot;https://mangkyu.tistory.com/96&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Eqavx/hyXDdfS5S5/sM93nAvaqneMWFkjkHnQRK/img.png?width=700&amp;amp;height=429&amp;amp;face=0_0_700_429,https://scrap.kakaocdn.net/dn/egTofe/hyXDi2yP1c/5NiHsIaPqtr2QyymvkvUn0/img.png?width=700&amp;amp;height=429&amp;amp;face=0_0_700_429,https://scrap.kakaocdn.net/dn/V6k0r/hyXDlkGy17/qtxko3Etv23JjcgfVmfoY1/img.png?width=936&amp;amp;height=680&amp;amp;face=0_0_936_680&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/96&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mangkyu.tistory.com/96&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Eqavx/hyXDdfS5S5/sM93nAvaqneMWFkjkHnQRK/img.png?width=700&amp;amp;height=429&amp;amp;face=0_0_700_429,https://scrap.kakaocdn.net/dn/egTofe/hyXDi2yP1c/5NiHsIaPqtr2QyymvkvUn0/img.png?width=700&amp;amp;height=429&amp;amp;face=0_0_700_429,https://scrap.kakaocdn.net/dn/V6k0r/hyXDlkGy17/qtxko3Etv23JjcgfVmfoY1/img.png?width=936&amp;amp;height=680&amp;amp;face=0_0_936_680');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Database] 인덱스(index)란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. 인덱스(Index)란? [ 인덱스(index)란? ] 인덱스란 추가적인 쓰기 작업과 저장 공간을 활용하여 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조이다. 만약 우리가 책에서 원하는 내&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mangkyu.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>cs</category>
      <category>db</category>
      <category>index</category>
      <category>오블완</category>
      <category>인덱스</category>
      <category>티스토리챌린지</category>
      <author>주다애</author>
      <guid isPermaLink="true">https://lightworld.tistory.com/62</guid>
      <comments>https://lightworld.tistory.com/62#entry62comment</comments>
      <pubDate>Mon, 25 Nov 2024 13:02:06 +0900</pubDate>
    </item>
  </channel>
</rss>