<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발 잘하고 싶어서 시작한 블로그</title>
    <link>https://055055.tistory.com/</link>
    <description>안녕하세요. 주로 개인 기록용으로 블로그를 작성합니다.
잘못된 내용이나, 고쳐야할 부분이 있다면 댓글 부탁드립니다. 
감사합니다.  </description>
    <language>ko</language>
    <pubDate>Tue, 19 May 2026 07:29:35 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>055055</managingEditor>
    <image>
      <title>개발 잘하고 싶어서 시작한 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/3007493/attach/3bb03445ed154ac5ae1b51fbe98eb9c5</url>
      <link>https://055055.tistory.com</link>
    </image>
    <item>
      <title>Gradle Wrapper</title>
      <link>https://055055.tistory.com/124</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gradle_logo.png&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjVGRK/btrLiV6fwQ6/lKMM0y7hoeF4b4aNHzpAlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjVGRK/btrLiV6fwQ6/lKMM0y7hoeF4b4aNHzpAlk/img.png&quot; data-alt=&quot;https://ko.wikipedia.org/wiki/Gradle&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjVGRK/btrLiV6fwQ6/lKMM0y7hoeF4b4aNHzpAlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjVGRK%2FbtrLiV6fwQ6%2FlKMM0y7hoeF4b4aNHzpAlk%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;876&quot; height=&quot;306&quot; data-filename=&quot;Gradle_logo.png&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://ko.wikipedia.org/wiki/Gradle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Gradle Wrapper&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;The recommended way to execute any Gradle build is with the help of the Gradle Wrapper (in short just &amp;ldquo;Wrapper&amp;rdquo;). The Wrapper is a script that invokes a declared version of Gradle, downloading it beforehand if necessary. As a result, developers can get up and running with a Gradle project quickly without having to follow manual installation processes saving your company time and money.&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;그레이들 래퍼(Gradle Wrapper)를 이용하면 개발자들이 직접 그레이들을 설치하지 않아도 빌드할 수 있다.&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-filename=&quot;스크린샷 2022-09-02 오후 10.10.14.png&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;445&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qE2mz/btrLgq8hCOh/sQ5aIavSXy5fvbmrPVHfy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qE2mz/btrLgq8hCOh/sQ5aIavSXy5fvbmrPVHfy0/img.png&quot; data-alt=&quot;https://docs.gradle.org/current/userguide/gradle_wrapper.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qE2mz/btrLgq8hCOh/sQ5aIavSXy5fvbmrPVHfy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqE2mz%2FbtrLgq8hCOh%2FsQ5aIavSXy5fvbmrPVHfy0%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;910&quot; height=&quot;445&quot; data-filename=&quot;스크린샷 2022-09-02 오후 10.10.14.png&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;445&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.gradle.org/current/userguide/gradle_wrapper.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서브버전(Subversion)이나 깃(git) 같은 저장소에서 프로젝트를 체크아웃 한다.&lt;/li&gt;
&lt;li&gt;gradlew 명령을 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Gradle Wrawpper 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 대부분 intellij와 같은 IDE를 사용하여 프로젝트를 손쉽게 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;intellij에서 기본적으로 gradle을 빌드툴로 선택시 아래와 같이 이미 gradle wrapper가 추가되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-02 오후 10.15.17.png&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;411&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GXzzc/btrLiqSWz8y/SVb9rgYcUoVXk5GT8OthEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GXzzc/btrLiqSWz8y/SVb9rgYcUoVXk5GT8OthEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GXzzc/btrLiqSWz8y/SVb9rgYcUoVXk5GT8OthEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGXzzc%2FbtrLiqSWz8y%2FSVb9rgYcUoVXk5GT8OthEK%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;399&quot; height=&quot;411&quot; data-filename=&quot;스크린샷 2022-09-02 오후 10.15.17.png&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;411&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 hello task를 추가한후 터미널에서 ./gradlew hello 를 입력하면 손쉽게 작동하는 걸 확인할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mac: ./gradlew task&amp;nbsp; &amp;nbsp; window: gradlew task&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_스크린샷 2022-09-02 오후 10.18.45.png&quot; data-origin-width=&quot;796&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HUrtL/btrLh9cSy8N/qg4cxQjzETkAcfPCxSh4m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HUrtL/btrLh9cSy8N/qg4cxQjzETkAcfPCxSh4m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HUrtL/btrLh9cSy8N/qg4cxQjzETkAcfPCxSh4m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHUrtL%2FbtrLh9cSy8N%2Fqg4cxQjzETkAcfPCxSh4m1%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;796&quot; height=&quot;674&quot; data-filename=&quot;edited_스크린샷 2022-09-02 오후 10.18.45.png&quot; data-origin-width=&quot;796&quot; data-origin-height=&quot;674&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬에서 Gradle을 설치한 경우에는 아래와 같이 wrapper task를 실행시킨다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;gradle wrapper
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-02 오후 10.21.30.png&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;199&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ggkv2/btrLhJyP7zO/7diHiBBmPk3GtKDkmFw1h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ggkv2/btrLhJyP7zO/7diHiBBmPk3GtKDkmFw1h1/img.png&quot; data-alt=&quot;https://docs.gradle.org/current/userguide/gradle_wrapper.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ggkv2/btrLhJyP7zO/7diHiBBmPk3GtKDkmFw1h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGgkv2%2FbtrLhJyP7zO%2F7diHiBBmPk3GtKDkmFw1h1%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;818&quot; height=&quot;199&quot; data-filename=&quot;스크린샷 2022-09-02 오후 10.21.30.png&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;199&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.gradle.org/current/userguide/gradle_wrapper.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;gradle wrapper 이후 생성된 파일을 git과 같은 곳에 올려두어서 사용하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-02 오후 10.21.52.png&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEk4QM/btrLhI7MPEw/kqxGAFMv1LJoLs3QfQAah0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEk4QM/btrLhI7MPEw/kqxGAFMv1LJoLs3QfQAah0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEk4QM/btrLhI7MPEw/kqxGAFMv1LJoLs3QfQAah0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEk4QM%2FbtrLhI7MPEw%2FkqxGAFMv1LJoLs3QfQAah0%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;768&quot; height=&quot;362&quot; data-filename=&quot;스크린샷 2022-09-02 오후 10.21.52.png&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;gradle-wrapper.jar&lt;br /&gt;The Wrapper JAR file containing code for downloading the Gradle distribution.&lt;br /&gt;&lt;br /&gt;gradle-wrapper.properties&lt;br /&gt;A properties file responsible for configuring the Wrapper runtime behavior e.g. the Gradle version compatible with this version. Note that more generic settings, like&amp;nbsp;configuring the Wrapper to use a proxy, need to go into a&amp;nbsp;different file.&lt;br /&gt;&lt;br /&gt;gradlew,&amp;nbsp;gradlew.bat&lt;br /&gt;A shell script and a Windows batch script for executing the build with the Wrapper.&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;gradle wrapper 이후 생성된 파일 내역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gradle/wrapper/gradle-wrapper.jar&lt;/td&gt;
&lt;td&gt;그레이들 래퍼의 부트스트랩&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gradle/wrapper/gradle-wrapper.properties&lt;/td&gt;
&lt;td&gt;그레이들 래퍼의 설정 파일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gradlew&lt;/td&gt;
&lt;td&gt;그레이들 래퍼 실행용 셀 스크립트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gradlew.bat&lt;/td&gt;
&lt;td&gt;그레이들 래퍼 실행용 배치(윈도우용)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그레이들 래퍼의 장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용할 그레이들 버전을 고정할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그레이들의 버전 차이에 따른 빌드 오류를 방지할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;젠킨스 같은 CI툴 실행 환경에 그레이들을 설치하지 않아도 된다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CI 환경을 구축하는 데 드는 수고를 덜 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://search.naver.com/search.naver?where=nexearch&amp;amp;sm=top_hty&amp;amp;fbm=1&amp;amp;ie=utf8&amp;amp;query=gradle+%EC%B2%A0%EC%A0%80%EC%9E%85%EB%AC%B8&quot;&gt;Gradle 철저 입문&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.gradle.org/&quot;&gt;그레이들 공식 사이트&lt;/a&gt;&lt;/p&gt;</description>
      <category>build/gradle</category>
      <category>gradle</category>
      <category>gradle wrapper</category>
      <category>gradle wrapper 장점</category>
      <category>gradle wrapper란</category>
      <category>gradlew</category>
      <category>gradlew 란</category>
      <category>gradlew 장점</category>
      <category>gradlew.bat</category>
      <category>그레이들</category>
      <category>그레이들 래퍼</category>
      <author>055055</author>
      <guid isPermaLink="true">https://055055.tistory.com/124</guid>
      <comments>https://055055.tistory.com/124#entry124comment</comments>
      <pubDate>Sat, 3 Sep 2022 10:29:29 +0900</pubDate>
    </item>
    <item>
      <title>Gradle (그레이들)이란</title>
      <link>https://055055.tistory.com/123</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gradle_logo.png&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TxjiE/btrLgu3JUjh/OHopQEeYQPKMKib7TZ5qfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TxjiE/btrLgu3JUjh/OHopQEeYQPKMKib7TZ5qfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TxjiE/btrLgu3JUjh/OHopQEeYQPKMKib7TZ5qfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTxjiE%2FbtrLgu3JUjh%2FOHopQEeYQPKMKib7TZ5qfk%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;876&quot; height=&quot;306&quot; data-filename=&quot;Gradle_logo.png&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Gradle&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Overview&lt;br /&gt;Gradle is an open-source build automation tool that is designed to be flexible enough to build almost any type of software. The following is a high-level overview of some of its most important features:&lt;br /&gt;High performance, JVM foundation, Conventions, Extensibility, IDE support... etc&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;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간단하게 빌드 스크립트 작성&lt;/li&gt;
&lt;li&gt;기존 자원을 최대한 활용하며 단계적으로 그레이들 이전 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앤트, 메이븐 같은 기존 툴의 기능과 시스템 활용 가능(앤트 테스크, 앤트 빌드 스크립트, 메이븐 저장소)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;빌드툴이란?&lt;/h3&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소스코드(.java)를 컴파일해서 클래스 파일(.class)을 생성&lt;/li&gt;
&lt;li&gt;코딩 규약에 맞게 작성했는지 확인&lt;/li&gt;
&lt;li&gt;코드를 정적으로 해석&lt;/li&gt;
&lt;li&gt;테스트를 하고 테스트 결과나 커버리지 측정 결과를 리포트로 출력&lt;/li&gt;
&lt;li&gt;javadoc과 같은 문서 작성&lt;/li&gt;
&lt;li&gt;클래스 파일과 리소스 파일을 패키징해서 압축 파일 생성(.jar or .war)&lt;/li&gt;
&lt;li&gt;압축 파일을 테스트 환경이나 스테이징 환경에 배포&lt;/li&gt;
&lt;li&gt;압축 파일을 저장소에 등록&lt;/li&gt;
&lt;/ul&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;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수작업: 컴파일 &amp;gt; 테스트 &amp;gt; 패키징 &amp;gt; 배포를 수작업으로 진행하여 실수 및 시간과 수고가 많이 든다.&lt;/li&gt;
&lt;li&gt;변경 사항이 반영되지 않은 메뉴얼&lt;/li&gt;
&lt;li&gt;환경 의존(local/dev/prod)&lt;/li&gt;
&lt;li&gt;무한 라이브러리 지옥 - 라이브러리 의존 관계&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Gradle 개요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그레이들 웹사이트에서는 그레이들을 &lt;b&gt;Build Automation Evolved 즉 진화된 빌드 자동화라고 소개한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그레이들은 빌드 자동화 뿐만 아니라 테스트 실행, 결과물 공개, 각종 소프트웨어 개발 태스크 자동화, 정적 웹 사이트 생성, 문서 생성과 같은 소프트웨어 개발 이외의 프로젝트 자동화에도 적용할 수 있다.&lt;/li&gt;
&lt;li&gt;그레이들은 앤트의 유연성, 메이븐의 의존관계 관리와 규칙 기반 등 기존 빌드 툴의 장점을 가져왔으며, 그루비 DSL을 도입해서 효율적인 빌드를 실현했다.&lt;/li&gt;
&lt;/ul&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확장 가능한 그레이들 빌드 언어(DSL) 제공&lt;/li&gt;
&lt;li&gt;빌드 분할이나 공통 컴포넌트 추출 등을 체계화 하기 쉬움&lt;/li&gt;
&lt;li&gt;IDE와 연계해서 그레이들을 외부에서 제어할 수 있는 API 제공&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;/li&gt;
&lt;li&gt;빌드 스크립트 작성 언어로 그루비 이용&lt;/li&gt;
&lt;li&gt;그레이들 래퍼(wrapper)를 이용해서 그레이들이 설치되지 않은 환경에서도 빌드 가능&lt;/li&gt;
&lt;li&gt;호환성 배려&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Gradle만의 장점 (왜 Gradle이어야만 하는지)&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. 빌드 스크립트 생산성이 높다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;규칙 기반 필드 접근법 (규칙에 따라 프로젝트 구조를 만들면 빌드 스크립트 내용을 크게 줄일 수 있다.)&lt;/li&gt;
&lt;li&gt;JVM 언어인 그루비 사용&lt;/li&gt;
&lt;li&gt;자체 DSL 제공&lt;/li&gt;
&lt;li&gt;크로스 플랫폼 대응 (특정 플랫폼에 종속적이지 않다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 빌드 순서를 제어하기 쉽다.&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-filename=&quot;스크린샷 2022-09-02 오후 9.35.49.png&quot; data-origin-width=&quot;838&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9vQf3/btrLhgjmrju/BUe2MLqpbY8bQyfDragaEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9vQf3/btrLhgjmrju/BUe2MLqpbY8bQyfDragaEK/img.png&quot; data-alt=&quot;https://docs.gradle.org/current/userguide/what_is_gradle.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9vQf3/btrLhgjmrju/BUe2MLqpbY8bQyfDragaEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9vQf3%2FbtrLhgjmrju%2FBUe2MLqpbY8bQyfDragaEK%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;624&quot; height=&quot;341&quot; data-filename=&quot;스크린샷 2022-09-02 오후 9.35.49.png&quot; data-origin-width=&quot;838&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.gradle.org/current/userguide/what_is_gradle.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메이븐처럼 빌드 순서가 정해져 있지 않다.&lt;/li&gt;
&lt;li&gt;플러그인을 사용하여 태스크 의존관계의 기본 구성을 할 수 있다. (빌드 스크립트의 복잡성 해결)빌드 순서를 제어하기 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 멀티 프로젝트에 대응한다.&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-filename=&quot;스크린샷 2022-09-02 오후 9.49.37.png&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZHIry/btrLhtpiSKI/Ld3tXDSy48sNfHtOdbEsVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZHIry/btrLhtpiSKI/Ld3tXDSy48sNfHtOdbEsVK/img.png&quot; data-alt=&quot;https://docs.gradle.org/current/userguide/intro_multi_project_builds.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZHIry/btrLhtpiSKI/Ld3tXDSy48sNfHtOdbEsVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZHIry%2FbtrLhtpiSKI%2FLd3tXDSy48sNfHtOdbEsVK%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;777&quot; height=&quot;386&quot; data-filename=&quot;스크린샷 2022-09-02 오후 9.49.37.png&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.gradle.org/current/userguide/intro_multi_project_builds.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 컴포넌트로 만들기 쉽다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인을 개발하려면 어느 정도 지식과 노력이 필요하지만, 빌드 스크립트의 공통 처리를 추출해서 메서드나 클래스로 만드는 정도라면 그렇게 어렵지 않다. 그레이들의 장점은 이렇게 컴포넌트화 수준을 단계적으로 향상할 수 있다는 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공통 기능을 컴포넌트로 만들어서 재사용하는데 비용이 적다&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;빌드 스크립트에서 메서드나 클래스 추출&lt;/li&gt;
&lt;li&gt;빌드 스크립트의 분할과 재사용(apply from이용)&lt;/li&gt;
&lt;li&gt;프로젝트에서만 사용할 수 있는 확장 모듈(buildSrc 프로젝트)&lt;/li&gt;
&lt;li&gt;여러 프로젝트에서 범용적으로 재사용할 수 있는 라이브러리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 별도로 설치할 필요가 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앤트나 메이븐 사용시 사용자 환경에 설치해야 하는 불편함으로 관리가 되지 않는 점이있다. 그레이들은 그레이들 래퍼(wrapper)라는 구조를 제공한다. 그레이들 래퍼는 프로젝트 안에 그레이들의 부트스트랩(bootstrap)을 심어서 지정한 버전의 그레이들을 자동으로 설치해주는 기능이다.&lt;/li&gt;
&lt;li&gt;그레이들 래퍼 태스크를 실행해서 부트스트랩을 생성하기만 하면된다. gradlew 명령을 실행하면 그레이들의 바이너리가 다운되며 빌드가 실행한다. 이를 통해 젠킨스 같은 CI 서버에서 빌드할 때 편리하게 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 호환성을 최대한 배려 한다.(그레이들 방침)&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 기능을 갑자기 사용할 수 없게 되는 변경은 하지 않는다.&lt;/li&gt;
&lt;li&gt;기능을 제거해야 한다면 장래에 폐지될 가능성이 있음을 명시하고 단계적 제거&lt;/li&gt;
&lt;li&gt;신기능은 피드백을 충분히 받아서 안정화한 후에 추가&lt;/li&gt;
&lt;/ul&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;h2 data-ke-size=&quot;size26&quot;&gt;출처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Gradle 철저 입문&quot; href=&quot;https://search.naver.com/search.naver?where=nexearch&amp;amp;sm=top_hty&amp;amp;fbm=1&amp;amp;ie=utf8&amp;amp;query=gradle+%EC%B2%A0%EC%A0%80%EC%9E%85%EB%AC%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Gradle 철저 입문&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.gradle.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;그레이들 공식 사이트&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>build/gradle</category>
      <category>build tool</category>
      <category>buil란</category>
      <category>gradle</category>
      <category>gradle build tool</category>
      <category>gradle 장점</category>
      <category>gradle 철저 입문</category>
      <category>gradle 특징</category>
      <category>그레이들</category>
      <category>그레이들 장점</category>
      <category>그레이들 특징</category>
      <author>055055</author>
      <guid isPermaLink="true">https://055055.tistory.com/123</guid>
      <comments>https://055055.tistory.com/123#entry123comment</comments>
      <pubDate>Fri, 2 Sep 2022 22:00:22 +0900</pubDate>
    </item>
    <item>
      <title>[EffectiveJava 3/E] 2장 객체 생성과 파괴 - 아이템4 인스턴스화를 막으려거든 private 생성자를 사용하라</title>
      <link>https://055055.tistory.com/122</link>
      <description>&lt;h1&gt;인스턴스화를 막으려거든 private 생성자를 사용하라&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 멤버만 담은 유틸리티 클래스는 인스턴스로 만들어 쓰려고 설계한 것이 아니다. 하지만 생성자를 명시하지 않으면 컴파일러가 자동으로 기본 생성자를 만들어 준다.&lt;br /&gt;실제로 공개된 API들에서도 이처럼 의도치 않게 인스턴스화할 수 있게 된 클래스가 종종 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인스턴스화를 막는 법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;private 생성자를 추가하면 클래스의 인스턴스화를 막을 수 있다.&lt;/b&gt; (컴파일러가 기본 생성자를 만드는 경우는 오직 명시된 생성자가 없을 때 뿐이니..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;추상 클래스로 만들어서 인스턴스화를 막을 수 없다. 하위 클래스를 만들어 인스턴스화 하면 그만이다..&lt;br /&gt;또한, 이를 상속해서 쓰라는 뜻으로 오해할 수 있으니.. 그렇다면 더욱 큰일이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class UtilityClass {

    private UtilityClass(){
        //기본 생성자가 만들어지는 것을 막는다(인스턴스화 방지용)
        throw new AssertionError();
    }

    public static void main(String[] args) {
        UtilityClass utilityClass = new UtilityClass(); // assertionError
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이코드는 어떤 환경에서도 클래스가 인스턴스화되는 것을 막아준다.&lt;br /&gt;이 방식은 상속을 불가능하게 하는 효과도 있다 모든 생성자는 명시적이든 묵시적이든 상위 클래스의 생성자를 호출하게 되는데&lt;br /&gt;이를 private으로 선언했으니 하위 클래스가 상위 클래스의 생성자에 접근할 길이 막혀버린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://search.shopping.naver.com/book/catalog/32436239326?cat_id=50010920&amp;amp;frm=PBOKMOD&amp;amp;query=%08%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C+%EC%9E%90%EB%B0%94&amp;amp;NaPm=ct%3Dl6z138ns%7Cci%3Db0f99d2f2a318dd7ab9dac8495e63c0076fddd86%7Ctr%3Dboknx%7Csn%3D95694%7Chk%3D71936725f1d33db581c3d8c6a56703ade1a7c790&quot;&gt;이펙티브자바&lt;/a&gt;&lt;/p&gt;</description>
      <category>jvm언어관련/effective-java</category>
      <category>effectiva java</category>
      <category>effective java item4</category>
      <category>private constructor</category>
      <category>private 생성자</category>
      <category>utill class</category>
      <category>이펙티브 자바 아이템4</category>
      <category>이펙티브자바</category>
      <category>인스턴스화 방지</category>
      <author>055055</author>
      <guid isPermaLink="true">https://055055.tistory.com/122</guid>
      <comments>https://055055.tistory.com/122#entry122comment</comments>
      <pubDate>Tue, 30 Aug 2022 21:52:24 +0900</pubDate>
    </item>
    <item>
      <title>[몽고DB 완벽 가이드 3판] 도큐먼트 생성, 갱신, 삭제</title>
      <link>https://055055.tistory.com/121</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;다음을 포함한 데이터베이스에서의 기본적인 데이터 입출력을 다룬다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컬렉션에 새 도큐먼트 추가하기&lt;/li&gt;
&lt;li&gt;컬렉셔네서 도큐먼트 삭제하기&lt;/li&gt;
&lt;li&gt;기존 도큐먼트 갱신하기&lt;/li&gt;
&lt;li&gt;연산(operation)을 수행할 때 안전성과 속도 중 맞는 수준 선택하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.1 도큐먼트 삽입&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삽입은 몽고DB에 데이터를 추가하는 기본 방법이다. 컬렉션의 insertOne 메서드를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661767224459&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.movies.insertOne({&quot;title&quot;: &quot;STand by Me&quot;})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1.1 insertMany&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 도큐먼트를 컬렉션에 삽입하려면 insertMany로 도큐먼트 배열을 데이터베이스에 전달한다. 코드가 삽입된 각 도큐먼트에 대해 데이터베이스로 왕복하지 않고 도큐먼트를 대량 삽입(bulk insert)하므로 훨씬 효율적이다.&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;insertMany는 여러 도큐먼트를 단일 컬렉션에 삽입할 때 유용하다. 몽고DB의 현재 버전에서는 48메가바이트까지의 크기 제한을 두고 있다.&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;insertMany의 두번째 매개변수로 옵션 도큐먼트를 지정할 수 있다. 도큐먼트가 제공된 순서대로 삽입되도록 옵션 도큐먼트에 &quot;orderId&quot;키에 true를 지정한다. 순서가 지정되지 않았다면 정렬된 삽입이 기본값이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1.2 삽입 유효성 검사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유효하지 않은 데이터가 입력되지 않도록 _id필드와 용량(16메가바이트) 등의 최소한의 검사를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1.3 삽입&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고DB 3.0 이전에서는 insert만 사용했으나 3.2의 경우 insertOne과 insertMany 등의 API를 지원한다. 이전 버전과의 호환성 때문에 insert를 지원하나 가급적 insertOne과 insertMany를 사용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.2 도큐먼트 삭제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 삭제에는 deleteOne과 deleteMany를 제공한다. 두 메서드 모두 필터 도큐먼트를 첫 번째 매개변수로 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661767806235&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.movies.deleteOne({&quot;_id&quot;:4})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬렉션 내 여러 도큐먼트와 일치하는 필터를 지정할 수도 있는데 이때 deleteOne은 필터와 일치하는 첫 번째 도큐먼트를 삭제한다.&amp;nbsp; 어떤 도큐먼트가 먼저 발견되는지는 도큐먼트가 삽입되는 순서, 갱신, 인덱스 지정등의 요인에 따라 달라진다.&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;몽고DB 3.0 이전 버전에는 도큐먼트를 삭제하는 데 remove를 주로 사용했다. remove는 이전 버전과의 호환성을 위해 지원되지만 deleteOne과 deleteMany를 사용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2.1 drop&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 컬렉션을 삭제하려면 다음과 같이 drop을 사용하는 편이 delete보다 빠르다.&lt;/p&gt;
&lt;pre id=&quot;code_1661767988112&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.movies.drop()&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;이전에 백업된 데이터를 복원하는 방법 외에 delete 또는 drop 작업을 취소하거나 삭제된 도큐먼트를 복구한느 방법은 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백업및 복원에 대한 내용은 23장에 다시 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.3 도큐먼트 갱신&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;updateOne, updateMany, replaceOne과 같은 갱신 메서드를 사용해 변경한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;updateOne과 updateMany는 필터 도큐먼트를 첫 번째 매개변수로, 변경 사항을 설명하는 수정자 도큐먼트를 두 번째 매개변수로 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;replaceOne도 첫 번째 매개변수로 필터를 사용하지만 두 번째 매개변수는 필터와 일치하는 도큐먼트를 교체할 도큐먼트다.&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;h3 data-ke-size=&quot;size23&quot;&gt;3.3.1 도큐먼트 치환&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;replaceOne은 도큐먼트를 새로운 것으로 완전히 치환한다. 이는 대대적인 스키마 마이그레이션에 유용하다.&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;h3 data-ke-size=&quot;size23&quot;&gt;3.3.2 갱신 연산자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 도큐먼트의 특정 부분만 갱신하는 경우가 많다. 부분 갱신에는 원자적 &lt;b&gt;갱신 연산자&lt;/b&gt;를 사용한다. 갱신 연산자는 키를 변경, 추가, 제거하고, 심지어 배열과 내장 도큐먼트를 조작하는 복잡한 갱신 연산을 지정하는 데 사용하는 특수키다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;$set 제한자 사용하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$set은 필드 값을 설정한다. 필드가 존재하지 않으면 새 필드가 생성된다. 이 기능은 스키마를 갱신하거나 사용자 정의 키를 추가할 때 편리하다.&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;$set은 키의 데이터형도 변경할 수 있다. 기존 문자열을 배열로도 변경 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1661768671369&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.users.updateOne({&quot;name&quot;: &quot;joe&quot;}, {&quot;$set&quot;: {&quot;favorite book&quot; : [&quot;....&quot;, &quot;blabla&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;$unset을 이용하여 키와 값을 모두 제거할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1661768723816&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.uesrs.updateOne({&quot;name&quot;: &quot;joe&quot;}, {&quot;$unset: {&quot;favorite book&quot;: 1}})&lt;/code&gt;&lt;/pre&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;$inc 연산자는 이미 존재하는 키의 값을 변경하거나 새 키를 생성하는 데 사용한다. 분석, 분위기, 투표 등과 같이 자주 변하는 수치 값을 갱신하는 데 매우 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$inc는 $set과 비슷하지만 숫자를 증감하기 위해 설계 됐다. int, long, double, decimal 타입 값에만 사용할 수 있다. null, 불리언, 문자열로 나타낸 숫자와 같이 여러 언어에서 숫자로 자동 변환되는 데이터형의 값에는 사용할 수 없다.&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;요소 추가하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$push는 배열이 이미 존재하면 배열 끝에 요소를 추가하고, 존재 하지 않으면 새로운 배열을 생성한다.&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;$push에 $each 제한자를 사용하면 작업 한 번으로 값을 여러 개 추가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 배열을 특정 길이로 늘이려면 $slice를 $push와 결합해 사용한다. 배열이 특정 크기 이상으로 늘어나지 않게 하고 효과적으로 'topN' 목록을 만들 수 있다.&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;$slice나 $sort를 배열상에서 $push와 함께 쓰려면 반드시 $each도 포함해야 한다.&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;p data-ke-size=&quot;size16&quot;&gt;특정 값이 배열에 존재하지 않을 때 해당 값을 추가하면서, 배열을 집합처럼 처리하려면 쿼리 도큐먼트에 $ne를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$addToSet은 $ne가 작동하지 않을 때나 $addToSet을 사용하면 무슨 일이 일어났는지 더 잘 알수 있을 때 유용하다.&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;고유한 값을 여러 개 추가하려면 $addToSet과 $each를 결합해 사용한다. 이는 $ne와 $push조합으로는 할 수 없는 작업이다.&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;p data-ke-size=&quot;size16&quot;&gt;배열을 큐나 스택처럼 사용하려면 배열의 양쪽 끝에서 요소를 제거하는 $pop을 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661773588657&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;$pop&quot;: {&quot;key&quot;: 1}}  //배열의 마지막 부터 요소 제거
{&quot;$pop&quot;: {&quot;key&quot;: -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;$pull은 주어진 조건에 맞는 배열 요소를 제거하는데 사용한다. 도큐먼트에서 조건과 일치하는 요소를 모두 제거한다.&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;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;p data-ke-size=&quot;size16&quot;&gt;몽고DB 3.6에서는 개별 배열 요소를 갱신하는 배열 필터인 arrayFilters를 도입해 특정 조건에 맞는 배열 요소를 갱신할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 반대표가 5표 이상인 댓글을 숨기거나 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661773829984&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;arrayFilters: [{&quot;elem.votes&quot;: {$lie: -5}}]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3.3 갱신 입력&lt;/h3&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; 입력을 사용하면 코드를 줄이고 경쟁 상태를 피할 수 있다. updateOne과 updateMany의 세번째 매개변수는 옵션 도큐먼트로, 갱신 입력을 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661773992598&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.analytics.updateOne({&quot;url&quot;: &quot;/blog&quot;}, {&quot;$inc&quot;: {&quot;pageviews: 1}}, {&quot;upsert&quot;: 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;도큐먼트가 생성될 때 필드가 설정돼야 할 때가 종종 있는데, 이후 갱신에서는 변경되지 않아야 한다. 이때 $setOnInsert를 사용한다. 이는 도큐먼트가 삽입될 때 필드값을 설정하는 데만 사용하는 연산자다.&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;p data-ke-size=&quot;size16&quot;&gt;save는 도큐먼트가 존재하지 않으면 도큐먼트를 삽입하고, 존재하면 도큐먼트를 갱신하게 하는 셸 함수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도큐먼트가 _id키를 포함하면 save는 갱신 입력을 실행하고, 포함하지 않으면 삽입을 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661774266406&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var x = db.testcol.findOne()
x.num = 42
db.testcol.save(x)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3.4 다중 도큐먼트 갱신&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;updateMany는 스키마를 변경하거나 특정 사용자에게 새로운 정보를 추가할 때 쓰기 좋다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 특정 날짜에 생일을 맞이하는 모든 사용자에게 선물을 준다고 가정하면 updateMany를 통해 gift를 추가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3.5 갱신한 도큐먼트 반환&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 사례에서는 수정된 도큐먼트를 반환하는 것이 중요한데, 몽고DB의 이전 버전에서는 그럴 때 findAndModify를 사용했다.&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;몽고DB 3.2에서는 findOneAndDelete, findOneAndReplace, findOneAndUpdate를 셸에 도입했다. updateOne 같은 메서드와의 큰 차이점은 사용자가 수정된 도큐먼트의 값을 원자적으로 얻을 수 있다는 점이다. 이는 한번의 연산으로 반환하고 갱신할 수 있어서 경쟁 상태를 피할 수 있다.&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;몽고DB 4.2는 갱신을 위한 집계 파이프라인을 수용하도록 findOneAndUpdate를 확장했다. 파이프라인은 $addFields(별칭 $set), $project(별칭 $unset), $replaceRoot(별칭 $replaceWith)로 구성될 수 있다.&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;findOneAndUpdate 메서드는 기본적으로 도큐먼트의 상태를 수정하기 전에 반환한다. 옵션 도큐먼트의 returnNewDocument 필드를 true로 설정하면 갱신된 도큐먼트를 반환한다.&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;findOneAndReplace는 동일한 매개변수를 사용하며, returnNewDocument의 값에 따라 교체 전이나 후에 필터와 일치하는 도큐먼트를 반환한다.&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;findOneAndDelete도 유사하지만 갱신 도큐먼트를 매개변수로 사용하지 않으며 다른 두 메서드의 옵션을 부분적으로 가진다. 삭제된 도큐먼트를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://search.shopping.naver.com/book/catalog/32481967929?query=%EB%AA%BD%EA%B3%A0DB%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C&amp;amp;NaPm=ct%3Dl74vfpgg%7Cci%3Ddee02388f64806d5b95e748f8c3620581a70d2c6%7Ctr%3Dboksl%7Csn%3D95694%7Chk%3D4ee1a27cdd0b5603cffcc242b6d17fbc261c8a54&quot;&gt;https://search.shopping.naver.com/book/catalog/32481967929?query=%EB%AA%BD%EA%B3%A0DB%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C&amp;amp;NaPm=ct%3Dl74vfpgg%7Cci%3Ddee02388f64806d5b95e748f8c3620581a70d2c6%7Ctr%3Dboksl%7Csn%3D95694%7Chk%3D4ee1a27cdd0b5603cffcc242b6d17fbc261c8a54&lt;/a&gt;&lt;/p&gt;</description>
      <category>DB/MongoDB</category>
      <category>MongoDB</category>
      <category>mongodb delete</category>
      <category>mongodb document</category>
      <category>mongodb find</category>
      <category>mongodb insert</category>
      <category>mongodb update</category>
      <category>mongodb upsert</category>
      <category>mongodb 완벽 가이드</category>
      <category>몽고DB</category>
      <category>몽고DB 완벽가이드</category>
      <author>055055</author>
      <guid isPermaLink="true">https://055055.tistory.com/121</guid>
      <comments>https://055055.tistory.com/121#entry121comment</comments>
      <pubDate>Mon, 29 Aug 2022 21:09:17 +0900</pubDate>
    </item>
    <item>
      <title>[몽고DB 완벽 가이드 3판] 몽고 DB 기본</title>
      <link>https://055055.tistory.com/120</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;몽고DB는 매우 강력하면서도 진입장벽이 낮다. 몽고 DB의 기본 개념을 소개한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;몽고DB 데이터의 기본 단위는 도큐먼트이며, 이는 관계형 데이터베이스의 행과 유사하다(하지만 더 다양한 자료 표현이 가능하다.)&lt;/li&gt;
&lt;li&gt;같은 맥락에서 컬렉션(collection)은 동적 스키마(dynamic schema)가 있는 테이블과 같다.&lt;/li&gt;
&lt;li&gt;몽고DB의 단일 인스턴스는 자체적인 컬렉션을 갖는 여러 개의 독립적인 데이터베이스를 호스팅한다.&lt;/li&gt;
&lt;li&gt;모든 도큐먼트는 컬렉션 내에서 고유한 특수키인 &quot;_id&quot;를 가진다.&lt;/li&gt;
&lt;li&gt;몽고 DB는 몽고 셸(The mongo Shell)이라는 간단하지만 강력한 도구와 함께 배포된다. mongo 셸은 몽고DB 인스턴스를 관리하고 몽고DB 쿼리 언어로 데이터를 조작하기 위한 내장 자원을 제공한다. 또한 사용자가 다양한 목적으로 자신의 스크립트를 만들고 로드 할수 있는 완전한 기능의 자바스크립트 해석기다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.1 도큐먼트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고DB의 핵심은 정렬된 키와 연결된 값의 집합으로 이뤄진 &lt;b&gt;도큐먼트다. &lt;/b&gt;도큐먼트 표현 방식은 프로그래밍 언어마다 다르지만 대부분의 언어는 맵(map), 해시(hash), 딕셔너리(dictionary)와 같이 도큐먼트를 자연스럽게 표현하는 자료구조를 가진다. 예를 들어 자바스크립트에서 도큐먼트는 객체로 표현된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661691614913&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;greeting&quot; : &quot;Hello, world!&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 간단한 도큐먼트는 &quot;greeting&quot;이라는 키에 연결된 &quot;Hello, World!&quot;라는 값을 가진다. 대부분의 도큐먼트는 이보다 복잡한 다중 키/값 쌍을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661691671975&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;greeting&quot; : &quot;Hello, world!&quot;, &quot;views&quot; : 3}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 볼 수 있듯 도큐먼트의 값은 단지 blob형이 아니다. 값은 데이터형이어야 한다. (또는 내장 도큐먼트 전체가 되기도 한다. 2.6.4 '내장 도큐먼트' 참조). 예제에서 &quot;greeting&quot;의 값은 문자열이며 &quot;views&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;도큐먼트의 키는 문자열이다. 다음 예외 몇 가지를 제외하면 어떤 UTF-8 문자든 쓸 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;키는 \0(null 문자)을 포함하지 않는다. \0은 키와 끝을 나타내는 데 사용된다.&lt;/li&gt;
&lt;li&gt;.과 $ 문자는 몇 가지 특별한 속성을 가지며 이후 장에서 설명할 특정 상황에만 사용해야 한다. 이 문자들은 보통 예약어(reserved word)로 취급해야 하며 부적절하게 사용하면 드라이버에서 경고를 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고 DB는 데이터형과 대소문자를 구별한다. 예를 들어 다음 두 도큐먼트는 서로 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661691851389&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;count&quot;: 5}
{&quot;count&quot;: &quot;5&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;/p&gt;
&lt;pre id=&quot;code_1661691875488&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;count&quot;: 5}
{&quot;Count&quot;: 5}&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;또한 몽고 DB에서는 키가 중복될 수 없음을 기억하자. 예를 들어 다음 도큐먼트는 올바른 도큐먼트가 아니다.&lt;/p&gt;
&lt;pre id=&quot;code_1661691933289&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;greeting&quot;: &quot;Hello, world!&quot;, &quot;greeting&quot;: &quot;Hello, MongoDB!&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.2 컬렉션&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컬렉션&amp;nbsp;&lt;/b&gt;은 도큐먼트의 모음이다. 몽고DB의 도큐먼트가 관계형 데이터베이스의 행에 대응된다면 컬렉션은 테이블에 대응된다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2.1 동적 스키마&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬렉션은 &lt;b&gt;동적스키마를 가진다.&amp;nbsp;&lt;/b&gt;하나의 컬렉션 내 도큐먼트들이 모두 다른 구조를 가질 수 있다는 의미다. 예를 들어 다음 도큐먼트들을 하나의 컬렉션에 저장할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1661692044743&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;greeting&quot;: &quot;Hello, world!&quot;, &quot;views&quot;: 3}
{&quot;signoff&quot;: &quot;Good night, and good luck&quot;}&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;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 컬렉션에 다른 종류의 도큐먼트를 저장하면 개발자와 관리자에게 번거로운 일이 생길 수도 있다. 각 쿼리가 특정 스키마를 고수하는 도큐먼트를 반환하는지, 혹은 쿼리한 코드가 다른 구조의 도큐먼트를 다룰 수 있는지 확실히 확인하자. 예를 들어 블로그 게시물을 쿼리한 데이터 중 작성자 데이터만 제거하려면 상당히 번거롭다.&lt;/li&gt;
&lt;li&gt;컬렉션별로 목록을 뽑으면서 한 컬렉션 내 특정 데이터형별로 쿼리해서 목록을 뽑을 때보다 훨씬 빠르다. 예를 들어 &quot;요약&quot;, &quot;전체&quot;, &quot;뚱뚱이 원숭이&quot;와 같은 컬렉션형을 값으로 하는 &quot;type&quot; 키가 도큐먼트 내에 있다면, 단일 컬렉션에 들어 있는 값을 찾기보다 세 컬렉션 중에서 올바른 컬렉션을 쿼리하는 편이 훨씬 빠르다.&lt;/li&gt;
&lt;li&gt;같은 종류의 데이터를 하나의 컬렉션에 모아두면 데이터 지역성(data locality)에도 좋다. 블로그 게시물 여러 개를 뽑는 경우, 게시물과 저자 정보가 섞인 컬렉션보다 게시물만 들어 있는 컬렉션에서 뽑을 때 디스크 탐색 시간이 더 짧다.&lt;/li&gt;
&lt;li&gt;인덱스를 만들면 도큐먼트는 특정 구조를 가져아 한다.(고유 인덱스인 경우 특히 더 그렇다) 이러한 인덱스는 컬렉션별로 정의한다. 같은 유형의 도큐먼트를 하나의 컬렉션에 넣음으로써 컬렉션을 효율적으로 인덱싱할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스키마를 만들고 관련된 유형의 도큐먼트를 그룹화 하는 데는 타당한 이유가 있다. 애플리케이션 스키마는 기본적으로 필요하지 않지만 정의하면 좋다. &lt;b&gt;몽고 DB의 도큐먼트 유효성 검사기능과 객체-도큐머트 매핑 라이브러리를 이용하며 , 이는 많은 프로그래밍 언어에서 사용 가능하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2.2 네이밍&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬렉션은 이름으로 식별된다. UTF-8 문자열이든 쓸 수 있지만 몇 가지 제약 조건이 있다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빈 문자열(&quot;&quot;)은 유효한 컬렉션명이 아니다.&lt;/li&gt;
&lt;li&gt;\0(null 문자)은 컬렉셔명의 끝을 나타내는 문자이므로 컬렉션명에서 사용할 수 없다.&lt;/li&gt;
&lt;li&gt;system.으로 시작한느 컬렉션명은 시스템 컬렉션에서 사용하는 예약어로 사용할 수 없다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;사용자가 만든 컬렉션은 이름에 예약어인 $를 포함할 수 없다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&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;ex) blog.posts, blog.authors&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 단지 체계화를 위함이며 blog 컬렉션이나 자식 컬렉션과는 아무런 관계가 없다. 심지어 blog 컬렉션은 없어도 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;큰파일은 저장하는 프로토콜인 GridFS는 콘텐츠 데이터와 별도로 메타데이터를 저장하는 데 서브컬렉션을 사용한다.&lt;/li&gt;
&lt;li&gt;대부분의 드라이버는 특정 컬렉션의 서브컬렉션에 접근하는 몇 가지 편리한 문법을 제공한다. 예를 들어 데이터베이스 셸에서 db.blog는 blog컬렉션을 db.blog.posts는 blog.posts 컬렉션을 보여준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.3 데이터베이스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고DB는 컬렉션에 도큐먼트를 그룹화할 뿐 아니라 데이터베이스에 컬렉션을 그룹 지어 놓는다. 몽고DB의 단일 &lt;b&gt;인스턴스는&amp;nbsp; 여러 데이터베이스를 호스팅할 수 있으며,&amp;nbsp;&lt;/b&gt;각 데이터베이스를 완전히 독립적으로 취급할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;admin : admin 데이터베이스는 인증과 권한 부여 역할을 한다.&lt;/li&gt;
&lt;li&gt;local: local 데이터베이스는 단일 서버에 대한 데이터를 저장한다. 복제셋(replica set)에서 local은 복제 프로세스에 사용된 데이터를 저장한다.&lt;/li&gt;
&lt;li&gt;config: 샤딩된 몽고DB 클러스터는 config 데이터베이스를 사용해 각 샤드의 정보를 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬렉션을 저장하는 데이터베이스의 이름을 컬렉션명 앞에 붙이면 올바른 컬렉션명인 네임스페이스를 얻는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) cms.blog.posts &amp;gt; cms 데이터베이스의 blog.posts 컬렉션 사용&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;h2 data-ke-size=&quot;size26&quot;&gt;2.4 몽고DB 시작&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;별도 작성&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;docker 사용법&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1661696758410&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker pull mongo

docker images

docker run --name mongodb -v ~/mongodb/db:/data/db -d -p 27017:27017 mongo

docker exec -it mongodb bash

mongo&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.5 몽고DB 셸 소개&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고DB는 명령행에서 몽고 DB 인스턴스와 상호작용하는 자바스크립트 셸을 제공한다. 셸은 관리 기능이나, 실행 중인 인스턴스를 점검하거나 간단한 기능을 시험하는데 매우 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.5.1 셸 실행&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-08-28 오후 10.25.54.png&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;94&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEEnMb/btrKMnbIdCL/We6GS5tekuwlqnVQauz6XK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEEnMb/btrKMnbIdCL/We6GS5tekuwlqnVQauz6XK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEEnMb/btrKMnbIdCL/We6GS5tekuwlqnVQauz6XK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEEnMb%2FbtrKMnbIdCL%2FWe6GS5tekuwlqnVQauz6XK%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;871&quot; height=&quot;94&quot; data-filename=&quot;스크린샷 2022-08-28 오후 10.25.54.png&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;94&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-08-28 오후 10.26.29.png&quot; data-origin-width=&quot;593&quot; data-origin-height=&quot;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qSLtJ/btrKH0ojI3N/UY1fhXfobu7kGApFJlZ8j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qSLtJ/btrKH0ojI3N/UY1fhXfobu7kGApFJlZ8j1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qSLtJ/btrKH0ojI3N/UY1fhXfobu7kGApFJlZ8j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqSLtJ%2FbtrKH0ojI3N%2FUY1fhXfobu7kGApFJlZ8j1%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;593&quot; height=&quot;104&quot; data-filename=&quot;스크린샷 2022-08-28 오후 10.26.29.png&quot; data-origin-width=&quot;593&quot; data-origin-height=&quot;104&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;h3 data-ke-size=&quot;size23&quot;&gt;2.5.2 몽고DB 클라이언트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셸의 진수는 독자적으로 쓸 수 있는 몽고DB 클라이언트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 db에 할당된 데이터베이스를 확인하려면 db를 입력한다.&lt;/li&gt;
&lt;li&gt;데이터베이스를 선택하려면 use video를 입력한다.&lt;/li&gt;
&lt;li&gt;db.movies를 입력하면 현재 데이터베이스의 movies 컬렉션을 반환한다.&lt;/li&gt;
&lt;/ul&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-08-28 오후 10.31.16.png&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xeeGN/btrKK80akSN/TiEK3pVhkDGedwPDwvsCh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xeeGN/btrKK80akSN/TiEK3pVhkDGedwPDwvsCh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xeeGN/btrKK80akSN/TiEK3pVhkDGedwPDwvsCh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxeeGN%2FbtrKK80akSN%2FTiEK3pVhkDGedwPDwvsCh1%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;715&quot; height=&quot;388&quot; data-filename=&quot;스크린샷 2022-08-28 오후 10.31.16.png&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.5.3 셸 기본 작업&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셸에서 데이터를 조작하거나 보려면 생성, 읽기, 갱신, 삭제의 네 가지 기본적인 작업(CRUD)을 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;insertOne 함수는 컬렉션에 도큐먼트를 추가한다. movie라는 지역변수를 생성하고 insertOne 함수를 이용해 movies 컬렉션에 저장할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1661693780142&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.movies.insertOne(move)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;읽기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;find와 findOne은 컬렉션을 쿼리하는 데 사용한다. 컬렉션에서 단일 도큐먼트를 읽으려면 findOne을 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661693788966&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.movies.findOne()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;find와 findOne은 쿼리 도큐먼트 형태로 조건 전달도 가능하다. 따라서 쿼리에서 일치하는 도큐머트로 결과를 제한한다.&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;updateOne을 사용한다. updateOne의 매개변수는 최소 두 개다. 첫번쨰는 수정할 도큐먼트를 찾는 기준이고, 두 번째는 갱신 작업을 설명하는 도큐먼트다.&amp;nbsp; 갱신하려면 갱신 연산자인 set을 이용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661694289999&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.movies.updateOne(title: &quot;....&quot;}, {$set: {reviews: []}})&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;삭제&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;deleteOne과 deleteMany는 도큐먼트를 데이터베이스에서 영구적으로 삭제한다. 두 함수 모두 필터 도큐먼트로 삭제 조건을 지정한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터와 일치하는 모든 도큐먼트를 삭제하려면 deleteMany를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661694318291&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.movies.deleteOne({title: &quot;....&quot;})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-08-28 오후 10.42.19.png&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BQkqw/btrKHdn946U/7BMSi32bFtq5Gk7SBuqmK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BQkqw/btrKHdn946U/7BMSi32bFtq5Gk7SBuqmK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BQkqw/btrKHdn946U/7BMSi32bFtq5Gk7SBuqmK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBQkqw%2FbtrKHdn946U%2F7BMSi32bFtq5Gk7SBuqmK0%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;777&quot; height=&quot;207&quot; data-filename=&quot;스크린샷 2022-08-28 오후 10.42.19.png&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;207&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;h2 data-ke-size=&quot;size26&quot;&gt;2.6 데이터형&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고DB는 도큐먼트의 값으로 다양한 데이터형을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.6.1 기본 데이터형&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고DB는 JSON의 키/값 쌍 성질을 유지하면서 기본 타입(null, 불리언, 숫자, 문자열, 배열, 객체) 외 추가적인 데이터형을 지원한다.&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;null&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null값과 존재하지 않는 필드를 표현하는데 사용&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;불리언&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참과 거짓에 사용 true/false&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;숫자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셸은 64비트 부동소수점 수를 기본으로 사용한다. 따라서 다음 수들은 셸에서 정상이다.&lt;/p&gt;
&lt;pre id=&quot;code_1661694603322&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;x&quot;: 3.14}
{&quot;x&quot;: 3}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4바이트 혹은 8바이트의 부호 정수(signed integer)는 각각 NumberInt 혹은 NumberLong 클래스를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661694670623&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;x&quot;: NumberInt(&quot;3&quot;)}
{&quot;x&quot;: NumberLong(&quot;3&quot;)}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;문자열&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 UTF-8문자열이든 문자열형 표현 가능&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;날짜&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고 DB는 1970년 1월 1일부터의 시간을 1/1000초 단위로 나타내는 64비트 정수로 날짜를 저장한다. 표준 시간대(time zone)는 저장하지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1661694750126&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;x&quot;: new Date()}&lt;/code&gt;&lt;/pre&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;h4 data-ke-size=&quot;size20&quot;&gt;배열&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값의 셋이나 리스트를 배열로 표현할 수 있다.&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;pre id=&quot;code_1661694889771&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;x&quot;: {&quot;foo&quot;: &quot;bar&quot;}}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;객체 ID&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체ID는 도큐먼트용 12바이트 ID다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이진데이터&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진 데이터는 임의의 바이트 문자열이며 셸에서는 조작이 불가하다. UTF-8이 아닌 문자열을 저장하는 유일한 방법이다.&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;pre id=&quot;code_1661694992520&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;X&quot;: function(){/*...*/}}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.6.2 날짜&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 Date 객체를 생성할 때는 항상 Date()가 아닌 new Date()를 호출해야 한다. 함수로 생성자를 호출하면 (new가 포함되지 않은 것)실제 Date 객체가 아닌 날짜의 문자열 표현을 반환한다. 이는 몽고DB가 아니라 자바스크립트의 작동 방식 때문이다. &lt;b&gt;항상 Date 생성자를 주의하지 않으면 문자열과 날짜가 뒤범벅되며 삭제, 수정 쿼리 등에서 문제가 발생할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.6.3 배열&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열은 정렬연산(리스트, 스택, 큐)과 비정렬 연산(셋)에 호환성 있게 사용 가능한 값이다.&lt;/p&gt;
&lt;pre id=&quot;code_1661695291782&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;things&quot; : [&quot;pie&quot;, 3.14]}&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;배열에 쿼리하거나 배열의 내용을 이용해 인덱스를 만들 수 있으며 배열의 내용을 수정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.6.4 내장 도큐먼트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도큐먼트는 키에 대한 값이 될 수 있는데, 이를 내장 도큐먼트라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661695473896&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{ 
	&quot;name&quot;: &quot;john&quot;,
    &quot;address&quot; : {
 		&quot;street&quot; : &quot;123 street&quot;,
        &quot;city&quot; : &quot;Anytown&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 address 키 에 대한 값은 street, city의 키/값 쌍을 갖는 내장 도큐먼트다. 배열과 마찬가지로 내장도큐먼트의 구조를 이해하고 인덱스르 구성하고 쿼리하며 갱신하기 위해 내장도큐먼트 내부에 접근한다.&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;관계형 데이터베이스에서는 이 도큐먼트가 두 개의 테이블(people과 address)에 분리된 두 개의 행으로 모델링된다. 몽고DB에서는 people 도큐먼트 내에 바로 address 도큐먼트를 내장할 수 있다. 이를 통해 더 자연스럽게 정보를 표현할 수 있다.&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;그러나 더 많은 데이터 반복이 생길 수 있다는 단점이 있다. 관계형데이터베이스에서는 데이터 수정시 address 하나의 테이블에서 주소를 수정하면 people에서 address 테이블을 조인하여 수정된 데이터를 보여줄 수 있다. 그러나 몽고DB는 각 사람의 도큐먼트에서 오타를 수정해야 한다.&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;2.6.5 _id와 ObjectId&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장된 모든 도큐먼트는 _id 키를 가진다.&amp;nbsp; _id 키 값은 어떤 데이터형이여도 상관없지만 ObjectId가 기본이다. 하나의 컬렉션에서 모든 도큐먼트는 고유한 _id 값을 가지며 이 값은 컬렉션 내에서 고유하게 식별된다.&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;ObjectId&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ObjectId는 _id의 기본 데이터형 이다. 여러 장비에 걸쳐 전역적으로 고유하게 생성하기 쉽게 설계됐다. 자동 증가 기본키 처럼 전통적인 것을 사용하지 않는 이유는 몽고DB의 분산 특성 때문이다. 자동증가키는 키를 동기화하는 작업이 어렵고 시간이 걸린다. 몽고DB의 이러한 방식은 분산데이터베이스로 설계되어서 샤딩된 환경에서 고유 식별자를 생성하는 것이 매우 중요했다.&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;ObjectId 12바이트는 타임스탬프 + 랜덤 +카운터(랜덤시작값)을 이용해 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 4바이트 : 타임스탬프&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 5바이트: 랜덤값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 3바이트: 랜덤값으로 시작하는 카운터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ObjectId의 앞 9바이트는 1초 동안 여러 장비와 프로세스에 걸쳐 유일성을 보장한다. 마지막 3바이트는 단순히 증분하는 숫자로 1초 내 단일 프로세스의 유일성을 보장한다. 고유한 ObjectId는 프로세스당 1초에 1677만 7216개까지 생성된다&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;_id 자동생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도큐먼트 입력시 _id키를 명시하지 않으면 입력된 도큐먼트에 키가 자동으로 추가된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.7 몽고DB 셸 사용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 시스템에서 연결 할 수 있는 어떤 몽고DB인스턴스든 셸을 연결할 수 잇다.&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;help를 입력하면 셸에 내장된 도움말을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 수준의 도움말은 db.help(), 컬렉션 수준의 도움말은 db.컬렉션.help()로 확인한다.&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;h2 data-ke-size=&quot;size26&quot;&gt;출처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://search.shopping.naver.com/book/catalog/32481967929?query=%EB%AA%BD%EA%B3%A0DB%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C&amp;amp;NaPm=ct%3Dl74vfpgg%7Cci%3Ddee02388f64806d5b95e748f8c3620581a70d2c6%7Ctr%3Dboksl%7Csn%3D95694%7Chk%3D4ee1a27cdd0b5603cffcc242b6d17fbc261c8a54&quot;&gt;https://search.shopping.naver.com/book/catalog/32481967929?query=%EB%AA%BD%EA%B3%A0DB%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C&amp;amp;NaPm=ct%3Dl74vfpgg%7Cci%3Ddee02388f64806d5b95e748f8c3620581a70d2c6%7Ctr%3Dboksl%7Csn%3D95694%7Chk%3D4ee1a27cdd0b5603cffcc242b6d17fbc261c8a54&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap &quot;&gt;
&lt;div class=&quot;revenue_unit_item adsense  responsive&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&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;</description>
      <category>DB/MongoDB</category>
      <category>mogodb document</category>
      <category>mongodb collection</category>
      <category>몽고DB collection</category>
      <category>몽고DB document</category>
      <category>몽고DB 기본형</category>
      <category>몽고DB 완벽가이드</category>
      <category>몽고DB기본</category>
      <category>몽고디비</category>
      <category>몽고디비 도큐먼트</category>
      <category>몽고디비 콜렉션</category>
      <author>055055</author>
      <guid isPermaLink="true">https://055055.tistory.com/120</guid>
      <comments>https://055055.tistory.com/120#entry120comment</comments>
      <pubDate>Sun, 28 Aug 2022 23:21:29 +0900</pubDate>
    </item>
    <item>
      <title>[EffectiveJava 3/E] 2장 객체 생성과 파괴 - 아이템3 private 생성자나 열거 타입으로 싱글턴임을 보장하라</title>
      <link>https://055055.tistory.com/119</link>
      <description>&lt;h1&gt;private 생성자나 열거 타입으로 싱글턴임을 보증하라&lt;/h1&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;싱글턴(singleton)이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 말한다.  
싱글턴의 전형적인 예로는 함수와 같은 무상태(stateless) 객체나 설계상 유일해야 하는 시스템 컴포넌트를 들 수 있다.  
한번의 객체 생성으로 재사용할 수 있기 떄문에 메모리 낭비를 방지할 수 있다.  

클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트하기가 어려워질 수 있다.  
싱글턴 인스턴스를 가짜(mock) 구현으로 대체할 수 없기 때문이다.  
또한 멀티 쓰레드 환경에서 동기화 문제가 발생할 수 도 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;싱글턴을 만드는 방식&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필드 방식의 싱글턴&lt;/h3&gt;
&lt;pre class=&quot;nimrod&quot;&gt;&lt;code&gt;public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() {...}

    public void leaveTheBuilding() {...}
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Elvis {
    public static final Elvis INSTATNCE = new Elvis();

    private Elvis() {

    }

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

    public static void main(String[] args) {
        Elvis instatnce = Elvis.INSTATNCE;
        instatnce.leaveTheBuilding();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;private 생성자는 public static final 필드인 Elvis.INSTANCE를 초기화할 때 딱 한번 호출된다.&lt;br /&gt;public이나 protected 생성자가 없으므로 Elvis 클래스가 초기화될 때 만들어진 인스턴스가 전체 시스템에서 하나 뿐임을 보장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 클래스가 싱글턴임이 API에 명백히 드러난다. public static 필드가 final이니 절대로 다른 객체를 참조할 수 없다.&lt;/li&gt;
&lt;li&gt;간결함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리플랙션 API의 AccessibleObject.setAccessible을 사용해 private 생성자를 호출 할 수 있다.&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666;&quot;&gt;이러한 공격을 방어하려면 생성자를 수정하여 두 번째 객체가 생성되려 할 때 예외를 던지게 하면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정적 팩터리 방식의 싱글턴&lt;/h3&gt;
&lt;pre class=&quot;nimrod&quot;&gt;&lt;code&gt;public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() {...}
    public static Elvis getInstance() { return INSTATNCE;}

    public void leaveTheBuilding() {...}
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Elvis {
    private static final Elvis INSTATNCE = new Elvis();
    private Elvis(){

    }

    public static Elvis getInstance(){
        return INSTATNCE;
    }

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

    public static void main(String[] args) {
        Elvis instance = Elvis.getInstance();
        instance.leaveTheBuilding();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Elvis.getInstance는 항상 같은 객체의 참조를 반환하므로 제 2의 Elvis 인스턴스란 결코 만들어지지 않는다.(리플렉션 예외는 동일 적용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;언제든지 API를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다.&lt;/li&gt;
&lt;li&gt;정적 팩터리를 제네릭 싱글턴 팩터리로 만들 수 있다.&lt;/li&gt;
&lt;li&gt;정적 팩터리의 메서드 참조를 공급자(supplier)로 사용할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) Elvis::getInstance를 Supplier로 사용하는 식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리플랙션 API의 AccessibleObject.setAccessible을 사용해 private 생성자를 호출 할 수 있다.&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666;&quot;&gt;방어 하려면 두 번째 객체가 생성되려 할 때 예외를 던지게 하면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 장점이 필요없다면 1번이 더 나을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;열거 타입 방식의 싱글턴 - 바람직한 방법&lt;/h3&gt;
&lt;pre class=&quot;nimrod&quot;&gt;&lt;code&gt;public enum Elvis {
    INSTANCE;

    public void leaveThebuilding() {...}
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;public enum Elvis {
    INSTANCE;

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

    public static void main(String[] args) {
        Elvis instance = Elvis.INSTANCE;
        instance.leaveTheBuilding();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;public 필드 방식과 비슷하지만, 더 간결하고, 추가 노력 없이 직렬화 할 수 있고, 심지어 아주 복잡한 직렬화 상황이나 리플렉션 공격에서도 제2의 인스턴스가 생기는 일을 완벽히 막아준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;조금 부자연스러워 보일 수는 있으나 대부분 상황에서는 원소가 하나뿐인 열거 타입이 싱글턴을 만드는 가장 좋은 방법이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 만들려는 싱글턴이 Enum 외의 클래스를 상속해야 한다면 이 방법은 사용할 수 없다.&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;이러한 싱글턴 클래스를 직렬화하려면 단순히 Serializable을 구현한다고 선언하는 것만으로는 부족하다.&lt;br /&gt;모든 인스턴스 필드를 일시적(transient)이라고 선언하고 readResolve 메서드를 제공해야 한다.&lt;br /&gt;이렇게 하지 않으면 직렬화된 인스턴스를 역직렬화할 때마다 새로운 인스턴스가 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;가짜 Elvis 탄생을 예방하고 싶다면 Elvis 클래스에 다음의 readResolve 메서드를 추가하자.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;private Object readResolve() {
   //'진짜' Elvis를 반환하고, 가짜 Elvis는 가비지 컬렉터에 맡긴다. 
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://search.shopping.naver.com/book/catalog/32436239326?cat_id=50010920&amp;amp;frm=PBOKMOD&amp;amp;query=%08%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C+%EC%9E%90%EB%B0%94&amp;amp;NaPm=ct%3Dl6z138ns%7Cci%3Db0f99d2f2a318dd7ab9dac8495e63c0076fddd86%7Ctr%3Dboknx%7Csn%3D95694%7Chk%3D71936725f1d33db581c3d8c6a56703ade1a7c790&quot;&gt;이펙티브자바&lt;/a&gt;&lt;/p&gt;</description>
      <category>jvm언어관련/effective-java</category>
      <category>effectivejava singleton</category>
      <category>Singleton</category>
      <category>싱글턴</category>
      <category>싱글턴 만드는 법</category>
      <category>싱글톤</category>
      <category>열거 타입 방식의 싱글턴</category>
      <category>이펙티브자바 singleton</category>
      <category>이펙티브자바 싱글턴</category>
      <category>정적 팩터리 방식의 싱글턴</category>
      <category>필드 방식의 싱글턴</category>
      <author>055055</author>
      <guid isPermaLink="true">https://055055.tistory.com/119</guid>
      <comments>https://055055.tistory.com/119#entry119comment</comments>
      <pubDate>Sat, 27 Aug 2022 10:07:40 +0900</pubDate>
    </item>
    <item>
      <title>Mocking a Webclient(MockWebServer)</title>
      <link>https://055055.tistory.com/118</link>
      <description>&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-6601531371760634&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 API를 테스트할 때 실제 API를 호출해서 테스트 코드를 작성할 수도 있겠지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 되면 다양한 응답값에 대한 테스트의 어려움이 있을 수 있고 외부 API 상태에 따라 테스트 코드가 실패하는 경우가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 경우 MockWebServer를 이용하여 WebClient를 Mocking해서 다양한 테스트 케이스를 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MockWebServer&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;A scriptable web server for testing HTTP clients&lt;br /&gt;&lt;br /&gt;Motivation&lt;br /&gt;This library makes it easy to test that your app Does The Right Thing when it makes HTTP and HTTPS calls. It lets you specify which responses to return and then verify that requests were made as expected.&lt;br /&gt;Because it exercises your full HTTP stack, you can be confident that you're testing everything. You can even copy &amp;amp; paste HTTP responses from your real web server to create representative test cases. Or test that your code survives in awkward-to-reproduce situations like 500 errors or slow-loading responses.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Http Client 테스트를 위한 스크립트 가능한 웹 서버로, 애플리케이션이 Http 또는 Https 호출을 할때 제대로 동작하는지 쉽게 테스트 할 수 있다. 이를 통해 반환할 응답을 지정한 다음 제대로 요청이 이뤄졌는지 확인할 수 있고, 응답지연이나 Http Status Error 500 같은 케이스도 테스트 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법은 굉장히 간단하다. 아래와 같이 라이브러리를 추가 한다.(적용 환경 springboot v2.5.2 /gradle-7.0.2)&lt;/p&gt;
&lt;pre id=&quot;code_1661519051791&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    testImplementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10'
    testImplementation  'com.squareup.okhttp3:mockwebserver:5.0.0-alpha.10'&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;아래와 같이 테스트 전 MockWebServer를 생성하고, 테스트 종료 후 종료시키면 된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661519291981&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MockWebServerTest {

    public MockWebServer mockWebServer;

    @BeforeEach
    static void setUp() {
        mockWebServer = new MockWebServer();
    }

    @AfterEach
    static void tearDown() throws IOException {
        mockWebServer.shutdown();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Http 호출시 응답하는 Response 데이터는 아래와 같이 enqueue 메서드를 통해 추가할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러 Http 호출이 이루어질 때 MockWebServer에서 enqueue에 넣은 데이터를 반환할 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1661519538253&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Test
void test() throws Exception {
	//given 
    TestResponse response = new TestResponse(&quot;055055&quot;, &quot;tester&quot;);
    mockWebServer.enqueue(new MockResponse()
      .setBody(objectMapper.writeValueAsString(response))
      .addHeader(&quot;Content-Type&quot;, &quot;application/json&quot;));
  	
    //when 
    ...
    Http Call
    
    //then
    
    Verify
}&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실제 적용 예제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 github api를 호출하여 repository에 대한 정보들을 받는 예제이다.&lt;/p&gt;
&lt;pre id=&quot;code_1661522543459&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Component
public class WebClientConfig {
    @Bean
    public WebClient testWebClient() {
        HttpClient httpClient = HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1500)
                .doOnConnected(conn -&amp;gt;
                        conn.addHandlerLast(new ReadTimeoutHandler(1500, TimeUnit.MILLISECONDS))
                                .addHandlerLast(new WriteTimeoutHandler(1500, TimeUnit.MILLISECONDS)));

        return WebClient.builder()
                .filters(functions -&amp;gt; {
                    functions.add(logRequest());
                    functions.add(logResponse());
                })
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .baseUrl(&quot;https://api.github.com&quot;)
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1661522502714&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@RequiredArgsConstructor
public class TestHandler {
    private final WebClient testWebClient;

    public Flux&amp;lt;GitHubDto&amp;gt; clientDemo6(String userName) {
        if (!StringUtils.hasText(userName)) {
            throw new RuntimeException(&quot;user is Required&quot;);
        }

        Flux&amp;lt;GitHubDto&amp;gt; values = testWebClient.get()
                .uri(&quot;/users/{name}/repos&quot;, userName)
                .retrieve()
                .bodyToFlux(GitHubDto.class);
        return values;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1661522589564&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Data
public class GitHubDto {

    private String id;

    @JsonProperty(&quot;full_name&quot;)
    private String fullName;

    @JsonProperty(&quot;open_issues_count&quot;)
    private int issueCount;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1661522447296&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class TestHandlerTest {
    MockWebServer mockWebServer;
    TestHandler handler;

    ObjectMapper objectMapper = new ObjectMapper();

    @BeforeEach
    void setUp() {
        mockWebServer = new MockWebServer();
        WebClient webClient = WebClient.create(mockWebServer.url(&quot;/github&quot;).toString());
        handler = new TestHandler(webClient);
    }

    @AfterEach
    void shutDown() throws IOException {
        mockWebServer.shutdown();
    }

    @DisplayName(&quot;webMockServer Test&quot;)
    @Test
    public void test() {
        //given
        mockWebServer.enqueue(
                new MockResponse().setResponseCode(400)
        );

        //when
        StepVerifier.create(handler.clientDemo6(&quot;055055&quot;))
                .expectError();

    }

    @DisplayName(&quot;webMockServer Test- 200&quot;)
    @Test
    public void test_200() throws JsonProcessingException {
        //given
        GitHubDto dto1 = new GitHubDto();
        dto1.setId(&quot;1622941913451&quot;);
        dto1.setFullName(&quot;055055/bank&quot;);
        dto1.setIssueCount(0);

        GitHubDto dto2 = new GitHubDto();
        dto2.setId(&quot;1600580264324&quot;);
        dto2.setFullName(&quot;055055/market&quot;);
        dto2.setIssueCount(0);


        GitHubDto dto3 = new GitHubDto();
        dto3.setId(&quot;444423408312312&quot;);
        dto3.setFullName(&quot;055055/user&quot;);
        dto3.setIssueCount(0);

        List&amp;lt;GitHubDto&amp;gt; gitHubDtoList = Arrays.asList(dto1, dto2, dto3);
        String response = objectMapper.writeValueAsString(gitHubDtoList);

        mockWebServer.enqueue(
                new MockResponse().setBody(response)
                        .addHeader(&quot;Content-Type&quot;, MediaType.APPLICATION_JSON_VALUE)

        );

        //when
        StepVerifier.create(handler.clientDemo6(&quot;055055&quot;))
                .assertNext(result -&amp;gt; {
                            assertThat(result.getId()).isEqualTo(dto1.getId());
                            assertThat(result.getFullName()).isEqualTo(dto1.getFullName());
                            assertThat(result.getIssueCount()).isEqualTo(dto1.getIssueCount());
                        }
                )
                .assertNext(result -&amp;gt; {
                            assertThat(result.getId()).isEqualTo(dto2.getId());
                            assertThat(result.getFullName()).isEqualTo(dto2.getFullName());
                            assertThat(result.getIssueCount()).isEqualTo(dto2.getIssueCount());
                        }
                )
                .assertNext(result -&amp;gt; {
                            assertThat(result.getId()).isEqualTo(dto3.getId());
                            assertThat(result.getFullName()).isEqualTo(dto3.getFullName());
                            assertThat(result.getIssueCount()).isEqualTo(dto3.getIssueCount());
                        }
                )
                .verifyComplete();
    }
}&lt;/code&gt;&lt;/pre&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-filename=&quot;스크린샷 2022-08-26 오후 11.05.32.png&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;539&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPTjpW/btrKHa4Lqu8/Kj57npfvX9CTDOJtVL99EK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPTjpW/btrKHa4Lqu8/Kj57npfvX9CTDOJtVL99EK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPTjpW/btrKHa4Lqu8/Kj57npfvX9CTDOJtVL99EK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPTjpW%2FbtrKHa4Lqu8%2FKj57npfvX9CTDOJtVL99EK%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;860&quot; height=&quot;303&quot; data-filename=&quot;스크린샷 2022-08-26 오후 11.05.32.png&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;539&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 MockWebServer가 띄워진 port로 요청을 보내고 미리 넣어둔 데이터가 Response 된 것을 볼 수 있다.&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;MockWebServer 뿐만 아니라 Mockito를 사용하여 테스트 할 수도 있지만, 아래와 같이 데이터들을 stubbing하는데 많은 번거로움이 존재한다. MockWebServer가 사용하기 더 편리하고 깔끔하기 때문에 사용을 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;a href=&quot;https://github.com/spring-projects/spring-framework/issues/19852&quot;&gt;https://github.com/spring-projects/spring-framework/issues/19852&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이슈를 보면 스프링팀도 MockWebServer를 사용하여 테스트를 하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-08-26 오후 11.28.06.png&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/canYTG/btrKIsjjLyX/GttyJ1lM4zBGjt6sKj7yLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/canYTG/btrKIsjjLyX/GttyJ1lM4zBGjt6sKj7yLk/img.png&quot; data-alt=&quot;Mockito 사용시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/canYTG/btrKIsjjLyX/GttyJ1lM4zBGjt6sKj7yLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcanYTG%2FbtrKIsjjLyX%2FGttyJ1lM4zBGjt6sKj7yLk%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;853&quot; height=&quot;558&quot; data-filename=&quot;스크린샷 2022-08-26 오후 11.28.06.png&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Mockito 사용시&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개인적인 사용의 어려움&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 SpringBean으로 Handler와 WebClient를 주입받아서 사용하려는 경우에는 MockWebServer를 사용하는데 어려움이 있었다. 실제 코드를 짤때는 예제 코드 처럼 심플한 경우 보다는 여러 가지 Spring의 기술들이 들어간 경우가 많아서 Spring으로 Bean을 주입 받아서 테스트 코드를 짜야 하는 경우들이 있다. WebClient Bean을 생성할 때 url 정보를 입력해 줘야되서 MockWebServer를 생성해서 url을 만들어야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이러한 경우에는 MockWebServer를 재사용하게 되어서 테스트 코드 순서를 보장하지 않는 경우에는 안에 데이터가 꼬이게 되는 문제가 발생하였다.&amp;nbsp; 그래서 꼭 재사용하게 된다면 테스트 순서를 보장하도록 어노테이션을 사용하거나 (@TestMethodOrder, @Order) 한번의 요청만 하도록 작성하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로는 재사용할 수 있도록 clear()라는 메서드가 있으면 더 좋겠다는 생각이 들었다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_스크린샷 2022-08-26 오후 11.35.47.png&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/poEE5/btrKHawV3w5/D8wr8ujNtJ4jf5Hu9AQ9Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/poEE5/btrKHawV3w5/D8wr8ujNtJ4jf5Hu9AQ9Kk/img.png&quot; data-alt=&quot;https://github.com/square/okhttp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/poEE5/btrKHawV3w5/D8wr8ujNtJ4jf5Hu9AQ9Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpoEE5%2FbtrKHawV3w5%2FD8wr8ujNtJ4jf5Hu9AQ9Kk%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;812&quot; height=&quot;100&quot; data-filename=&quot;edited_스크린샷 2022-08-26 오후 11.35.47.png&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/square/okhttp&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1661524235172&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@TestConfiguration
public class TestWebClientConfig {
    Logger log = LoggerFactory.getLogger(TestWebClientConfig.class);

    public static MockWebServer mockWebServer = new MockWebServer();

    @Bean
    public WebClient testWebClient() {
        HttpClient httpClient = HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1500)
                .doOnConnected(conn -&amp;gt;
                        conn.addHandlerLast(new ReadTimeoutHandler(1500, TimeUnit.MILLISECONDS))
                                .addHandlerLast(new WriteTimeoutHandler(1500, TimeUnit.MILLISECONDS)));

        return WebClient.builder()
                .filters(functions -&amp;gt; {
                    functions.add(logRequest());
                    functions.add(logResponse());
                })
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .baseUrl(mockWebServer.url(&quot;/github-test&quot;).toString())
                .build();
    }

    public ExchangeFilterFunction logRequest() {
        return (clientRequest, next) -&amp;gt; {
            log.info(&quot;Request: {} {}&quot;, clientRequest.method(), clientRequest.url());
            clientRequest.headers()
                    .forEach((name, values) -&amp;gt; values.forEach(value -&amp;gt; log.info(&quot;{}={}&quot;, name, value)));

            return next.exchange(clientRequest);
        };
    }

    public ExchangeFilterFunction logResponse() {
        return ExchangeFilterFunction.ofResponseProcessor(clientResponse -&amp;gt; {
            log.info(&quot;Response: {}&quot;, clientResponse.headers().asHttpHeaders().get(&quot;property-header&quot;));
            return Mono.just(clientResponse);
        });
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661524218259&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Import(TestWebClientConfig.class)
@SpringBootTest(classes = TestHandler.class)
class TestHandlerTestSpringBean {

    @Autowired
    TestHandler handler;

    ObjectMapper objectMapper = new ObjectMapper();

    @DisplayName(&quot;webMockServer Test&quot;)
    @Test
    public void test() {
        //given
        mockWebServer.enqueue(
                new MockResponse().setResponseCode(400)
        );

        //when
        StepVerifier.create(handler.clientDemo6(&quot;055055&quot;))
                .expectError();

    }

    @DisplayName(&quot;webMockServer Test- 200&quot;)
    @Test
    public void test_200() throws JsonProcessingException {
        //given
        GitHubDto dto1 = new GitHubDto();
        dto1.setId(&quot;1622941913451&quot;);
        dto1.setFullName(&quot;055055/bank&quot;);
        dto1.setIssueCount(0);

        GitHubDto dto2 = new GitHubDto();
        dto2.setId(&quot;1600580264324&quot;);
        dto2.setFullName(&quot;055055/market&quot;);
        dto2.setIssueCount(0);


        GitHubDto dto3 = new GitHubDto();
        dto3.setId(&quot;444423408312312&quot;);
        dto3.setFullName(&quot;055055/user&quot;);
        dto3.setIssueCount(0);

        List&amp;lt;GitHubDto&amp;gt; gitHubDtoList = Arrays.asList(dto1, dto2, dto3);
        String response = objectMapper.writeValueAsString(gitHubDtoList);

        mockWebServer.enqueue(
                new MockResponse().setBody(response)
                        .addHeader(&quot;Content-Type&quot;, MediaType.APPLICATION_JSON_VALUE)

        );

        //when
        StepVerifier.create(handler.clientDemo6(&quot;055055&quot;))
                .assertNext(result -&amp;gt; {
                            assertThat(result.getId()).isEqualTo(dto1.getId());
                            assertThat(result.getFullName()).isEqualTo(dto1.getFullName());
                            assertThat(result.getIssueCount()).isEqualTo(dto1.getIssueCount());
                        }
                )
                .assertNext(result -&amp;gt; {
                            assertThat(result.getId()).isEqualTo(dto2.getId());
                            assertThat(result.getFullName()).isEqualTo(dto2.getFullName());
                            assertThat(result.getIssueCount()).isEqualTo(dto2.getIssueCount());
                        }
                )
                .assertNext(result -&amp;gt; {
                            assertThat(result.getId()).isEqualTo(dto3.getId());
                            assertThat(result.getFullName()).isEqualTo(dto3.getFullName());
                            assertThat(result.getIssueCount()).isEqualTo(dto3.getIssueCount());
                        }
                )
                .verifyComplete();
    }&lt;/code&gt;&lt;/pre&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-filename=&quot;스크린샷 2022-08-26 오후 11.43.47.png&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2WgEP/btrKHwzLuoG/cFKKj7jvrlvKfBiAy8vKz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2WgEP/btrKHwzLuoG/cFKKj7jvrlvKfBiAy8vKz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2WgEP/btrKHwzLuoG/cFKKj7jvrlvKfBiAy8vKz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2WgEP%2FbtrKHwzLuoG%2FcFKKj7jvrlvKfBiAy8vKz0%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;1128&quot; height=&quot;140&quot; data-filename=&quot;스크린샷 2022-08-26 오후 11.43.47.png&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;140&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MockWebServer For Junit5&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MockWebServer를 이용하여 예제를 구현해봤는데 아래와 같이 Junit5/Junit4 를 위한 라이브러리도 있으니 사용해보면 좋을 것 같다.&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-filename=&quot;스크린샷 2022-08-26 오후 11.47.40.png&quot; data-origin-width=&quot;1095&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CEB2r/btrKFFLMigV/q0VEvezZ8NkkoEk8fMmmT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CEB2r/btrKFFLMigV/q0VEvezZ8NkkoEk8fMmmT1/img.png&quot; data-alt=&quot;https://github.com/square/okhttp/tree/master/mockwebserver-junit5&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CEB2r/btrKFFLMigV/q0VEvezZ8NkkoEk8fMmmT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCEB2r%2FbtrKFFLMigV%2Fq0VEvezZ8NkkoEk8fMmmT1%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;1095&quot; height=&quot;439&quot; data-filename=&quot;스크린샷 2022-08-26 오후 11.47.40.png&quot; data-origin-width=&quot;1095&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/square/okhttp/tree/master/mockwebserver-junit5&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/square/okhttp/tree/master/mockwebserver&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MockWebServer&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/square/okhttp/tree/master/mockwebserver-junit5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MockWebServer For Junit5&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/square/okhttp/tree/master/mockwebserver-junit4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MockWebServer for Junit4&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/square/okhttp/tree/master/mockwebserver&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MockWebServer&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/spring-mocking-webclient&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; Baeldung&lt;/a&gt;&lt;/p&gt;</description>
      <category>jvm언어관련/Test</category>
      <category>HttpClient Mock</category>
      <category>Mock WebClient</category>
      <category>Mocking WebClient</category>
      <category>MockWebServer</category>
      <category>Stubbing WebClient</category>
      <category>webclient</category>
      <category>WebClient Mock</category>
      <author>055055</author>
      <guid isPermaLink="true">https://055055.tistory.com/118</guid>
      <comments>https://055055.tistory.com/118#entry118comment</comments>
      <pubDate>Fri, 26 Aug 2022 22:59:22 +0900</pubDate>
    </item>
    <item>
      <title>[몽고DB 완벽 가이드 3판] 몽고 DB 소개</title>
      <link>https://055055.tistory.com/117</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;몽고 DB&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고 DB는 강력하고 유연하며 확장성 높은 범용 데이터베이스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 보조 인덱스(secondary index)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 범위 쿼리(range query)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 정렬(sorting)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 집계(aggregation)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 공간 정보 인덱스(geospatial index)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 손쉬운 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고 DB는 관계형 데이터베이스가 아니라 도큐먼트 지향 데이터베이스다. 관계형 모델을 사용하지 않는 주된 이유는 분산 확장을 쉽게 하기 위함이지만 다른 이점도 있다.&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; 도큐먼트(document)&lt;/b&gt;를 사용한다.&lt;b&gt; 내장 도큐먼트와 배열을 허용함으로써 도큐먼트 지향 모델은 복잡한 계층관계를 하나의 레코드로 표현할 수 있&lt;/b&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;또한, 몽고DB에서는 도큐먼트의 키와 값을 미리 정의하지 않는다. 따라서 &lt;b&gt;고정된 스키마가 없다.&lt;/b&gt; 고정된 스키마가 없으므로 필요할 때마다 쉽게 필드를 추가하거나 제거할 수 있다. 덕분에 개발 과정을 빠르게 반복할 수 있어서 개발 속도가 향상된다. 또한 모델을 실험해보기도 쉽다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2&amp;nbsp; 확장 가능한 설계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장할 데이터가 증가함에 따라 개발자는 '데이터베이스를 어떻게 확장할 것인가'와 같은 어려운 의사 결정을 해야 하는 상황에 직면한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스의 확장은 결국 더 큰 장비로 성능 확장(scale-up) 할지 혹은 여러 장비에 데이터를 나눠 분산 확장할지(scale-out) 결정해야 하는 갈림길에 서게 된다.&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;일반적으로 성능 확장(scale-up)이 더 편한 길이지만 단점이 있다. 대형 장비는 대체로 가격이 비싸고 결국에는 더는 확장할 수 없는 물리적 한계에 부딪히고 만다.&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;반면에 분산 확장(sacle-out)은 저장 공간을 늘리거나 처리량을 높이고 서버를 구매해서 클러스터에 추가하는 방법이다 따라서 경제적이고 확장이 용이하다. 하지만 수천대의 장비를 운영해야 하기 때문에 화나의 장비만 관리할 때 보다 관리가 더 어려워진다.&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;몽고DB는 분산 확장을 염두에 두고 설계됐다. 도큐먼트 지향 데이터 모델은 데이터를 여러 서버에 더 쉽게 분산하게 해준다. 도큐먼트를 자동으로 재분배하고 사용자 요청을 올바른 장비에 라우팅함으로써 클러스터 내 데이터 양과 부하를 조절할 수 있다.&lt;/b&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-filename=&quot;몽고DB확장.jpeg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;843&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZSuPQ/btrKhRexpDC/7q3ueLKqdTUDLktytoGCPK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZSuPQ/btrKhRexpDC/7q3ueLKqdTUDLktytoGCPK/img.jpg&quot; data-alt=&quot;여러 서버에 걸쳐 샤딩을 사용한 몽고 DB 확장&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZSuPQ/btrKhRexpDC/7q3ueLKqdTUDLktytoGCPK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZSuPQ%2FbtrKhRexpDC%2F7q3ueLKqdTUDLktytoGCPK%2Fimg.jpg&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;1440&quot; height=&quot;843&quot; data-filename=&quot;몽고DB확장.jpeg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;843&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;여러 서버에 걸쳐 샤딩을 사용한 몽고 DB 확장&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;몽고 DB 클러스터의 토폴로지(topology)나 데이터베이스 연결의 다른 쪽 끝에 단일 노드가 아닌 클러스터가 있는지는 애플리케이션에서 분명히 알 수 있다. 따라서 개발자는 애플리케이션을 확장이 아니라 프로그래밍에 집중할 수 있다.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.3 다양한 기능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고DB는 범용 데이터베이스로 만들어졌기 때문에 데이터의 생성, 읽기, 변경, 삭제 외에도 데이터베이스 관리 시스템(DBMS)의 대부분의 기능과 더불어 다음과 같은 기능을 제공한다.&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;몽고DB는 일반적인 보조 인덱스를 지원하며 고유(unique), 복합(compound), 공간 정보, 전문(full-text) 인덱싱 기능도 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩된 도큐먼트(nested document) 및 배열과 같은 계층 구조의 보조 인덱스도 지원하며, 개발자는 모델링 기능을 자신의 애플리케이션에 가장 적합한 방식으로 최대한 활용할 수 있다.&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;몽고DB는 데이터 처리 파이프라인 개념을 기반으로 한 집계 프레임워크를 제공한다. 집계 파이프라인(aggregation pipeline)은 데이터베이스 최적화를 최대한 활용해, 서버측에서 비교적 간단한 일련의 단계로 데이터를 처리함으로써 복잡한 분석 엔진을 구축하게 해준다.&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;몽고 DB는 로그와 같은 최신 데이터를 유지하고자 세션이나 고정 크기 컬렉션(제한 컬렉션)과 같이 특정 시간에 만료해야 하는 데이터에 대해 유효시간 (TTL: Time-To-Live) 컬렉션을 지원한다. 또한 기준 필터와 일치하는 도큐먼트에 한정된 부분 인덱스를 지원함으로써 효율성을 높이고 필요한 저장공간을 줄인다.&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;몽고DB는 큰 파일과 파일 메타데이터를 편리하게 저장하는 프로토콜을 지원한다.&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;관계형 데이터베이스에 공통적으로 사용되는 일부 기능, 특히 복잡한 조인은 몽고 DB에 존재하지 않는다. 몽고DB는 3.2에 도입된 $lookup 집계 연산자를 사용함으로써 매우 제한된 방식으로 조인하도록 지원한다. 3.6 버전에서는 관련 없는 서브쿼리 뿐만 아니라 여러 조인 조건으로 보다 복잡한 조인도 할 수 있다. 두 가지 기능 모두 분산 시스템에서 효율적으로 제공하기 어렵기 때문에, 몽고 DB의 조인은 더 큰 확장성을 허용하기 위한 아키텍처적인 결정이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.4 고성능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고 DB의 주요 목표인 성능은 몽고 DB 설계에 지대한 영향을 미쳤다. 몽고DB에서는 동시성(concurrency)과 처리량을 극대화하기 위해 와이어드타이거(WiredTiger) 스토리지 엔진(storage engine)에 기회적 락(opportunistic locking)을 사용했다. 따라서 캐시처럼 제한된 용량의 램으로 쿼리에 알맞은 인덱스를 자동으로 선택할 수 있다. &lt;b&gt;요약하자면 몽고 DB는 모든 측면에서 고성능을 유지하기 위해 설계됐다.&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;몽고DB는 강력한 성능을 제공하면서도 관계형 시스템의 많은 기능을 포함한다. 하지만 관계형 데이터베이스의 작업을 전부 하려는 것은 아니다. 일부 기능의 경우 데이터베이스 서버는 처리와 로직을 클라이언트에 오프로드한다.(드라이버 또는 사용자의 애플리케이션 코드에 의 해 처리된다.) 이러한 간소한 설계 덕분에 몽고DB는 뛰어난 성능을 발휘한다.&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;h3 data-ke-size=&quot;size23&quot;&gt;1.5 몽고 DB의 철학&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 몽고 DB 프로젝트의 주 관심사는 확장성이 높으며 유연하고 빠른, 즉 완전한 기능을 갖춘 데이터 스토리지를 만드는 일이다.&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;h2 data-ke-size=&quot;size26&quot;&gt;출처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;몽고DB완벽가이드&quot; href=&quot;https://search.shopping.naver.com/book/catalog/32481967929?query=%EB%AA%BD%EA%B3%A0DB%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C&amp;amp;NaPm=ct%3Dl74vfpgg%7Cci%3Ddee02388f64806d5b95e748f8c3620581a70d2c6%7Ctr%3Dboksl%7Csn%3D95694%7Chk%3D4ee1a27cdd0b5603cffcc242b6d17fbc261c8a54&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://search.shopping.naver.com/book/catalog/32481967929?query=%EB%AA%BD%EA%B3%A0DB%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C&amp;amp;NaPm=ct%3Dl74vfpgg%7Cci%3Ddee02388f64806d5b95e748f8c3620581a70d2c6%7Ctr%3Dboksl%7Csn%3D95694%7Chk%3D4ee1a27cdd0b5603cffcc242b6d17fbc261c8a54&lt;/a&gt;&lt;/p&gt;</description>
      <category>DB/MongoDB</category>
      <category>MongoDB</category>
      <category>mongodb 특징</category>
      <category>몽고 디비 장점</category>
      <category>몽고DB 완벽 가이드</category>
      <category>몽고DB완벽가이드</category>
      <category>몽고디비 장점</category>
      <category>몽고디비 특징</category>
      <author>055055</author>
      <guid isPermaLink="true">https://055055.tistory.com/117</guid>
      <comments>https://055055.tistory.com/117#entry117comment</comments>
      <pubDate>Tue, 23 Aug 2022 09:00:04 +0900</pubDate>
    </item>
    <item>
      <title>[EffectiveJava 3/E] 2장 객체 생성과 파괴 - 아이템2 생성자에 매개변수가 많다면 빌더를 고려하라</title>
      <link>https://055055.tistory.com/116</link>
      <description>&lt;h1&gt;생성자에 매개변수가 많다면 빌더를 고려하라&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩터리와 생성자에는 똑같은 제약이 하나 있다. 선택적 매개변수가 많을 때 적절히 대응하기 어렵다는 점이다.&lt;br /&gt;식품 포장의 영양정보를 표현하는 클래스를 생각해보자. 필수 항목 몇개와 대부분 값이 그냥 0인 선택 항목으로 이루어 진다.&lt;br /&gt;프로그래머들은 아래와 같은 방식들로 해결하고자 하였다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;점층적 생성자 패턴(telescoping constructor pattern)&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;   public class NutritionFacts {
    private final int servingSize;      // (ml, 1회 제공량)   필수
    private final int servings;         // (회, 총 n회 제공량)  필수
    private final int calories;         // (1회 제공량당)      선택
    private final int fat;              // (g/1회 제공량)     선택
    private final int sodium;           // (mg/1회 제공량)    선택
    private final int carbohydrate;     // (g/1회 제공량)     선택

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

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

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

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

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

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 클래스의 인스턴스를 만들려면 원하는 매개변수를 모두 포함한 생성자 중 가장 짧은 것을 골라 호출하면 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점층적 생성자 패턴은 매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다는 단점이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&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;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자바 빈즈패턴(javaBeans pattern)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수가 없는 생성자로 객체를 만든 후, 세터(setter) 메서드들을 호출해 원하는 매개변수의 값을 설정하는 방식&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;public class NutritionFacts {
    private int servingSize = -1; //필수; 기본값 없음
    private int servings = -1;    //필수; 기본값 없음
    private int calories = 0;
    private int fat = 0;
    private int sodium = 0;
    private int carbohydrate = 0;

    public NutritionFacts() {
    }

    public void setServingSize(int servingSize) {
        this.servingSize = servingSize;
    }

    public void setServings(int servings) {
        this.servings = servings;
    }

    public void setCalories(int calories) {
        this.calories = calories;
    }

    public void setFat(int fat) {
        this.fat = fat;
    }

    public void setSodium(int sodium) {
        this.sodium = sodium;
    }

    public void setCarbohydrate(int carbohydrate) {
        this.carbohydrate = carbohydrate;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점층적 생성자 패턴의 단점들이 자바빈즈 패턴에서는 더 이상 보이지 않는다.&lt;br /&gt;코드가 길어지기는 했지만 인스턴스를 만들기 쉽고, 그 결과 더 읽기 쉬운 코드가 되었다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 불행히도 자바빈즈는 자신만의 심각한 단점을 지니고 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바빈즈 패턴에서는 객체 하나를 만들려면 메서드를 여러 개 호출해야 하고, 객체가 완전히 생성되기 전까지는 일관성이 무너진 상태에 놓이게 된다.&lt;br /&gt;이처럼 일관성이 무너지는 문제 때문에 자바빈즈 패턴에서는 클래스를 불변으로 만들 수 없으며 스레드 안전성을 얻으려면 프로그래머가 추가 작업을 해줘야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;빌더 패턴(Builder pattern)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점층적 생성자 패턴의 안전성과 자바빈즈 패턴의 가독성을 겸비했다.&lt;br /&gt;클라이언트는 필요한 객체를 직접 만드는 대신, 필수 매개변수만으로 생성자(혹은 정적 팩터리)를 호출해 빌더 객체를 얻는다. 그런 다음 빌더 객체가 제공하는 일종의 세터 메서드들로 원하는 선택 매개변수들을 설정한다.&lt;br /&gt;마지막으로 매개변수가 없는 build 메서드를 호출해 드디어 우리에게 필요한(보통은 불변인) 객체를 얻는다. 빌더는 생성할 클래스 안에 정적 멤버 클래스로 만들어두는게 보통이다.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

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

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

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

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

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

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

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

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

    private NutritionFacts(Builder builder){
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NutritionFacts 클래스는 불변이며, 모든 매개변수의 기본값들을 한곳에 모아뒀다.&lt;br /&gt;빌더의 세터 메서드들은 빌더 자신을 반환하기 때문에 연쇄적으로 호출할 수 있다.(플루언트 api or 메서드 연쇄)&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
                .calories(100).sodium(35).carbohydrate(27).build();&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 정리&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;점층적 생성타 패턴 - 확장하기 어렵다.&lt;/li&gt;
&lt;li&gt;자바빈즈 패턴 - 일관성이 깨지고, 불변으로 만들 수 없다.&lt;/li&gt;
&lt;li&gt;빌더 패턴 - 점층적 생성자 패턴과 자바빈즈 패턴의 장점만 취했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자나 정적 팩터리가 처리해야 할 매개변수가 많다면 빌더패턴을 선택하는게 더 낫다.&lt;br /&gt;매개변수 중 다수가 필수가 아니거나 같은 타입이면 특히 더 그렇다. 빌더는 점층적 생성자보다 클라이언트 코드를 읽고 쓰기가 훨씬 간결하고, 자바빈즈보다 훨씬 안전하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://search.shopping.naver.com/book/catalog/32436239326?cat_id=50010920&amp;amp;frm=PBOKMOD&amp;amp;query=%08%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C+%EC%9E%90%EB%B0%94&amp;amp;NaPm=ct%3Dl6z138ns%7Cci%3Db0f99d2f2a318dd7ab9dac8495e63c0076fddd86%7Ctr%3Dboknx%7Csn%3D95694%7Chk%3D71936725f1d33db581c3d8c6a56703ade1a7c790&quot;&gt;이펙티브자바&lt;/a&gt;&lt;/p&gt;</description>
      <category>jvm언어관련/effective-java</category>
      <category>Builder</category>
      <category>builder pattern</category>
      <category>Effective Java</category>
      <category>effective java builder pattern</category>
      <category>effectivejava</category>
      <category>java</category>
      <category>빌더</category>
      <category>빌더패턴</category>
      <category>이펙티브 자바 빌더패턴</category>
      <category>이펙티브자바</category>
      <author>055055</author>
      <guid isPermaLink="true">https://055055.tistory.com/116</guid>
      <comments>https://055055.tistory.com/116#entry116comment</comments>
      <pubDate>Mon, 22 Aug 2022 23:15:01 +0900</pubDate>
    </item>
    <item>
      <title>[EffectiveJava 3/E] 2장 객체 생성과 파괴 - 아이템1 생성자 대신 정적 팩터리 메서드를 고려하라</title>
      <link>https://055055.tistory.com/115</link>
      <description>&lt;h1&gt;생성자 대신 정적 팩터리 메서드를 고려하라&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 클래스의 인스턴스를 얻는 전통적인 수단은 public 생성자다.&lt;br /&gt;하지만 모든 프로그래머가 꼭 알아둬야 할 기법이 하나 더 있다.&lt;br /&gt;클래스는 생성자와 별도로 정적 팩터리 메서드(static factory method)를 제공할 수 있다.&lt;br /&gt;그 클래스의 인스턴스를 반환하는 단순한 정적 메서드 말이다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;public static Boolean valueOf (boolean b) {
        return b?Boolean.TRUE:Boolean.FALSE;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 boolean 기본 타입의 박싱 클래스(boxed class)인 Boolean에서 발췌한 간단한예다.&lt;br /&gt;이 메서드는 기본 타입인 boolean 값을 받아 Boolean 객체 참조로 변환해준다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정적 팩터리 메서드가 생성자보다 좋은 다섯가지 장점&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름을 가질 수 있다.&lt;br /&gt;생성자에 넘기는 매개변수와 생성자 자체만으로는 반환될 객체의 특성을 제대로 설명하지 못한다.&lt;br /&gt;반면 정적 팩터리는 이름만 잘 지으면 반환될 객체의 특성을 쉽게 묘사할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;ex) BigInteger(int, int, Random)와 정적 팩터리 메서드인 BigInteger.probablePrime 중 어느 쪽이 값이 소수인 BigInteger를 반환한다는 의미를 더 잘 설명하는지 생각해보자.&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 덕분에 불변 클래스(immutable class)는 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체 생성을 피할 수 있다.&lt;br /&gt;　&lt;br /&gt;ex) Boolean.valueOf(boolean) 메서드는 객체를 아예 생성하지 않는다.&lt;br /&gt;따라서 (특히 생성 비용이 큰) 같은 객체가 자주 요청되는 상황이라면 성능을 상당히 끌어올려 준다.&lt;/li&gt;
&lt;li&gt;반복되는 요청에 같은 객체를 반환하는 식으로 정적 팩터리 방식의 클래스는 언제 어느 인스턴스를 살아있게 할지를 철저히 통제할 수 있다. 이런 클래스를 &lt;b&gt;인스턴스 통제(instance-controlled)&lt;/b&gt; 클래스라 한다.&lt;br /&gt;　&lt;br /&gt;인스턴스를 통제하면 클래스를 싱글턴으로 만들 수도, 인스턴스화 불가로 만들수도 있다. 또한 불변 값 클래스에서 동치인 인스턴스가 단 하나뿐임을 보장할 수 있다.(a == b 일때만 a.equals(b)가 성립).&lt;br /&gt;인스턴스 통제는 플라이웨이트 패턴의 근간이 되며, 열거 타입은 인스턴스가 하나만 만들어짐을 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;3&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.&lt;br /&gt;이를 통해 반환할 객체의 클래스를 자유롭게 선택할 수 있게 하는 '엄청난 유연성을' 선물한다.&lt;br /&gt;API를 만들 때 이 유연성을 응용하면 구현 클래스를 공개하지 않고도 그 객체를 반환할 수 있어 API를 작게 유지할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.&lt;br /&gt;반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관 없다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.&lt;br /&gt;클래스를 미리 만들지 않아도 컴파일이 가능하도록 유연함 제공&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정적 팩터리의 단점&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.&lt;br /&gt;즉 private 생성자만 제공해서는 상속을 할 수 없다. 그러나 이 제약은 상속보다 컴포지션을 사용하도록 유도하고 불변 타입으로 만들려면 이 제약을 지켜야 한다는 점에서 오히려 장점이 될 수도 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 팩터리 메서드는 프로그래머가 찾기 어렵다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정적 팩터리 메서드에 흔히 사용하는 명명 방식&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;from : 매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ex) Date d = Date.from(instant);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;of: 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ex) Set&amp;lt;Rank&amp;gt; faceCards = EnumSet.of(JACK, QUEEN, KING);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;valueOf: from과 of의 더 자세한 버전&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ex) BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;instance 혹은 getInstance: (매개변수를 받는다면) 매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장하지는 않는다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ex) StackWalker luke = StackWalker.getInstance(options);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;create 혹은 newInstance: instance 혹은 getInstance와 같지만, 매번 새로운 인스턴스를 생성해 반환함을 보장한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ex) Object newArray = Array.newInstance(classObject, arrayLen);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;getType: getInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 쓴다. &quot;Type&quot;은 팩터리 메서드가 반환할 객체의 타입이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ex) FileStore fs = Files.getFileStore(path);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;newType: newInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 쓴다. &quot;Type&quot;은 팩터리 메서드가 반환할 객체의 타입이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ex) BufferedReader br = Files.newBufferedReader(path);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;type: getType과 newType의 간결한 버전&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ex) List&amp;lt;Complaint&amp;gt; litany = Collections.list(legacyLitany);&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 정리&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩터리 메서드와 public 생성자는 각자의 쓰임새가 있으니 상대적인 장단점을 이해하고 사용하는 것이 좋다.&lt;br /&gt;그렇다고 하더라도 정적 팩터리를 사용하는 게 유리한 경우가 더 많으므로 무작정 public 생성자를 제공하던 습관이 있다면 고치자&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://search.shopping.naver.com/book/catalog/32436239326?cat_id=50010920&amp;amp;frm=PBOKMOD&amp;amp;query=%08%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C+%EC%9E%90%EB%B0%94&amp;amp;NaPm=ct%3Dl6z138ns%7Cci%3Db0f99d2f2a318dd7ab9dac8495e63c0076fddd86%7Ctr%3Dboknx%7Csn%3D95694%7Chk%3D71936725f1d33db581c3d8c6a56703ade1a7c790&quot;&gt;이펙티브자바&lt;/a&gt;&lt;/p&gt;</description>
      <category>jvm언어관련/effective-java</category>
      <category>Effective Java</category>
      <category>effectivejava</category>
      <category>java</category>
      <category>java of</category>
      <category>이펙티브 자바</category>
      <category>이펙티브자바</category>
      <category>정적 팩터리 메서드</category>
      <category>정적 팩터리 메서드 단점</category>
      <category>정적 팩터리 메서드 장점</category>
      <author>055055</author>
      <guid isPermaLink="true">https://055055.tistory.com/115</guid>
      <comments>https://055055.tistory.com/115#entry115comment</comments>
      <pubDate>Thu, 18 Aug 2022 21:39:41 +0900</pubDate>
    </item>
  </channel>
</rss>