<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>지노리 블로그</title>
    <link>https://yeowool0217.tistory.com/</link>
    <description>노닥거리며 개발하는 블로그</description>
    <language>ko</language>
    <pubDate>Sun, 5 Apr 2026 23:08:31 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>jinori</managingEditor>
    <image>
      <title>지노리 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/1869208/attach/5708701b46204df69e64d74284ae3e30</url>
      <link>https://yeowool0217.tistory.com</link>
    </image>
    <item>
      <title>Java Stream API 에서 peek 메서드 사용</title>
      <link>https://yeowool0217.tistory.com/679</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이에서 Stream 사용 중 peek를 사용하라고 추천해줬다.&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;예를 들면 아래와 같이 map을 사용하면 peek을 추천해주는데&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;896&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bttmWl/dJMb99SHNRS/GmABfy3PqTaBS1LTBWsnUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bttmWl/dJMb99SHNRS/GmABfy3PqTaBS1LTBWsnUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bttmWl/dJMb99SHNRS/GmABfy3PqTaBS1LTBWsnUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbttmWl%2FdJMb99SHNRS%2FGmABfy3PqTaBS1LTBWsnUK%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;612&quot; height=&quot;318&quot; data-origin-width=&quot;896&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGbEkI/dJMcahJYAyN/iKmlr3ARlpse5kAoNnHkh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGbEkI/dJMcahJYAyN/iKmlr3ARlpse5kAoNnHkh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGbEkI/dJMcahJYAyN/iKmlr3ARlpse5kAoNnHkh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGbEkI%2FdJMcahJYAyN%2FiKmlr3ARlpse5kAoNnHkh1%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;442&quot; height=&quot;166&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;202&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;인텔리제이에서 추천해주니 Replace하고 그냥 사용할 수 있지만 주석에 써있기로는 디버깅용으로 사용하라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 추천한걸까? 디버깅이 필요해 보였나?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eFflO2/dJMcaaqxkrc/Eooc5XPmesJgjaG90cSdpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eFflO2/dJMcaaqxkrc/Eooc5XPmesJgjaG90cSdpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eFflO2/dJMcaaqxkrc/Eooc5XPmesJgjaG90cSdpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeFflO2%2FdJMcaaqxkrc%2FEooc5XPmesJgjaG90cSdpk%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;648&quot; height=&quot;320&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;620&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;peek로 변경하면 아래와 같은 코드로 변경되는데 결과 값은 map을 사용했을 때와 같다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;.peek(u -&amp;gt; u.setName(&quot;New Name&quot;))&lt;/code&gt;&lt;/pre&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;결과 값이 같은데 왜 peek를 디버깅용에만 사용하라고 하는걸까?&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;주석 내용을 읽어보면 최종 연산 즉 Collect()나 toList() 가 이루어지지 않으면 peek는 실행되지 않으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바가 자체적으로 Stream 최적화를 진행할 때 불필요하다고 판단하면 peek 로직이 무시 될 수 있다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 병렬 스레드에서 peek가 언제 실행되는지 보장되지 않는다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추천 코드도 다시 확인하자.&lt;/p&gt;</description>
      <category>개발노트</category>
      <category>map</category>
      <category>peek</category>
      <category>Stream API</category>
      <author>jinori</author>
      <guid isPermaLink="true">https://yeowool0217.tistory.com/679</guid>
      <comments>https://yeowool0217.tistory.com/679#entry679comment</comments>
      <pubDate>Wed, 21 Jan 2026 23:45:29 +0900</pubDate>
    </item>
    <item>
      <title>GitHub Action으로 CI/CD</title>
      <link>https://yeowool0217.tistory.com/674</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;WorkFlow 생성&lt;/span&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;GitHub 프로젝트 생성 후 Actions 탭으로 이동해 &quot;New workflow&quot; 버튼을 눌러 WorkFlow를 생성합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ekVSqs/btsIvR0Ofo6/PzAKByPjmFlPMTSk6sSOIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ekVSqs/btsIvR0Ofo6/PzAKByPjmFlPMTSk6sSOIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ekVSqs/btsIvR0Ofo6/PzAKByPjmFlPMTSk6sSOIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FekVSqs%2FbtsIvR0Ofo6%2FPzAKByPjmFlPMTSk6sSOIk%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;1666&quot; height=&quot;436&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;436&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;배포하고자 하는 프로젝트언어와 같거나 비슷한 workflow를 찾습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;없으면 &quot;set up a workflow yourselg&quot;를 클릭해 직접 만들면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2518&quot; data-origin-height=&quot;1222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BO2cF/btsIvxIi3SQ/hYV29zA9gqfc6hVePcKReK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BO2cF/btsIvxIi3SQ/hYV29zA9gqfc6hVePcKReK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BO2cF/btsIvxIi3SQ/hYV29zA9gqfc6hVePcKReK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBO2cF%2FbtsIvxIi3SQ%2FhYV29zA9gqfc6hVePcKReK%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;2518&quot; height=&quot;1222&quot; data-origin-width=&quot;2518&quot; data-origin-height=&quot;1222&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;배포 파일 생성&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;파일은 .github/worflows 아래 만들어지게 됩니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;직접 작성한 YML 파일의 예는 아래와 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1720704947005&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: Deploy to Raspberry Pi

on:
  workflow_dispatch:
  push:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-java@v4
      with:
        distribution: 'temurin' # See 'Supported distributions' for available options
        java-version: '21'

    - name: Build with Gradle
      run: ./gradlew build

    - name: Rename jar file
      run: mv ./build/libs/csv-to-json-0.0.1-SNAPSHOT.jar ./app.jar

        
    - name: SCP Jar to server
      uses: appleboy/scp-action@v0.1.7
      with:
        host: ${{ secrets.HOST }}
        username: ${{ secrets.NAME }}
        password: ${{ secrets.PASSWORD }}
        port: ${{ secrets.PORT }}
        source: &quot;app.jar,deploy.sh&quot;
        target: /www/csv-to-json
      
        
    - name: SSH to server and deploy
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.HOST }}
        username: ${{ secrets.NAME }}
        password: ${{ secrets.PASSWORD }}
        port: ${{ secrets.PORT }}
        script: |
          chmod +x /www/csv-to-json/deploy.sh
          sh /www/csv-to-json/deploy.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;푸시 브랜치를 master로 해서 master에 커밋 시 자동으로 실행 되도록 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Java 21 으로 프로젝트를 만들었기 때문에 같은 버전으로 빌드를 돌려주도록 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;만들어진 jar파일은 &quot;appleboy/scp-action&quot;을 사용해서 대상 서버로 jar파일과 배포 스크립트를 전송합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 후 &quot;appleboy/ssh-action&quot;을 사용해 deploy.sh 스크립트를 실행해 배포를 완료합니다.&lt;/span&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Secrets Key/Value 등록&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위 내용에서 보이는 ${{ secrets.xxx }} 와 같은 키들은 Settings 탭에서 Secrets and variables &amp;gt; Actions 에서 등록 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2230&quot; data-origin-height=&quot;1402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bthu3A/btsIvbyL93I/9y4EiaosR8m2Jhh8p283F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bthu3A/btsIvbyL93I/9y4EiaosR8m2Jhh8p283F1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bthu3A/btsIvbyL93I/9y4EiaosR8m2Jhh8p283F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbthu3A%2FbtsIvbyL93I%2F9y4EiaosR8m2Jhh8p283F1%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;2230&quot; height=&quot;1402&quot; data-origin-width=&quot;2230&quot; data-origin-height=&quot;1402&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;배포 실행&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;배포 성공/실패 시 아래와 같이 Actions에서 확인 가능하며&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Run workflow &amp;gt; Run workflow로 수동 배포가 가능합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;(수동 배포를 하기 위해서는 workflow_dispatch: 옵션을 넣어줘야 합니다.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2496&quot; data-origin-height=&quot;704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpmCr9/btsIv6cqLot/RbIB966XJMYAcqpDLbF8T0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpmCr9/btsIv6cqLot/RbIB966XJMYAcqpDLbF8T0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpmCr9/btsIv6cqLot/RbIB966XJMYAcqpDLbF8T0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpmCr9%2FbtsIv6cqLot%2FRbIB966XJMYAcqpDLbF8T0%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;2496&quot; height=&quot;704&quot; data-origin-width=&quot;2496&quot; data-origin-height=&quot;704&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;</description>
      <category>개발노트</category>
      <category>Action</category>
      <category>CI/CD</category>
      <category>Github</category>
      <author>jinori</author>
      <guid isPermaLink="true">https://yeowool0217.tistory.com/674</guid>
      <comments>https://yeowool0217.tistory.com/674#entry674comment</comments>
      <pubDate>Thu, 11 Jul 2024 22:52:24 +0900</pubDate>
    </item>
    <item>
      <title>람다 캡처링 (Lambda Capturing) 정리</title>
      <link>https://yeowool0217.tistory.com/672</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;람다 캡처링은 자바 람다 표현식이 자신이 정의된 범의 외부에 있는 변수를 사용할 수 있는 기능을 의미한다.&lt;/span&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;캡처링 : 람다 표현식이 외부 변수를 사용하기 위해 이를 캡처하는 기능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;final : 람다 표현식에서 사용되는 로컬 변수와 매개변수는 변경되지 않도록 제한해야 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;일관성, 안정성, 스레드 안전성, 컴파일러 최적화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;사용 예 : 콜백 함수, 비동기 처리&lt;/span&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;왜 final 또는 사실상 final이어야 하는가?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;로컬 변수와 메서드 매개변수는 final 또는 사실상 final이어야 람다 표현식 내부에서 사용할 수 있으며 이유는 아래와 같다.&lt;/span&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;일관성 및 안정성: 람다 표현식은 비동기 코드나 콜백으로 사용되는데 이 때 람다 표현식 내부에서 사용하는 외부 변수가 변경될 수 있다면, 값이 예상치 못하게 변경될 위험이 있기 때문에&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;스레드 안전성: 여러 스레드가 동시에 실행되는 환경에서는 변수가 변경될 수 있기 때문에 값의 일관성 유지를 위해&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;컴파일러 최적화: 컴파일러는 해당 변수가 변경되지 않음을 보장할 수 있어야 최적화 하기 쉽다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;람다 캡처링의 사용 예시&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;버튼 클릭 이벤트에서 람다 캡처링&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1717422183615&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import javax.swing.JButton;
import javax.swing.JFrame;

public class LambdaCaptureExample {
    public static void main(String[] args) {
        JFrame frame = new JFrame(&quot;Lambda Capture Example&quot;);
        JButton button = new JButton(&quot;Click Me&quot;);

        String message = &quot;Button clicked!&quot;;

        button.addActionListener(e -&amp;gt; System.out.println(message));

        frame.add(button);
        frame.setSize(200, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위 예제에서 message 변수는 람다 표현식 내부에서 사용되고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 변수는 변경되지 않으므로 사실상 final이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;비동기 처리에서 람다 캡처링&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1717422201820&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.concurrent.CompletableFuture;

public class AsyncLambdaCapture {
    public static void main(String[] args) {
        String taskName = &quot;Async Task&quot;;

        CompletableFuture.runAsync(() -&amp;gt; {
            // 비동기 작업 수행
            System.out.println(taskName + &quot; is running&quot;);
        });

        // 메인 스레드에서 다른 작업 수행
        System.out.println(&quot;Main thread is running&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위 예제에서 taskName 변수는 비동기 작업을 수행하는 람다 표현식 내부에서 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt; 이 변수는 변경되지 않으므로 사실상 final 이다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발노트</category>
      <category>Final</category>
      <category>lambda capturing</category>
      <category>람다</category>
      <category>비동기 처리</category>
      <category>캡처링</category>
      <author>jinori</author>
      <guid isPermaLink="true">https://yeowool0217.tistory.com/672</guid>
      <comments>https://yeowool0217.tistory.com/672#entry672comment</comments>
      <pubDate>Mon, 20 May 2024 23:05:19 +0900</pubDate>
    </item>
    <item>
      <title>CentOS 7 to Rocky Linux 8</title>
      <link>https://yeowool0217.tistory.com/671</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1e1928; text-align: start; font-family: 'Nanum Gothic';&quot;&gt;CentOS Linux 7가 2024년 6월 30일 지원 종료(End of Life, EOL)될 예정으로 OS 마이그레이션이나&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1e1928; text-align: start; font-family: 'Nanum Gothic';&quot;&gt; Rocky 8 OS 설치 후 서비스를 이전하는 방법 둘 중 하나를 진행해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://docs.rockylinux.org/guides/migrate2rocky/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.rockylinux.org/guides/migrate2rocky/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1724678673420&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Migrating To Rocky Linux - Documentation&quot; data-og-description=&quot;How to migrate to Rocky Linux from CentOS Stream, CentOS, AlmaLinux, RHEL, or Oracle LinuxPrerequisites and assumptionsCentOS Stream, CentOS, AlmaLinux, RHEL, or Oracle Linux running on a hardware server or VPS. Non-Stream CentOS is frozen at version 8.5. &quot; data-og-host=&quot;docs.rockylinux.org&quot; data-og-source-url=&quot;https://docs.rockylinux.org/guides/migrate2rocky/&quot; data-og-url=&quot;https://docs.rockylinux.org/guides/migrate2rocky/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bESyx1/hyWShkbv1p/BbNSW6duV8XsAfR8KmhXxK/img.png?width=1044&amp;amp;height=888&amp;amp;face=0_0_1044_888,https://scrap.kakaocdn.net/dn/vTjBF/hyWSak1rNj/Zek6b08FnA1g942aVjV8IK/img.png?width=1096&amp;amp;height=490&amp;amp;face=0_0_1096_490,https://scrap.kakaocdn.net/dn/wWZh4/hyWSpbqQJ8/AuUZjz69ymQOJoJ52fLwKk/img.png?width=1044&amp;amp;height=277&amp;amp;face=0_0_1044_277&quot;&gt;&lt;a href=&quot;https://docs.rockylinux.org/guides/migrate2rocky/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.rockylinux.org/guides/migrate2rocky/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bESyx1/hyWShkbv1p/BbNSW6duV8XsAfR8KmhXxK/img.png?width=1044&amp;amp;height=888&amp;amp;face=0_0_1044_888,https://scrap.kakaocdn.net/dn/vTjBF/hyWSak1rNj/Zek6b08FnA1g942aVjV8IK/img.png?width=1096&amp;amp;height=490&amp;amp;face=0_0_1096_490,https://scrap.kakaocdn.net/dn/wWZh4/hyWSpbqQJ8/AuUZjz69ymQOJoJ52fLwKk/img.png?width=1044&amp;amp;height=277&amp;amp;face=0_0_1044_277');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Migrating To Rocky Linux - Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;How to migrate to Rocky Linux from CentOS Stream, CentOS, AlmaLinux, RHEL, or Oracle LinuxPrerequisites and assumptionsCentOS Stream, CentOS, AlmaLinux, RHEL, or Oracle Linux running on a hardware server or VPS. Non-Stream CentOS is frozen at version 8.5.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.rockylinux.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위 사이트를 참고해 rockylinux에서 제공하는 스크립트를 사용해 마이그레이션을 진행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;스크립트는 rocky-lunux 깃허브 &lt;a href=&quot;https://github.com/rocky-linux/rocky-tools/tree/main/migrate2rocky&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/rocky-linux/rocky-tools/tree/main/migrate2rocky&lt;/a&gt;&amp;nbsp;에서 다운로드 받거나&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;명령어로 다운로드 받을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1724679523482&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl https://raw.githubusercontent.com/rocky-linux/rocky-tools/main/migrate2rocky/migrate2rocky.sh -o migrate2rocky.sh&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;다운로드 받고 서비스 종료 및 백업 후 아래 순서대로 마이그레이션을 진행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;마이그레이션은 CentOS 7 -&amp;gt; CentOS 8 -&amp;gt; Rocky 8 순서로 진행한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1724680382678&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# CentOS7 업데이트 및 reboot
sudo yum -y update --exclude=java*,nginx*
sudo reboot

# 커널 버전 확인
sudo uname -r

# epel-release 설치
sudo yum install epel-release -y

# 패키지 설치
sudo yum install -y yum-utils rpmconf

# YUM 에서 DNF 로 업그레이드
sudo yum install -y dnf
sudo dnf remove -y yum yum-metadata-parser 
sudo rm -Rf /etc/yum

# DNF repository 용 캐시 구성
sudo dnf -y makecache

# CentOS7 패키지 업그레이드
sudo dnf -y upgrade --exclude=java*,nginx*

# epel-release repository 업그레이드
sudo dnf upgrade -y epel-release

# CentOS7 에서 CentOS8 업그레이드
cd /etc/yum.repos.d
  
sudo mkdir backups
  
sudo mv CentOS-* backups
  
# Create new for CentOS BaseOS repo
sudo tee CentOS-Linux-BaseOS.repo&amp;lt;&amp;lt;EOM
[baseos]
name=CentOS Linux \$releasever - BaseOS
baseurl=http://vault.centos.org/8.5.2111/BaseOS/\$basearch/os/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
EOM
  
# Create new for CentOS AppStream repo
sudo tee CentOS-Linux-AppStream.repo&amp;lt;&amp;lt;EOM
[appstream]
name=CentOS Linux \$releasever - AppStream
baseurl=http://vault.centos.org/8.5.2111/AppStream/\$basearch/os/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
EOM


# CentOS 커널 제거
sudo rpm -e `rpm -q kernel` --nodeps
sudo rpm -e `rpm -q kernel-devel` --nodeps

# 충돌 패키지 제거
sudo rpm -e --nodeps sysvinit-tools

# CentOS8 업그레이드
# 업그레이드 시 충돌하는 패키지 오류가 있을 수 있다. 오류나는 패키지는 삭제 후 진행한다.
sudo dnf remove -y dracut-network redhat-rpm-config openssl11-libs-1:1.1.1k-7.el7.x86_64 python36-rpmconf
 
# dracut-network
# redhat-rpm-config
# openssl11-libs-1:1.1.1k-5.el7.x86_64
# python36-rpmconf
 
sudo dnf -y --releasever=8 --allowerasing --setopt=deltarpm=false distro-sync --exclude=java*,nginx*

# CentOS8 커널 설치
sudo dnf install -y kernel-core

# CentOS8 설치
sudo dnf -y groupupdate &quot;Core&quot; &quot;Minimal Install&quot;

# 시스템 재시작
sudo systemctl reboot

# 재시작 후 버전 확인
cat /etc/redhat-release&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;정상적인 재시작 확인 후 버전이 CentOS 8 인 것을 확인 후 아래 스크립트를 진행한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1724680594588&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo dnf -y install wget
wget https://raw.githubusercontent.com/rocky-linux/rocky-tools/main/migrate2rocky/migrate2rocky.sh
  
# 스크립트 실행 권한 부여
chmod a+x migrate2rocky.sh
  
# migrate2rocky.sh 내용 중 아래의 내용을 추가한다.
# infomsg $'\nSyncing packages\n\n'
# dnf -y distro-sync --exclude=java*,nginx* || exit_message &quot;Error during distro-sync.&quot;
  
sudo ./migrate2rocky.sh -r

# 재시작 
sudo reboot now

# Rocky OS 8 정보 확인
cat /etc/os-release
 
 
NAME=&quot;Rocky Linux&quot;
VERSION=&quot;8.9 (Green Obsidian)&quot;
ID=&quot;rocky&quot;
ID_LIKE=&quot;rhel centos fedora&quot;
VERSION_ID=&quot;8.9&quot;
PLATFORM_ID=&quot;platform:el8&quot;
PRETTY_NAME=&quot;Rocky Linux 8.9 (Green Obsidian)&quot;
ANSI_COLOR=&quot;0;32&quot;
LOGO=&quot;fedora-logo-icon&quot;
CPE_NAME=&quot;cpe:/o:rocky:rocky:8:GA&quot;
HOME_URL=&quot;https://rockylinux.org/&quot;
BUG_REPORT_URL=&quot;https://bugs.rockylinux.org/&quot;
SUPPORT_END=&quot;2029-05-31&quot;
ROCKY_SUPPORT_PRODUCT=&quot;Rocky-Linux-8&quot;
ROCKY_SUPPORT_PRODUCT_VERSION=&quot;8.9&quot;
REDHAT_SUPPORT_PRODUCT=&quot;Rocky Linux&quot;
REDHAT_SUPPORT_PRODUCT_VERSION=&quot;8.9&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발노트</category>
      <category>centos 7</category>
      <category>centos 8</category>
      <category>EOL</category>
      <category>EOS</category>
      <category>rocky 8</category>
      <category>마이그레이션</category>
      <author>jinori</author>
      <guid isPermaLink="true">https://yeowool0217.tistory.com/671</guid>
      <comments>https://yeowool0217.tistory.com/671#entry671comment</comments>
      <pubDate>Sat, 18 May 2024 13:20:33 +0900</pubDate>
    </item>
    <item>
      <title>StringJoiner, StringBuilder 정리</title>
      <link>https://yeowool0217.tistory.com/670</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;실무에서 관습적으로 StringBuilder를 사용해 구분자를 처리하는 로직을 보고 StringJoiner로 리팩터링을 진행하며 관련 내용을 정리한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #424242; border-left: 10px solid #424242; border-bottom: 3px solid #424242; padding: 5px; padding-left: 20px;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;StringBuilder&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;StringBuilder는 문자열을 연결할 때 사용하며&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;구분자를 추가 할 때 아래와 같이 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1717074062216&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;StringBuilder builder = new StringBuilder();
builder.append(&quot;Hello&quot;);
builder.append(&quot;,&quot;);
builder.append(&quot;World&quot;);
String result = builder.toString(); // &quot;Hello,World&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #424242; border-left: 10px solid #424242; border-bottom: 3px solid #424242; padding: 5px; padding-left: 20px;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;StringJoiner&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;StringJoiner는 내부에서 StringBuilder를 사용해 문자열과 문자열을 구분자로 연결하는 작업을 하며&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;구분자를 추가 할 때 아래와 같이 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1717074123650&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;StringJoiner joiner = new StringJoiner(&quot;,&quot;);
joiner.add(&quot;Hello&quot;);
joiner.add(&quot;World&quot;);
String result = joiner.toString(); // &quot;Hello,World&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #424242; border-left: 10px solid #424242; border-bottom: 3px solid #424242; padding: 5px; padding-left: 20px;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;벤치마크&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;StringJoiner는 불필요한 구분자 추가 코드가 필요 없어 코드가 깔끔해진다. 하지만 구분자를 추가하는 후처리 작업을 해주기 때문에 성능은 떨어진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;StringBuilder의 경우 구분자를 모두 append()로 추가해줘야 하기 때문에 코드가 불필요하게 길어 질 수 있지만 후처리 작업 없이 문자열 그대로를 사용하므로 성능이 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;아래는  StringBuilder와 StringJoiner의 처리 속도 비교를 위한 밴치마크 코드이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1717074926730&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

import java.util.StringJoiner;
import java.util.function.Supplier;

public class 문자열벤치마크테스트 {

    @Test
    public void stringBuilder벤치마크테스트() {
        int 반복횟수 = 100000;
        int 실행횟수 = 10;

        long stringBuilder평균시간 = 벤치마크(실행횟수, () -&amp;gt; {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i &amp;lt; 반복횟수; i++) {
                sb.append(&quot;테스트&quot;).append(i);
            }
            return sb;
        });

        System.out.println(&quot;StringBuilder 평균 시간: &quot; + stringBuilder평균시간 + &quot; ns&quot;);
        assertTrue(stringBuilder평균시간 &amp;gt; 0); // 시간 측정이 제대로 되었는지 확인
    }

    @Test
    public void stringJoiner벤치마크테스트() {
        int 반복횟수 = 100000;
        int 실행횟수 = 10;

        long stringJoiner평균시간 = 벤치마크(실행횟수, () -&amp;gt; {
            StringJoiner sj = new StringJoiner(&quot;, &quot;);
            for (int i = 0; i &amp;lt; 반복횟수; i++) {
                sj.add(&quot;테스트&quot; + i);
            }
            return sj;
        });

        System.out.println(&quot;StringJoiner 평균 시간: &quot; + stringJoiner평균시간 + &quot; ns&quot;);
        assertTrue(stringJoiner평균시간 &amp;gt; 0); // 시간 측정이 제대로 되었는지 확인
    }

    private long 벤치마크(int 실행횟수, Supplier&amp;lt;?&amp;gt; 작업) {
        long 총시간 = 0;

        for (int j = 0; j &amp;lt; 실행횟수; j++) {
            long 시작시간 = System.nanoTime();
            작업.get();
            long 종료시간 = System.nanoTime();
            총시간 += (종료시간 - 시작시간);
        }

        return 총시간 / 실행횟수;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;StringBuilder 평균 시간: 4508579 ns (약 4.5초)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;StringJoiner 평균 시간: 6739203 ns (약 6.7초)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;StringBuilder가 StringJoiner보다 평균 약 1.49배(6,739,203 나노초를 4,508,579 나노초로 나눈 값) 더 빠른 것을 알 수 있다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발노트</category>
      <category>java</category>
      <category>StringBuilder</category>
      <category>stringjoiner</category>
      <author>jinori</author>
      <guid isPermaLink="true">https://yeowool0217.tistory.com/670</guid>
      <comments>https://yeowool0217.tistory.com/670#entry670comment</comments>
      <pubDate>Sat, 18 May 2024 13:11:07 +0900</pubDate>
    </item>
    <item>
      <title>Nginx Reverse Proxy 정리</title>
      <link>https://yeowool0217.tistory.com/669</link>
      <description>&lt;h2 style=&quot;color: #424242; border-left: 10px solid #424242; border-bottom: 3px solid #424242; padding: 5px; padding-left: 20px;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt; 리버스 프록시란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;리버스 프록시는 클라이언트로부터의 요청을 받아 이를 적절한 백엔드 서버에 전달하고 백엔드 서버의 응답을 다시 클라이언트로 전달하는 역할을 한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 글에서는 Nginx 리버스 프록시에 대한 주요 기능과 기본 설정만 정리한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;자세한 내용은 공식 문서 참고 &lt;a href=&quot;https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716558973789&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;NGINX Reverse Proxy | NGINX Documentation&quot; data-og-description=&quot;NGINX Reverse Proxy Configure NGINX as a reverse proxy for HTTP and other protocols, with support for modifying request headers and fine-tuned buffering of responses. This article describes the basic configuration of a proxy server. You will learn how to p&quot; data-og-host=&quot;docs.nginx.com&quot; data-og-source-url=&quot;https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/&quot; data-og-url=&quot;https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uhlCN/hyV91uTQ4B/TcdwrVeK45k5oxhSadGv10/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/nAepv/hyV9Q1eoFK/hXAXsqMB5sY6WyKpCnXd01/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uhlCN/hyV91uTQ4B/TcdwrVeK45k5oxhSadGv10/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/nAepv/hyV9Q1eoFK/hXAXsqMB5sY6WyKpCnXd01/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;NGINX Reverse Proxy | NGINX Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;NGINX Reverse Proxy Configure NGINX as a reverse proxy for HTTP and other protocols, with support for modifying request headers and fine-tuned buffering of responses. This article describes the basic configuration of a proxy server. You will learn how to p&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.nginx.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #424242; border-left: 10px solid #424242; border-bottom: 3px solid #424242; padding: 5px; padding-left: 20px;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt; Nginx 리버스 프록시 주요 기능&lt;/span&gt;&lt;/h2&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;font-family: 'Nanum Gothic';&quot;&gt;로드 밸런싱(Load Balancong)&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;font-family: 'Nanum Gothic';&quot;&gt;Nginx는 여러 백엔드 서버로 트래픽을 분산시켜 서버 간 부하를 균등하게 분산시킬 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;라운드 로빈, 가중치 기반 분산, IP 해시 등 다양한 기법을 지원한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;SSL/TLS 종료(SSL&lt;sup class=&quot;footnote&quot;&gt;&lt;a href=&quot;#footnote_669_1&quot; id=&quot;footnote_link_669_1&quot; onmouseover=&quot;tistoryFootnote.show(this, 669, 1)&quot; onmouseout=&quot;tistoryFootnote.hide(669, 1)&quot; style=&quot;color:#f9650d; font-family: Verdana, Sans-serif; display: inline;&quot;&gt;&lt;span style=&quot;display: none;&quot;&gt;[각주:&lt;/span&gt;1&lt;span style=&quot;display: none;&quot;&gt;]&lt;/span&gt;&lt;/a&gt;&lt;/sup&gt;/TLS&lt;sup class=&quot;footnote&quot;&gt;&lt;a href=&quot;#footnote_669_2&quot; id=&quot;footnote_link_669_2&quot; onmouseover=&quot;tistoryFootnote.show(this, 669, 2)&quot; onmouseout=&quot;tistoryFootnote.hide(669, 2)&quot; style=&quot;color:#f9650d; font-family: Verdana, Sans-serif; display: inline;&quot;&gt;&lt;span style=&quot;display: none;&quot;&gt;[각주:&lt;/span&gt;2&lt;span style=&quot;display: none;&quot;&gt;]&lt;/span&gt;&lt;/a&gt;&lt;/sup&gt; Termination)&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;font-family: 'Nanum Gothic';&quot;&gt;Nginx는 클라이트와의 SSL/TLS 연결을 처리하여 백엔드 서버가 암호화되지 않은 트래픽만 처리하도록 할 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이를 통해 백엔드 서버의 부하를 줄이고 SSL 인증서를 중앙에서 관리할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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;font-family: 'Nanum Gothic';&quot;&gt;정적 콘텐츠나 API 응답 등을 캐싱하여 서버 부하를 줄이고 응답 시간을 단축할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;특정 조건에 따라 캐시를 제여 할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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;font-family: 'Nanum Gothic';&quot;&gt;Nginx는 클라이언트로 전달하기 전에 응답 데이터를 압축하여 전송 시간을 줄일 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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;font-family: 'Nanum Gothic';&quot;&gt;클라이언트와 백엔드 서버 간의 통신을 분리하려 보안을 강화할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;방화벽 기능, IP 차단, DDoS 방어 등을 통해 서버를 보호할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #424242; border-left: 10px solid #424242; border-bottom: 3px solid #424242; padding: 5px; padding-left: 20px;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Nginx 리버스 프록시 설정 예시&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Nginx가 example.com 도메인으로 들어오는 요청을 백엔드 서버(예: http://localhost:8080)로 전달&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716558663576&quot; class=&quot;nginx&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}&lt;/code&gt;&lt;/pre&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;font-family: 'Nanum Gothic';&quot;&gt;listen: Nginx가 청취할 포트 번호를 지정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;location /: 모든 요청을 처리할 위치 블록을 정의&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;proxy_pass: 요청을 전달할 백엔드 서버의 주소를 지정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;proxy_set_header: 백엔드 서버로 전달할 헤더를 설정, 이를 통해 클라이언트의 원래 IP 주소와 프로토콜 정보를 백엔드 서버가 알 수 있게 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol class=&quot;footnotes&quot;&gt;
    &lt;li id=&quot;footnote_669_1&quot;&gt;SSL이란 보안 소켓 계층(Secure Sockets Layer)으로서 사용자와 서버 간의 데이터를 암호화하는 표준 기술을 말합니다. &lt;a href=&quot;#footnote_link_669_1&quot;&gt;[본문으로]&lt;/a&gt;&lt;/li&gt;
    &lt;li id=&quot;footnote_669_2&quot;&gt;전송 계층 보안(TLS)은 기존 SSL 취약성을 수정하는 업그레이드된 SSL 버전입니다. TLS는 더 효율적으로 인증하고 암호화된 통신 채널을 계속 지원합니다. &lt;a href=&quot;#footnote_link_669_2&quot;&gt;[본문으로]&lt;/a&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</description>
      <category>개발노트</category>
      <category>nginx</category>
      <category>reverse proxy</category>
      <category>데브옵스</category>
      <author>jinori</author>
      <guid isPermaLink="true">https://yeowool0217.tistory.com/669</guid>
      <comments>https://yeowool0217.tistory.com/669#entry669comment</comments>
      <pubDate>Sat, 18 May 2024 13:09:10 +0900</pubDate>
    </item>
    <item>
      <title>InputStream,  OutputStream 정리</title>
      <link>https://yeowool0217.tistory.com/668</link>
      <description>&lt;h2 style=&quot;color: #424242; border-left: 10px solid #424242; border-bottom: 3px solid #424242; padding: 5px; padding-left: 20px;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;스트림(Stream)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;스트림이란 데이터를 연속적으로 처리하는 개념이다. 데이터가 한번에 전체가 아닌, 일정한 크기 단위로 전달되거나 처리되는 것을 말하며&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;실시간 데이터 처리, 자원을 절약하여 효율적인 데이터 처리에 유리하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Java에서 사용하는 InputStream, OutputStream은 'java.io' 패키지에 속하며 파일, 네트워크 연결, 메모리 배열 등의 데이터 소스로부터 데이터를 읽거나 쓸 때 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #424242; border-left: 10px solid #424242; border-bottom: 3px solid #424242; padding: 5px; padding-left: 20px;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;InputStream&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;바이트 단위로 데이터를 읽기 위한 추상 클래스이며 주요 메서드는 아래와 같다.&lt;/span&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;int read() : 하나의 바이트를 읽고, 해당 바이트를 정수(0-255)로 반환한다. 더 이상 읽을 데이터가 없으면 -1을 반환한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;int read(byte[] b) : 바이트 배열 'b'에 데이터를 읽어 채운다. 읽은 바이트 수를 반환하며, 더 이상 읽을 데이터가 없으면 -1을 반환한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;int read(byte[] b, int off, int len) : 바이트 배열 'b'의 'off' 위치부터 최대 'len' 바이트를 읽어 채운다. 읽은 바이트 수를 반환하며, 더 이상 읽을 데이터가 없으면 -1을 반환한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;void close() : 스트림을 닫고, 사용한 리소스를 해제한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;아래와 같이 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716212393325&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.FileInputStream;
import java.io.IOException;

public class InputStreamExample {
    public static void main(String[] args) {
        try (InputStream inputStream = new FileInputStream(&quot;example.txt&quot;)) {
            int data;
            while ((data = inputStream.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #424242; border-left: 10px solid #424242; border-bottom: 3px solid #424242; padding: 5px; padding-left: 20px;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;OutputStream&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;바이트 단위로 데이터를 쓰기 위한 추상 클래스이며 주요 메서드는 아래와 같다.&lt;/span&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;void write(int b) : 하나의 바이트를 쓴다. 입력으로 주어진 정수의 하위 8비트를 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;void write(byte[] b) : 바이트 배열 'b'의 모든 바이트를 쓴다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;void write(byte[] b, int off, int len) : 바이트 배열 'b'의 'off' 위치부터 'len' 바이트를 쓴다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;void close() : 스트림을 닫고, 사용한 리소스를 해제한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;void flush() : 버퍼에 저장된 모든 출력 바이트를 강제롤 출력한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;아래와 같이 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716212606405&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.FileOutputStream;
import java.io.IOException;

public class OutputStreamExample {
    public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream(&quot;output.txt&quot;)) {
            String data = &quot;Hello, World!&quot;;
            outputStream.write(data.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}&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 style=&quot;color: #424242; border-left: 10px solid #424242; border-bottom: 3px solid #424242; padding: 5px; padding-left: 20px;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;스트림 사용 시 주의점&lt;/span&gt;&lt;/h2&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;font-family: 'Nanum Gothic';&quot;&gt;자원 관리 : InputStream과 OutputStream은 시스템 리소스를 사용하므로 사용 후 반드시 닫아야 한다. 이를 위해 'try-with-resources' 구문을 사용해 자동으로 닫히게 하는 것이 좋다. (Java 7 부터 구문 사용 시 AutoCloseable&lt;sup class=&quot;footnote&quot;&gt;&lt;a href=&quot;#footnote_668_1&quot; id=&quot;footnote_link_668_1&quot; onmouseover=&quot;tistoryFootnote.show(this, 668, 1)&quot; onmouseout=&quot;tistoryFootnote.hide(668, 1)&quot; style=&quot;color:#f9650d; font-family: Verdana, Sans-serif; display: inline;&quot;&gt;&lt;span style=&quot;display: none;&quot;&gt;[각주:&lt;/span&gt;1&lt;span style=&quot;display: none;&quot;&gt;]&lt;/span&gt;&lt;/a&gt;&lt;/sup&gt; 인터페이스를 구현한 객체에 대해 자원을 자동으로 반납한다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Nanum Gothic';&quot;&gt;예외 처리 : 스트림 작업 중에는 예외가 발생할 수 있으므로 적절한 예외 처리가 필요하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Nanum Gothic';&quot;&gt;버퍼링 : 성능을 향샹시키기 위해 버퍼링된 스트림(BufferedInputStream, BufferedOutputStream)을 사용하는 것이 일반적이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol class=&quot;footnotes&quot;&gt;
    &lt;li id=&quot;footnote_668_1&quot;&gt;AutoCloseable은&amp;nbsp;Java&amp;nbsp;7에서&amp;nbsp;도입된&amp;nbsp;인터페이스로,&amp;nbsp;리소스를&amp;nbsp;명시적으로&amp;nbsp;닫아야&amp;nbsp;하는&amp;nbsp;객체를&amp;nbsp;자동으로&amp;nbsp;닫을&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;해줍니다.&amp;nbsp;이&amp;nbsp;인터페이스는&amp;nbsp;java.lang&amp;nbsp;패키지에&amp;nbsp;속하며,&amp;nbsp;try-with-resources&amp;nbsp;문과&amp;nbsp;함께&amp;nbsp;사용됩니다.&amp;nbsp;AutoCloseable을&amp;nbsp;구현하는&amp;nbsp;클래스는&amp;nbsp;close()&amp;nbsp;메서드를&amp;nbsp;구현해야&amp;nbsp;합니다. &lt;a href=&quot;#footnote_link_668_1&quot;&gt;[본문으로]&lt;/a&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</description>
      <category>개발노트</category>
      <category>InputStream</category>
      <category>java</category>
      <category>OutputStream</category>
      <author>jinori</author>
      <guid isPermaLink="true">https://yeowool0217.tistory.com/668</guid>
      <comments>https://yeowool0217.tistory.com/668#entry668comment</comments>
      <pubDate>Sat, 18 May 2024 12:59:52 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] @Scheduled 사용</title>
      <link>https://yeowool0217.tistory.com/667</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;특정 기간, 원하는 주기에 따라 작업을 실행하기 위해 &lt;b&gt;@Scheduled&lt;/b&gt; 가 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@Scheduled&lt;/b&gt; 를 사용하기 위해서는 관련 클래스에 &lt;b&gt;@EnableScheduling&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;&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;@Scheduled 가 부텨되는 메서드는 파라미터를 가질 수 없고 반드시 void 리턴 타입이어야 하며&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;fixedDelay: 이전 작업이 끝난 후 동작&lt;/li&gt;
&lt;li&gt;fixedRate: 이전 작업이 끝나는 것과 별개로 일정한 시간 간격으로 실행&lt;/li&gt;
&lt;li&gt;cron:&amp;nbsp; 크론 주기에 따라 실행&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;cron&lt;/h2&gt;
&lt;pre id=&quot;code_1638778132155&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableScheduling
@Component
public class Scheduler {

    @Scheduled(cron = &quot;1 * * * * *&quot;) // 1분 주기
    public void cronJob() {
        SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss.SSS&quot;);
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println(&quot;strDate = &quot; + strDate);

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@EnableScheduling &lt;/b&gt;사용으로 스프링에게 &lt;b&gt;@Scheduled &lt;/b&gt;사용을 알리고&amp;nbsp; 위와 같이 크론 주기에 따라 코드를 실행 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1638853712869&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// OutPut
strDate = 2021-12-07 14:08:01.002
strDate = 2021-12-07 14:09:01.003&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;fixedDelay&lt;/h2&gt;
&lt;pre id=&quot;code_1638853187593&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableScheduling
@Component
public class Scheduler {

    @Scheduled(fixedDelay = 1000) // 작업 후 1초 후 실행
    public void fixedDelayJob() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss.SSS&quot;);
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println(&quot;FixedDelayJob = &quot; + strDate);
        System.out.println(Thread.currentThread().getName());
        Thread.sleep(4000);

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4초 동안 sleep 하기 때문에 5초 후에 다음 작업이 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1638853577643&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// OutPut
FixedDelayJob = 2021-12-07 14:06:52.925
scheduling-1
FixedDelayJob = 2021-12-07 14:06:57.927
scheduling-1
FixedDelayJob = 2021-12-07 14:07:02.931
scheduling-1&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;fixedRate&lt;/h2&gt;
&lt;pre id=&quot;code_1638853391587&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableScheduling
@Component
public class Scheduler {

    @Scheduled(fixedRate = 1000) // 1초 간격 실행
    public void fixedRateJob() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss.SSS&quot;);
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println(&quot;FixedDelayJob = &quot; + strDate);
        System.out.println(Thread.currentThread().getName());
        Thread.sleep(2000);

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2초 동안 sleep 하지만 &lt;b&gt;작업이 시작된 시점부터&lt;/b&gt; 1초 간격으로 실행 되기 때문에 2초가 지난 후 바로 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1638853590781&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// OutPut
FixedDelayJob = 2021-12-07 14:05:42.568
scheduling-1
FixedDelayJob = 2021-12-07 14:05:44.573
scheduling-1
FixedDelayJob = 2021-12-07 14:05:46.575
scheduling-1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 1초 간격으로 실행되기 위해서는 비동기화가 필요하다.&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;이제 fixedRate 코드 메서드에 &lt;b&gt;@Async&amp;nbsp;&lt;/b&gt;를 붙여주면 비동기 작업을 지원해 일정 간격으로 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 작업이 길어 쓰레드가 모두 사용중이라면 대기 시간을 가지게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1638858889704&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
@EnableScheduling
@EnableAsync
public class Scheduler {

    @Scheduled(fixedRate = 100) // 1초 간격 실행
    @Async
    public void fixedRateJob() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss.SSS&quot;);
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println(&quot;FixedDelayJob = &quot; + strDate);
        System.out.println(Thread.currentThread().getName());
        Thread.sleep(3000);

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;@Async&amp;nbsp;&lt;/b&gt;사용을 위해 &lt;b&gt;@EnableAsync&lt;/b&gt; 어노테이션으로 스프링에게 비동기 사용을 알린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1638859352238&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// OutPut
FixedDelayJob = 2021-12-07 15:42:00.487
task-1
FixedDelayJob = 2021-12-07 15:42:01.481
task-2
FixedDelayJob = 2021-12-07 15:42:02.478
task-3
FixedDelayJob = 2021-12-07 15:42:03.478
task-4
FixedDelayJob = 2021-12-07 15:42:04.478
task-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;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 아래와 같이 스레드풀을 사용하도록 설정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1638858956663&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class SchdulerConfig implements SchedulingConfigurer {

    private final int POOL_SIZE = 10;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(POOL_SIZE); // 대기하는 스레드 개수
        threadPoolTaskScheduler.setThreadNamePrefix(&quot;my-scheduled-task-pool-&quot;); // 스레드 이름
        threadPoolTaskScheduler.initialize();
        scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1638860219968&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
@EnableScheduling
public class Scheduler {

    @Scheduled(fixedDelay = 1000) // 작업 후 1초 후 실행
    public void fixedDelayJob() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss.SSS&quot;);
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println(&quot;FixedDelayJob = &quot; + strDate);
        System.out.println(Thread.currentThread().getName());
    }

    @Scheduled(fixedRate = 1000) // 1초 간격
    public void fixedRateJob() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss.SSS&quot;);
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println(&quot;FixedDelayJob = &quot; + strDate);
        System.out.println(Thread.currentThread().getName());
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1638860255120&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// OutPut
FixedDelayJob = 2021-12-07 15:56:42.973
jin-Scheduling2
FixedDelayJob = 2021-12-07 15:56:42.973
jin-Scheduling1
FixedDelayJob = 2021-12-07 15:56:43.973
jin-Scheduling3
FixedDelayJob = 2021-12-07 15:56:43.974
jin-Scheduling4
FixedDelayJob = 2021-12-07 15:56:44.973
jin-Scheduling2
FixedDelayJob = 2021-12-07 15:56:44.974
jin-Scheduling5
FixedDelayJob = 2021-12-07 15:56:45.975
jin-Scheduling6&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발노트</category>
      <author>jinori</author>
      <guid isPermaLink="true">https://yeowool0217.tistory.com/667</guid>
      <comments>https://yeowool0217.tistory.com/667#entry667comment</comments>
      <pubDate>Mon, 6 Dec 2021 17:12:45 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Stream 정리</title>
      <link>https://yeowool0217.tistory.com/666</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Stream 특징&lt;/span&gt;&lt;/h2&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;font-family: 'Nanum Gothic';&quot;&gt;Java 8 부터 지원한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;원본데이터 자체를 변경하지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;재사용이 불가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;내부 반복을 사용으로 코드가 간결해지고 병렬처리가 쉽다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;람다 표현식을 이용해서 선언형으로 코드를 구현할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;스트림 자신을 반환해 스트림 연산 끼리 연결, 파이프 라인을 구성한다. (데이터 소스에 적용하는 데이터베이스 질의와 비슷하다.)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이전 코드와 비교&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; font-family: 'Nanum Gothic';&quot;&gt;테스트를 위한 Dish 클래스&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Dish {

  private final String name;
  private final boolean vegetarian;
  private final int calories;
  private final Type type;

  public Dish(String name, boolean vegetarian, int calories, Type type) {
    this.name = name;
    this.vegetarian = vegetarian;
    this.calories = calories;
    this.type = type;
  }

  public String getName() {
    return name;
  }

  public boolean isVegetarian() {
    return vegetarian;
  }

  public int getCalories() {
    return calories;
  }

  public Type getType() {
    return type;
  }

  public enum Type {
    MEAT,
    FISH,
    OTHER
  }

  @Override
  public String toString() {
    return name;
  }

  public static final List&amp;lt;Dish&amp;gt; menu = Arrays.asList(
      new Dish(&quot;pork&quot;, false, 800, Type.MEAT),
      new Dish(&quot;beef&quot;, false, 700, Type.MEAT),
      new Dish(&quot;chicken&quot;, false, 400, Type.MEAT),
      new Dish(&quot;french fries&quot;, true, 530, Type.OTHER),
      new Dish(&quot;rice&quot;, true, 350, Type.OTHER),
      new Dish(&quot;season fruit&quot;, true, 120, Type.OTHER),
      new Dish(&quot;pizza&quot;, true, 550, Type.OTHER),
      new Dish(&quot;prawns&quot;, false, 400, Type.FISH),
      new Dish(&quot;salmon&quot;, false, 450, Type.FISH)
  );

}&lt;/code&gt;&lt;/pre&gt;
&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;저칼로리의 요리명을 반환하고, 칼로리를 기준으로 요리를 정렬하는 코드를 구현 한다고 할 때&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt; Stream 을 사용하지 않고 코드를 작성&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;아래와 같이원하는 값을 찾기 위해 전처리하는 과정에서 코드가 길어지게 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1638690964198&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static List&amp;lt;String&amp;gt; getLowCaloricDishesNamesInJava7(List&amp;lt;Dish&amp;gt; dishes) {
    List&amp;lt;Dish&amp;gt; lowCaloricDishes = new ArrayList&amp;lt;&amp;gt;();
    for (Dish d : dishes) {      // 누적자로 요소 필터링
      if (d.getCalories() &amp;lt; 400) {
        lowCaloricDishes.add(d);
      }
    }
    List&amp;lt;String&amp;gt; lowCaloricDishesName = new ArrayList&amp;lt;&amp;gt;();
    Collections.sort(lowCaloricDishes, new Comparator&amp;lt;Dish&amp;gt;() { // 익명 클래스로 요리 정렬
      @Override
      public int compare(Dish d1, Dish d2) {
        return Integer.compare(d1.getCalories(), d2.getCalories());
      }
    });
    for (Dish d : lowCaloricDishes) {
      lowCaloricDishesName.add(d.getName()); // 정렬된 리스트를 처리하며 요리 이름 선택
    }
    return lowCaloricDishesName;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Stream으로 코드 작성&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;선언형 코드 구현, 내부 반복으로 짧은 코드로 구현 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1638691010721&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static List&amp;lt;String&amp;gt; getLowCaloricDishesNamesInJava8(List&amp;lt;Dish&amp;gt; dishes) {
    return dishes.stream()
        .filter(d -&amp;gt; d.getCalories() &amp;lt; 400) // 400 칼로리 이하의 요리 선택
        .sorted(comparing(Dish::getCalories)) // 칼로리로 요리 정렬
        .map(Dish::getName) // 요리명 추출
        .collect(toList()); // 추출된 모든 요리명을 리스트에 저장
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;두 코드는 모두 아래와 같은 Output 을 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638691291701&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;season fruit
rice&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Stream 이란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Stream 이란 '데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소' 로 정의된다.&lt;/span&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;연속된 요소 : 컬렉션과 마찬가지로 연속된 값 집합의 인터페이스를 제공, 컬렉션은 자료구조로 시간과 공간에 관련된 요소 저장 및 연산이 주를 이룬다면 스트림은 filter, sorted, map 같은 표현 계산식이 주를 이룬다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;소스 : 리스트로 스트림을 만들면 스트림의 요소는 리스트의 요소의 순서를 유지한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터 처리 연산 : 스트림은 함수형 프로그래밍, 데이터베이스와 비슷한 연산을 지원 (filter, map, reduce, find, match, sort 등의 사용) 하며 순차적 또는 병렬 연산 수행이 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예제로 확인&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638692289369&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; names = menu.stream() // 요리 리스트에서 스트림을 얻음
        .filter(dish -&amp;gt; dish.getCalories() &amp;gt; 300) // 파이프라인 연산 - 코칼로리 요리 필터링
        .map(dish -&amp;gt; dish.getName()) // 요리명 추출
        .limit(3) // 선착순 세 개만 선택
        .collect(toList()); // 결과를 다른 리스트로 저장&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Output&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638692315855&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[pork, beef, chicken]&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Stream 연산&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;stream 생성 -&amp;gt; 중간 연산 -&amp;gt; 최종 연산 순으로 연산이 이루어진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;중간 연산&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;filter, sorted 같은 중간 연산은 다른 스트림을 반환해 여러 중간 연산을 연결해 질의를 만들 수 있으며&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;중간 연산을 모두 합쳐 최종 연산으로 한 번에 처리 된다는 특징이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 110px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 8.95352%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;연산&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.4418%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;반환 형식&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 15%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;연산의 인수&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 13.2559%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;함수 디스크럽터&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;설명&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 8.95352%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;filter&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.4418%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Stream&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 15%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Predicate&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 13.2559%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;T -&amp;gt; boolean&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;해당 스트림에서 주어진 조건(predicate)에 맞는 요소만으로 구성된 새로운 스트림을 반환&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 8.95352%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;map&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.4418%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Stream&amp;lt;R&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 15%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Function&amp;lt;T, R&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 13.2559%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;T -&amp;gt; R&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;해당 스트림의 요소들을 주어진 함수에 인수로 전달하며, 그 반환 값으로 이루어진 새로운 스트림을 반환&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 8.95352%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;limit&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.4418%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Stream&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 15%; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 13.2559%; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;해당 스트림에서 전달된 개수만큼의 요소만으로 이루어진 새로은 스트림을 반환&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 8.95352%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;sorted&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.4418%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Stream&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 15%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Comparator&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 13.2559%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;(T, T) -&amp;gt; int&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;비교자(comparator)를 이용해 정렬하며비교자를 전달하지 않으면 영문사전 순으로 정렬&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 8.95352%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;distinct&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.4418%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Stream&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 15%; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 13.2559%; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;해당 스트림에서 중복된 요소가 제거된 새로운 스트림을 반환&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;최종 연산&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;결과를 도출하며 보통 최종 연산에 의해 List, Integer, void 등 스트림 이외의 결과가 반환된다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 74px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 9.06976%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;연산&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.907%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;반환 형식&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 53.0232%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;설명&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 9.06976%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;forEach&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.907%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;void&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 53.0232%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;스트림의 각 요소를 소비하면서 람다를 적용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 9.06976%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;count&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.907%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;long(generic)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 53.0232%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;스트림의 요소 개수를 반환&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 9.06976%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;collect&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.907%; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 53.0232%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;스트림을 리듀스해 리스트, 맵, 정수 형식의 컬렉션을 만든다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발노트</category>
      <author>jinori</author>
      <guid isPermaLink="true">https://yeowool0217.tistory.com/666</guid>
      <comments>https://yeowool0217.tistory.com/666#entry666comment</comments>
      <pubDate>Sun, 5 Dec 2021 16:48:29 +0900</pubDate>
    </item>
    <item>
      <title>docker + Nginx Reverse-proxy + Wildcard SSL</title>
      <link>https://yeowool0217.tistory.com/665</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Wildcard SSL이란?&lt;/span&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;도메인 뿐만이 아니라 서브 도메인 모두 인증지원하는 인증서&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예를 들어 도메인이 devgno.me 일 때 이 인증서를 발급, 적용하면&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;site1.devgno.me, site2.devgno.me ....&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;모든 서브 도메인도 ssl인증이 적용된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;도메인 준비&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;ssl인증서 발급을 위해 도메인을 구한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 글에서는 무료 도메인인 &lt;a href=&quot;https://www.freenom.com/en/index.html?lang=en&quot;&gt;freenom&lt;/a&gt;을 사용&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;인증서 발급&lt;/span&gt;&lt;/h2&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;font-family: 'Nanum Gothic';&quot;&gt;certbot 설치&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;code&gt;sudo apt install certbot&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Wildcard ssl 발급&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;sudo certbot certonly --manual --preferred-challenges=dns --email \ &lt;a href=&quot;mailto:jinho021712@gmail.com&quot;&gt;jinho021712@gmail.com&lt;/a&gt; --server &lt;a href=&quot;https://acme-v02.api.letsencrypt.org/directory&quot;&gt;https://acme-v02.api.letsencrypt.org/directory&lt;/a&gt; --agree-tos -d devgno.me -d \*.devgno.me&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;발급 받을 서버에 위 명령어를 입력한다. 이메일과 도메인 입력에 주의&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;IP주소 수집, 로그로 기록됨을 동의&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for hiseon.me

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y&lt;/code&gt;&lt;/pre&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;font-family: 'Nanum Gothic';&quot;&gt;도메인 인증&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;인증이 된 후 엔터를 치고 넘어가야한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;도메인 인증을 위해 위 값을 freenom DNS Management에서 아래와 같이 등록&lt;/span&gt;&lt;br /&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/09vq2/btq7DS2mLzZ/gbc0eEzNAyZEx0QWPavWK0/img.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;인증 확인&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;code&gt;watch dig -t txt \_acme-challenge.devgno.me +short&lt;/code&gt;도메인 인증에 시간이 걸릴 수 있으므로 위 명령어로 도메인 인증되었는지 모니터링&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;docker-compose.yml&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;version: '3'

services:

  reverse-proxy:
    image: nginx:1.17.10
    container_name: reverse_proxy_demo
    depends_on:
        - ex-page1
        - ex-page2

    volumes:
      - ./reverse_proxy/conf.d:/etc/nginx/conf.d
      - ./reverse_proxy/nginx.conf:/etc/nginx/nginx.conf
      - /etc/letsencrypt:/etc/letsencrypt
      - /etc/ssl/certs/dhparam.pem:/etc/ssl/certs/dhparam.pem

    ports:
      - 80:80
      - 443:443

  ex-page1:
    image: ex-page1
    container_name: ex-page1
    build: 
      context: ./ex-page1
    ports:
      - 5001:5001
    restart: on-failure

  ex-page2:
    image: ex-page2
    container_name: ex-page2
    build: 
      context: ./ex-page2
    ports:
      - 5001:5002
    restart: on-failure&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기존에 docker-compose를 사용하고 있었다면 port에 443포트만 매핑시켜주고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;ssh인증서 폴더 &quot;/etc/letsencrypt&quot;를 volumes에 매핑한다. (개별 파일을 매핑하면 링크파일로 매핑 될 수 있다. 폴더를 매핑시켜주자)&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;nginx.conf&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;user nginx;
worker_processes auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    server_names_hash_bucket_size 64;
    server_names_hash_max_size 8192;

    log_format  main  '$remote_addr - $remote_user [$time_local] &quot;$request&quot; '
                      '$status $body_bytes_sent &quot;$http_referer&quot; '
                      '&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;

    server_tokens off; # 응답 헤더에서 서버 버전 숨기기

    keepalive_timeout  65;

    include /etc/nginx/conf.d/*.conf;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;conf.d 폴더의 conf확장자 파일을 모두 include하도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Nginx conf 작성 참고&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;NGINX Docs | Creating NGINX Plus and NGINX Configuration Files&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Understand the basic elements in an NGINX or NGINX Plus configuration file, including directives and contexts.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;docs.nginx.com&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;](&lt;a href=&quot;https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/&quot;&gt;https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/&lt;/a&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;conf.d &amp;gt; default.conf&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;conf.d폴더 안에 페이지별 conf 파일을 작성한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;return을 줘서 http로 연결하면 https로 연결을 유도한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;https를 사용하기 위해 인증서 위치를 적어준다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;# ex-page1

server {
        listen 80;
        server_name ex-page1.devgno.ga www.ex-page1.devgno.ga;
        charset utf-8;

    # https로 연결
        location / {
        return 307 https://ex-page1.devgno.ga$request_uri;
        }

}

server {
        listen 443;
        listen [::]:443;
        ssl on;
        server_name ex-page1.devgno.ga www.ex-page1.devgno.ga;

        # ssl인증서 위치
        ssl_certificate /etc/letsencrypt/live/devgno.ga/fullchain.pem; 
        ssl_certificate_key /etc/letsencrypt/live/devgno.ga/privkey.pem; 
        ssl_dhparam /etc/ssl/certs/dhparam.pem;

        location / {
                # include proxy_params;
                proxy_pass http://172.17.0.1:5001;
        }

}

&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발노트</category>
      <category>docker-compose</category>
      <category>nginx</category>
      <category>proxy</category>
      <category>REVERSE</category>
      <category>ssl</category>
      <category>WildCard</category>
      <author>jinori</author>
      <guid isPermaLink="true">https://yeowool0217.tistory.com/665</guid>
      <comments>https://yeowool0217.tistory.com/665#entry665comment</comments>
      <pubDate>Sun, 20 Jun 2021 00:02:11 +0900</pubDate>
    </item>
  </channel>
</rss>