<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Kani's Blog</title>
    <link>https://dev-kani.tistory.com/</link>
    <description>개발자 Kani의 기술 블로그</description>
    <language>ko</language>
    <pubDate>Mon, 1 Jun 2026 10:00:37 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>개발자 카니</managingEditor>
    <image>
      <title>Kani's Blog</title>
      <url>https://tistory1.daumcdn.net/tistory/2862678/attach/51e88b6a8c9c43799250fda41e3a0e2b</url>
      <link>https://dev-kani.tistory.com</link>
    </image>
    <item>
      <title>2022년 올해 목표!</title>
      <link>https://dev-kani.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;역시 목표는 1달 지나고 세우는게 국룰인가...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;티스토리 게시글 연간 게시글 15개 이상 쓰기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Github 2021 contributions 800 이상 달성하기 (회사 커밋 포함)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Group Study 2개 이상 하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;영어 회화, 일본어 등 어학 공부&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;음역대 넓히기 : B4 이상 노래 완창 하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;League Of Legends 자랭 플래티넘&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다이어트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;회사 서비스 동접자 3배 이상 상승시키기...&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;작년 목표에서 대부분 유지하고...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아무튼 좀 더 잘해보자!&lt;/span&gt;&lt;/p&gt;</description>
      <category>소개</category>
      <category>2022년</category>
      <category>계획</category>
      <category>다짐</category>
      <category>목표</category>
      <author>개발자 카니</author>
      <guid isPermaLink="true">https://dev-kani.tistory.com/41</guid>
      <comments>https://dev-kani.tistory.com/41#entry41comment</comments>
      <pubDate>Sat, 5 Feb 2022 15:33:48 +0900</pubDate>
    </item>
    <item>
      <title>보내고 싶지 않은 2021년... 이제는 놓아줄 때가...</title>
      <link>https://dev-kani.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;정말 오랜만에 적는 글이자 2022년 첫 글을 작년의 회고로 시작하려고 한다.&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;제목에 적었듯 2021년 한 해는 정말 무얼 했나 싶을 정도로 빠르게 지나갔다.&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;그리고 2022년이 된 지금 나에게도 올 거 같지 않고 믿기 힘들었던 그날이 찾아왔다.&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;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;또 하루 멀어져 간다..&lt;/s&gt;&lt;/span&gt;&lt;/i&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;두서없음 주의!&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369; 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;&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;2021년 백신의 등장과 1, 2차 접종으로 인해 드디어 위드 코로나를 실행할 수 있지 않을까라는 기대감이 컸다.&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;실제로 2021년 11월 무렵부터 위드 코로나를 시행했고 백신 패스의 도입과 함께 일상생활이 어느 정도 회복되는 듯했다.&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;식당 및 카페의 영업 제한 시간이 사라지고 인원도 8인이었던가? 가벼운 모임을 하기에도 큰 무리가 없을 정도로 규제가 완화되었다.&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;물론 백신 접종을 2차까지 마친 사람들에 한해서지만 6주 이상 진행되었고 코로나 확진자 수가 늘어나는 것도 그냥 감기 환자가 늘어났구나 싶을 정도로 크게 와닿지는 않았다.&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;2021년 끝자락 12월에 전 세계적으로 오미크론 등 변이 바이러스로 인해 백신 접종자에게도 돌파 감염이 일어나게 되고 이 횟수가 급속히 늘어남에 따라 위드 코로나도 잠정적으로 중단되었다.&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;3천 명 선을 유지하고 1 천명대까지 떨어졌던 일일 확진자 수가 5천, 7천을 넘게 되었다.&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;이 글을 쓰고 있는 오늘은 확진자 수가 역대 최대인 3.6만 명을 기록했다.&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;얼른 완치제와 백신이 잘 개발되어서 코로나 사태가 종식되었으면 좋겠다.&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;size18&quot;&gt;&lt;span style=&quot;color: #ef5369; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;# 스터디&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;아파치 카프카 애플리케이션 프로그래밍 with. 자바 (저자. 최원영)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;917&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/63xxT/btrstKDwPF3/G627dMPba6hHsCEn9m9m11/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/63xxT/btrstKDwPF3/G627dMPba6hHsCEn9m9m11/img.jpg&quot; data-alt=&quot;아파치 카프카 애플리케이션 프로그래밍 with 자바&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/63xxT/btrstKDwPF3/G627dMPba6hHsCEn9m9m11/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F63xxT%2FbtrstKDwPF3%2FG627dMPba6hHsCEn9m9m11%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;214&quot; height=&quot;284&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;917&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;아파치 카프카 애플리케이션 프로그래밍 with 자바&lt;/figcaption&gt;
&lt;/figure&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;카카오 인턴을 하던 시절 자주 들었지만 뭔지 몰랐던 미들웨어인 카프카에 대해서 학습할 수 있는 좋은 기회였다.&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;&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터 중심 애플리케이션 설계 - 신뢰할 수 있고 확장 가능하며 유지 보수하기 쉬운 시스템을 지탱하는 핵심 아이디어 (저자. 마틴 클레프만)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lwnYV/btrsxEJdjPy/yHSzm7w25e4uCMsrvDsjGk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lwnYV/btrsxEJdjPy/yHSzm7w25e4uCMsrvDsjGk/img.jpg&quot; data-alt=&quot;데이터 중심 애플리케이션 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lwnYV/btrsxEJdjPy/yHSzm7w25e4uCMsrvDsjGk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlwnYV%2FbtrsxEJdjPy%2FyHSzm7w25e4uCMsrvDsjGk%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;206&quot; height=&quot;263&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데이터 중심 애플리케이션 설계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;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'; letter-spacing: 0px;&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;신입 개발자인 내가 읽기에는 아직 지식적으로 부족한 내용도 많았고 안 써본 기술도 많았다.&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;그래서 지금의 나에겐 상당히 버거운 내용이었고 시간 또한 많이 들어가고 있다. &lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;(아직도 진행 중인 스터디다.)&lt;/s&gt;&lt;/span&gt;&lt;/i&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;개발 3년 차 이상이 되면 기능 구현이나 코드의 구조 등에 대해서는 지식이 많이 쌓여 어느 정도 쉽게 구현하게 될 것이다.&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;이 책은 3년차 이상이 보는 것을 추천한다.&lt;/span&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;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Real MySQL 8.0 (개정판) (저자. 백은빈, 이성욱)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beLe9u/btrsveRYiAu/kCImv8UUGvkOaRxaRUX650/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beLe9u/btrsveRYiAu/kCImv8UUGvkOaRxaRUX650/img.jpg&quot; data-alt=&quot;Real MySQL 8.0 (1권) (개정판)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beLe9u/btrsveRYiAu/kCImv8UUGvkOaRxaRUX650/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeLe9u%2FbtrsveRYiAu%2FkCImv8UUGvkOaRxaRUX650%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;229&quot; height=&quot;285&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Real MySQL 8.0 (1권) (개정판)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;860&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Is3xD/btrsrVTeEKy/wTcNUI89L3P1YxuD7dWCb1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Is3xD/btrsrVTeEKy/wTcNUI89L3P1YxuD7dWCb1/img.jpg&quot; data-alt=&quot;Real MySQL 8.0 (2권) (개정판)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Is3xD/btrsrVTeEKy/wTcNUI89L3P1YxuD7dWCb1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIs3xD%2FbtrsrVTeEKy%2FwTcNUI89L3P1YxuD7dWCb1%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;229&quot; height=&quot;860&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;860&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Real MySQL 8.0 (2권) (개정판)&lt;/figcaption&gt;
&lt;/figure&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;이성욱님의 전작이었던 'Real MySQL - 개발자와 DBA를 위한'의 후속작이 거의 10년 만에 출시되었기 때문이다.&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;이 책의 경우 커뮤니티에서 모집된 스터디를 통해 5명이서 5개월 동안 스터디를 진행했다.&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;전작이 MySQL 5.5~5.6 정도까지의 버전을 소개했다면 이 책은 5.7 이후부터 8.0의 일부 마이너 버전까지를 설명하고 있다.&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;책의 내용이 많아서 인지 두 권으로 분리해서 출시되었는데 1권에서는 주로 MySQL의 기능 및 동작에 대한 이론적인 부분을 설명하고 있고 2권에서는 좀 더 실무적으로 쓰이면 좋을 옵션이나 클러스터 운영 등을 가이드하고 있다.&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;MySQL을 사용하는 개발자에게는 다소 '이런 거까지 알아야 해?'라고 생각될 정도의 내용도 상당히 존재하지만 이런 게 있다! 정도로 가볍게 넘어가면 필요한 순간에 아! 이런게 있었지! 하고 다시 찾아볼 수 있을 것이다.&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;MySQL의 내용에 대한 어느 정도의 깊이를 알고 싶다면 꼭 읽어보는 것을 추천한다.&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;책 읽는 스터디를 동시에 2개 이상 진행하는 것은 게으른 나에게 맞지 않는 것 같다. 주말에 다른 일을 하기에 너무 부담이 되었다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369; 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1월에 취업을 한 후 1년 동안 뽐뿌한 내용들을 한 번 정리해보려고 한다.&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Intellij Ultimate License&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 회사에서 업무용으로 Jetbrains All Products Package를 제공해준다. 하지만 업무 외적인 작업(공부 등)을 할 경우가 좀 있을 것 같아서 구매했다. 연 단위로 18만원 정도로 기억한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;iPad Pro 11' (M1)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 내가 살면서 구매한 첫 Apple 제품이다. IT 업계에서 일하면서 애플 제품을 한 번도 써보지 않은 것은 그렇게 좋지 않은 것 같았다. 개발을 할 때도 우리는 여러 플랫폼에 대응을 해야 한다. 처음에 아이패드 프로를 구매한다고 했을 때 주변에서 많이 추천하지는 않았다. 태블릿의 성격상 휴대폰과 컴퓨터의 중간 위치에 있고 대부분 집에서 유튜브 시청용으로 사용할 텐데 그 비싼 돈 주고 왜 아이패드 프로를 구매하냐라는 의견이 많았다. 이 말에는 어느 정도 동의를 한다. 실제로도 현재 아이패드의 주요 사용처는 유튜브 시청이다. 하지만 회사에서 뭔가를 정리한다거나 다른 사람들에게 설명할 때도 아이패드를 잘 사용하고 있고 앱 테스트 용으로도 종종 사용하고 있다. 또한 iOS만의 UI, UX 방식에도 어느 정도 적응을 해가고 있다. 잘 구매한 것 같다. 용량 128gb에 wifi + cellular이고 110만원 정도에 구매했고 추가로 애플 펜슬 2세대(14만원 정도)도 구매했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Desktop&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt; - 비트코인의 채굴과 함께 그래픽 카드 시장이 급속도로 비싸졌다... 생산은 줄었는지 매물은 적고 원하는 수요는 많으니 가격이 자연스레 비싸졌다. 최소 20만원 이상씩은 다 뻥튀기되었다. 그렇다 보니 조립 컴퓨터를 맞출 때 우선 어떤 그래픽 카드를 구매할지를 정하고 그에 따라 나머지 부품들을 선택하는 식으로 견적을 짜게 되었다. 내 성격상 맞추는 타이밍에 어느 정도 괜찮은 사양을 맞추는 스타일이어서 여러 가지를 타협해가면서 그래픽 카드는 지포스의 RTX 3060 Ti를 선택했다. 이에 따라 사양을 맞추게 되었고 cpu는 intel 계열 cpu 중 i5를 선택했다. 다다익램의 공식에 맞게 삼성의 32gb 램을 선택했고 보조 저장 장치로 ssd 500gb, hdd 2tb를 선택했다. 전체 구매 가격은 210만원 정도였고 추가로 모니터도 구매했는데 알파스캔의 27인치 모니터를 두 개 구매했고 하나는 FHD 144hz이고 하나는 4K로 75만원(2개 합쳐서) 정도 들었다. 상세 견적은 아래와 같다.&lt;/span&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;946&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uWSpW/btrsxFOQKR8/UY1qRReYVVh1IOkgww0qG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uWSpW/btrsxFOQKR8/UY1qRReYVVh1IOkgww0qG1/img.png&quot; data-alt=&quot;데스크탑&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uWSpW/btrsxFOQKR8/UY1qRReYVVh1IOkgww0qG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuWSpW%2FbtrsxFOQKR8%2FUY1qRReYVVh1IOkgww0qG1%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;602&quot; height=&quot;465&quot; data-origin-width=&quot;946&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데스크탑&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzYHnc/btrswUMs0Q2/IRQgKZ3KKae7D0y2JzUCF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzYHnc/btrswUMs0Q2/IRQgKZ3KKae7D0y2JzUCF0/img.png&quot; data-alt=&quot;모니터&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzYHnc/btrswUMs0Q2/IRQgKZ3KKae7D0y2JzUCF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYHnc%2FbtrswUMs0Q2%2FIRQgKZ3KKae7D0y2JzUCF0%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;652&quot; height=&quot;144&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모니터&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Macbook Pro 16' (M1 pro)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 날이 갈수록 점점 힘이 빠져가는 5살 노트북은 더 이상 카페에서 꺼내기 민망할 정도로 시끄러운 소음과 성능으로 인해 교체가 필요했고 이번엔 맥북을 구매하려고 생각을 하고 있었다. 그리고 결국 원하던 &lt;i&gt;&lt;s&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;스타벅스 출입증&lt;/span&gt;&lt;/s&gt;&lt;/i&gt; 맥북을 구매했다. 개발자하면 맥북! 2020년 말 m1 칩이 처음 공개되었고 많은 사람들이 intel 칩을 사용하는 맥과 비교하며 새로운 맥북에 대해서 칭찬을 아끼지 않았다. 그리고 드디어 작년 10월에 m1의 상위 모델인 m1 pro를 탑재한 맥북 프로가 공개되었다. 다들 m1 맥북의 미친 가성비를 원하긴 했지만 m1 pro의 경우 가성비보다는 성능을 좀 더 중시한 것 같다. 가격은 기존 맥북 프로보다 20%가량 비쌌고 외형은 오히려 더 퇴화된 느낌을 주었다. 하지만 반대로 내부적으로는 배터리 효율, 해상도, cpu 등 많은 것이 바뀌었고 특히 m1 pro의 최상위 모델인 m1 max의 경우 gpu에 좀 더 힘이 들어가게 되었다. 사실 이번 m1 pro의 경우 개발자보다는 디자이너나 혹은 음향 쪽을 하는 분들께 더 적합한 스펙인 것 같다. 만족도는 현재까진 매우 높다. 거의 없다시피 한 소음과 낮은 발열, 높은 배터리 효율 등 카페에서 코딩하기에 최적의 조건을 모두 갖추고 있다. 앞으로 주말마다 카페를 애용해야겠다. 스펙은 m1 max, 32gb ram, 1tb ssd이고 공홈에서 애플 케어 없이 470만원 정도에 구매했다. 참고로 회사에서도 작년 12월 초쯤 신형 맥북이 보급되었는데 m1 pro, 32gb ram, 512gb ssd를 사용 중이다. 가격이 부담이 된다면 m1 pro(8core), 16gb ram, 512gb ssd, 14' 디스플레이 정도의 스펙(96W 충전기 포함 270만원)만 사용해도 충분히 만족도 높게 개발을 할 수 있을 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369; 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;5월부터 다시 보컬 트레이닝을 시작했다. 예전에 알던 지인에게 노래를 배우기 시작했다.&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;보통 우리가 과외를 받는 이유는 2가지 정도 있다고 생각한다. 남들보다 뒤처지기 때문이거나 남들보다 앞서가기 위함이다.&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;노래라는 것은 듣거나 부르는 것 자체만으로도 즐거움이지만 잘 부르고 싶은 욕망은 누구나 있을 것이다.&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;&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;연습량만이 중요한 것은 아니다. 내가 잘못된 방향으로 연습하는 것은 오히려 좋지 않기 때문이다.&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;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;i&gt;&lt;s&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;대부분 당신이 생각한 것과는 많이 다를 것이다.&lt;/span&gt;&lt;/s&gt;&lt;/i&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;흔히 보컬 트레이닝을 받았을 때 가장 변화가 많이 발생한다고 하는 기간은 처음 1~2달이다. 그동안 사람들이 노래를 잘 못 부르는 이유는 많이 불러보지 않았기 때문일 가능성이 크다.&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;그 기간 이후에는 기본기를 쌓고 디테일을 잡는 작업들을 보통 한다.&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;/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;당연히 실력은 정체되어 있다.&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;보통 1~2달 빡세게 하면 보통 되는 비브라토도 몇 달을 끌고 있는지 모르겠다.&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;올해는 연습을 더 열심히 해야겠다.&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;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;&lt;s&gt;(나이가 나오는 노래가 많네.. 흠...)&lt;/s&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&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;If Only - 나윤권&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;날 안아줘 - 40&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;그 남잔 말야 -&amp;nbsp; MC.THE.MAX (현재 진행중)&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369; 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;&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;신입 1년 차 개발자인 내겐 상당히 과분하면서도 힘든 업무들이 주어졌다.&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;위에서 힘들다고 한 것은 일이 많아서 힘들다는 것과는 다르다. 아직 미숙한 내가 할 수 있는 역량 이상의 무언가를 하려니 힘든 것이었다.&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;/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;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;(무야호!!)&lt;/s&gt;&lt;/span&gt;&lt;/i&gt;&amp;nbsp;&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;/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;이번 섹션의 마지막으로 1년간 다녀본 회사에서 느낀 부분에 대해서 서술해보려고 한다. 보통 1년을 고비로 스타트업들 중 8~9할은 망한다고 한다. 그마저도 남은 1~2할의 회사 중 대부분은 3년 정도 지나면 성장이 멈춘 채로 제자리에 머물다 기억 속에서 잊힌다고 한다. 하지만 우리 회사는 작년 8월 3주년을 넘기고도 카카오라는 대기업에서 엄청난 투자를 받기도 했고 지금까지도 여러 방면에서 성장 중인 회사이다.&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;너나 할 것 없이 모든 직원이 자신이 맡은 분야에서 최선을 다하고 있고 어느 부서나 다양한 아이디어를 내고 있다. 이것이 성장하는 회사의 원동력이 아닐까라는 생각이 든다.&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;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;이런 회사에 합류하고 싶으신 분이 계시다면 (스팸 아님)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;darr;&lt;br /&gt;&lt;a title=&quot;여기로&quot; href=&quot;https://www.notion.so/gripshow/Grip-53058b39e23d470d9c223f6e59c8c726&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기로&lt;/a&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;uarr;&lt;/span&gt;&lt;/span&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;size18&quot;&gt;&lt;span style=&quot;color: #ef5369; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;# 고민&lt;/span&gt;&lt;/span&gt;&lt;/b&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;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;크게 2가지 정도의 고민이 든다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;하나는 '나의 성장'이고 나머진 '워라벨?'이다.&lt;/span&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;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;분명히 나는 '&lt;b&gt;성장&lt;/b&gt;'하고 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;성장을 하는데 뭐가 고민이냐?라고 생각되겠지만 고민은 성장의 방향성이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;개발자로서 1년 차 개발자인 나는 흔히 말하는 이 연차 때 이 정도는 하지?라고 생각되는 걸 할 수 있을까? 와&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;직장인으로서 나는 일을 잘하는가? 일반적으로 회사에서 일을 잘한다는 것은 주어진 기간 내에 일을 잘 마칠 수 있고 다른 사람들과 무리 없이 커뮤니케이션하는 것이라고 생각한다. 여기서 포인트는 내가 회사일에 대해서 얼마나 이해하고 있을까? 인가다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이 두 가지 중 어느 방향으로 나아가야 할까이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;물론 기술적으로 성장하는 게 결국에는 일을 잘하는 것으로 귀결될 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이 고민을 정리하면 이론에 가까운 기술 vs 도메인 지식으로 분류할 수 있을 것 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;어떻게 보면 개발자가 계속 공부하는 것은 당연하다고 볼 수 있다. 개발자를 직업으로 하다 보면 현재 개발하고 있는 분야에 대한 도메인 지식을 이해하는 것은 필수 불가결하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이런 의문이 들 수는 있다. 공부한 걸 회사에 적용시켜보면 되지 않아?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;내가 공부한 것을 회사에 직접적으로 사용할 수 있으면 최고가 맞지만 현실적으로 쉽지 않은 이야기다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;회사일은 내가 생각한 것보다 기간이 짧을 수도 있고 여러 사람과 같이 일을 하는 것이기 때문이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;아무튼 올해는 이 두 가지를 적절히 선택해 가면서 공부할 예정이다.&lt;/span&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;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;두 번째 고민은 '&lt;b&gt;워라벨&lt;/b&gt;'이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이 회사는 워라벨이 안 좋아?라고 물어본다면 괜찮은 편인 것 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;내가 말하는 것은 나 자신이 이 벨런스를 맞추지 못하고 있는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;단편적으로 작년에 받은 11개의 연차 중에서 이월된 연차가 10.75일이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;업무가 그 정도로 많아?라고 생각될 수 있는데 맞는 말이기도 하고 틀린 말이기도 하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;초반에는 기본적으로 주어진 업무를 내가 쳐내기에 기간이 짧은 느낌이 있었다. 이런 부분은 회사 업무나 코드를 이해하면서 자연스레 완화되었다. 여기서 문제는 여기서 내가 코드적으로나 여러 가지로 너무 고민을 많이 했고 이로 인해 개발 시간을 많이 까먹어서 추가적인 업무를 하게 되었거나 업무의 우선순위가 높지 않은 일인데도 굳이 하려고 하거나 하는 등 열정이 앞선 케이스가 많았다. 이로 인해 자연스레 OT가 발생했고 보상 휴가가 생겼다. 이 휴가들을 우선적으로 사용하다 보니 연차는 오히려 거의 쓰질 않았다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;주변에서는 좀 쉬라는 말을 많이 들었다. 너무 휴가를 안 쓰고 다른 사람이 보기에 일만 하는 것처럼 비치니 그런 얘기를 많이 하신 것 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;그래서 하루 날 잡고 휴가를 썼는데 정말 당황스러웠다. 뭘 해야 할지 몰랐기 때문이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;취준 기간 동안 시간 나면 카페 가서 코딩을 하거나 해서 이것이 일상이 되어버린 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;너무 할 게 없어서 다른 분들께 보통 휴가 쓰면 뭐하세요?라고 물어보기까지 했다. &lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;(다들 경악했다.)&lt;/s&gt;&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;쉰다는 게 뭘까? 이것에 대해서 진지하게 고민하는 것도 웃기지만 쉬는 시간에 할만한 것을 좀 생각해봐야겠다.&lt;/span&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;size18&quot;&gt;&lt;span style=&quot;color: #ef5369; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;# 목표 달성&lt;/span&gt;&lt;/span&gt;&lt;/b&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;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2021년 목표는 얼마나 달성했을까...?&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 796px;&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: 5.81396%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;No&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1861%; text-align: center; 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: 11.6279%; text-align: center; 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: 38.3721%; text-align: center; 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: 20px;&quot;&gt;
&lt;td style=&quot;width: 5.81396%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1861%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;티스토리&amp;nbsp;게시글&amp;nbsp;연간&amp;nbsp;게시글&amp;nbsp;30개&amp;nbsp;이상&amp;nbsp;쓰기&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 11.6279%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;10%&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;3개...부지런히 살자..&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 255px;&quot;&gt;
&lt;td style=&quot;width: 5.81396%; text-align: center; height: 255px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1861%; height: 255px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Github&amp;nbsp;2021&amp;nbsp;contributions&amp;nbsp;800&amp;nbsp;이상&amp;nbsp;달성하기&amp;nbsp;(회사&amp;nbsp;커밋&amp;nbsp;포함)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 11.6279%; text-align: center; height: 255px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;87.5%&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 255px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;1002&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cX1wcv/btrsxgaIH7v/JP8PYuBeIovfZOiTrfHqhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cX1wcv/btrsxgaIH7v/JP8PYuBeIovfZOiTrfHqhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cX1wcv/btrsxgaIH7v/JP8PYuBeIovfZOiTrfHqhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcX1wcv%2FbtrsxgaIH7v%2FJP8PYuBeIovfZOiTrfHqhk%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;1460&quot; height=&quot;1002&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;대부분 회사 커밋이지만 700 컨트리뷰트를 달성했다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 5.81396%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;3&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1861%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Group&amp;nbsp;Study&amp;nbsp;2개&amp;nbsp;이상&amp;nbsp;하기&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 11.6279%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;125%&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2개를 완료했고 1개를 진행중이다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 5.81396%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;4&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1861%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;영어&amp;nbsp;회화,&amp;nbsp;일본어&amp;nbsp;등&amp;nbsp;어학&amp;nbsp;공부&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 11.6279%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;0%&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; 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: 40px;&quot;&gt;
&lt;td style=&quot;width: 5.81396%; text-align: center; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;5&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1861%; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;음역대&amp;nbsp;넓히기&amp;nbsp;:&amp;nbsp;B4&amp;nbsp;이상&amp;nbsp;노래&amp;nbsp;완창&amp;nbsp;하기&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 11.6279%; text-align: center; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;50%&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 40px;&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: 321px;&quot;&gt;
&lt;td style=&quot;width: 5.81396%; text-align: center; height: 321px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;6&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1861%; height: 321px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;League&amp;nbsp;Of&amp;nbsp;Legends&amp;nbsp;솔랭&amp;nbsp;플래티넘&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 11.6279%; text-align: center; height: 321px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;0%&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 321px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPxWE8/btrstLo23AC/UBCBemXBkx3AAy3ifkxuxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPxWE8/btrstLo23AC/UBCBemXBkx3AAy3ifkxuxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPxWE8/btrstLo23AC/UBCBemXBkx3AAy3ifkxuxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPxWE8%2FbtrstLo23AC%2FUBCBemXBkx3AAy3ifkxuxK%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;594&quot; height=&quot;494&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;&lt;br /&gt;솔랭을 한판도 안했다 ^^;;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 5.81396%; text-align: center; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;7&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1861%; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;인맥&amp;nbsp;쌓기&amp;nbsp;(Linked&amp;nbsp;In,&amp;nbsp;로켓펀치&amp;nbsp;등&amp;nbsp;나의&amp;nbsp;활동을&amp;nbsp;공유하며&amp;nbsp;여러&amp;nbsp;분야의&amp;nbsp;인맥을&amp;nbsp;쌓아보자!)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 11.6279%; text-align: center; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;20%&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Linked In 1촌은 늘었지만 이걸 인맥으로 치기 약간 민망하다. (회사분들이 대부분...)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 5.81396%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;8&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1861%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;운동&amp;nbsp;시작하기(스쿼시&amp;nbsp;마렵다...)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 11.6279%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;50%&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;11월부터 늦게 '시작'은 했으니 50&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 5.81396%; text-align: center; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;9&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1861%; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;회사&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;많다.)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 11.6279%; height: 40px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;50%&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;도메인 하나하나에 대한 깊이가 생각보다 깊다.&amp;nbsp;&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2021년 한 해는 내 인생에서 잊지 못할 한 해 중 하나인 것 같다.&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;&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;</description>
      <category>소개</category>
      <category>2021</category>
      <category>개발자</category>
      <category>회고</category>
      <author>개발자 카니</author>
      <guid isPermaLink="true">https://dev-kani.tistory.com/40</guid>
      <comments>https://dev-kani.tistory.com/40#entry40comment</comments>
      <pubDate>Sat, 5 Feb 2022 15:19:43 +0900</pubDate>
    </item>
    <item>
      <title>[Intellij] java 개발 중 import 문이 자동으로 와일드카드(*)로 바뀐다면?</title>
      <link>https://dev-kani.tistory.com/39</link>
      <description>&lt;p&gt;Intellij를 사용해서 java 개발을 하다 보면 파일 내부에 자연스럽게 많은 import 문이 만들어진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;369&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TjEOc/btqZ7KcamCU/s3rk2xVjLB41V5k9Xm9Bb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TjEOc/btqZ7KcamCU/s3rk2xVjLB41V5k9Xm9Bb0/img.png&quot; data-alt=&quot;아직 와일드카드로 바뀌지 않은 import 문&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TjEOc/btqZ7KcamCU/s3rk2xVjLB41V5k9Xm9Bb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTjEOc%2FbtqZ7KcamCU%2Fs3rk2xVjLB41V5k9Xm9Bb0%2Fimg.png&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;369&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;아직 와일드카드로 바뀌지 않은 import 문&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;눈치가 빠른 사람들은 알 수도 있지만 어느 순간 내가 사용한 패키지 내의 클래스들이 하나로 묶여 와일드카드(*)로 import 될 때가 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;300&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b914eS/btqZ09LgtqB/l9ERzvBn1XqgDdnkUSb4T1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b914eS/btqZ09LgtqB/l9ERzvBn1XqgDdnkUSb4T1/img.png&quot; data-alt=&quot;와일드카드로 바뀌어버린 import 문&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b914eS/btqZ09LgtqB/l9ERzvBn1XqgDdnkUSb4T1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb914eS%2FbtqZ09LgtqB%2Fl9ERzvBn1XqgDdnkUSb4T1%2Fimg.png&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;300&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;와일드카드로 바뀌어버린 import 문&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Intellij의 기본 설정을 확인해보면 왜 이런 현상이 일어나는지 알 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;File -&amp;gt; Settings -&amp;gt; Editor -&amp;gt; Code Style -&amp;gt; Java -&amp;gt; Imports&lt;/b&gt;&lt;/span&gt; 탭을 확인해보면 다음과 같은 내용을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1281&quot; data-origin-height=&quot;1022&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mXhCn/btqZ2b2S08P/TjdgSkcGxw8ar9GhdLRkSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mXhCn/btqZ2b2S08P/TjdgSkcGxw8ar9GhdLRkSk/img.png&quot; data-alt=&quot;import 문 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mXhCn/btqZ2b2S08P/TjdgSkcGxw8ar9GhdLRkSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmXhCn%2FbtqZ2b2S08P%2FTjdgSkcGxw8ar9GhdLRkSk%2Fimg.png&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1281&quot; data-origin-height=&quot;1022&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;import 문 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;중간에 표시된 영역을 보면&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Class count to use import with '*': 5&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Names count to use static import with '*': 3&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;으로 설정되어 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;내용을 보면 바로 알 수 있듯이 이 부분은 와일드카드(*)를 가진 import 문으로 변경시키기 위한 &lt;b&gt;threshold&lt;/b&gt;다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 숫자 값을 높여줌으로써 자동으로 import 문에 와일드카드(*)가 추가되는 것을 방지할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;필자는 이 값(&lt;span style=&quot;color: #333333;&quot;&gt;Class count to use import with '*')&lt;/span&gt;을 &lt;b&gt;500&lt;/b&gt;으로 설정해서 웬만하면 와일드카드(*) import가 나타나지 않도록 변경하였다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;(이 설정을 끄는 옵션을 찾지 못했다.)&lt;/s&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;435&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bawzbx/btqZ0tJXKDX/Zuc99hG4bSXcsPWQWogqF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bawzbx/btqZ0tJXKDX/Zuc99hG4bSXcsPWQWogqF1/img.png&quot; data-alt=&quot;와일드카드가 나타나지 않은 import 문&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bawzbx/btqZ0tJXKDX/Zuc99hG4bSXcsPWQWogqF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbawzbx%2FbtqZ0tJXKDX%2FZuc99hG4bSXcsPWQWogqF1%2Fimg.png&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;435&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;와일드카드가 나타나지 않은 import 문&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실 이 설정이 왜 필요한지 궁금할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여러 명이서 개발을 할 때 개발 컨벤션을 맞추기 마련인데 이런 부분도 컨벤션의 일종으로 조절해야 할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그때 팀에서 저렇게 와일드카드(*)에 관대할 수도 있지만 그렇지 않은 경우 인텔리제이에서 멋대로 바꿔버리면 컨벤션이 깨지게 된다.&lt;/p&gt;</description>
      <category>기타</category>
      <category>import문</category>
      <category>IntelliJ</category>
      <category>java</category>
      <category>와일드카드</category>
      <category>컨벤션</category>
      <author>개발자 카니</author>
      <guid isPermaLink="true">https://dev-kani.tistory.com/39</guid>
      <comments>https://dev-kani.tistory.com/39#entry39comment</comments>
      <pubDate>Mon, 15 Mar 2021 01:05:40 +0900</pubDate>
    </item>
    <item>
      <title>[Modern Java] 람다 표현식(Lambda Expression)</title>
      <link>https://dev-kani.tistory.com/38</link>
      <description>&lt;ul&gt;
&lt;li&gt;아래 내용은 '&lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&amp;amp;mallGb=KOR&amp;amp;barcode=9791162242025&amp;amp;orderClick=&amp;amp;Kc=&quot;&gt;모던 자바 인 액션&lt;/a&gt;'을 읽고 정리한 글로 책 내용의 순서를 따라간다.&lt;/li&gt;
&lt;li&gt;상세한 내용이나 예시는 책과 상이할 수 있다.&lt;/li&gt;
&lt;li&gt;예제 코드에서 사용되고 있는 스펙
&lt;ol&gt;
&lt;li&gt;Java 15 preview (record라는 새로운 클래스 개념을 사용하기 위해서 해당 프리뷰 버전을 사용. 하위 버전의 경우는 일반 클래스를 생성한 후 getter를 만들고 사용하면 됨) - &lt;a href=&quot;https://dev-kani.tistory.com/27&quot;&gt;참고&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;JUnit 5&lt;/li&gt;
&lt;li&gt;Gradle 6.7&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/YoungWukJeon/tistory-upload/tree/main/src/main/java/lambda&quot;&gt;소스코드&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1&gt;람다(Lambda)란?&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;람다 표현식&lt;/b&gt;은 메서드로 전달할 수 있는 익명 함수를 단순화한 것이라고 할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;람다의 특징&lt;/p&gt;
&lt;ul&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;/li&gt;
&lt;li&gt;
&lt;p&gt;람다의 구조&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu6kQY/btqXgpcd9oq/3t2snduFt9KbXutBXidlHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu6kQY/btqXgpcd9oq/3t2snduFt9KbXutBXidlHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu6kQY/btqXgpcd9oq/3t2snduFt9KbXutBXidlHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu6kQY%2FbtqXgpcd9oq%2F3t2snduFt9KbXutBXidlHK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul&gt;
&lt;li&gt;파라미터 리스트
&lt;ul&gt;
&lt;li&gt;파라미터 타입 생략 가능&lt;/li&gt;
&lt;li&gt;파라미터가 하나일때 &lt;code&gt;()&lt;/code&gt; 생략 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;화살표&lt;/li&gt;
&lt;li&gt;람다 바디
&lt;ul&gt;
&lt;li&gt;실행 내용이 단일 실행(한 줄)일때 &lt;code&gt;{}&lt;/code&gt; 생략 가능. &lt;code&gt;{}&lt;/code&gt;이 생략되면 &lt;b&gt;return&lt;/b&gt; 키워드와 &lt;b&gt;;(세미콜론)&lt;/b&gt;도 같이 생략해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;표현식 스타일(expression style) 람다(기본 문법)&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;  (parameters) -&amp;gt; expression&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;블록 스타일(block style) 람다&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;  (parameters) -&amp;gt; { statements; }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Q. 퀴즈-1) 람다 문법&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;앞에서 설명한 람다 규칙에 맞지 않는 람다 표현식을 고르시오.
&lt;ol&gt;
&lt;li&gt;() &amp;rarr; {}&lt;/li&gt;
&lt;li&gt;() &amp;rarr; &quot;Raoul&quot;&lt;/li&gt;
&lt;li&gt;() &amp;rarr; { return &quot;Mario&quot;; }&lt;/li&gt;
&lt;li&gt;(Integer i) &amp;rarr; return &quot;Alan&quot; + i;&lt;/li&gt;
&lt;li&gt;(String s) &amp;rarr; { &quot;Iron Man&quot;; }&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A. 정답&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;정답
&lt;ul&gt;
&lt;li&gt;4번과 5번이 유효하지 않은 람다 표현식이다.&lt;/li&gt;
&lt;li&gt;4번은 (Integer i) &amp;rarr; { return &quot;Alan&quot; + i; }처럼 되어야 올바른 람다 표현식이다.&lt;/li&gt;
&lt;li&gt;5번은 (String s) &amp;rarr; &quot;Iron Man&quot; 또는 (String s) &amp;rarr; { return &quot;Iron Man&quot;; }처럼 되어야 올바른 람다 표현식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;람다 예제&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;불리언 표현식&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;  (List&amp;lt;String&amp;gt; list) -&amp;gt; list.isEmpty()&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;객체 생성&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;  () -&amp;gt; new Apple(10)&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;객체에서 소비&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;  (Apple a) -&amp;gt; {
          System.out.println(a.getWeight());
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;객체에서 선택/추출&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;  (String s) -&amp;gt; s.length()&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;두 값을 조합&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;  (int a, int b) -&amp;gt; a * b&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;두 객체 비교&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;  (Apple a1, Apple a2) -&amp;gt; a1.getWeight().compareTo(a2.getWeight())&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1&gt;어디에, 어떻게 람다를 사용할까?&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;함수형 인터페이스(Functional Interface)&lt;/li&gt;
&lt;li&gt;함수 디스크립터(Function Descriptor)&lt;/li&gt;
&lt;li&gt;위의 내용은 따로 포스팅해서 다룰 예정이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1&gt;람다 활용 : 실행 어라운드 패턴&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;자원 처리 (예를 들면 데이터베이스의 파일 처리)에 사용하는 순환 패턴(recurrent pattern)은 자원을 열고, 처리한 다음에, 자원을 닫는 순서로 이루어진다.&lt;/li&gt;
&lt;li&gt;설정(setup)과 정리(cleanup) 과정은 대부분 비슷하다.&lt;/li&gt;
&lt;li&gt;아래 그림과 같은 형식의 코드를 &lt;b&gt;실행 어라운드 패턴&lt;/b&gt;(execute around pattern)이라고 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;public String processFile() throw IOException {
        try (BufferedReader br = new BufferedReader(new FileReader(&quot;data.txt&quot;))) {
                return br.readLine(); // &amp;lt;- 실제 필요한 작업을 하는 행이다.
        }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sSLey/btqXb1vSkma/MW2qzBtm4T64kGmLD1ZtK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sSLey/btqXb1vSkma/MW2qzBtm4T64kGmLD1ZtK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sSLey/btqXb1vSkma/MW2qzBtm4T64kGmLD1ZtK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsSLey%2FbtqXb1vSkma%2FMW2qzBtm4T64kGmLD1ZtK1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;1단계 : 동작 파라미터화를 기억하라&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;현재 코드는 파일에서 한 번에 한 줄만 읽을 수 있다.&lt;/li&gt;
&lt;li&gt;한 번에 두 줄을 읽거나 가장 자주 사용되는 단어를 반환하려면 어떻게 해야 할까?&lt;/li&gt;
&lt;li&gt;기존의 설정, 정리 과정은 재사용하고 &lt;b&gt;processFile&lt;/b&gt; 메서드만 다른 동작을 수행하도록 명령할 수 있다면 좋을 것이다.&lt;/li&gt;
&lt;li&gt;여기서는 processFile의 동작을 파라미터화하면 된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;processFile&lt;/b&gt; 메서드가 한 번에 두 행을 읽게 하려면 우선 &lt;code&gt;BufferedReader&lt;/code&gt;를 인수로 받아서 &lt;code&gt;String&lt;/code&gt;을 반환하는 람다가 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;String result = processFile((BufferedReader br) -&amp;gt; br.readLine() + br.readLine());&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2단계 : 함수형 인터페이스를 이용해서 동작 전달&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;함수형 인터페이스 자리에 람다를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@FunctionalInterface
public interface BufferedReaderProcessor {
        String process(BufferedReader b) throws IOException;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;정의한 인터페이스를 &lt;b&gt;processFile&lt;/b&gt; 메서드의 인수로 전달할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public String processFile(BufferedReaderProcessor p) throws IOException {
        ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3단계 : 동작 실행&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이제 &lt;code&gt;BufferedReaderProcessor&lt;/code&gt;에 정의된 &lt;b&gt;process&lt;/b&gt; 메서드의 시그니처 (&lt;code&gt;BufferedReader &amp;rarr; String&lt;/code&gt;)와 일치하는 람다를 전달할 수 있다.&lt;/li&gt;
&lt;li&gt;따라서 &lt;b&gt;processFile&lt;/b&gt; 바디 내에서 &lt;code&gt;BufferedReaderProcessor&lt;/code&gt; 객체의 &lt;b&gt;process&lt;/b&gt;를 호출할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;public String processFile(BufferedReaderProcessor p) throws IOException {
        try (BufferedReader br = new BufferedReader(new FileReader(&quot;data.txt&quot;))) {
                return p.process(br);
        }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4단계 : 람다 전달&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 한 행을 처리하는 코드다.
String oneLine = processFile((BufferedReader br) -&amp;gt; br.readLine());

// 두 행을 처리하는 코드다.
String twoLines = processFile((BufferedReader br) -&amp;gt; br.readLine() + br.readLine());&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h1&gt;함수형 인터페이스 사용&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;별도 포스팅&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1&gt;형식 검사, 형식 추론, 제약&lt;/h1&gt;
&lt;h2&gt;형식 검사&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;람다가 사용되는 컨텍스트(context)를 이용해서 람다의 형식(type)을 추론할 수 있다.&lt;/li&gt;
&lt;li&gt;어떤 컨텍스트에서 기대되는 람다 표현식의 형식을 &lt;b&gt;대상 형식&lt;/b&gt;(target type)이라고 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;List&amp;lt;Apple&amp;gt; heavierThan150g = filter(inventory, (Apple apple) -&amp;gt; apple.getWeight() &amp;gt; 150);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 코드의 형식 확인 과정을 보여준다.
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;filter&lt;/b&gt; 메서드의 선언을 확인한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;filter&lt;/b&gt; 메서드는 두 번째 파라미터로 &lt;code&gt;Predicate&amp;lt;Apple&amp;gt;&lt;/code&gt; 형식(대상 형식)을 기대한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Predicate&amp;lt;Apple&amp;gt;&lt;/code&gt;은 &lt;b&gt;test&lt;/b&gt;라는 한 개의 추상 메서드를 정의하는 함수형 인터페이스다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;test&lt;/b&gt; 메서드는 &lt;b&gt;Apple&lt;/b&gt;을 받아 &lt;b&gt;boolean&lt;/b&gt;을 반환하는 함수 디스크립터를 묘사한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;filter&lt;/b&gt; 메서드로 전달된 인수는 이와 같은 요구사항을 만족해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m3Z0h/btqXgnZHYOw/au7DIzQ2boxXBddtFNWnrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m3Z0h/btqXgnZHYOw/au7DIzQ2boxXBddtFNWnrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m3Z0h/btqXgnZHYOw/au7DIzQ2boxXBddtFNWnrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm3Z0h%2FbtqXgnZHYOw%2Fau7DIzQ2boxXBddtFNWnrK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;람다 표현식이 예외를 던질 수 있다면 추상 메서드도 같이 예외를 던질 수 있도록 &lt;b&gt;throws&lt;/b&gt;로 선언해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;같은 람다, 다른 함수형 인터페이스&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;대상 형식(target typing)이라는 특징 때문에 같은 람다 표현식이더라도 호환되는 추상 메서드를 가진 다른 함수형 인터페이스로 사용될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Comparator&amp;lt;Apple&amp;gt; c1 = (Apple a1, Apple a2) -&amp;gt; a1.getWeight().compareTo(a2.getWeight());
ToIntBiFunction&amp;lt;Apple, Apple&amp;gt; c2 = (Apple a1, Apple a2) -&amp;gt; a1.getWeight().compareTo(a2.getWeight());
BiFunction&amp;lt;Apple, Apple, Integer&amp;gt; c3 = (Apple a1, Apple a2) -&amp;gt; a1.getWeight().compareTo(a2.getWeight());&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;다이아몬드 연산자&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Java 7에서도 다이아몬드 연산자(&amp;lt;&amp;gt;)로 컨텍스트에 따른 제네릭 형식을 추론할 수 있었다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;주어진 클래스 인스턴스 표현식을 두 개 이상의 다양한 컨텍스트에 사용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이때 인스턴스 표현식의 형식 인수는 컨텍스트에 의해 추론된다.&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; listOfStrings = new ArrayList&amp;lt;&amp;gt;();
List&amp;lt;Integer&amp;gt; listOfIntegers = new ArrayList&amp;lt;&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;특별한 void 호환 규칙&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;람다의 바디에 일반 표현식이 있으면 void를 반환하는 함수 디스크립터와 호환된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;물론 파라미터 리스트도 호환되어야 한다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// Predicate는 불리언 반환값을 갖는다.
Predicate&amp;lt;String&amp;gt; p = s -&amp;gt; list.add(s);
// Consumer는 void 반환값을 갖는다.
Consumer&amp;lt;String&amp;gt; b = s -&amp;gt; list.add(s);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;할당문 컨텍스트, 메서드 호출 컨텍스트(파라미터, 반환값), 형변환(cast) 컨텍스트 등으로 람다 표현식의 형식을 추론할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Q. 퀴즈-2) 형식 검사 문제, 다음 코드를 컴파일할 수 없는 이유는?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;다음 코드의 문제를 해결하시오.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;  Object o = () -&amp;gt; {
          System.out.println(&quot;Tricky example&quot;);
  };&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A. 정답&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;정답&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;람다 표현식의 컨텍스트는 Object(대상 형식)다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만 Object는 함수형 인터페이스가 아니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;따라서 void 형식의 함수 디스크립터를 갖는 &lt;code&gt;Runnable&lt;/code&gt;로 대상 형식을 바꿔서 문제를 해결할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;  Runnable r = () -&amp;gt; {
          System.out.println(&quot;Tricky example&quot;);
  };&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;람다 표현식을 명시적으로 대상 형식을 제공하는 &lt;code&gt;Runnable&lt;/code&gt;로 캐스팅해서 문제를 해결할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;  Object o = (Runnable) () -&amp;gt; {
          System.out.println(&quot;Tricky example&quot;);
  };&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;같은 함수형 디스크립터를 가진 두 함수형 인터페이스를 갖는 메서드를 오버로딩할 때 이와 같은 기법을 활용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;어떤 메서드의 시그니처가 사용되어야 하는지를 명시적으로 구분하도록 람다를 캐스트할 수 있다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예를 들어 &lt;code&gt;execute(() -&amp;gt; {})&lt;/code&gt;라는 람다 표현식이 있다면 &lt;code&gt;Runnable&lt;/code&gt;과 &lt;code&gt;Action&lt;/code&gt;의 함수 디스크립터가 같으므로 누구를 가리키는지가 명확하지 않다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;  public void execute(Runnable runnable) {
          runnable.run();
  }
  public void execute(Action&amp;lt;T&amp;gt; action) {
          action.act();
  }
  @FunctionalInterface
  interface Action {
          void act();
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만 &lt;code&gt;execute((Action) () -&amp;gt; {});&lt;/code&gt;처럼 캐스트를 하면 누구를 호출할 것인지가 명확해진다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;형식 추론&lt;/h2&gt;
&lt;ul&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UaWAH/btqXxe069JQ/WeCIXlsm1LjuVyHIkp1hu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UaWAH/btqXxe069JQ/WeCIXlsm1LjuVyHIkp1hu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UaWAH/btqXxe069JQ/WeCIXlsm1LjuVyHIkp1hu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUaWAH%2FbtqXxe069JQ%2FWeCIXlsm1LjuVyHIkp1hu1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;여러 파라미터를 포함하는 람다 표현식에서는 코드 가독성 향상이 더 두드러진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIuaq9/btqXb0X4WPd/zivMRQH1Nj1s8JAtwQJqp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIuaq9/btqXb0X4WPd/zivMRQH1Nj1s8JAtwQJqp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIuaq9/btqXb0X4WPd/zivMRQH1Nj1s8JAtwQJqp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIuaq9%2FbtqXb0X4WPd%2FzivMRQH1Nj1s8JAtwQJqp0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;상황에 따라 명시적으로 형식을 포함하는 것이 좋을 때도 있고 형식을 배제하는 것이 가독성을 향상시킬 때도 있다.&lt;/li&gt;
&lt;li&gt;어떤 방법이 좋은지 정해진 규칙은 없다.&lt;/li&gt;
&lt;li&gt;개발자 스스로 어떤 코드가 가독성을 향상시킬 수 있는지 결정해야 한다.
&lt;ul&gt;
&lt;li&gt;필자의 경우 잘 모르는 메서드를 쓰거나 할때 간혹 명시하기도 하지만 대부분 생략한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;지역 변수 사용&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;람다 표현식에서는 익명 함수가 하는 것처럼 &lt;b&gt;자유 변수&lt;/b&gt;(free variable, 파라미터로 넘겨진 변수가 아닌 외부에서 정의된 변수)를 활용할 수 있다.&lt;/li&gt;
&lt;li&gt;이와 같은 동작을 &lt;b&gt;람다 캡처링&lt;/b&gt;(capturing lambda)이라고 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;int portNumber = 1337;
Runnable r = () -&amp;gt; System.out.println(portNumber);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;람다는 인스턴스 변수와 정적 변수를 자유롭게 캡처(자신의 바디에서 참조할 수 있도록)할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만 그러려면 지역 변수는 명시적으로 &lt;b&gt;final&lt;/b&gt;로 선언되어 있어야 하거나 실질적으로 &lt;b&gt;final&lt;/b&gt;로 선언된 변수와 똑같이 사용되어야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;즉, 람다 표현식은 한 번만 할당할 수 있는 지역 변수를 캡처할 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;인스턴스 변수 캡처는 &lt;b&gt;final&lt;/b&gt; 지역 변수 &lt;b&gt;this&lt;/b&gt;를 캡처하는 것과 마찬가지다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4EDMV/btqXyLEmHnu/oDgpTJhzkoJby7tej0UNy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4EDMV/btqXyLEmHnu/oDgpTJhzkoJby7tej0UNy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4EDMV/btqXyLEmHnu/oDgpTJhzkoJby7tej0UNy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4EDMV%2FbtqXyLEmHnu%2FoDgpTJhzkoJby7tej0UNy1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;지역 변수의 제약&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;인스턴스 변수는 힙에 저장되는 반면 지역 변수는 스택에 위치한다.&lt;/li&gt;
&lt;li&gt;람다에서 지역 변수에 바로 접근할 수 있다는 가정하에 람다가 스레드에서 실행된다면 변수를 할당한 스레드가 사라져서 변수 할당이 해제되었는데도 람다를 실행하는 스레드에서는 해당 변수에 접근하려 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dg6fQU/btqXgpi1A5m/sEL1igWUrIOEoU8wOLnkDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dg6fQU/btqXgpi1A5m/sEL1igWUrIOEoU8wOLnkDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dg6fQU/btqXgpi1A5m/sEL1igWUrIOEoU8wOLnkDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdg6fQU%2FbtqXgpi1A5m%2FsEL1igWUrIOEoU8wOLnkDk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;따라서 자바 구현에서는 원래 변수에 접근을 허용하는 것이 아니라 자유 지역 변수의 복사본을 제공한다.&lt;/li&gt;
&lt;li&gt;따라서 복사본의 값이 바뀌지 않아야 하므로 지역 변수에는 한 번만 값을 할당해야 한다는 제약이 생긴 것이다.&lt;/li&gt;
&lt;li&gt;또한 지역 변수의 제약 때문에 외부 변수를 변화시키는 일반적인 명령형 프로그래밍 패턴(병렬화를 방해하는 요소)에 제동을 걸 수 있다.&lt;/li&gt;
&lt;li&gt;클로저(closure)
&lt;ul&gt;
&lt;li&gt;원칙적으로 클로저란 함수의 비지역 변수를 자유롭게 참조할 수 있는 함수의 인스턴스를 가리킨다.&lt;/li&gt;
&lt;li&gt;예를 들어 클로저를 다른 함수의 인수로 전달할 수 있고 클로저는 클로저 외부에 정의된 변수의 값에 접근하고, 값을 바꿀 수 있다.&lt;/li&gt;
&lt;li&gt;Java 8의 람다와 익명 클래스는 클로저와 비슷한 동작을 수행한다.&lt;/li&gt;
&lt;li&gt;다만 람다와 익명 클래스는 람다가 정의된 메서드의 지역 변수의 값을 바꿀 수 없다.&lt;/li&gt;
&lt;li&gt;람다가 정의된 메서드의 지역 변숫값은 &lt;b&gt;final&lt;/b&gt; 변수여야 한다.
&lt;ul&gt;
&lt;li&gt;인스턴스 변수는 스레드가 공유하는 힙에 존재하므로 특별한 제약이 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1&gt;메서드 참조(Method References)&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Java 8의 새로운 기능이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;inventory.sort((Apple a1, Apple a2) -&amp;gt; a1.getWeight().compareTo(a2.getWeight()));&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위와 같이 람다로 표현된 코드가 있을 때 &lt;code&gt;java.util.Comparator.comparing&lt;/code&gt; 메서드를 활용해서 다음과 같이 표현할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;inventory.sort(comparing(Apple::getWeight));&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;메서드 참조는 특정 메서드만 호출하는 람다의 축약형이라고 생각할 수 있다.&lt;/li&gt;
&lt;li&gt;메서드 참조를 이용하면 기존 메서드 구현으로 람다 표현식을 만들 수 있다.&lt;/li&gt;
&lt;li&gt;이때 명시적으로 메서드명을 참조함으로써 &lt;b&gt;가독성을 높일 수 있다&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P5Y9d/btqW8GZQCCH/9peGLMODClSprVP7skGavk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P5Y9d/btqW8GZQCCH/9peGLMODClSprVP7skGavk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P5Y9d/btqW8GZQCCH/9peGLMODClSprVP7skGavk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP5Y9d%2FbtqW8GZQCCH%2F9peGLMODClSprVP7skGavk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;메서드 참조 만드는 방법&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;정적 메서드 참조 : ex) &lt;code&gt;s -&amp;gt; Integer.parseInt(s)&lt;/code&gt; &amp;rarr; &lt;code&gt;Integer::parseInt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;다양한 형식의 인스턴스 메서드 참조 : ex) &lt;code&gt;s -&amp;gt; s.length()&lt;/code&gt; &amp;rarr; &lt;code&gt;String::length&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;기존 객체의 인스턴스 메서드 참조 : ex) 람다 외부 변수 &lt;b&gt;apple&lt;/b&gt;에 대해 &lt;code&gt;() -&amp;gt; apple.getWeight()&lt;/code&gt; &amp;rarr; &lt;code&gt;apple::getWeight&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3KcGp/btqW8GFAq5V/6McoUygWccLiiGr6BR4Ork/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3KcGp/btqW8GFAq5V/6McoUygWccLiiGr6BR4Ork/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3KcGp/btqW8GFAq5V/6McoUygWccLiiGr6BR4Ork/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3KcGp%2FbtqW8GFAq5V%2F6McoUygWccLiiGr6BR4Ork%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;생성자, 배열 생성자, super 호출 등에 사용할 수 있는 특별한 형식의 메서드 참조도 있다.&lt;/li&gt;
&lt;li&gt;컴파일러는 람다 표현식의 형식을 검사하던 방식과 비슷한 과정으로 메서드 참조가 주어진 함수형 인터페이스와 호환하는지 확인한다.&lt;/li&gt;
&lt;li&gt;즉, 메서드 참조는 컨텍스트의 형식과 일치해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Q. 퀴즈-3) 메서드 참조&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;다음의 람다 표현식과 일치하는 메서드 참조를 구현하시오.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;  1. ToIntFunction&amp;lt;String&amp;gt; stringToInt = (String s) -&amp;gt; Integer.parseInt(s);
  2. BiPredicate&amp;lt;List&amp;lt;String, String&amp;gt;&amp;gt; contains = (list, element) -&amp;gt; list.contains(element);
  3. Predicate&amp;lt;String&amp;gt; startsWithNumber = (String string) -&amp;gt; this.startsWithNumber(string);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A. 정답&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/YoungWukJeon/tistory-upload/tree/main/src/main/java/lambda/quiz_3&quot;&gt;정답&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;  1. ToIntFunction&amp;lt;String&amp;gt; stringToInt = Integer::parseInt
  2. BiPredicate&amp;lt;List&amp;lt;String, String&amp;gt;&amp;gt; contains = List::contains
  3. Predicate&amp;lt;String&amp;gt; startsWithNumber = this::startsWithNumber&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;생성자 참조&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ClassName::new&lt;/code&gt;처럼 클래스명과 &lt;b&gt;new&lt;/b&gt; 키워드를 이용해서 깆곤 생성자의 참조를 만들 수 있다.&lt;/li&gt;
&lt;li&gt;인수가 없는 생성자, 즉 &lt;code&gt;Supplier&lt;/code&gt;의 &lt;code&gt;() -&amp;gt; Apple&lt;/code&gt;과 같은 시그니처를 갖는 생성자가 있다고 가정하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;Supplier&amp;lt;Apple&amp;gt; c1 = Apple::new; // Apple()인 디폴트 생성자 참조
Apple a1 = c1.get(); // Supplier의 get 메서드를 호출해서 새로운 Apple 객체를 만들 수 있다.

// 위 코드는 다음과 같다.
Supplier&amp;lt;Apple&amp;gt; c1 = () -&amp;gt; new Apple();&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Apple(Integer weight)&lt;/code&gt;라는 시그니처를 갖는 생성자는 &lt;code&gt;Function&lt;/code&gt; 인터페이스의 시그니처와 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;oxygene&quot;&gt;&lt;code&gt;Function&amp;lt;Integer, Apple&amp;gt; c2 = Apple::new; // Apple(Integer weight)의 생성자 참조
Apple a2 = c2.apply(110); // Function의 apply 메서드에 무게를 인수로 호출해서 새로운 Apple 객체를 만들 수 있다.

// 위 코드는 다음과 같다.
Function&amp;lt;Apple&amp;gt; c2 = (weight) -&amp;gt; new Apple(weight);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이제 다양한 색과 무게를 갖는 사과를 다음과 같은 방식으로 만들어 보려고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;BiFunction&amp;lt;String, Integer, Apple&amp;gt; c3 = Apple::new; // Apple(String color, Integer weight)의 생성자 참조
Apple a3 = c3.apply(GREEN, 110); // BiFunction의 apply 메서드에 색과 무게를 인수로 제공해서 새로운 Apple 객체를 만들 수 있다.

// 위 코드는 다음과 같다.
BiFunction&amp;lt;String, Integer, Apple&amp;gt; c3 = (color, weight) -&amp;gt; new Apple(color, weight);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;인스턴스화하지 않고도 생성자에 접근할 수 있는 기능을 다양한 상황에 응용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;static Map&amp;lt;String, Function&amp;lt;Integer, Fruit&amp;gt;&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
static {
        map.put(&quot;apple&quot;, Apple::new);
        map.put(&quot;orange&quot;, Orange::new);
        // 등등
}

public static Fruit giveMeFruit(String fruit, Integer weight) {
        return map.get(fruit.toLowerCase()) // map에서 Function&amp;lt;Integer, Fruit&amp;gt;를 얻었다.
                        .apply(weight); // Function의 apply 메서드에 정수 무게 파라미터를 제공해서 Fruit를 만들 수 있다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Q. 퀴즈-4) 생성자 참조&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;지금까지 인수가 없거나, 하나 또는 둘인 생성자를 생성자 참조로 바꾸는 방법을 살펴봤다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Color(int, int, int)&lt;/code&gt;처럼 인수가 세 개인 생성자의 생성자 참조를 사용하려면 어떻게 해야 할까?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A. 정답&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/YoungWukJeon/tistory-upload/tree/main/src/main/java/lambda/quiz_4&quot;&gt;정답&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;생성자 참조 문법은 &lt;code&gt;ClassName::new&lt;/code&gt;이므로 &lt;code&gt;Color&lt;/code&gt; 생성자의 참조는 &lt;code&gt;Color::new&lt;/code&gt;가 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만 이를 사용하려면 생정자 참조와 일치하는 시그니처를 갖는 함수형 인터페이스가 필요하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;현재 이런 시그니처를 갖는 함수형 인터페이스는 제공되지 않으므로 우리가 직접 다음과 같은 함수형 인터페이스를 만들어야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;public interface TriFunction&amp;lt;T, U, V, R&amp;gt; {
      R apply(T t, U u, V v);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이제 다음처럼 새로운 생성자 참조를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;TriFunction&amp;lt;Integer, Integer, Integer, Color&amp;gt; colorFactory = Color::new;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;람다 표현식을 조합할 수 있는 유용한 메서드&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Java 8 API의 몇몇 함수형 인터페이스는 다양한 유틸리티 메서드를 포함한다.&lt;/li&gt;
&lt;li&gt;예를 들어, &lt;code&gt;Comparator&lt;/code&gt;, &lt;code&gt;Function&lt;/code&gt;, &lt;code&gt;Predicate&lt;/code&gt; 같은 함수형 인터페이스는 람다 표현식을 조합할 수 있도록 유틸리티 메서드를 제공한다.&lt;/li&gt;
&lt;li&gt;여러 개의 람다 표현식을 조합해서 복잡한 람다 표현식을 만들 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디폴트 메서드(default method)&lt;/b&gt;가 이것을 가능하게 해준다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디폴트 메서드&lt;/b&gt;에 대해서는 다른 포스팅에서 다루도록 하겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Comparator 조합&lt;/h2&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;Comparator&amp;lt;Apple&amp;gt; c = Comparator.comparing(Apple::getWeight);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;역정렬&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;사과의 무게를 역정렬하고 싶다고 다른 &lt;code&gt;Comparator&lt;/code&gt; 인스턴스를 만들 필요가 없다.&lt;/li&gt;
&lt;li&gt;인터페이스 자체에서 주어진 비교자의 순서를 뒤바꾸는 &lt;b&gt;reverse&lt;/b&gt;라는 디폴트 메서드를 제공하기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;inventory.sort(comparing(Apple::getWeight).reversed());&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Comparator 연결&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;무게가 같은 두 사과가 존재한다면 다른 정렬 조건이 필요할 수도 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;thenComparing&lt;/b&gt; 메서드로 두 번째 비교자를 만들 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;thenComparing&lt;/b&gt;은 (comparing 메서드처럼) 함수를 인수로 받아 첫 번째 비교자를 이용해서 두 객체가 같다고 판단되면 두 번째 비교자에 객체를 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;inventory.sort(comparing(Apple::getWeight)
                .reversed()
                .thenComparing(Apple::getCountry)); // 두 사과의 무게가 같으면 국가별로 정렬&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Predicate 조합&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Predicate&lt;/code&gt; 인터페이스는 복잡한 프레디케이트를 만들 수 있도록 &lt;b&gt;negate&lt;/b&gt;, &lt;b&gt;and&lt;/b&gt;, &lt;b&gt;or&lt;/b&gt; 세 가지 메서드를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;Predicate&amp;lt;Apple&amp;gt; notRedApple = redApple.negate(); // 기존 프레디케이트 객체 redApple의 결과를 반전시킨 객체를 만든다.
Predicate&amp;lt;Apple&amp;gt; redAndHeavyApple = redApple.and(apple -&amp;gt; apple.getWeight() &amp;gt; 150); // 두 프레디케이트를 연결해서 새로운 프레디케이트 객체를 만든다.
Predicate&amp;lt;Apple&amp;gt; redAndHeavyAppleOrGreen = redApple.and(apple -&amp;gt; apple.getWeight() &amp;gt; 150)
                                .or(apple -&amp;gt; GREEN.equals(a.getColor())); // 프레디케이트 메서드를 연결해서 더 복잡한 프레디케이트 객체를 만든다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Function 조합&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Function&lt;/code&gt; 인터페이스에서 제공하는 람다 표현식도 조합할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Function&lt;/code&gt; 인터페이스는 &lt;code&gt;Function&lt;/code&gt; 인스턴스를 반환하는 &lt;b&gt;andThen&lt;/b&gt;, &lt;b&gt;compose&lt;/b&gt; 두 가지 디폴트 메서드를 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;andThen&lt;/b&gt; 메서드는 주어진 함수를 먼저 적용한 결과를 다른 함수의 입력으로 전달하는 함수를 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;Function&amp;lt;Integer, Integer&amp;gt; f = x -&amp;gt; x + 1;
Function&amp;lt;Integer, Integer&amp;gt; g = x -&amp;gt; x * 2;
Function&amp;lt;Integer, Integer&amp;gt; h = f.andThen(g); // 수학으로는 write g(f(x)) 또는 (g ∘ f)(x)라고 표현
int result = h.apply(1); // 4를 반환&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;compose&lt;/b&gt; 메서드는 인수로 주어진 함수를 먼저 실행한 다음에 그 결과를 외부 함수의 인수로 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;Function&amp;lt;Integer, Integer&amp;gt; f = x -&amp;gt; x + 1;
Function&amp;lt;Integer, Integer&amp;gt; g = x -&amp;gt; x * 2;
Function&amp;lt;Integer, Integer&amp;gt; h = f.compose(g); // 수학으로는 f(g(x)) 또는 (f ∘ g)(x)라고 표현
int result = h.apply(1); // 3을 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n34kZ/btqXu45J53v/1Tyqi2cSs4bpYyHlvPPLV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n34kZ/btqXu45J53v/1Tyqi2cSs4bpYyHlvPPLV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n34kZ/btqXu45J53v/1Tyqi2cSs4bpYyHlvPPLV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn34kZ%2FbtqXu45J53v%2F1Tyqi2cSs4bpYyHlvPPLV1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;다음처럼 문자열로 구성된 편지 내용을 변환하는 다양한 유틸리티 메서드가 있다고 가정하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Letter {
        public static String addHeader(String text) {
                return &quot;From Raoul, Mario and Alan: &quot; + text;
        }

        public static String addFooter(String text) {
                return text + &quot; Kind regards&quot;;
        }

        public static String checkSpelling(String text) {
                return text.replaceAll(&quot;labda&quot;, &quot;lambda&quot;);
        }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;여러 유틸리티 메서드를 조합해서 다양한 변환 파이프라인을 만들 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Function&amp;lt;String, String&amp;gt; addHeader = Letter::addHeader;
Function&amp;lt;String, String&amp;gt; transformationPipeline = addHeader.andThen(Letter::checkSpelling)
                        .andThen(Letter::addFooter);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOVGH8/btqXyMJ3rsJ/AZ8TZKKSCsPmvLnUp0TWDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOVGH8/btqXyMJ3rsJ/AZ8TZKKSCsPmvLnUp0TWDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOVGH8/btqXyMJ3rsJ/AZ8TZKKSCsPmvLnUp0TWDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOVGH8%2FbtqXyMJ3rsJ%2FAZ8TZKKSCsPmvLnUp0TWDk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;비슷한 수학적 개념&lt;/h1&gt;
&lt;h2&gt;적분&lt;/h2&gt;
&lt;p&gt;$f(x) = x + 10$&lt;/p&gt;
&lt;p&gt;$\int_3^7f(x)dx,또는,\int_3^7(x+10)dx$&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0AuDn/btqXobYHgdP/usaILIbRq00KDLF2BKztd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0AuDn/btqXobYHgdP/usaILIbRq00KDLF2BKztd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0AuDn/btqXobYHgdP/usaILIbRq00KDLF2BKztd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0AuDn%2FbtqXobYHgdP%2FusaILIbRq00KDLF2BKztd0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;공학에서는 함수가 차지하는 영역을 묻는 질문이 자주 등장한다.&lt;/li&gt;
&lt;li&gt;이 예제에서 함수 f는 직선이므로 사다리꼴 기법으로 정답을 쉽게 찾을 수 있다.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 / 2 X ((3 + 10) + (7 + 10)) X (7 - 3) = 60&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이제 이 공식을 어떻게 자바로 표현할 수 있을까?&lt;/li&gt;
&lt;li&gt;먼저 적분 기호나 dy/dx 등 이상한 기호를 어떻게 처리할 것인지가 문제다.&lt;/li&gt;
&lt;li&gt;우선은 f와 한계값(여기서는 3.0과 7.0)을 인수로 받는 &lt;b&gt;integrate&lt;/b&gt;라는 함수를 만들어야 한다.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;integrate(f, 3, 7)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그러나 다음처럼 간단히 구현할 수는 없다.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;integrate(x + 10, 3, 7)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;우선 이 식에서는 x의 범위가 불분명하며, &lt;b&gt;f&lt;/b&gt;를 전달하는 것이 아니라 &lt;code&gt;x + 10&lt;/code&gt;이라는 값을 전달하게 되기 때문에 잘못된 식이다.&lt;/li&gt;
&lt;li&gt;수학에서 &lt;b&gt;dx&lt;/b&gt;의 정체는 &lt;b&gt;'x를 인수로 받아 x + 10의 결과를 만드는 함수'&lt;/b&gt;로 정의할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;자바 8 람다로 연결&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;람다를 이용해서 &lt;code&gt;integrate((double x) -&amp;gt; x + 10, 3, 7)&lt;/code&gt; 또는 &lt;code&gt;integrate((double x) -&amp;gt; f(x), 3, 7)&lt;/code&gt; 같이 나타낼 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;C&lt;/b&gt;가 정적 메서드 &lt;b&gt;f&lt;/b&gt;를 포함하는 클래스라 가정하면 메서드 참조를 사용해서 코드를 더 간단하게 만들 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;integrate(C::f, 3, 7)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;여기서 &lt;b&gt;integrate&lt;/b&gt; 메서드를 구현할 때 &lt;code&gt;DoubleFunction&lt;/code&gt; 인터페이스를 사용해서 구현할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;  public double integrate(DoubleFunction&amp;lt;Double&amp;gt; f, double a, double b) {
          return (f.apply(a) + f.apply(b)) * (b - a) / 2.0;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;또한 &lt;code&gt;DoubleUnaryOperator&lt;/code&gt;를 이용해도 결과를 박싱할 필요가 없다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;  public double integrate(DoubleUnaryOperator f, double a, double b) {
          return (f.applyAsDouble(a) + f.applyAsDouble(b)) * (b - a) / 2.0;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;참고로 수학처럼 &lt;b&gt;f(a)&lt;/b&gt;라고 표현할 수 없고 &lt;code&gt;f.apply(a)&lt;/code&gt;라고 구현했는데, 이는 자바가 진정으로 함수를 허용하지 않고 모든 것을 객체로 여기는 것을 포기할 수 없기 때문이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/Java</category>
      <category>java</category>
      <category>Java8</category>
      <category>lambda</category>
      <category>Lambda Expression</category>
      <category>modern java</category>
      <category>람다</category>
      <category>람다 표현식</category>
      <category>익명 클래스</category>
      <author>개발자 카니</author>
      <guid isPermaLink="true">https://dev-kani.tistory.com/38</guid>
      <comments>https://dev-kani.tistory.com/38#entry38comment</comments>
      <pubDate>Tue, 16 Feb 2021 00:00:31 +0900</pubDate>
    </item>
    <item>
      <title>2021년 올해 목표!</title>
      <link>https://dev-kani.tistory.com/37</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2021년도 벌써 1달이 지났다. 그동안 여러 일들로 미뤄뒀던 올해 목표를 세워보려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;티스토리 게시글 연간 게시글 30개 이상 쓰기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Github 2021 contributions 800 이상 달성하기 (회사 커밋 포함)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Group Study 2개 이상 하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;영어 회화, 일본어 등 어학 공부&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;음역대 넓히기 : B4 이상 노래 완창 하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;League Of Legends 솔랭 플래티넘&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인맥 쌓기 (Linked In, 로켓펀치 등 나의 활동을 공유하며 여러 분야의 인맥을 쌓아보자!)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;운동 시작하기(스쿼시 마렵다...)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;회사 시스템 파악 (서비스가 다양한 분야에 걸쳐있는 만큼 배우고 적용해 볼 수 있는 도메인 많다.)&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일단 생각나는 것은 이 정도이고 사실 작년과 대부분 유사하다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;작년의 목표를 고려했을 때 막연한 목표보다는 달성할 수 있는 확실한 목표를 정하는 것이 중요하다고 생각한다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;새로운 생활이 시작된 만큼 개발은 개발대로 개발 외적인 부분은 그거대로 두 마리의 토끼를 잘 잡고 싶다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>소개</category>
      <category>2021년</category>
      <category>계획</category>
      <category>다짐</category>
      <category>목표</category>
      <author>개발자 카니</author>
      <guid isPermaLink="true">https://dev-kani.tistory.com/37</guid>
      <comments>https://dev-kani.tistory.com/37#entry37comment</comments>
      <pubDate>Sat, 30 Jan 2021 14:11:18 +0900</pubDate>
    </item>
    <item>
      <title>[Modern Java] 동작 파라미터화(Behavior Parameterization)</title>
      <link>https://dev-kani.tistory.com/36</link>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;p&gt;아래 내용은 &amp;#39;&lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&amp;amp;mallGb=KOR&amp;amp;barcode=9791162242025&amp;amp;orderClick=&amp;amp;Kc=&quot;&gt;모던 자바 인 액션&lt;/a&gt;&amp;#39;을 읽고 정리한 글로 책 내용의 순서를 따라간다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;상세한 내용이나 예시는 책과 상이할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;예제 코드에서 사용되고 있는 스펙&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Java 15 preview (record라는 새로운 클래스 개념을 사용하기 위해서 해당 프리뷰 버전을 사용. 하위 버전의 경우는 일반 클래스를 생성한 후 getter를 만들고 사용하면 됨) - &lt;a href=&quot;https://dev-kani.tistory.com/27&quot;&gt;참고&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;JUnit 5&lt;/li&gt;
&lt;li&gt;Gradle 6.7&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/YoungWukJeon/tistory-upload/blob/main/src/main/java/behavior_parameterization/BehaviorParameterizationExample.java&quot;&gt;소스코드&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h1&gt;배경&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;소프트웨어 개발에서 요구사항은 항상 변한다.&lt;/li&gt;
&lt;li&gt;이러한 요구사항을 반영하면서도 엔지니어링적인 비용이 가장 최소화될 수 있으면 좋다.&lt;/li&gt;
&lt;li&gt;그뿐 아니라 새로 추가한 기능은 쉽게 구현할 수 있어야 하며 장기적인 관점에서 유지보수가 쉬어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h1&gt;변화에 대응하기&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;첫 번째 시도 : 녹색 사과 필터링&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  enum Color { RED, GREEN }

  public List&amp;lt;Apple&amp;gt; filterGreenApples(List&amp;lt;Apple&amp;gt; inventory) {
      final var result = new ArrayList&amp;lt;Apple&amp;gt;();
      for (final var apple : inventory) {
          if (GREEN.equals(apple.color())) {
              result.add(apple);
          }
      }
      return result;
  }&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;위의 코드는 &lt;strong&gt;녹색 사과&lt;/strong&gt;를 필터링하는 것이다.&lt;/li&gt;
&lt;li&gt;현재의 요구사항은 &lt;strong&gt;녹색 사과만 필터링&lt;/strong&gt;하는 것이지만 &lt;strong&gt;빨간색 사과만 필터링&lt;/strong&gt;하라는 요구사항이 올 수도 있다.&lt;/li&gt;
&lt;li&gt;이럴 경우 위의 코드와 유사하게 작성될 것이다.&lt;/li&gt;
&lt;li&gt;거의 비슷한 코드가 반복 존재한다면 그 코드를 추상화해본다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;두 번째 시도 : 색을 파라미터화&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  public List&amp;lt;Apple&amp;gt; filterApplesByColor(List&amp;lt;Apple&amp;gt; inventory, Color color) {
      final var result = new ArrayList&amp;lt;Apple&amp;gt;();
      for (final var apple : inventory) {
          if (color.equals(apple.color())) {
              result.add(apple);
          }
      }
      return result;
  }&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;지금까지 &lt;strong&gt;색&lt;/strong&gt;을 이용한 필터링을 잘 구현했다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;여기서 &lt;strong&gt;색&lt;/strong&gt; 이외에 &lt;strong&gt;무게&lt;/strong&gt;를 이용해서 사과를 필터링하고 싶다는 요구사항이 생기면 어떻게 해야할까?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  public List&amp;lt;Apple&amp;gt; filterApplesByWeight(List&amp;lt;Apple&amp;gt; inventory, int weight) {
        final var result = new ArrayList&amp;lt;Apple&amp;gt;();
        for (final var apple : inventory) {
            if (apple.weight() &amp;gt; weight) {
                result.add(apple);
            }
        }
        return result;
  }&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;색&lt;/strong&gt;을 파라미터화 했듯이 &lt;strong&gt;무게&lt;/strong&gt;를 파라미터화해서 위와 같이 해결할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;하지만 &lt;strong&gt;색&lt;/strong&gt;, &lt;strong&gt;무게&lt;/strong&gt;를 각각 파라미터화해서 조건에 적용한 것 외에는 대부분 중복되는 코드다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이는 소프트웨어 공학의 &lt;strong&gt;DRY(do not repeat yourself) 원칙&lt;/strong&gt;을 어기는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;세 번째 시도 : 가능한 모든 속성으로 필터링&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  public List&amp;lt;Apple&amp;gt; filterApples(List&amp;lt;Apple&amp;gt; inventory, Color color, int weight, boolean flag) {
      final var result = new ArrayList&amp;lt;Apple&amp;gt;();
      for (final var apple : inventory) {
          if ((flag &amp;amp;&amp;amp; apple.color().equals(color)) ||
                  (!flag &amp;amp;&amp;amp; apple.weight() &amp;gt; weight)) {
              result.add(apple);
          }
      }
      return result;
  }&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;색&lt;/strong&gt;과 &lt;strong&gt;무게&lt;/strong&gt;를 합쳐서 하나의 filter를 구현하는 방법도 있지만 이것 또한 어떤 상황에서 &lt;strong&gt;색&lt;/strong&gt;과 &lt;strong&gt;무게&lt;/strong&gt;를 구분할지 별도의 기준이 필요하다.&lt;/li&gt;
&lt;li&gt;위의 코드에서는 &lt;strong&gt;색&lt;/strong&gt;과 &lt;strong&gt;무게&lt;/strong&gt; 중 어떤 기준에 따라 필터링할지를 결정하기 위해 boolean 타입의 &lt;strong&gt;flag&lt;/strong&gt;라는 파라미터를 사용했는데, 이 메서드를 사용하는 사용자 입장에서 어떤 것이 &lt;strong&gt;true&lt;/strong&gt;이고 어떤 것이 &lt;strong&gt;false&lt;/strong&gt;인지 불명확하다.&lt;/li&gt;
&lt;li&gt;게다가 &lt;strong&gt;색&lt;/strong&gt;과 &lt;strong&gt;무게&lt;/strong&gt; 이외에 &lt;code&gt;Apple&lt;/code&gt; 클래스에 또 다른 속성(크기, 모양, 출하지 등)이 부여된다면 현재의 코드로는 유연하게 대처할 수 없다.&lt;/li&gt;
&lt;li&gt;심지어 &lt;strong&gt;빨간색 사과 중에 무거운 사과&lt;/strong&gt;를 필터링하고 싶다면? 결국 중복된 메서드를 계속해서 만들 수 밖에 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;지금까지는 &lt;strong&gt;문자열&lt;/strong&gt;, &lt;strong&gt;정수&lt;/strong&gt;, &lt;strong&gt;불리언&lt;/strong&gt; 등의 값으로 &lt;code&gt;filterApples&lt;/code&gt; 메서드를 파라미터화했다.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;동작 파라미터화(Behavior Parameterization)&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;아직은 어떻게 실행할 것인지 결정하지 않은 코드 블럭을 의미한다.&lt;/li&gt;
&lt;li&gt;즉, 코드 블럭의 실행은 나중으로 미뤄진다.&lt;/li&gt;
&lt;li&gt;결과적으로 코드 블럭에 따라 메서드의 동작이 파라미터화된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 예시를 다시 보자&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;우리는 선택 조건을 다음처럼 결정할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;사과의 어떤 속성에 기초해서 불리언값을 반환 (예를 들어 사과가 녹색인가? 200그램 이상인가?)하는 방법이 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;참 또는 거짓을 반환하는 함수를 &lt;strong&gt;프레디케이트&lt;/strong&gt;라고 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;선택 조건을 결정하는 인터페이스&lt;/strong&gt;를 정의하자.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  public interface ApplePredicate {
      boolean test(Apple apple);
  }&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;다음과 같이 다양한 선택 조건을 대표하는 &lt;code&gt;ApplePredicate&lt;/code&gt;를 정의할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  public class AppleHeavyWeightPredicate implements ApplePredicate {
      @Override
      public boolean test(Apple apple) {
          return apple.weight() &amp;gt; 200;
      }
  }&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;  public class AppleGreenColorPredicate implements ApplePredicate {
      @Override
      public boolean test(Apple apple) {
          return GREEN.equals(apple.color());
      }
  }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;  &lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnqAnb/btqTNAfOc0F/P7ApB2WA5gx3OX8qV8TKUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnqAnb/btqTNAfOc0F/P7ApB2WA5gx3OX8qV8TKUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnqAnb/btqTNAfOc0F/P7ApB2WA5gx3OX8qV8TKUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnqAnb%2FbtqTNAfOc0F%2FP7ApB2WA5gx3OX8qV8TKUk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위 조건에 따라 filter 메서드가 다르게 동작할 것이라고 예상할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이를 &lt;a href=&quot;http://en.wikipedia.org/wiki/Strategy_pattern&quot;&gt;전략 디자인 패턴(strategy design pattern)&lt;/a&gt;이라고 부른다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;전략 디자인 패턴은 각 알고리즘(전략이라 불리는)을 캡슐화하는 알고리즘 패밀리를 정의해둔 다음에 런타임에 알고리즘을 선택하는 기법이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;filterApples&lt;/code&gt;에서 &lt;code&gt;ApplePredicate&lt;/code&gt; 객체를 받아 애플의 조건을 검사하도록 메서드를 고쳐야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이렇게 &lt;strong&gt;동작 파라미터화&lt;/strong&gt;, 즉 메서드가 다양한 동작(또는 전략)을 &lt;strong&gt;받아서&lt;/strong&gt; 내부적으로 다양한 &lt;strong&gt;동작&lt;/strong&gt;을 수행할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이렇게 하면 &lt;code&gt;filterApples&lt;/code&gt; 메서드 내부에서 컬렉션을 반복하는 로직과 컬렉션의 각 요소에 적용할 동작(우리 예제에서는 프레디케이트)을 분리할 수 있다는 점에서 소프트웨어 엔지니어링적으로 큰 이득을 얻는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;네 번째 시도 : 추상적 조건으로 필터링&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  public List&amp;lt;Apple&amp;gt; filterApples(List&amp;lt;Apple&amp;gt; inventory, ApplePredicate applePredicate) {
      final var result = new ArrayList&amp;lt;Apple&amp;gt;();
      for (final var apple : inventory) {
          if (applePredicate.test(apple)) {
              result.add(apple);
          }
      }
      return result;
  }&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;첫 번째 코드에 비해 더 유연한 코드를 얻었으며 동시에 가독성도 좋아졌을 뿐 아니라 사용하기도 쉬워졌다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;필요한 조건에 따라 &lt;code&gt;ApplePredicate&lt;/code&gt;를 만들어서 &lt;code&gt;filterApples&lt;/code&gt; 메서드에 전달해 주기만 하면 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이제 세 번째 시도 마지막 부분에서 언급했던 &lt;strong&gt;빨간색 사과 중에 무거운 사과&lt;/strong&gt;를 다음과 같이 만들 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    public class AppleRedAndHeavyPredicate implements ApplePredicate {
        @Override
        public boolean test(Apple apple) {
            return RED.equals(apple.color()) &amp;amp;&amp;amp;
                    apple.weight() &amp;gt; 180;
        }
    }&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;이렇게 우리가 전달한 &lt;code&gt;ApplePredicate&lt;/code&gt; 객체에 의해 &lt;code&gt;filterApples&lt;/code&gt; 메서드의 동작이 결정된다.&lt;/p&gt;
&lt;p&gt;  &lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/efcSUs/btqTPznnAbO/QMDiaRiCvsC3dhLeDQdwvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/efcSUs/btqTPznnAbO/QMDiaRiCvsC3dhLeDQdwvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/efcSUs/btqTPznnAbO/QMDiaRiCvsC3dhLeDQdwvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FefcSUs%2FbtqTPznnAbO%2FQMDiaRiCvsC3dhLeDQdwvK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위의 그림에서 보여주는 것처럼 &lt;code&gt;ApplePredicate&lt;/code&gt;의 실제 구현은 &lt;code&gt;test&lt;/code&gt; 메서드에 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;안타깝게도 메서드는 객체만 인수로 받으므로 &lt;code&gt;test&lt;/code&gt; 메서드를 &lt;code&gt;ApplePredicate&lt;/code&gt; 객체로 감싸서 전달해야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Java 8에 새롭게 추가된 &lt;strong&gt;람다 표현식&lt;/strong&gt;을 이용하게 되면 여러 개의 &lt;code&gt;ApplePredicate&lt;/code&gt;를 정의하지 않고도 &lt;code&gt;test&lt;/code&gt; 메서드의 내부 구현 &lt;code&gt;filter&lt;/code&gt; 메서드로 전달할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;지금까지 살펴본 것처럼 컬렉션 탐색 로직과 각 항목에 적용할 동작을 분리할 수 있다는 것이 &lt;strong&gt;동작 파라미터화&lt;/strong&gt;의 강점이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5tsDr/btqTLyWN5iM/0ywnb82fnKSGiKZQvLmH5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5tsDr/btqTLyWN5iM/0ywnb82fnKSGiKZQvLmH5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5tsDr/btqTLyWN5iM/0ywnb82fnKSGiKZQvLmH5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5tsDr%2FbtqTLyWN5iM%2F0ywnb82fnKSGiKZQvLmH5k%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;Q. 퀴즈-1) 유연한 prettyPrintApple 메서드 구현하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;사과 리스트를 인수로 받아 다양한 방법으로 문자열을 생성(커스터마이즈된 다양한 toString 메서드와 같이)할 수 있도록 파라미터화된 &lt;code&gt;prettyPrintApple&lt;/code&gt; 메서드를 구현하시오.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;예를 들어, &lt;code&gt;prettyPrintApple&lt;/code&gt; 메서드가 각각의 사과 무게를 출력하도록 지시할 수 있다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;혹은 각각의 사과가 무거운지, 가벼운지 출력하도록 지시할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;prettyPrintApple&lt;/code&gt; 메서드는 지금까지 살펴본 필터링 예제와 비슷한 방법으로 구현할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;대략적인 코드는 다음과 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  public void prettyPrintApple(List&amp;lt;Apple&amp;gt; inventory, ???) {
        for (Apple apple : inventory) {
          String output = ???.???(apple);
          System.out.println(output);
      }
  }&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;???&lt;/code&gt; 에 들어갈 내용을 생각해보면 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A. 정답&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/YoungWukJeon/tistory-upload/blob/main/src/main/java/behavior_parameterization/quiz_1/QuizAnswer.java&quot;&gt;정답&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;우선 Apple을 인수로 받아 정해진 형식의 문자열로 반환할 수단이 있어야 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  public interface AppleFormatter {
        String accept(Apple a);
  }&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이제 다음과 같이 다양한 동작을 하는 &lt;code&gt;AppleFormatter&lt;/code&gt;를 만든다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  public class AppleFancyFormatter implements AppleFormatter {
        @Override
        public String accept(Apple apple) {
            String characteristic = apple.weight() &amp;gt; 200 ? &amp;quot;heavy&amp;quot; : &amp;quot;light&amp;quot;;
            return &amp;quot;A &amp;quot; + characteristic + &amp;quot; &amp;quot; + apple.color() + &amp;quot; apple&amp;quot;;
      }
  }&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;  public class AppleSimpleFormatter implements AppleFormatter {
        @Override
        public String accept(Apple apple) {
            return &amp;quot;An apple of &amp;quot; + apple.weight() + &amp;quot;g&amp;quot;;
        }
  }&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;마지막으로 &lt;code&gt;prettyPrintApple&lt;/code&gt; 메서드가 &lt;code&gt;AppleFormatter&lt;/code&gt; 객체를 인수로 받아 내부적으로 사용하도록 지시한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  public void prettyPrintApple(List&amp;lt;Apple&amp;gt; inventory, AppleFormatter appleFormatter) {
        for (final var apple : inventory) {
            String output = appleFormatter.accept(apple);
            System.out.println(output);
        }
  }&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;AppleFormatter&lt;/code&gt;의 구현을 객체화한 다음에 &lt;code&gt;prettyPrintApple&lt;/code&gt;의 인수로 전달한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음과 같은 데이터가 있다고 하자.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  private final List&amp;lt;Apple&amp;gt; inventory = List.of(
        new Apple(Color.GREEN, 200),
        new Apple(Color.GREEN, 220),
        new Apple(Color.RED, 180),
        new Apple(Color.GREEN, 200),
        new Apple(Color.RED, 190));&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;  @Test
  @DisplayName(&amp;quot;AppleFancyFormatter를 이용한 사과 출력&amp;quot;)
  void prettyPrintAppleWithAppleFancyFormatter() {
        quizAnswer.prettyPrintApple(inventory, new AppleFancyFormatter());
  }&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음 코드를 실행한 결과다.&lt;/p&gt;
&lt;p&gt;  &lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcPqT5/btqTJT8i1Ng/1J8kO08zlCAvKBtAMgPvL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcPqT5/btqTJT8i1Ng/1J8kO08zlCAvKBtAMgPvL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcPqT5/btqTJT8i1Ng/1J8kO08zlCAvKBtAMgPvL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcPqT5%2FbtqTJT8i1Ng%2F1J8kO08zlCAvKBtAMgPvL1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  @Test
  @DisplayName(&amp;quot;AppleSimpleFormatter를 이용한 사과 출력&amp;quot;)
  void prettyPrintAppleWithAppleSimpleFormatter() {
        quizAnswer.prettyPrintApple(inventory, new AppleSimpleFormatter());
  }&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음 코드를 실행한 결과다.&lt;/p&gt;
&lt;p&gt;  &lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bA0p8n/btqTDJlut20/lKzmoh0FwMYtf3deYsFZ50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bA0p8n/btqTDJlut20/lKzmoh0FwMYtf3deYsFZ50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bA0p8n/btqTDJlut20/lKzmoh0FwMYtf3deYsFZ50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbA0p8n%2FbtqTDJlut20%2FlKzmoh0FwMYtf3deYsFZ50%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h1&gt;복잡한 과정 간소화&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;지금까지의 작업에서 &lt;code&gt;filterApples&lt;/code&gt; 메서드로 새로운 동작을 전달하려면 &lt;code&gt;ApplePredicate&lt;/code&gt; 인터페이스를 구현하는 여러 클래스를 정의한 다음에 인스턴스화해야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;로직과 관련 없는 코드가 많이 추가되었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;여기서 로직은 실제로 사과를 필터링하는 &lt;code&gt;test&lt;/code&gt; 메서드 내부의 동작&lt;/li&gt;
&lt;li&gt;&lt;code&gt;test&lt;/code&gt; 메서드의 내부 동작을 구현하기 위해서 &lt;code&gt;ApplePredicate&lt;/code&gt; 인터페이스를 구현한 클래스들을 만들었고 인스턴스화했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;자바는 클래스의 선언과 인스턴스화를 동시에 수행할 수 있도록 &lt;strong&gt;익명 클래스(anonymous class)&lt;/strong&gt; 라는 기법을 제공한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;익명 클래스&lt;/strong&gt;는 자바의 지역 클래스(local class, 블럭 내부에 선언된 클래스)와 비슷한 개념이다.&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;/li&gt;
&lt;li&gt;&lt;p&gt;다섯 번째 시도 : 익명 클래스 사용&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  @Test
  @DisplayName(&amp;quot;익명 클래스를 이용한 필터링&amp;quot;)
  void filterApplesWithApplePredicateUsingAnonymousClassTest() {
      final var expected = List.of(new Apple(Color.RED, 180), new Apple(Color.RED, 190));
      final var result = behaviorParameterizationExample.filterApples(inventory, new ApplePredicate() {
          @Override
          public boolean test(Apple apple) {
              return Color.RED.equals(apple.color());
          }
      });
      assertIterableEquals(expected, result);
  }&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;자바 스윙이나 AWT 같은 GUI 애플리케이션에서 이벤트 핸들러 객체를 구현할 때 익명 클래스를 종종 사용한다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;익명 클래스를 사용해도 아직 부족한 점이 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;여전히 많은 공간을 차지한다. (클래스의 생성과 인스턴스화를 동시에 하는 것이지만 생성을 위해 메서드 내부에 클래스를 정의해야 한다.)&lt;/li&gt;
&lt;li&gt;많은 프로그래머가 익명 클래스의 사용에 익숙하지 않다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Q. 퀴즈-2) 익명 클래스 문제&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;다음 코드를 실행한 결과는 4, 5, 6, 42 중 어느 것일까?&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public class MeaningOfThis {
    public final int value = 4;

    public void doIt() {
        final var value = 6;
        Runnable r = new Runnable() {
            public final int value = 5;
            @Override
            public void run() {
                final var value = 10;
                System.out.println(this.value);
            }
        };
        r.run();
    }
}

class MeaningOfThisTest {
    private final MeaningOfThis meaningOfThis = new MeaningOfThis();

    @Test
    @DisplayName(&amp;quot;익명 클래스 문제&amp;quot;)
    void anonymousClassTest() {
        meaningOfThis.doIt();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;A. 정답&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/YoungWukJeon/tistory-upload/blob/main/src/main/java/behavior_parameterization/quiz_2/MeaningOfThis.java&quot;&gt;정답&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;코드에서 &lt;code&gt;this&lt;/code&gt;는 &lt;code&gt;MeaningOfThis&lt;/code&gt;가 아니라 &lt;code&gt;Runnable&lt;/code&gt;을 참조하고 있으므로 &lt;strong&gt;5&lt;/strong&gt;가 정답이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;여섯 번째 시도 : 람다 표현식 사용&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Java 8의 람다 표현식을 이용해서 익명 클래스로 만든 예제 코드를 다음과 같이 간단하게 재구현할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@Test
@DisplayName(&amp;quot;람다를 이용한 필터링&amp;quot;)
void filterApplesWithApplePredicateUsingLambdaTest() {
  final var expected = List.of(new Apple(Color.RED, 180), new Apple(Color.RED, 190));
  final var result = behaviorParameterizationExample.filterApples(inventory, apple -&amp;gt; Color.RED.equals(apple.color()));
  assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;코드가 훨씬 더 간결해졌다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3QUNB/btqTNzHWujn/4SjbBjKmgnDq3QGKOkknnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3QUNB/btqTNzHWujn/4SjbBjKmgnDq3QGKOkknnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3QUNB/btqTNzHWujn/4SjbBjKmgnDq3QGKOkknnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3QUNB%2FbtqTNzHWujn%2F4SjbBjKmgnDq3QGKOkknnk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;일곱 번째 시도 : 리스트 형식으로 추상화&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;지금까지 &lt;strong&gt;사과&lt;/strong&gt;를 사용한 예제를 만들었다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이제는 &lt;strong&gt;바나나&lt;/strong&gt;, &lt;strong&gt;오렌지&lt;/strong&gt;, &lt;strong&gt;정수&lt;/strong&gt;, &lt;strong&gt;문자열&lt;/strong&gt; 등의 리스트에 필터 메서드를 사용하려고 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public &amp;lt;T&amp;gt; List&amp;lt;T&amp;gt; filter(List&amp;lt;T&amp;gt; list, Predicate&amp;lt;T&amp;gt; p) {
  final var result = new ArrayList&amp;lt;T&amp;gt;();
  for (T e : list) {
      if (p.test(e)) {
          result.add(e);
      }
  }
  return result;
}

@Test
@DisplayName(&amp;quot;리스트 요소를 추상화한 후 필터링&amp;quot;)
void filterApplesWithListElementsAbstractionTest() {
  final var expected = List.of(&amp;quot;brown&amp;quot;);
  final var names = List.of(&amp;quot;hong&amp;quot;, &amp;quot;brown&amp;quot;, &amp;quot;mike&amp;quot;);
  final var result = behaviorParameterizationExample.filter(names, name -&amp;gt; name.length() &amp;gt; 4);
  assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위 코드에서 리스트의 요소들을 추상화(제네릭을 사용)했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;문자열 형태(&lt;strong&gt;이름&lt;/strong&gt;)의 리스트를 받아서 길이가 4보다 큰 요소로 필터링했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h1&gt;&lt;a href=&quot;https://github.com/YoungWukJeon/tistory-upload/tree/main/src/test/java/behavior_parameterization/practice&quot;&gt;실전 예제&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;Comparator로 정렬하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Java 8의 &lt;code&gt;List&lt;/code&gt;에는 &lt;code&gt;sort&lt;/code&gt; 메서드가 포함되어 있다.&lt;/li&gt;
&lt;li&gt;다음과 같은 인터페이스를 갖는 &lt;code&gt;java.util.Comparator&lt;/code&gt; 객체를 이용해서 &lt;code&gt;sort&lt;/code&gt;의 동작을 파라미터화할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// java.util.Comparator
public interface Comparator&amp;lt;T&amp;gt; {
    int compare(T o1, T o2);
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;사과를 무거운 순서대로 정렬해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public class ComparatorTest {
    private final List&amp;lt;Apple&amp;gt; inventory = List.of(
            new Apple(Color.GREEN, 200),
            new Apple(Color.GREEN, 220),
            new Apple(Color.RED, 180),
            new Apple(Color.GREEN, 200),
            new Apple(Color.RED, 190));

    @Test
    @DisplayName(&amp;quot;익명 클래스를 이용한 정렬&amp;quot;)
    void comparatorUsingAnonymousClassTest() {
        final var inventory = new ArrayList&amp;lt;&amp;gt;(this.inventory);
        final var expected = List.of(
                new Apple(Color.GREEN, 220),
                new Apple(Color.GREEN, 200),
                new Apple(Color.GREEN, 200),
                new Apple(Color.RED, 190),
                new Apple(Color.RED, 180));
        inventory.sort(new Comparator&amp;lt;Apple&amp;gt;() {
            @Override
            public int compare(Apple a1, Apple a2) {
                return Integer.compare(a2.weight(), a1.weight());
            }
        });

        assertIterableEquals(expected, inventory);
    }

    @Test
    @DisplayName(&amp;quot;람다를 이용한 정렬&amp;quot;)
    void comparatorUsingLambdaTest() {
        final var inventory = new ArrayList&amp;lt;&amp;gt;(this.inventory);
        final var expected = List.of(
                new Apple(Color.GREEN, 220),
                new Apple(Color.GREEN, 200),
                new Apple(Color.GREEN, 200),
                new Apple(Color.RED, 190),
                new Apple(Color.RED, 180));
        inventory.sort((a1, a2) -&amp;gt; Integer.compare(a2.weight(), a1.weight()));

        assertIterableEquals(expected, inventory);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;이렇게 익명 클래스나 람다를 이용해서 &lt;code&gt;Comparator&lt;/code&gt;를 구현한 후 요구 사항에 맞게 정렬을 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Runnable로 코드 블럭 실행하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;자바 스레드를 이용하면 병렬로 코드 블럭을 실행할 수 있다.&lt;/li&gt;
&lt;li&gt;Java 8까지는 &lt;code&gt;Thread&lt;/code&gt; 생성자에 객체만을 전달할 수 있었으므로 보통 결과를 반환하지 않는 &lt;code&gt;void run&lt;/code&gt; 메서드를 포함하는 익명 클래스가 &lt;code&gt;Runnable&lt;/code&gt; 인터페이스를 구현하도록 하는 것이 일반적인 방법이었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// java.lang.Runnable
public interface Runnable {
    void run();
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Runnable&lt;/code&gt;을 이용해서 다양한 동작을 스레드로 실행할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public class RunnableTest {
    @Test
    @DisplayName(&amp;quot;익명 클래스를 이용한 실행&amp;quot;)
    void runnableUsingAnonymousClassTest() {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(&amp;quot;Hello World&amp;quot;);
            }
        });
        thread.start();
    }

    @Test
    @DisplayName(&amp;quot;람다를 이용한 실행&amp;quot;)
    void runnableUsingLambdaTest() {
        Thread thread = new Thread(() -&amp;gt; System.out.println(&amp;quot;Hello World&amp;quot;));
        thread.start();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;ExecutorService로 실행 결과 반환받기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Java 5부터는 &lt;code&gt;ExecutorService&lt;/code&gt; 추상화 개념을 지원한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExecutorService&lt;/code&gt; 인터페이스는 태스크를 제출과 실행 과정의 연관성을 끊어준다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExecutorService&lt;/code&gt;를 이용하면 태스크를 스레드 풀로 보내조 결과를 &lt;code&gt;Future&lt;/code&gt;로 저장할 수 있다는 점이 스레드와 &lt;code&gt;Runnable&lt;/code&gt;을 이용하는 방식과는 다르다.&lt;/li&gt;
&lt;li&gt;여기서는 &lt;code&gt;ExecutorService&lt;/code&gt;의 개념에 대해서는 잘 몰라도 되고 여기서는 &lt;code&gt;Callable&lt;/code&gt; 인터페이스를 이용해 결과를 반환하는 태스크를 만들수 있다는 사실만 알면된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// java.util.concurrent.Callable
public interface Callable&amp;lt;V&amp;gt; {
    V call();
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;아래 코드에서 볼 수 있듯이 &lt;code&gt;ExecutorService&lt;/code&gt;에 태스크를 제출해서 위 코드를 활용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public class ExecutorServiceTest {
    @Test
    @DisplayName(&amp;quot;익명 클래스를 이용한 서비스 실행&amp;quot;)
    void executorServiceUsingAnonymousClassTest() throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future&amp;lt;String&amp;gt; threadName = executorService.submit(new Callable&amp;lt;String&amp;gt;() {
            @Override
            public String call() throws Exception {
                return Thread.currentThread().getName();
            }
        });
        System.out.println(threadName.get());
    }

    @Test
    @DisplayName(&amp;quot;람다를 이용한 서비스 실행&amp;quot;)
    void executorServiceUsingLambdaTest() throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future&amp;lt;String&amp;gt; threadName = executorService.submit(() -&amp;gt; Thread.currentThread().getName());
        System.out.println(threadName.get());
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;GUI 이벤트 처리하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;일반적으로 GUI 프로그래밍은 마우스 클릭이나 문자열 위로 이동하는 등의 이벤트에 대응하는 동작을 수행하는 식으로 동작한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자바FX(JavaFX)&lt;/strong&gt; 에서는 &lt;code&gt;setOnAction&lt;/code&gt; 메서드에 &lt;code&gt;EventHandler&lt;/code&gt;를 전달함으로써 이벤트에 어떻게 반응할지 설정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Button button = new Button(&amp;quot;Send&amp;quot;);

// 익명 클래스 이용
button.setOnAction(new EventHandler&amp;lt;ActionEvent&amp;gt;() {
    public void handle(ActionEvent event) {
        label.setText(&amp;quot;Sent!!&amp;quot;);
    }
});

// Lambda 이용
button.setOnAction(event -&amp;gt; label.setText(&amp;quot;Sent!!&amp;quot;));&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;p&gt;메서드로 인자를 전달할 때 값을 파라미터화 하는 것보다 실제 동작을 파라미터화해서 전달하면 좀 더 유연한 코드를 작성할 수 있다.&lt;br&gt;동작을 파라미터화해서 전달할 때 Java 8의 람다를 사용하면 좀 더 간결한 코드를 작성할 수 있다.&lt;/p&gt;</description>
      <category>프로그래밍/Java</category>
      <category>Behavior parameterization</category>
      <category>java</category>
      <category>Java8</category>
      <category>lambda</category>
      <category>modern java</category>
      <category>동작 파라미터화</category>
      <category>람다</category>
      <category>익명 클래스</category>
      <author>개발자 카니</author>
      <guid isPermaLink="true">https://dev-kani.tistory.com/36</guid>
      <comments>https://dev-kani.tistory.com/36#entry36comment</comments>
      <pubDate>Sun, 17 Jan 2021 05:13:34 +0900</pubDate>
    </item>
    <item>
      <title>[Stream API] 중간 연산 - sorted 메서드</title>
      <link>https://dev-kani.tistory.com/35</link>
      <description>&lt;h1&gt;개념&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;스트림에서는 스트림 요소들을 정렬할 수 있는 &lt;code&gt;sorted&lt;/code&gt;라는 메서드를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XZO9b/btqSGivDBpz/9WKFAr2iC1bE2BzkdBjir0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XZO9b/btqSGivDBpz/9WKFAr2iC1bE2BzkdBjir0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XZO9b/btqSGivDBpz/9WKFAr2iC1bE2BzkdBjir0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXZO9b%2FbtqSGivDBpz%2F9WKFAr2iC1bE2BzkdBjir0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sorted&lt;/code&gt; 메서드는 파라미터에 따라 2가지 버전이 존재한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;sorted() 메서드&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkggbg/btqSWaIIXkB/T3KfhX4pUEkLnOw0X6Xl7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkggbg/btqSWaIIXkB/T3KfhX4pUEkLnOw0X6Xl7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkggbg/btqSWaIIXkB/T3KfhX4pUEkLnOw0X6Xl7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdkggbg%2FbtqSWaIIXkB%2FT3KfhX4pUEkLnOw0X6Xl7k%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;스트림 요소의 타입에 따라 기본적으로 제공되는 정렬 방식을 사용한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Number&lt;/code&gt;나 &lt;code&gt;String&lt;/code&gt; 같은 경우는 오름차순, 커스텀 클래스의 경우 &lt;code&gt;Comparable&amp;lt;T&amp;gt;&lt;/code&gt;로 구현한 방식을 따른다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;커스텀 클래스의 경우 &lt;code&gt;Comparable&amp;lt;T&amp;gt;&lt;/code&gt; 인터페이스를 구현하지 않았을 경우 최종 연산 이후에 &lt;code&gt;ClassCastException&lt;/code&gt;이 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;일반 스트림과 기본형 특화 스트림 모두에 제공되며 기본형 특화 스트림의 경우 해당 primitive 타입에 맞는 정렬 방식이 적용된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;sorted(Comparator) 메서드&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm0AZx/btqSNmjsgBV/XcJvO2jkmxJqCqkPFUVrFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm0AZx/btqSNmjsgBV/XcJvO2jkmxJqCqkPFUVrFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm0AZx/btqSNmjsgBV/XcJvO2jkmxJqCqkPFUVrFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm0AZx%2FbtqSNmjsgBV%2FXcJvO2jkmxJqCqkPFUVrFK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;스트림 요소들을 함수형 인터페이스 &lt;code&gt;Comparator&amp;lt;? super T&amp;gt; comparator&lt;/code&gt;에서 정의한 방식으로 정렬할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Comparable&amp;lt;T&amp;gt;&lt;/code&gt; 인터페이스를 구현하지 않은 커스텀 클래스는 이 메서드를 사용해서 정렬 방식을 정해주어야 &lt;code&gt;ClassCastException&lt;/code&gt;의 발생을 방지할 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;기본적으로 정렬 기준이 정해져있는 클래스들의 경우 이 메서드를 사용해서 &lt;code&gt;Comparator&amp;lt;T&amp;gt;&lt;/code&gt;를 정의한다면 기존 정렬 방식을 무시하고 새로운 정렬 방식을 적용할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Number&lt;/code&gt;나 &lt;code&gt;String&lt;/code&gt; 같은 경우는 이 메서드를 사용해서 내림차순으로 정렬할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;기본형 특화 스트림의 경우는 이 메서드가 제공되지 않는다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;따라서 기본형 특화 스트림의 정렬 기준을 변경하고 싶다면 &lt;code&gt;boxed&lt;/code&gt; 메서드를 통해 박싱한 후 이 메서드를 사용해서 정렬을 해야할 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;예제&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;아래 예제에서 사용되는 내용들은 다양한 상황을 연출하기 위해서 임의로 만들어진 내용이므로 특정 속성에 대해서 왜 저 타입이 사용되었는지 의문을 가지지 말자!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예제 코드에서 사용되고 있는 스펙&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Java 15 preview (record라는 새로운 클래스 개념을 사용하기 위해서 해당 프리뷰 버전을 사용. 하위 버전의 경우는 일반 클래스를 생성한 후 getter를 만들고 사용하면 됨) - &lt;a href=&quot;https://dev-kani.tistory.com/27&quot;&gt;참고&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;JUnit 5&lt;/li&gt;
&lt;li&gt;Gradle 6.7&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/YoungWukJeon/tistory-upload/blob/main/src/main/java/stream/intermediate_operations/sorted/SortedUsage.java&quot;&gt;소스 코드&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;기본 데이터 생성&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public record Student(int no, String name, int koreanScore, int mathScore, int englishScore) implements Comparable&amp;lt;Student&amp;gt; {
    @Override
    public int compareTo(Student student) {
        int sum = koreanScore + mathScore + englishScore;
        int studentSum = student.koreanScore() + student.mathScore() + student.englishScore();
        return studentSum - sum;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;private final SortedUsage sortedUsage = new SortedUsage();
private final List&amp;lt;Student&amp;gt; students = List.of(
        new Student(1, &quot;hong&quot;, 75, 90, 80),
        new Student(2, &quot;sujin&quot;, 50, 90, 100),
        new Student(3, &quot;kate&quot;, 90, 75, 75),
        new Student(4, &quot;tae&quot;, 100, 100, 100),
        new Student(5, &quot;lob&quot;, 75, 75, 100));&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;짝수 오름차순으로 정렬하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Number&lt;/code&gt;나 &lt;code&gt;String&lt;/code&gt; 같은 자바의 기본 값으로 쓰이는 클래스들을 정렬해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;public List&amp;lt;Integer&amp;gt; getEvenNumbersOrderBy(List&amp;lt;Integer&amp;gt; numbers) {
    return numbers.stream()
            .filter(n -&amp;gt; n % 2 == 0)
            .sorted()
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이런 클래스들은 기본적으로 정렬 방식이 정해져 있다.&lt;/li&gt;
&lt;li&gt;가볍게 &lt;code&gt;filter&lt;/code&gt;를 사용해서 짝수를 판별하고 &lt;code&gt;sorted&lt;/code&gt;를 사용해서 &lt;code&gt;Integer&lt;/code&gt; 클래스의 기본 정렬 방식(정수의 오름차순)을 사용해서 정렬을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;짝수 오름차순으로 정렬하기&quot;)
void getEvenNumbersOrderByTest() {
    final var numbers = List.of(5, 2, 3, 9, 4);
    final var expected = List.of(2, 4);
    final var result = sortedUsage.getEvenNumbersOrderBy(numbers);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;문자열 내림차순으로 정렬하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이번에는 내림차순으로 정렬해보려고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public List&amp;lt;String&amp;gt; getStringsOrderByDesc(List&amp;lt;String&amp;gt; strings) {
    return strings.stream()
            .sorted(Collections.reverseOrder())
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Number&lt;/code&gt;나 &lt;code&gt;String&lt;/code&gt; 타입의 기본 정렬 방식은 &lt;b&gt;오름차순&lt;/b&gt;이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만 &lt;b&gt;내림차순&lt;/b&gt;이 필요한 경우가 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이 경우에 &lt;code&gt;Comparator&amp;lt;T&amp;gt;&lt;/code&gt;를 파라미터로 받는 버전의 &lt;code&gt;sorted&lt;/code&gt; 메서드를 사용한다.&lt;/li&gt;
&lt;li&gt;여기서는 &lt;code&gt;Collections&lt;/code&gt; 클래스에서 제공하는 순서를 뒤집어주는 &lt;code&gt;Collections.reverseOrder()&lt;/code&gt;라는 메서드를 사용했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;문자열을 내림차순(사전 역순)으로 정렬해보자.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;문자열 내림차순으로 정렬하기&quot;)
void getStringsOrderByDescTest() {
    final var strings = List.of(&quot;banana&quot;, &quot;orange&quot;, &quot;apple&quot;);
    final var expected = List.of(&quot;orange&quot;, &quot;banana&quot;, &quot;apple&quot;);
    final var result = sortedUsage.getStringsOrderByDesc(strings);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;학생들 총점순으로 내림차순 정렬하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이번에는 커스텀으로 만든 클래스를 정렬해 보려고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public List&amp;lt;Student&amp;gt; getStudentsOrderByDesc(List&amp;lt;Student&amp;gt; students) {
    return students.stream()
            .sorted()
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위에서 생성한 기본 데이터 클래스인 &lt;code&gt;Student&lt;/code&gt; 클래스는 &lt;code&gt;Comparable&amp;lt;Student&amp;gt;&lt;/code&gt; 인터페이스를 구현하고 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Student&lt;/code&gt; 클래스의 &lt;code&gt;compareTo&lt;/code&gt; 메서드를 보면 학생들의 총점을 구해서 점수가 큰 순으로 비교할 수 있도록 구현되어 있다.&lt;/li&gt;
&lt;li&gt;따라서 &lt;code&gt;Student&lt;/code&gt; 클래스의 기본 정렬 방식은 학생들의 총점의 내림차순이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;학생들 총점순으로 내림차순 정렬하기&quot;)
void getStudentsOrderByDescTest() {
    final var expected = List.of(
            new Student(4, &quot;tae&quot;, 100, 100, 100),
            new Student(5, &quot;lob&quot;, 75, 75, 100),
            new Student(1, &quot;hong&quot;, 75, 90, 80),
            new Student(2, &quot;sujin&quot;, 50, 90, 100),
            new Student(3, &quot;kate&quot;, 90, 75, 75));
    final var result = sortedUsage.getStudentsOrderByDesc(students);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;학생들 수학 점수순으로 내림차순 정렬하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이번에는 위에서 사용한 &lt;code&gt;Student&lt;/code&gt; 클래스에서 정렬 방식을 바꿔서 수학 점수의 내림차순으로 정렬하려고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;public List&amp;lt;Student&amp;gt; getStudentsOrderByMathScoreDesc(List&amp;lt;Student&amp;gt; students) {
    return students.stream()
            .sorted((a, b) -&amp;gt; b.mathScore() - a.mathScore())
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Student&lt;/code&gt; 클래스의 기본 정렬 방식은 총점에 의한 내림차순이지만 &lt;code&gt;sorted&lt;/code&gt; 메서드 부분을 살펴보면 &lt;code&gt;Comparator&amp;lt;T&amp;gt;&lt;/code&gt;를 파라미터로 받는 버전을 사용하고 있는 것을 알 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이로 인해 기본 정렬 방식은 무시되고 내부에 람다로 구현된 &lt;code&gt;Comparator&amp;lt;T&amp;gt;&lt;/code&gt;를 가지고 정렬 작업(수학 점수의 내림차순)을 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이렇게 클래스가 기본적으로 정렬 방식 정해져 있다고 해도 필요에 따라 메서드나 람다를 활용해서 자유롭게 정렬 방식을 변경할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;학생들 수학 점수순으로 내림차순 정렬하기&quot;)
void getStudentsOrderByMathScoreDescTest() {
    final var expected = List.of(
            new Student(4, &quot;tae&quot;, 100, 100, 100),
            new Student(1, &quot;hong&quot;, 75, 90, 80),
            new Student(2, &quot;sujin&quot;, 50, 90, 100),
            new Student(3, &quot;kate&quot;, 90, 75, 75),
            new Student(5, &quot;lob&quot;, 75, 75, 100));
    final var result = sortedUsage.getStudentsOrderByMathScoreDesc(students);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이상 Stream API에서 사용하는 중간 연산 중 데이터 처리의 기본인 &lt;code&gt;sorted&lt;/code&gt; 메서드에 대해 살펴보았다.&lt;/p&gt;</description>
      <category>프로그래밍/Java</category>
      <category>java</category>
      <category>Sorted</category>
      <category>Stream API</category>
      <category>스트림</category>
      <category>자바</category>
      <category>정렬</category>
      <category>중간 연산</category>
      <author>개발자 카니</author>
      <guid isPermaLink="true">https://dev-kani.tistory.com/35</guid>
      <comments>https://dev-kani.tistory.com/35#entry35comment</comments>
      <pubDate>Thu, 7 Jan 2021 12:08:44 +0900</pubDate>
    </item>
    <item>
      <title>[Stream API] 중간 연산 - peek 메서드</title>
      <link>https://dev-kani.tistory.com/34</link>
      <description>&lt;h1&gt;개념&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;스트림에는 중간 연산의 수행 결과를 디버깅할 수 있는 수단인 &lt;code&gt;peek&lt;/code&gt; 메서드를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9kwnF/btqSGiCjZal/NDwilwGccFntgiGErj7261/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9kwnF/btqSGiCjZal/NDwilwGccFntgiGErj7261/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9kwnF/btqSGiCjZal/NDwilwGccFntgiGErj7261/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9kwnF%2FbtqSGiCjZal%2FNDwilwGccFntgiGErj7261%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;파라미터로 주어지는 &lt;code&gt;Consumer&amp;lt;? super T&amp;gt; action&lt;/code&gt;를 사용해서 스트림의 요소들을 소모한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Consumer&amp;lt;T&amp;gt;&lt;/code&gt;를 받는 만큼 스트림의 요소들을 가지고 추가적은 작업을 수행할 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;peek&lt;/code&gt; 메서드와 &lt;code&gt;forEach&lt;/code&gt; 메서드를 혼동해서는 안된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;peek&lt;/code&gt; 메서드와 &lt;code&gt;forEach&lt;/code&gt; 메서드 모두 &lt;code&gt;System.out::println&lt;/code&gt; 같은 &lt;code&gt;Consumer&amp;lt;T&amp;gt;&lt;/code&gt;를 파라미터로 받는다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;forEach&lt;/code&gt; 메서드는 최종 연산이기 때문에 결과를 확인할 수 있으나 &lt;code&gt;peek&lt;/code&gt; 메서드는 중간 연산이기 때문에 어떠한 최종 연산도 하지 않으면 아무것도 확인할 수가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;보통 잘 사용되지 않는 메서드지만 알아둘 필요는 있다고 생각한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;예제&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;아래 예제에서 사용되는 내용들은 다양한 상황을 연출하기 위해서 임의로 만들어진 내용이므로 특정 속성에 대해서 왜 저 타입이 사용되었는지 의문을 가지지 말자!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예제 코드에서 사용되고 있는 스펙&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;JUnit 5&lt;/li&gt;
&lt;li&gt;Gradle 6.7&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/YoungWukJeon/tistory-upload/blob/main/src/main/java/stream/intermediate_operations/peek/PeekUsage.java&quot;&gt;소스 코드&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;기본 데이터 생성&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Address {
    private final int no;
    private final String si;
    private final String gu;
    private final String dong;
    private String fullName;

    public Address(int no, String si, String gu, String dong, String fullName) {
        this.no = no;
        this.si = si;
        this.gu = gu;
        this.dong = dong;
        this.fullName = fullName;
    }

    public String getSi() {
        return si;
    }

    public String getGu() {
        return gu;
    }

    public String getDong() {
        return dong;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    @Override
    public boolean equals(Object obj) {
        final var address = (Address) obj;
        return no == address.no &amp;amp;&amp;amp; si.equals(address.getSi())
                &amp;amp;&amp;amp; gu.equals(address.getGu()) &amp;amp;&amp;amp; dong.equals(address.getDong())
                &amp;amp;&amp;amp; ((fullName == null &amp;amp;&amp;amp; address.getFullName() == null) || fullName.equals(address.getFullName()));
    }

    @Override
    public String toString() {
        return &quot;Address{&quot; +
                &quot;no=&quot; + no +
                &quot;, si='&quot; + si + '\'' +
                &quot;, gu='&quot; + gu + '\'' +
                &quot;, dong='&quot; + dong + '\'' +
                &quot;, fullName='&quot; + fullName + '\'' +
                '}';
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;현재 Java의 단점이 부각되는 코드인 것 같다. 데이터 클래스를 하나 만드는데 너무 많은 내용이 필요하다.&lt;/del&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;private final PeekUsage peekUsage = new PeekUsage();
private final List&amp;lt;Address&amp;gt; addresses = List.of(
        new Address(1, &quot;서울시&quot;, &quot;관악구&quot;, &quot;신림동&quot;, null),
        new Address(2, &quot;서울시&quot;, &quot;강남구&quot;, &quot;논현동&quot;, null),
        new Address(3, &quot;서울시&quot;, &quot;동작구&quot;, &quot;사당동&quot;, null),
        new Address(4, &quot;서울시&quot;, &quot;서초구&quot;, &quot;양재동&quot;, null));&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;두 수의 공배수 구하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;주어진 두 수와 최댓값 n이 주어질 때 두 수의 공배수를 구해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public List&amp;lt;Integer&amp;gt; getCommonMultipleAAndBUntilN(int a, int b, int n) {
    return IntStream.rangeClosed(1, n)
            .filter(i -&amp;gt; i % a == 0)
            .peek(i -&amp;gt; System.out.println(a + &quot;의 배수: &quot; + i))
            .filter(i -&amp;gt; i % b == 0)
            .peek(i -&amp;gt; System.out.println(a + &quot;와 &quot; + b + &quot;의 공배수: &quot; + i))
            .boxed()
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;IntStream.rangedClosed(1, n)&lt;/code&gt;을 이용해서 &lt;b&gt;1&lt;/b&gt;부터 &lt;b&gt;n&lt;/b&gt;까지의 자연수를 생성하는 스트림을 만들었다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;두 번의 &lt;code&gt;filter&lt;/code&gt; 메서드를 통해서 배수 판별을 하고 중간중간에 &lt;code&gt;peek&lt;/code&gt; 메서드를 사용해서 &lt;b&gt;디버깅&lt;/b&gt;을 하고 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;collect&lt;/code&gt; 메서드를 호출하기 전에 &lt;code&gt;boxed&lt;/code&gt; 메서드를 사용해서 기본형 특화 스트림의 primitive 타입을 Wrapper 클래스 타입으로 박싱한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;박싱을 하지 않으면 &lt;code&gt;collect(Collectors.toList())&lt;/code&gt;를 사용해서 &lt;code&gt;List&amp;lt;Integer&amp;gt;&lt;/code&gt;로 모을 수 없다.&lt;/li&gt;
&lt;li&gt;여기서 사용되는 &lt;code&gt;boxed&lt;/code&gt;라는 메서드 또한 중간 연산 중 하나지만 내용 자체가 기본형 특화 스트림을 그에 해당하는 Wrapper 클래스 타입의 스트림으로 박싱하는 역할만 하기 때문에 별도로 포스팅하지는 않을 것이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IntStream&lt;/code&gt;을 &lt;code&gt;Stream&amp;lt;Integer&amp;gt;&lt;/code&gt;, &lt;code&gt;LongStream&lt;/code&gt;을 &lt;code&gt;Stream&amp;lt;Long&amp;gt;&lt;/code&gt; 그리고 &lt;code&gt;DoubleStream&lt;/code&gt;을 &lt;code&gt;Stream&amp;lt;Double&amp;gt;&lt;/code&gt;로 변환시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;15 이하의 자연수 중에서 2와 3의 공배수를 구해보자.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;두 수의 공배수 구하기&quot;)
void getCommonMultipleAAndBUntilNTest() {
    final var expected = List.of(6, 12);
    final var result = peekUsage.getCommonMultipleAAndBUntilN(2, 3, 15);
    assertEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;다음과 같은 내용이 콘솔에 출력되고 테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bun1Ae/btqSPGaMo5Q/A2iKNEY5K8ckcQfm1STNVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bun1Ae/btqSPGaMo5Q/A2iKNEY5K8ckcQfm1STNVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bun1Ae/btqSPGaMo5Q/A2iKNEY5K8ckcQfm1STNVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbun1Ae%2FbtqSPGaMo5Q%2FA2iKNEY5K8ckcQfm1STNVk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;주소 풀네임 조합하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;시, 구, 동 데이터가 있을 때 이것을 합쳐서 전체 주소로 만들어보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public List&amp;lt;Address&amp;gt; makeFullNameInAddress(List&amp;lt;Address&amp;gt; addresses) {
    return addresses.stream()
            .peek(a -&amp;gt; a.setFullName(a.getSi() + &quot; &quot; + a.getGu() + &quot; &quot; + a.getDong()))
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;여기서는 &lt;code&gt;peek&lt;/code&gt; 메서드를 사용해서 시, 구, 동을 하나의 문자열로 합치는 작업을 수행했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;개인적으로 이런 방식을 선호하지는 않는다.&lt;/li&gt;
&lt;li&gt;객체 내부의 값을 변경하는 방식 자체는 부수 효과를 유발할 수 있고 동기화 문제에 대해서도 별도의 처리가 필요하기 때문이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;map&lt;/code&gt;을 통해 매번 새로운 객체를 생성하는 방법으로 유사한 동작을 할 수 있다.&lt;/li&gt;
&lt;li&gt;하지만 실제로 코드를 짜다보면 객체를 새로 생성하는 것에 대한 비용이 크다면 &lt;code&gt;peek&lt;/code&gt; 메서드 사용을 고려해 볼 수는 있다.&lt;/li&gt;
&lt;li&gt;상황에 맞는 적절한 방식을 찾는 것이 중요한 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;주소 풀네임 조합하기&quot;)
void getEvenNumbersCountTest() {
    final var expected = List.of(
            new Address(1, &quot;서울시&quot;, &quot;관악구&quot;, &quot;신림동&quot;, &quot;서울시 관악구 신림동&quot;),
            new Address(2, &quot;서울시&quot;, &quot;강남구&quot;, &quot;논현동&quot;, &quot;서울시 강남구 논현동&quot;),
            new Address(3, &quot;서울시&quot;, &quot;동작구&quot;, &quot;사당동&quot;, &quot;서울시 동작구 사당동&quot;),
            new Address(4, &quot;서울시&quot;, &quot;서초구&quot;, &quot;양재동&quot;, &quot;서울시 서초구 양재동&quot;));
    final var result = peekUsage.makeFullNameInAddress(addresses);
    result.forEach(System.out::println);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;다음과 같은 내용이 콘솔에 출력되고 테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BjiDi/btqSWahyXRw/xP87JWxYp3N4R8CLxWOnIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BjiDi/btqSWahyXRw/xP87JWxYp3N4R8CLxWOnIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BjiDi/btqSWahyXRw/xP87JWxYp3N4R8CLxWOnIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBjiDi%2FbtqSWahyXRw%2FxP87JWxYp3N4R8CLxWOnIK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이상 Stream API에서 사용하는 중간 연산 중 &lt;code&gt;peek&lt;/code&gt; 메서드에 대해 살펴보았다.&lt;/p&gt;</description>
      <category>프로그래밍/Java</category>
      <category>boxed</category>
      <category>java</category>
      <category>Peek</category>
      <category>Stream API</category>
      <category>스트림</category>
      <category>자바</category>
      <category>중간 연산</category>
      <author>개발자 카니</author>
      <guid isPermaLink="true">https://dev-kani.tistory.com/34</guid>
      <comments>https://dev-kani.tistory.com/34#entry34comment</comments>
      <pubDate>Thu, 7 Jan 2021 09:43:37 +0900</pubDate>
    </item>
    <item>
      <title>[Stream API] 중간 연산 - flatMap 메서드</title>
      <link>https://dev-kani.tistory.com/33</link>
      <description>&lt;h1&gt;개념&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;스트림은 파라미터로 제공되는 함수(&lt;code&gt;Function&amp;lt;T, Stream&amp;lt;R&amp;gt;&amp;gt;&lt;/code&gt;과 관련된 함수형 인터페이스)를 적용해서 스트림의 각 값을 다른 스트림으로 만든 다음에 모든 스트림을 하나의 스트림으로 연결하는 기능을 수행하는 &lt;code&gt;flatMap&lt;/code&gt;이라는 메서드를 제공한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;추상적으로 설명하면 차원을 낮추는 개념이라고 생각된다. (ex. &lt;code&gt;Stream&amp;lt;String[]&amp;gt;&lt;/code&gt; -&amp;gt; &lt;code&gt;Stream&amp;lt;String&amp;gt;&lt;/code&gt;)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;위의 예시의 경우 일반 &lt;code&gt;map&lt;/code&gt; 메서드를 사용할 경우 &lt;code&gt;String[]&lt;/code&gt; 배열 자체가 다른 객체로 변환되는데 &lt;code&gt;flatMap&lt;/code&gt;을 사용하면 &lt;code&gt;String[]&lt;/code&gt; 내부의 값들 자체를 다른 객체로 변환하는 것이 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;여기서 말하는 함수는 &lt;code&gt;map&lt;/code&gt;의 &lt;code&gt;Function&amp;lt;T, R&amp;gt;&lt;/code&gt;과 약간의 차이가 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;flatMap&lt;/code&gt;에서 사용되는 &lt;code&gt;Function&lt;/code&gt;은 &lt;b&gt;T&lt;/b&gt;는 동일하지만 &lt;b&gt;R&lt;/b&gt;이 &lt;code&gt;Stream&amp;lt;R&amp;gt;&lt;/code&gt;이다.&lt;/li&gt;
&lt;li&gt;즉, 일반 제네릭 타입(&lt;b&gt;R&lt;/b&gt;)의 객체가 아니라 일반 스트림(&lt;code&gt;Stream&amp;lt;R&amp;gt;&lt;/code&gt;)이나 기본형 특화 스트림(&lt;code&gt;IntStream&lt;/code&gt;, &lt;code&gt;LongStream&lt;/code&gt;, &lt;code&gt;DoubleStream&lt;/code&gt;)과 같이 스트림 타입이 반환되어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddcK8F/btqSxCGMHCl/YlYFM4dP70LBFrk1KyNPz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddcK8F/btqSxCGMHCl/YlYFM4dP70LBFrk1KyNPz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddcK8F/btqSxCGMHCl/YlYFM4dP70LBFrk1KyNPz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddcK8F%2FbtqSxCGMHCl%2FYlYFM4dP70LBFrk1KyNPz1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;flatMap&lt;/code&gt;의 기본 개념은 위의 이미지와 같다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;또한 &lt;code&gt;flatMap&lt;/code&gt;은 반환하는 형태에 따라 여러 메서드가 존재한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;flatMapToInt&lt;/code&gt;, &lt;code&gt;flatMapToLong&lt;/code&gt;, &lt;code&gt;flatMapToDouble&lt;/code&gt;이 다음에 해당한다.&lt;/li&gt;
&lt;li&gt;일반 스트림에서만 위의 3가지 메서드가 추가로 존재하며 기본형 특화 스트림에서는 &lt;code&gt;flatMap&lt;/code&gt;만 존재하고 각각의 타입에 맞는 기본형 특화 스트림을 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;flatMap 메서드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;위에서 설명한 가장 기본적인 형태의 &lt;code&gt;flatMap&lt;/code&gt; 메서드다.&lt;/li&gt;
&lt;li&gt;일반 스트림과 기본형 특화 스트림 모두에서 제공하는 메서드다.&lt;/li&gt;
&lt;li&gt;스트림 별로 &lt;code&gt;flatMap&lt;/code&gt;의 파라미터를 살펴보면 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;Stream&lt;/b&gt;의 &lt;code&gt;flatMap&lt;/code&gt; 메서드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNW7VZ/btqSpGXn8Iu/roiWomxr9xO62K1HnZ9KLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNW7VZ/btqSpGXn8Iu/roiWomxr9xO62K1HnZ9KLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNW7VZ/btqSpGXn8Iu/roiWomxr9xO62K1HnZ9KLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNW7VZ%2FbtqSpGXn8Iu%2FroiWomxr9xO62K1HnZ9KLk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;IntStream&lt;/b&gt;의 &lt;code&gt;flatMap&lt;/code&gt; 메서드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bulJz0/btqSDtCwH9o/1UikkVh3QwV7tp31eDgG10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bulJz0/btqSDtCwH9o/1UikkVh3QwV7tp31eDgG10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bulJz0/btqSDtCwH9o/1UikkVh3QwV7tp31eDgG10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbulJz0%2FbtqSDtCwH9o%2F1UikkVh3QwV7tp31eDgG10%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;LongStream&lt;/b&gt;의 &lt;code&gt;flatMap&lt;/code&gt; 메서드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdGvhR/btqSAT9kf5S/B7RyWKXqiQFHGzOEH19KMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdGvhR/btqSAT9kf5S/B7RyWKXqiQFHGzOEH19KMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdGvhR/btqSAT9kf5S/B7RyWKXqiQFHGzOEH19KMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdGvhR%2FbtqSAT9kf5S%2FB7RyWKXqiQFHGzOEH19KMK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;DoubleStream&lt;/b&gt;의 &lt;code&gt;flatMap&lt;/code&gt; 메서드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cikznE/btqSpHaPMLg/NuvOQRKB5TfZDrMhqoDxD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cikznE/btqSpHaPMLg/NuvOQRKB5TfZDrMhqoDxD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cikznE/btqSpHaPMLg/NuvOQRKB5TfZDrMhqoDxD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcikznE%2FbtqSpHaPMLg%2FNuvOQRKB5TfZDrMhqoDxD0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;스트림의 종류에 따라 각 메서드의 파라미터로 &lt;code&gt;Function&amp;lt;T, Stream&amp;lt;R&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;IntFunction&amp;lt;IntStream&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;LongFunction&amp;lt;LongStream&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;DoubleFunction&amp;lt;DoubleStream&amp;gt;&amp;gt;&lt;/code&gt;을 전달받는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;flatMapToInt 메서드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;일반 스트림을 &lt;code&gt;IntStream&lt;/code&gt;으로 변환해주는 메서드다.&lt;/li&gt;
&lt;li&gt;일반 스트림에서만 제공하는 메서드다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dA4ms8/btqSsSiU1NA/CNhTSa25wA4sNiEqokT4xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dA4ms8/btqSsSiU1NA/CNhTSa25wA4sNiEqokT4xK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dA4ms8/btqSsSiU1NA/CNhTSa25wA4sNiEqokT4xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdA4ms8%2FbtqSsSiU1NA%2FCNhTSa25wA4sNiEqokT4xK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;위의 이미지처럼 일반 스트림으로 변환하는 &lt;code&gt;flatMap&lt;/code&gt;과 다르게 &lt;code&gt;Function&amp;lt;? super T,? extends IntStream&amp;gt; mapper&lt;/code&gt;를 파라미터로 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;flatMapToLong 메서드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;일반 스트림을 &lt;code&gt;LongStream&lt;/code&gt;으로 변환해주는 메서드다.&lt;/li&gt;
&lt;li&gt;일반 스트림에서만 제공하는 메서드다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eKKsCB/btqSDvG87al/sKc9VyZz9JxkALSbN1oLM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eKKsCB/btqSDvG87al/sKc9VyZz9JxkALSbN1oLM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eKKsCB/btqSDvG87al/sKc9VyZz9JxkALSbN1oLM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeKKsCB%2FbtqSDvG87al%2FsKc9VyZz9JxkALSbN1oLM1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;위의 이미지처럼 일반 스트림으로 변환하는 &lt;code&gt;flatMap&lt;/code&gt;과 다르게 &lt;code&gt;Function&amp;lt;? super T,? extends LongStream&amp;gt; mapper&lt;/code&gt;를 파라미터로 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;flatMapToDouble 메서드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;일반 스트림을 &lt;code&gt;DoubleStream&lt;/code&gt;으로 변환해주는 메서드다.&lt;/li&gt;
&lt;li&gt;일반 스트림에서만 제공하는 메서드다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wYXn7/btqSIZ1KZ8S/EQ1KzTjSHODJwm27Btyx21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wYXn7/btqSIZ1KZ8S/EQ1KzTjSHODJwm27Btyx21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wYXn7/btqSIZ1KZ8S/EQ1KzTjSHODJwm27Btyx21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwYXn7%2FbtqSIZ1KZ8S%2FEQ1KzTjSHODJwm27Btyx21%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;위의 이미지처럼 일반 스트림으로 변환하는 &lt;code&gt;flatMap&lt;/code&gt;과 다르게 &lt;code&gt;Function&amp;lt;? super T,? extends DoubleStream&amp;gt; mapper&lt;/code&gt;를 파라미터로 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;예제&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;아래 예제에서 사용되는 내용들은 다양한 상황을 연출하기 위해서 임의로 만들어진 내용이므로 특정 속성에 대해서 왜 저 타입이 사용되었는지 의문을 가지지 말자!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예제 코드에서 사용되고 있는 스펙&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Java 15 preview (record라는 새로운 클래스 개념을 사용하기 위해서 해당 프리뷰 버전을 사용. 하위 버전의 경우는 일반 클래스를 생성한 후 getter를 만들고 사용하면 됨) - &lt;a href=&quot;https://dev-kani.tistory.com/27&quot;&gt;참고&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;JUnit 5&lt;/li&gt;
&lt;li&gt;Gradle 6.7&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/YoungWukJeon/tistory-upload/blob/main/src/main/java/stream/intermediate_operations/flatmap/FlatMapUsage.java&quot;&gt;소스 코드&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;기본 데이터 생성&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public record AccessLog(Long no, String ip, LocalDate accessedAt, long responseTime) {
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;public record User(Long no, String id, List&amp;lt;AccessLog&amp;gt; accessLogs) {
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;private final FlatMapUsage flatMapUsage = new FlatMapUsage();
private final List&amp;lt;User&amp;gt; users = List.of(
        new User(1L, &quot;alpha&quot;, List.of(
                new AccessLog(1L, &quot;192.168.0.1&quot;, LocalDate.of(2015, 11, 30), 10),
                new AccessLog(3L, &quot;192.168.0.2&quot;, LocalDate.of(2018, 3, 3), 20),
                new AccessLog(7L, &quot;192.168.0.3&quot;, LocalDate.of(2020, 7, 15), 100))),
        new User(2L, &quot;beta&quot;, List.of(
                new AccessLog(2L, &quot;192.168.0.4&quot;, LocalDate.of(2018, 3, 3), 25),
                new AccessLog(4L, &quot;192.168.0.5&quot;, LocalDate.of(2019, 8, 23), 17),
                new AccessLog(6L, &quot;192.168.0.6&quot;, LocalDate.of(2020, 5, 1), 80))),
        new User(3L, &quot;gamma&quot;, List.of(
                new AccessLog(5L, &quot;192.168.0.7&quot;, LocalDate.of(2020, 2, 25), 150),
                new AccessLog(8L, &quot;192.168.0.8&quot;, LocalDate.of(2020, 8, 5), 200),
                new AccessLog(9L, &quot;192.168.0.9&quot;, LocalDate.of(2021, 1, 5), 55))));&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;문자열 리스트를 한 글자씩 분리해서 고유 문자들만 반환&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;주어진 문자열 리스트에서 고유 문자만 추출해서 반환해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;public List&amp;lt;String&amp;gt; getAllDistinctStrings(List&amp;lt;String&amp;gt; strings) {
    return strings.stream()
            .map(s -&amp;gt; s.split(&quot;&quot;))
            .flatMap(Arrays::stream)
            .distinct()
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;map&lt;/code&gt; 메서드가 &lt;code&gt;Stream&amp;lt;String[]&amp;gt;&lt;/code&gt;을 반환하고 &lt;code&gt;flatMap&lt;/code&gt; 내부에서 &lt;code&gt;Arrays::stream&lt;/code&gt;을 사용해서 &lt;code&gt;String[]&lt;/code&gt;을 &lt;code&gt;Stream&amp;lt;String&amp;gt;&lt;/code&gt;으로 변환함과 동시에 &lt;code&gt;Stream&amp;lt;Stream&amp;lt;String&amp;gt;&amp;gt;&lt;/code&gt;을 &lt;code&gt;Stream&amp;lt;String&amp;gt;&lt;/code&gt;으로 평면화시킨다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;여기서 &lt;code&gt;flatMap&lt;/code&gt;이 아닌 &lt;code&gt;map&lt;/code&gt;을 사용할 경우 &lt;code&gt;Stream&amp;lt;String&amp;gt;&lt;/code&gt;이 아니라 &lt;code&gt;Stream&amp;lt;Stream&amp;lt;String&amp;gt;&amp;gt;&lt;/code&gt;로 변환된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;문자열 리스트를 한글자씩 분리해서 고유 문자들만 반환&quot;)
void getAllDistinctStringsTest() {
    final var strings = List.of(&quot;Happy&quot;, &quot;New&quot;, &quot;Year&quot;);
    final var expected = List.of(&quot;H&quot;, &quot;a&quot;, &quot;p&quot;,  &quot;y&quot;, &quot;N&quot;, &quot;e&quot;, &quot;w&quot;, &quot;Y&quot;, &quot;r&quot;);
    final var result = flatMapUsage.getAllDistinctStrings(strings);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;다음과 같은 과정으로 테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5MTyy/btqSxCUprNo/52gWBdWhxOsWN9h2TAs2Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5MTyy/btqSxCUprNo/52gWBdWhxOsWN9h2TAs2Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5MTyy/btqSxCUprNo/52gWBdWhxOsWN9h2TAs2Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5MTyy%2FbtqSxCUprNo%2F52gWBdWhxOsWN9h2TAs2Gk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;특정 날짜 이후에 접속한 유저의 ip 조회&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;실무에서 사용할 법한 예시를 생각해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public List&amp;lt;String&amp;gt; getIpByAccessedAtAfter(List&amp;lt;User&amp;gt; users, LocalDate accessedAt) {
    return users.stream()
            .map(User::accessLogs)
            .flatMap(l -&amp;gt; l.stream()
                    .filter(a -&amp;gt; a.accessedAt().isAfter(accessedAt))
                    .map(AccessLog::ip))
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;유저 정보와 접속 기록 정보를 데이터베이스나 파일에서 가져왔다고 하고 특정 날짜 이후에 접속한 유저의 ip 목록을 조회해보자.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;map&lt;/code&gt; 메서드가 &lt;code&gt;Stream&amp;lt;List&amp;lt;AccessLog&amp;gt;&amp;gt;&lt;/code&gt;을 반환하고 &lt;code&gt;flatMap&lt;/code&gt; 내부에서 &lt;code&gt;List&amp;lt;AccessLog&amp;gt;&lt;/code&gt;를 &lt;code&gt;map&lt;/code&gt;을 사용해서 &lt;code&gt;Stream&amp;lt;String&amp;gt;&lt;/code&gt;으로 변환함과 동시에 &lt;code&gt;Stream&amp;lt;Stream&amp;lt;String&amp;gt;&amp;gt;&lt;/code&gt;을 &lt;code&gt;Stream&amp;lt;String&amp;gt;&lt;/code&gt;으로 평면화시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;특정 날짜 이후에 접속한 유저의 ip 조회&quot;)
void getIpByAccessedAtAfterTest() {
    final var accessedAt = LocalDate.of(2020, 1, 1);
    final var expected = List.of(&quot;192.168.0.3&quot;, &quot;192.168.0.6&quot;, &quot;192.168.0.7&quot;, &quot;192.168.0.8&quot;, &quot;192.168.0.9&quot;);
    final var result = flatMapUsage.getIpByAccessedAtAfter(users, accessedAt);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;다음과 같은 과정으로 테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQmWIe/btqSIYhzhV9/Gi2B8HwayT9S0niGp5ktI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQmWIe/btqSIYhzhV9/Gi2B8HwayT9S0niGp5ktI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQmWIe/btqSIYhzhV9/Gi2B8HwayT9S0niGp5ktI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQmWIe%2FbtqSIYhzhV9%2FGi2B8HwayT9S0niGp5ktI1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;모든 유저의 평균 응답 시간 구하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이번에는 기본형 특화 스트림을 파라미터로 가지는 &lt;code&gt;flatMap&lt;/code&gt; 관련 메서드를 사용해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public OptionalDouble getAverageResponseTime(List&amp;lt;User&amp;gt; users) {
    return users.stream()
            .map(User::accessLogs)
            .flatMapToLong(l -&amp;gt; l.stream()
                    .mapToLong(AccessLog::responseTime))
            .average();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;유저 정보와 접속 기록 정보를 데이터베이스나 파일에서 가져왔다고 하고 모든 유저들의 응답 시간 평균을 구해보자.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;map&lt;/code&gt; 메서드가 &lt;code&gt;Stream&amp;lt;List&amp;lt;AccessLog&amp;gt;&amp;gt;&lt;/code&gt;을 반환하고 &lt;code&gt;flatMapToLong&lt;/code&gt; 내부에서 &lt;code&gt;List&amp;lt;AccessLog&amp;gt;&lt;/code&gt;를 &lt;code&gt;mapToLong&lt;/code&gt;을 사용해서 기본형 특화 스트림인 &lt;code&gt;LongStream&lt;/code&gt;으로 변환함과 동시에 &lt;code&gt;Stream&amp;lt;LongStream&amp;gt;&amp;gt;&lt;/code&gt;을 &lt;code&gt;LongStream&lt;/code&gt;으로 평면화시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;모든 유저의 평균 응답 시간 구하기&quot;)
void getAverageResponseTimeTest() {
    final var expected = 73.0;
    final var result = flatMapUsage.getAverageResponseTime(users);
    assertTrue(result.isPresent());
    assertEquals(expected, result.getAsDouble());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;다음과 같은 과정으로 테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GK6o3/btqSIY9FRff/LQElZQg5Ko0DzEYZrrq9aK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GK6o3/btqSIY9FRff/LQElZQg5Ko0DzEYZrrq9aK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GK6o3/btqSIY9FRff/LQElZQg5Ko0DzEYZrrq9aK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGK6o3%2FbtqSIY9FRff%2FLQElZQg5Ko0DzEYZrrq9aK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이상 Stream API에서 사용하는 중간 연산 중 처음에 이해하기 어려운 &lt;code&gt;flatMap&lt;/code&gt; 관련 메서드에 대해 살펴보았다.&lt;/p&gt;</description>
      <category>프로그래밍/Java</category>
      <category>flaMap</category>
      <category>flatMapToDouble</category>
      <category>flatMapToInt</category>
      <category>flatMapToLong</category>
      <category>java</category>
      <category>Stream API</category>
      <category>스트림</category>
      <category>자바</category>
      <category>중간 연산</category>
      <author>개발자 카니</author>
      <guid isPermaLink="true">https://dev-kani.tistory.com/33</guid>
      <comments>https://dev-kani.tistory.com/33#entry33comment</comments>
      <pubDate>Tue, 5 Jan 2021 13:01:50 +0900</pubDate>
    </item>
    <item>
      <title>[Stream API] 중간 연산 - map 메서드</title>
      <link>https://dev-kani.tistory.com/32</link>
      <description>&lt;h1&gt;개념&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;스트림은 파라미터로 제공되는 함수(&lt;code&gt;Function&amp;lt;T, R&amp;gt;&lt;/code&gt;관련된 함수형 인터페이스)를 적용해서 기존 요소를 새로운 요소로 매핑시키는 &lt;code&gt;map&lt;/code&gt;이라는 메서드를 제공한다.&lt;/li&gt;
&lt;li&gt;기존 값을 변경한다는 개념보다는 새로운 값을 만든다는 개념이므로 &lt;b&gt;변환(transforming)&lt;/b&gt; 에 &lt;b&gt;매핑(mapping)&lt;/b&gt; 이라는 단어를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sNZYH/btqR9mkXzkh/uNwPmq7SrfZycBu5UHCJK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sNZYH/btqR9mkXzkh/uNwPmq7SrfZycBu5UHCJK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sNZYH/btqR9mkXzkh/uNwPmq7SrfZycBu5UHCJK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsNZYH%2FbtqR9mkXzkh%2FuNwPmq7SrfZycBu5UHCJK1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;map&lt;/code&gt;의 기본 개념은 위의 이미지와 같다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;또한 &lt;code&gt;map&lt;/code&gt;은 반환하는 형태에 따라 여러 메서드가 존재한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mapToInt&lt;/code&gt;, &lt;code&gt;mapToLong&lt;/code&gt;, &lt;code&gt;mapToDouble&lt;/code&gt;, &lt;code&gt;mapToObj&lt;/code&gt;가 다음에 해당한다.&lt;/li&gt;
&lt;li&gt;일반 스트림과 기본형 특화 스트림은 위의 메서드 중에서 타입에 따라 3가지씩 가지고 있고 없는 메서드는 결국 자기 자신 타입의 스트림을 반환하는 것이기 때문에 &lt;code&gt;map&lt;/code&gt; 메서드가 그 역할을 하는 것이다.&lt;/li&gt;
&lt;li&gt;예를 들어, &lt;code&gt;IntStream&lt;/code&gt;의 경우 &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;mapToLong&lt;/code&gt;, &lt;code&gt;mapToDouble&lt;/code&gt;, &lt;code&gt;mapToObj&lt;/code&gt;가 존재하며 &lt;code&gt;map&lt;/code&gt; 자체가 &lt;code&gt;IntStream&lt;/code&gt;을 반환하고 &lt;code&gt;Stream&lt;/code&gt;의 경우 &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;mapToInt&lt;/code&gt;, &lt;code&gt;mapToLong&lt;/code&gt;, &lt;code&gt;mapToDouble&lt;/code&gt;가 존재하며 &lt;code&gt;map&lt;/code&gt; 자체가 &lt;code&gt;Stream&lt;/code&gt;을 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;map 메서드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;위에서 설명한 가장 기본적인 형태의 &lt;code&gt;map&lt;/code&gt; 메서드이다.&lt;/li&gt;
&lt;li&gt;일반 스트림과 기본형 특화 스트림 모두에서 제공하는 메서드이다.&lt;/li&gt;
&lt;li&gt;스트림 별로 &lt;code&gt;map&lt;/code&gt;의 파라미터를 살펴보면 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;Stream&lt;/b&gt;의 &lt;code&gt;map&lt;/code&gt; 메서드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Eozx4/btqR9lTVFqe/vS9kfsldBwAGJXGMbtnvY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Eozx4/btqR9lTVFqe/vS9kfsldBwAGJXGMbtnvY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Eozx4/btqR9lTVFqe/vS9kfsldBwAGJXGMbtnvY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEozx4%2FbtqR9lTVFqe%2FvS9kfsldBwAGJXGMbtnvY1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;IntStream&lt;/b&gt;의 &lt;code&gt;map&lt;/code&gt; 메서드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnwrEY/btqSduQlTtu/axJkVx8gEOEVSfFsZGUmd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnwrEY/btqSduQlTtu/axJkVx8gEOEVSfFsZGUmd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnwrEY/btqSduQlTtu/axJkVx8gEOEVSfFsZGUmd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnwrEY%2FbtqSduQlTtu%2FaxJkVx8gEOEVSfFsZGUmd1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;LongStream&lt;/b&gt;의 &lt;code&gt;map&lt;/code&gt; 메서드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W7wod/btqSmAWtNvQ/UKP0nD6UZ1c4KsDCaq5YiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W7wod/btqSmAWtNvQ/UKP0nD6UZ1c4KsDCaq5YiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W7wod/btqSmAWtNvQ/UKP0nD6UZ1c4KsDCaq5YiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW7wod%2FbtqSmAWtNvQ%2FUKP0nD6UZ1c4KsDCaq5YiK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;DoubleStream&lt;/b&gt;의 &lt;code&gt;map&lt;/code&gt; 메서드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpbEwj/btqSATtOi4v/pi5p2d8ngPZcssHVlwVyv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpbEwj/btqSATtOi4v/pi5p2d8ngPZcssHVlwVyv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpbEwj/btqSATtOi4v/pi5p2d8ngPZcssHVlwVyv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpbEwj%2FbtqSATtOi4v%2Fpi5p2d8ngPZcssHVlwVyv0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;map&lt;/code&gt; 메서드의 경우 결국 자기 자신 타입의 스트림을 반환하는 것을 알 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;일반 스트림에서는 &lt;code&gt;Function&amp;lt;T, R&amp;gt;&lt;/code&gt;이 사용되고 있는데 &lt;b&gt;T&lt;/b&gt;가 변환 전 타입 &lt;b&gt;R&lt;/b&gt;이 변환 후 타입이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;T&lt;/b&gt;와 &lt;b&gt;R&lt;/b&gt;의 타입이 같을 수도 있다.&lt;/li&gt;
&lt;li&gt;필자의 경우도 마찬가지였지만 스트림을 처음 공부하는 사람들이 이런 부분을 놓치기 쉽고 응용을 어려워한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;기본형 특화 스트림에서는 타입의 변화가 없기 때문에 각각에 맞는 특화 타입의 &lt;code&gt;UnaryOperator&lt;/code&gt;가 사용되는 것은 어찌보면 당연하다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;mapToInt 메서드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;스트림을 &lt;code&gt;IntStream&lt;/code&gt;으로 변환해주는 메서드다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IntStream&lt;/code&gt;을 제외한 모든 스트림에서 동일하게 제공하는 메서드다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mYNXz/btqSmzXxWV7/awnDB16iKz92nxpaIvCNJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mYNXz/btqSmzXxWV7/awnDB16iKz92nxpaIvCNJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mYNXz/btqSmzXxWV7/awnDB16iKz92nxpaIvCNJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmYNXz%2FbtqSmzXxWV7%2FawnDB16iKz92nxpaIvCNJ0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;위의 이미지처럼 일반 스트림에서는 &lt;code&gt;map&lt;/code&gt; 메서드와 다르게 &lt;code&gt;ToIntFunction&amp;lt;? super T&amp;gt; mapper&lt;/code&gt;를 파라미터로 전달한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ToIntFunction&lt;/code&gt;라는 인터페이스의 이름에서 알 수 있듯이 기본 &lt;code&gt;Function&amp;lt;T, R&amp;gt;&lt;/code&gt;에서 &lt;b&gt;T&lt;/b&gt;만 제네릭 타입으로 전달받고 &lt;b&gt;R&lt;/b&gt;은 int로 정해져있다.&lt;/li&gt;
&lt;li&gt;다른 기본형 특화 스트림에서는 &lt;code&gt;LongToIntFunction&lt;/code&gt;나 &lt;code&gt;DoubleToIntFunction&lt;/code&gt;을 파라미터로 전달한다.&lt;/li&gt;
&lt;li&gt;인터페이스의 이름에서부터 알 수 있듯이 기본 &lt;code&gt;Function&amp;lt;T, R&amp;gt;&lt;/code&gt;에서 &lt;b&gt;T&lt;/b&gt;는 long과 double &lt;b&gt;R&lt;/b&gt;은 int로 정해져있기 때문에 제네릭 타입을 받지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;mapToLong 메서드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;스트림을 &lt;code&gt;LongStream&lt;/code&gt;으로 변환해주는 메서드다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LongStream&lt;/code&gt;을 제외한 모든 스트림에서 동일하게 제공하는 메서드다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vGhK5/btqR9lGloL4/Rp7TPX4W4Bcbu9KoHjaRXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vGhK5/btqR9lGloL4/Rp7TPX4W4Bcbu9KoHjaRXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vGhK5/btqR9lGloL4/Rp7TPX4W4Bcbu9KoHjaRXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvGhK5%2FbtqR9lGloL4%2FRp7TPX4W4Bcbu9KoHjaRXk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mapToInt&lt;/code&gt;와 이하 내용은 유사하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;mapToDouble 메서드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;스트림을 &lt;code&gt;DoubleStream&lt;/code&gt;으로 변환해주는 메서드다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DoubleStream&lt;/code&gt;을 제외한 모든 스트림에서 동일하게 제공하는 메서드다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNmHlp/btqSa9yTIfV/1rbUOLbOSaWkgjR83ZzUiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNmHlp/btqSa9yTIfV/1rbUOLbOSaWkgjR83ZzUiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNmHlp/btqSa9yTIfV/1rbUOLbOSaWkgjR83ZzUiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNmHlp%2FbtqSa9yTIfV%2F1rbUOLbOSaWkgjR83ZzUiK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mapToInt&lt;/code&gt;와 이하 내용은 유사하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;mapToObj 메서드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;기본형 특화 스트림을 &lt;code&gt;Stream&lt;/code&gt;으로 변환해주는 메서드다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhcXPz/btqSpGvkWSb/wj1IqQKUIewwVNKR3caIk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhcXPz/btqSpGvkWSb/wj1IqQKUIewwVNKR3caIk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhcXPz/btqSpGvkWSb/wj1IqQKUIewwVNKR3caIk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhcXPz%2FbtqSpGvkWSb%2Fwj1IqQKUIewwVNKR3caIk0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;위의 이미지는 &lt;code&gt;IntStream&lt;/code&gt;에서 &lt;code&gt;mapToObj&lt;/code&gt;를 살펴본 내용인데 &lt;code&gt;IntFunction&amp;lt;U&amp;gt;&lt;/code&gt;를 파라미터로 받는다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;기본형 특화 스트림의 종류에 따라 &lt;code&gt;LongFunction&amp;lt;U&amp;gt;&lt;/code&gt;, &lt;code&gt;DoubleFunction&amp;lt;U&amp;gt;&lt;/code&gt;이 사용된다.&lt;/li&gt;
&lt;li&gt;인터페이스의 이름에서 알 수 있듯이 기본 &lt;code&gt;Function&amp;lt;T, R&amp;gt;&lt;/code&gt;에서 &lt;b&gt;T&lt;/b&gt;가 int, long, double이고 &lt;b&gt;R&lt;/b&gt;이 &lt;b&gt;U&lt;/b&gt;가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;예제&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;아래 예제에서 사용되는 내용들은 다양한 상황을 연출하기 위해서 임의로 만들어진 내용이므로 특정 속성에 대해서 왜 저 타입이 사용되었는지 의문을 가지지 말자!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예제 코드에서 사용되고 있는 스펙&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Java 15 preview (record라는 새로운 클래스 개념을 사용하기 위해서 해당 프리뷰 버전을 사용. 하위 버전의 경우는 일반 클래스를 생성한 후 getter를 만들고 사용하면 됨) - &lt;a href=&quot;https://dev-kani.tistory.com/27&quot;&gt;참고&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;JUnit 5&lt;/li&gt;
&lt;li&gt;Gradle 6.7&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/YoungWukJeon/tistory-upload/blob/main/src/main/java/stream/intermediate_operations/map/MapUsage.java&quot;&gt;소스 코드&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;기본 데이터 생성&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public record Sports(int no, String name, int burnedCalories, boolean isIndoor) {
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;public record ProductEntity(Long id, String name, String category, LocalDate createdAt) {
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;public record Car(Long id, String name, LocalDate createdAt) {
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;private final MapUsage mapUsage = new MapUsage();
private final List&amp;lt;Sports&amp;gt; sports = List.of(
        new Sports(1, &quot;걷기&quot;, 40, false),
        new Sports(2, &quot;스쿼시&quot;, 126, true),
        new Sports(3, &quot;런닝머신&quot;, 110, true),
        new Sports(4, &quot;등산&quot;, 84, false));
private final List&amp;lt;ProductEntity&amp;gt; productEntities = List.of(
        new ProductEntity(1L, &quot;Aventador&quot;, &quot;CAR&quot;, LocalDate.of(2018, 3, 28)),
        new ProductEntity(2L, &quot;Ramen&quot;, &quot;FOOD&quot;, LocalDate.of(2001, 4, 11)),
        new ProductEntity(3L, &quot;Lego&quot;, &quot;TOY&quot;, LocalDate.of(1980, 11, 5)),
        new ProductEntity(4L, &quot;Veyron&quot;, &quot;CAR&quot;, LocalDate.of(2009, 12, 9)),
        new ProductEntity(5L, &quot;Modern Java in Action&quot;, &quot;BOOK&quot;, LocalDate.of(2019, 8, 1)));&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;문자열을 문자열 길이로 변환(String -&amp;gt; Integer)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;String&lt;/code&gt; 타입의 데이터를 그 길이로 변환하는 메서드를 만들어보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;public List&amp;lt;Integer&amp;gt; getLength(List&amp;lt;String&amp;gt; strings) {
    return strings.stream()
            .map(String::length)
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;주어진 단어들의 길이를 반환해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;문자열을 문자열 길이로 변환(String -&amp;gt; Integer)&quot;)
void getLengthTest() {
    final var strings = List.of(&quot;banana&quot;, &quot;apple&quot;, &quot;orange&quot;);
    final var expected = List.of(6, 5, 6);
    final var result = mapUsage.getLength(strings);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;정수를 문자열로 변환(int -&amp;gt; String)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;int&lt;/code&gt; 기본 타입을 주어진 문자열과 합쳐서 반환하는 메서드를 만들어보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public List&amp;lt;String&amp;gt; generateStrings(String prefix, int size) {
    return IntStream.range(0, size)
            .mapToObj(i -&amp;gt; prefix + i)
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;여기서 0부터 size까지의 정수 스트림을 만들기 위해 &lt;code&gt;IntStream.range()&lt;/code&gt; 메서드를 사용했다.&lt;/li&gt;
&lt;li&gt;기본형 특화 스트림(&lt;code&gt;IntStream&lt;/code&gt;)을 일반 스트림으로 변환하기 위해 &lt;code&gt;mapToObj()&lt;/code&gt; 메서드가 사용되었다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;waiting-&lt;/b&gt; 이라는 문자열에 0부터 size까지의 숫자를 붙인 문자열을 반환해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;정수를 문자열로 변환(int -&amp;gt; String)&quot;)
void generateStringsTest() {
    final var expected = List.of(&quot;waiting-0&quot;, &quot;waiting-1&quot;, &quot;waiting-2&quot;);
    final var result = mapUsage.generateStrings(&quot;waiting-&quot;, 3);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;실외 스포츠들의 이름 가져오기(Sports -&amp;gt; String)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Sports&lt;/code&gt; 타입의 리스트를 &lt;code&gt;String&lt;/code&gt; 타입으로 변환하는 메서드를 만들어보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public List&amp;lt;String&amp;gt; getSportsNamesByIndoor(List&amp;lt;Sports&amp;gt; sports, boolean isIndoor) {
    return sports.stream()
            .filter(s -&amp;gt; s.isIndoor() == isIndoor)
            .map(Sports::name)
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;실외 스포츠들의 이름을 조회해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;실외 스포츠들의 이름 가져오기(Sports -&amp;gt; String)&quot;)
void getSportsNamesByIndoorTest() {
    final var expected = List.of(&quot;걷기&quot;, &quot;등산&quot;);
    final var result = mapUsage.getSportsNamesByIndoor(sports, false);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;스포츠들의 칼로리 평균 구하기(Sports -&amp;gt; int)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Sports&lt;/code&gt; 타입의 리스트를 기본 정수 자료형(&lt;code&gt;int&lt;/code&gt;)으로 변환하는 메서드를 만들어보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;public OptionalDouble getAverage(List&amp;lt;Sports&amp;gt; sports) {
    return sports.stream()
            .mapToInt(Sports::burnedCalories)
            .average();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;위 코드에서 &lt;code&gt;mapToInt()&lt;/code&gt; 메서드를 통해서 레퍼런스 타입의 일반 스트림을 기본형 특화 스트림인 &lt;code&gt;IntStream&lt;/code&gt;으로 변환했다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;IntStream&lt;/code&gt;의 종단 연산 중 하나인 &lt;code&gt;average()&lt;/code&gt; 메서드를 사용해서 평균을 구했다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;average()&lt;/code&gt; 메서드의 경우에는 종단 연산에 대한 글을 쓸 때 다시 언급할 예정이다. 간단하게 평균을 구하는 메서드이고 반환 타입이 &lt;code&gt;OptionalDouble&lt;/code&gt;인데 이것은 기본형 특화 &lt;code&gt;Optional&lt;/code&gt;이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;sports&lt;/b&gt;가 빈 리스트일 경우에 평균을 구하는 로직에서 &lt;code&gt;Division by Zero&lt;/code&gt;가 발생할 수 있으므로 이 경우에 결괏값이 없을 수 있다는 것을 명시적으로 알려주기 위해서 &lt;code&gt;OptionalDouble&lt;/code&gt; 형태로 반환해주는 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;주어진 스포츠들의 칼로리 평균을 구해보자.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;스포츠들의 칼로리 평균 구하기(Sports -&amp;gt; int)&quot;)
void getAverageTest() {
    final var expected = 90.0;
    final var result = mapUsage.getAverage(sports);
    assertTrue(result.isPresent());
    assertEquals(expected, result.getAsDouble());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;테스트가 성공한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getAverage(sports)&lt;/code&gt;의 반환형이 &lt;code&gt;OptionalDouble&lt;/code&gt;이기 때문에 먼저 &lt;b&gt;result&lt;/b&gt;가 비어있는지 체크하기 위해 &lt;code&gt;result.isPresent()&lt;/code&gt;를 사용했고 이후에 &lt;code&gt;result.getAsDouble()&lt;/code&gt; 메서드를 사용해서 실제값을 가져오도록 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;문자열 대문자로 변환(String -&amp;gt; String)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;String&lt;/code&gt; 타입을 &lt;b&gt;같은&lt;/b&gt; &lt;code&gt;String&lt;/code&gt; 타입으로 변환해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;public List&amp;lt;String&amp;gt; getUpperStrings(List&amp;lt;String&amp;gt; strings) {
    return strings.stream()
            .map(String::toUpperCase)
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;스트림의 특성상 원본 &lt;b&gt;strings&lt;/b&gt;의 데이터는 변하지 않는다.&lt;/li&gt;
&lt;li&gt;주어진 문자열들을 대문자로 변환해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;문자열 대문자로 변환(String -&amp;gt; String)&quot;)
void getUpperStringsTest() {
    final var strings = List.of(&quot;banana&quot;, &quot;apple&quot;, &quot;orange&quot;);
    final var expected = List.of(&quot;BANANA&quot;, &quot;APPLE&quot;, &quot;ORANGE&quot;);
    final var result = mapUsage.getUpperStrings(strings);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;상품(Entity)에서 자동차(CAR)를 분류한 후 DTO로 변환(ProductEntity -&amp;gt; Car)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;좀 더 실무에서 사용될 법한 간단한 예시를 만들어보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public List&amp;lt;Car&amp;gt; getCarsFromProductEntities(List&amp;lt;ProductEntity&amp;gt; productEntities) {
    return productEntities.stream()
            .filter(p -&amp;gt; &quot;CAR&quot;.equals(p.category()))
            .map(p -&amp;gt; new Car(p.id(), p.name(), p.createdAt()))
            .collect(Collectors.toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;상품(Entity)을 데이터베이스에서 가져왔다고 하고 다른 레이어에서 사용할 수 있도록 DTO로 변환해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;상품(Entity)에서 자동차(CAR)를 분류한 후 DTO로 변환(ProductEntity -&amp;gt; Car)&quot;)
void getCarsFromProductEntitiesTest() {
    final var expected = List.of(
            new Car(1L, &quot;Aventador&quot;, LocalDate.of(2018, 3, 28)),
            new Car(4L, &quot;Veyron&quot;, LocalDate.of(2009, 12, 9)));
    final var result = mapUsage.getCarsFromProductEntities(productEntities);
    assertIterableEquals(expected, result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;테스트가 성공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이상 Stream API에서 사용하는 중간 연산 중 가장 중요하고 많이 사용되는 &lt;code&gt;map&lt;/code&gt; 관련 메서드에 대해 살펴보았다.&lt;/p&gt;</description>
      <category>프로그래밍/Java</category>
      <category>java</category>
      <category>map</category>
      <category>mapToDouble</category>
      <category>mapToInt</category>
      <category>mapToLong</category>
      <category>mapToObj</category>
      <category>Stream API</category>
      <category>스트림</category>
      <category>자바</category>
      <category>중간 연산</category>
      <author>개발자 카니</author>
      <guid isPermaLink="true">https://dev-kani.tistory.com/32</guid>
      <comments>https://dev-kani.tistory.com/32#entry32comment</comments>
      <pubDate>Mon, 4 Jan 2021 05:25:19 +0900</pubDate>
    </item>
  </channel>
</rss>