<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="https://kjh-sun.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://kjh-sun.github.io/" rel="alternate" type="text/html" /><updated>2021-10-31T15:10:52+00:00</updated><id>https://kjh-sun.github.io/feed.xml</id><title type="html">Today I Learned(TIL)</title><subtitle>주니어의 조금 더 나은 개발자가 되기 위한 블로그</subtitle><author><name>김주현</name></author><entry><title type="html">2021년 10월 31일 TIL</title><link href="https://kjh-sun.github.io/til/20211031/" rel="alternate" type="text/html" title="2021년 10월 31일 TIL" /><published>2021-10-31T04:23:00+00:00</published><updated>2021-10-31T04:23:00+00:00</updated><id>https://kjh-sun.github.io/til/20211031</id><content type="html" xml:base="https://kjh-sun.github.io/til/20211031/">&lt;h2 id=&quot;stream-api&quot;&gt;Stream API&lt;/h2&gt;

&lt;h3 id=&quot;stream-api의-특징&quot;&gt;Stream API의 특징&lt;/h3&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;h3 id=&quot;stream-api-flow&quot;&gt;Stream API flow&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;생성&lt;/li&gt;
  &lt;li&gt;가공&lt;/li&gt;
  &lt;li&gt;결과 산출&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;lambda-expression&quot;&gt;Lambda Expression&lt;/h3&gt;

&lt;p&gt;람다식이란 함수를 하나의 식으로 표현한 것이다. 익명 함수의 한 종류라고 볼 수도 있다. 익명함수들은 모두 일급 객체이다.&lt;/p&gt;

&lt;p&gt;final이 붙지 않아도 내부의 지역변수는 상수로 간주된다.&lt;/p&gt;

&lt;p&gt;람다식으로 선언된 변수명은 다른 변수명과 중복될 수 없다.&lt;/p&gt;

&lt;p&gt;람다를 사용하면서 만든 익명함수는 재사용이 불가능하다.&lt;/p&gt;

&lt;h3 id=&quot;functional-interface&quot;&gt;Functional Interface&lt;/h3&gt;

&lt;p&gt;Java는 기본적으로 객체지향 언어이기 때문에 순수 함수와 일반 함수를 다르게 취급하고 있으며, Java에서는 이를 구분하기 위해 함수형 인터페이스가 등장하게 되었다.&lt;/p&gt;

&lt;p&gt;함수형 인터페이스란 함수를 1급 객체처럼 다룰 수 있게 해주는 어노테이션으로, 인터페이스에 선언하여 단 하나의 추상 메소드만을 갖도록 제한하는 역할을 한다. 함수형 인터페이스를 사용하는 이유는 Java의 람다식이 함수형 인터페이스를 반환하기 때문이다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Supplier&lt;T&gt;&lt;/T&gt;&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;매개변수 없이 반환값만을 갖는 함수형 인터페이스
T get()을 추상 메소드로 갖고 있다.&lt;/p&gt;

        &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 정의 @FunctionalInterface &lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Supplier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;c1&quot;&gt;// 사용 예시 &lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Supplier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;supplier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;supplier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Consumer&lt;T&gt;&lt;/T&gt;&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;객체 T를 매개변수로 받아서 사용하며, 반환값은 없는 함수형 인터페이스&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;void accept(T t)를 추상메소드로 갖는다.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;또한 Consumer는 andThen이라는 함수를 제공하고 있는데, 이를 통해 하나의 함수가 끝난 후 다음 Consumer를 연쇄적으로 이용할 수 있다. 아래의 예제에서는 먼저 accept로 받아들인 Consumer를 먼저 처리하고, andThen으로 받은 두 번째 Consumer를 처리하고 있다. 함수형에서 함수는 값의 대입 또는 변경 등이 없기 때문에 첫 번째 Consumer가 split으로 데이터를 변경하였다 하더라도 원본의 데이터는 유지된다.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 정의 &lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@FunctionalInterface&lt;/span&gt; 
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;andThen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;          			&lt;span class=&quot;nc&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;requireNonNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;};&lt;/span&gt; 
     &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 예시 &lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]);&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;andThen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
&lt;span class=&quot;c1&quot;&gt;// 출력 &lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Hello&lt;/span&gt; 
&lt;span class=&quot;nc&quot;&gt;Hello&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;World&lt;/span&gt;
    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Function&amp;lt;T, R&amp;gt;&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;객체 T를 매개변수로 받아서 처리한 후 R로 반환하는 함수형 인터페이스이다.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Function은 R apply(T t)를 추상메소드로 갖는다.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;또한 Function은 Consumer와 마찬가지로 andThen을 제공하고 있으며, 추가적으로 compose를 제공하고 있다. 앞에서 andThen은 첫 번째 함수가 실행된 이후에 다음 함수를 연쇄적으로 실행하도록 연결해준다고 하였다. 하지만 compose는 첫 번째 함수 실행 이전에 먼저 함수를 실행하여 연쇄적으로 연결해준다는 점에서 차이가 있다.&lt;/p&gt;

        &lt;p&gt;또한 identity 함수가 존재하는데, 이는 자기 자신을 반환하는 static 함수이다.&lt;/p&gt;

        &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 정의 &lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@FunctionalInterface&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;no&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
        &lt;span class=&quot;nc&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;requireNonNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;andThen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
        &lt;span class=&quot;nc&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;requireNonNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;c1&quot;&gt;// 예시, 메소드 참조로 간소화 가능(String::length;) &lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Predicate&lt;T&gt;&lt;/T&gt;&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;Predicate는 객체 T를 매개 변수로 받아 처리한 후 Boolean을 반환한다.&lt;/p&gt;

        &lt;p&gt;Predicate는 Boolean test(T t)을 추상 메소드로 갖고 있다.&lt;/p&gt;

        &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 정의 &lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@FunctionalInterface&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
        &lt;span class=&quot;nc&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;requireNonNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;negate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
        &lt;span class=&quot;nc&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;requireNonNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isEqual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;Objects:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isNull&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;c1&quot;&gt;// 예시 &lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;predicate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;predicate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;stream-생성하기&quot;&gt;Stream 생성하기&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Collection 인터페이스를 구현한 객체들은 stream()을 통해 생성 가능하다.&lt;/li&gt;
  &lt;li&gt;배열의 경우에는, Stream.of() 또는 Arrays.stream()을 통해 생성 가능&lt;/li&gt;
  &lt;li&gt;primitive 자료형을 사용하기 위해서 특수한 종류의 Stream을 사용할 수도 있다.
    &lt;ul&gt;
      &lt;li&gt;IntStream stream = IntStream.range(4,10); // 4이상 10이하의 숫자를 갖는 IntStream&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;stream-가공하기&quot;&gt;Stream 가공하기&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Filter ( 필터링 )&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Stream에서 조건에 맞는 데이터만을 정제하여 더 작은 컬렉션을 만들어내는 연산&lt;/li&gt;
      &lt;li&gt;Predicate를 인자로 받기때문에, boolean을 반환하는 람다식을 작성하여 filter 함수를 구현할 수 있다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Map ( 데이터 변환 )&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;기존의 Stream 요소들을 변호나하여 새로운 Stream을 형성하는 연산이다. 저장된 값을 특정한 형태로 변환하는데 주로 사용되며, java에서는 map 함수의 인자로 함수형 인터페이스 function을 받고 있다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Sorted ( 데이터 정렬 )&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Stream의 요소 정렬을 위해서는 sorted를 사용해야 하며, 파라미터로 Comparator를 넘길 수 있다. Comparator 인자 없이 호출할 경우에는 오름차순이 default이다. (내림차순이 필요할 때는 Collections.reverseOrder)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Distinct ( 중복 제거 )&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Stream의 요소들에서 중복된 데이터가 존재하는 경우, 중복 제거를 위해 distinct를 사용할 수 있다. distinct는 중복된 데이터를 검사하기 위해 Object의 equals() 메소드를 사용한다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Peek ( 특정 연산 수행 )&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;Stream의 요소들을 대상으로 Stream에 영향을 주지 않고 특정 연산을 수행하기 위한 peek 함수가 존재한다. ‘확인해본다’라는 뜻을 지닌 peek 단어처럼, peek 함수는 Stream의 각각의 요소들에 대해 특정 작업을 수행할 뿐 결과에 영향을 주지 않는다. 또한 peek 함수는 파라미터로 함수형 인터페이스 Consumer를 인자로 받는다. 예를 들어 어떤 stream의 요소들을 중간에 출력하기를 원할 때 다음과 같이 활용할 수 있다.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IntStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;peek&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;원시 Stream &amp;lt;-&amp;gt; Stream&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// IntStream -&amp;gt; Stream&amp;lt;Integer&amp;gt; &lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;IntStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;mapToObj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Stream&amp;lt;Double&amp;gt; -&amp;gt; IntStream -&amp;gt; Stream&amp;lt;String&amp;gt; &lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;mapToInt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Double:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;intValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;mapToObj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    
&lt;span class=&quot;nl&quot;&gt;출처:&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;https:&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//mangkyu.tistory.com/114 [MangKyu's Diary]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;최종-연산&quot;&gt;최종 연산&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;[ 최댓값/최솟값/총합/평균/갯수 - Max/Min/Sum/Average/Count ]&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;Stream의 요소들을 대상으로 최솟값이나 최댓값 또는 총합을 구하기 위한 최종 연산들이 존재한다. 최솟값이나 최댓값을 구하기 위해서는 max와 min을 이용해야 하며, 총합 또는 평균 또는 개수를 구하기 위해서는 sum과 average, count를 이용해야 한다. min이나 max 또는 average는 Stream이 비어있는 경우에 값을 특정할 수 없다. 그렇기 때문에 다음과 같이 Optional로 값이 반환된다.&lt;/p&gt;

        &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;OptionalInt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;min&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IntStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt; 
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IntStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;orElse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
&lt;span class=&quot;nc&quot;&gt;IntStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ifPresent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;반면에 총합이나 갯수의 경우에는 값이 비어있는 경우 0으로 값을 특정할 수 있다. 그렇기 때문에 Stream API는 sum 메소드와 count 메소드에대해 Optional이 아닌 원시 값을 반환하도록 구현해두었다. 당연히 Stream이 비어있을 경우에는 0을 반환하게 된다.&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;collect ( 데이터 수집 )&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;Stream의 요소들을 List나 Set, Map, 등 다른 종류의 결과로 수집하고 싶은 경우에는 collect 함수를 이용할 수 있다. collect 함수는 어떻게 Stream의 요소들을 수집할 것인가를 정의한 Collector 타입을 인자로 받아서 처리한다. 일반적으로 List로 Stream의 요소들을 수집하는 경우가 많은데, 이렇듯 자주 사용하는 작업은 Collectors 객체에서 static 메소드로 제공하고 있다. 원하는 것이 없는 경우에는 Collector 인터페이스를 직접 구현하여 사용할 수도 있다.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Collectors.toList()&lt;/p&gt;

        &lt;ul&gt;
          &lt;li&gt;
            &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nameList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Product:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// set으로 반환 받고싶다면 Collectors.toSet()을 사용하면 된다.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;            &lt;/div&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Collectors.joining()&lt;/p&gt;

        &lt;ul&gt;
          &lt;li&gt;
            &lt;p&gt;Stream의 결과를 하나의 String으로 출력하길 원하는 경우 사용&lt;/p&gt;

            &lt;ul&gt;
              &lt;li&gt;
                &lt;p&gt;파라미터는 셋이다.&lt;/p&gt;

                &lt;ul&gt;
                  &lt;li&gt;delimiter : 각 요소 중간에 들어가 요소를 구분시켜주는 구분자&lt;/li&gt;
                  &lt;li&gt;prefix : 결과 맨 앞에 붙는 문자&lt;/li&gt;
                  &lt;li&gt;suffix : 결과 맨 뒤에 붙는 문자&lt;/li&gt;
                &lt;/ul&gt;
              &lt;/li&gt;
              &lt;li&gt;
                &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;listToString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Product:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;joining&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// potatoesorangelemonbreadsugar String listToString = productList.stream()     .map(Product::getName)     .collect(Collectors.joining(&quot; &quot;)); // potatoes orange lemon bread sugar String listToString = productList.stream()     .map(Product::getName)     .collect(Collectors.joining(&quot;, &quot;, &quot;&amp;lt;&quot;, &quot;&amp;gt;&quot;)); // &amp;lt;potatoes, orange, lemon, bread, sugar&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;                &lt;/div&gt;
              &lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Collectors.averagingInt(), Collectors.summingInt(), Collectors.summarizingInt()&lt;/p&gt;

        &lt;ul&gt;
          &lt;li&gt;
            &lt;p&gt;작업한 값의 평균값, 총합 등을 구하기위함&lt;/p&gt;

            &lt;ul&gt;
              &lt;li&gt;
                &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;averageAmount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;averagingInt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Product:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getAmount&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 86 Integer summingAmount = productList.stream() .collect(Collectors.summingInt(Product::getAmount)); // 86 Integer summingAmount = productList.stream() .mapToInt(Product::getAmount)     .sum();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;                &lt;/div&gt;
              &lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Collectors.groupingBy()&lt;/p&gt;

        &lt;ul&gt;
          &lt;li&gt;
            &lt;p&gt;Stream에서 작업한 결과를 특정 그룹으로 묶기를 원할 수 있다. 이러한 경우에는 Collectors.groupingBy()를 이용할 수 있으며, 결과는 Map으로 반환받게 된다. groupingBy는 매개변수로 함수형 인터페이스 Function을 필요로 한다.&lt;/p&gt;

            &lt;p&gt;예를 들어 수량을 기준으로 grouping을 원하는 경우에 다음과 같이 작성할 수 있으며, 같은 수량일 경우에는 List로 묶어서 값을 반환받게 된다.&lt;/p&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collectorMapOfLists&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;groupingBy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Product:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getAmount&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* {23=[Product{amount=23, name='potatoes'}, Product{amount=23, name='bread'}], 13=[Product{amount=13, name='lemon'}, Product{amount=13, name='sugar'}], 14=[Product{amount=14, name='orange'}]} */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;            &lt;/div&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Match ( 조건 검사 )&lt;/p&gt;

        &lt;ul&gt;
          &lt;li&gt;Stream의 요소들이 특정한 조건을 충족하는지 검사하고 싶은 경우에는 match 함수를 이용할 수 있다. match 함수는 함수형 인터페이스 Predicate를 받아서 해당 조건을 만족하는지 검사를 하게 되고, 검사 결과를 boolean으로 반환한다. match 함수에는 크게 다음의 3가지가 있다.
            &lt;ul&gt;
              &lt;li&gt;anyMatch: 1개의 요소라도 해당 조건을 만족하는가&lt;/li&gt;
              &lt;li&gt;allMatch: 모든 요소가 해당 조건을 만족하는가&lt;/li&gt;
              &lt;li&gt;nonMatch: 모든 요소가 해당 조건을 만족하지 않는가&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;flatmap을-통한-중첩-구조-제거&quot;&gt;FlatMap을 통한 중첩 구조 제거&lt;/h3&gt;

&lt;p&gt;만약 우리가 처리해야 하는 데이터가 2중 배열 또는 2중 리스트로 되어 있고, 이를 1차원으로 처리해야 한다면 어떻게 해야 할까? 이러한 경우에 map을 이용해도 결과는 2중 Stream의 형태일 것이다. 이처럼 중첩 구조를 한 단계 제거하기 위한 중간 연산이 필요한데, 이것이 바로 flatMap이다. flatMap은 Function 함수형 인터페이스를 매개 변수로 받고 있는데, 이 매개변수는 Stream을 extends하여 구현한 객체여야 한다.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// flatMap 함수 &amp;lt;R&amp;gt; Stream&amp;lt;R&amp;gt; flatMap(Function&amp;lt;? super T, ? extends Stream&amp;lt;? extends R&amp;gt;&amp;gt; mapper); // [[a], [b]] List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; list = Arrays.asList(Arrays.asList(&quot;a&quot;), Arrays.asList(&quot;b&quot;)); // [a, b] List&amp;lt;String&amp;gt; flatList = list.stream() .flatMap(Collection::stream) .collect(Collectors.toList());Stream&amp;lt;String[]&amp;gt; strStream = Stream.of( new String[] {&quot;a&quot;, &quot;b&quot;, &quot;c&quot;}, new String[] {&quot;d&quot;, &quot;e&quot;, &quot;f&quot;}); // map을 사용하면 2중 Stream이 반환됨 Stream&amp;lt;Stream&amp;lt;String&amp;gt;&amp;gt; stream = strStream.map(Arrays::stream); // flatMap을 사용하면 1중 Stream으로 차원을 낮출 수 있음 Stream&amp;lt;String&amp;gt; stream = strStream.flatMap(Arrays::stream);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;reduce를-통한-결과-생성&quot;&gt;Reduce를 통한 결과 생성&lt;/h3&gt;

&lt;p&gt;Reduce는 누산기(Accumulator)와 연산(Operation)으로 컬렉션에 있는 값을 처리하여 더 작은 컬렉션이나 단일 값을 만드는 작업이다. 예를 들어 다음과 같이 List&lt;Integer&gt;에서 총합을 구하는 연산은 sum 함수 말고 reduce로 처리할 수 있다. 또한 이러한 작업은 위의 그림에서 확인할 수 있듯이 Accumulator와 Operation를 사용한다.&lt;/Integer&gt;&lt;/p&gt;

&lt;p&gt;Stream API의 reduce 함수는 여러 요소들을 통해 새로운 결과를 만들어내는데, reduce 함수는 최대 3가지의 매개변수를 받을 수 있다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Accumulator: 각 요소를 계산한 중간 결과를 생성하기 위해 사용&lt;/li&gt;
  &lt;li&gt;Identity: 계산을 처리하기 위한 초기값&lt;/li&gt;
  &lt;li&gt;Combiner: Parlallel Stream에서 나누어 계산된 결과를 하나로 합치기 위한 로직&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;stream-api의-실행-순서&quot;&gt;Stream API의 실행 순서&lt;/h3&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;e&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;filter: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; 
                  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; 
                 &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;forEach: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt; 
&lt;span class=&quot;cm&quot;&gt;/* filter: a 
forEach: a 
filter: b 
forEach: b 
filter: c 
forEach: c 
filter: d 
forEach: d 
filter: e 
forEach: e 
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Stream API는 수직적인 구조로 진행이 되기 때문에 실행 순서를 고려하는 것이 상당히 중요하다. 잘못된 실행 속도는 연산의 횟수를 불필요하게 증가시키기 때문이다.&lt;/p&gt;

&lt;h3 id=&quot;parallel-stream&quot;&gt;Parallel Stream&lt;/h3&gt;

&lt;p&gt;Stream은 아주 많은 양의 데이터를 처리해야 하는 경우에 런타임 성능을 높이기 위해 병렬로 실행할 수 있는 기능인 병렬 스트림(Parallel Stream)을 제공하고 있다. Parallel Stream은 내부적으로 fork &amp;amp; join을 사용하고 있으며, ForkJoinPool.commonPool()을 통해 사용가능한 공통의 ForkJoinPool의 갯수를 확인할 수 있다. 내재되어 있는 ThreadPool의 갯수는 최대 5개이며, 사용가능한 물리적인 CPU 코어의 수에 따라 다르게 설정된다.&lt;/p&gt;</content><author><name>김주현</name></author><category term="TIL" /><summary type="html">Stream API</summary></entry><entry><title type="html">2021년 10월 27일 TIL</title><link href="https://kjh-sun.github.io/til/20211027/" rel="alternate" type="text/html" title="2021년 10월 27일 TIL" /><published>2021-10-27T04:23:00+00:00</published><updated>2021-10-27T04:23:00+00:00</updated><id>https://kjh-sun.github.io/til/20211027</id><content type="html" xml:base="https://kjh-sun.github.io/til/20211027/">&lt;h1 id=&quot;kafka&quot;&gt;Kafka&lt;/h1&gt;

&lt;h2 id=&quot;1-kafka란&quot;&gt;1. Kafka란?&lt;/h2&gt;

&lt;p&gt;Kafka는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pub-Sub&lt;/code&gt; 모델의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;메시지 큐&lt;/code&gt;입니다. 분산환경에 특화되어있는 특징을 가지고 있습니다. 이 글의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.Kafka의 주요개념&lt;/code&gt; 에서 Kafka의 개념들과 이들의 특징을 더 자세히 알아보도록 하겠습니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;h3 id=&quot;pubsub-모델이란&quot;&gt;Pub/Sub 모델이란?&lt;/h3&gt;

  &lt;p&gt;Publish/Subscribe의 줄임말. 메시지 기반의 미들웨어 시스템&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;publisher는 전송 대상은 모르면서 미들웨어로 메시지를 전송하고 이를 통해 subscriber에 전송하는 시스템&lt;/strong&gt;을 말합니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;h3 id=&quot;메시지-큐란&quot;&gt;메시지 큐란?&lt;/h3&gt;

  &lt;p&gt;프로세스 또는 프로그램 인스턴스가 데이터를 서로 교환할 때 사용하는 통신 방법. 메시지 지향 미들웨어를 구현한 시스템&lt;/p&gt;

  &lt;p&gt;더 큰 개념으로는 MOM(Message Oriented MiddleWare)를 의미한다. MOM이란 비동기 메시지를 사용하는 프로그램 간의 송수신을 의미한다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;2-구성요소&quot;&gt;2. 구성요소&lt;/h2&gt;

&lt;h3 id=&quot;21-event&quot;&gt;2.1 Event&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Event&lt;/code&gt;는, kafka에서 Producer와 Consumer가 데이터를 주고 받는 단위입니다. 이 글에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;이벤트 또는 메시지&lt;/code&gt;로 표기하겠습니다.&lt;/p&gt;

&lt;h3 id=&quot;22-producer&quot;&gt;2.2 Producer&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Producer&lt;/code&gt;는 kafka에 이벤트를 게시(post)하는 클라이언트 어플리케이션을 의미합니다.&lt;/p&gt;

&lt;h3 id=&quot;23-consumer&quot;&gt;2.3 Consumer&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Consumer&lt;/code&gt;는 이러한 Topic을 구독하고 이로부터 얻어낸 이벤트를 처리하는 클라이언트 어플리케이션 입니다.&lt;/p&gt;

&lt;h3 id=&quot;24-topic&quot;&gt;2.4 Topic&lt;/h3&gt;

&lt;p&gt;이벤트가 쓰이는 곳입니다. Producer는 이 Topic에 이벤트를 게시합니다. 그리고 Consumer는 Topic으로 부터 이벤트를 가져와 처리합니다. Topic은 파일시스템의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;폴더&lt;/code&gt;와 유사하며, 이벤트는 폴더안의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;파일&lt;/code&gt;과 유사합니다.&lt;/p&gt;

&lt;p&gt;Topic에 저장된 이벤트는 필요한 만큼 다시 읽을 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Partition&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Topic는 여러 Broker에 분산되어 저장되며, 이렇게 분산된 Topic을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Partition&lt;/code&gt;이라고 합니다. 어떤 이벤트가 Partition에 저장될지는 이벤트의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key(키)&lt;/code&gt;에 의해 정해지며, 같은 키를 가지는 이벤트는 항상 같은 Partition에 저장됩니다.&lt;/p&gt;

&lt;p&gt;Kafka는 Topic의 Partition에 지정된 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Consumer&lt;/code&gt;가 항상 정확히 동일한 순서로 Partition의 이벤트를 읽을것을 보장합니다.&lt;/p&gt;

&lt;h2 id=&quot;3-kafka의-주요-개념&quot;&gt;3. Kafka의 주요 개념&lt;/h2&gt;

&lt;h3 id=&quot;31-producer와-consumer의-분리&quot;&gt;3.1 Producer와 Consumer의 분리&lt;/h3&gt;

&lt;p&gt;Kafka의 Producer와 Consumer는 완전 별개로 동작을 합니다. Producer는 Broker의 Topic에 메시지를 게시하기만 하면되며, Consumer는 Broker의 특정 Topic에서 메시지를 가져와 처리를 하기만 합니다.&lt;/p&gt;

&lt;p&gt;이 덕분에 높은 Kafka는 높은 확장성을 제공합니다. 즉, Producer 또는 Consumer를 필요에 의해 스케일 인 아웃하기에 용이한 구조입니다. 만약, Producer와 Consumer가 직접적으로 연관을 가지고 있다면, 확장 또는 축소시 이들을 모두 연결 또는 해제를 하기가 매우 번거럽고 어려웠을 것입니다.&lt;/p&gt;

&lt;p&gt;exactly once 관련 숙지합시다.&lt;/p&gt;

&lt;h3 id=&quot;32-push-와-pull-모델&quot;&gt;3.2 Push 와 Pull 모델&lt;/h3&gt;

&lt;p&gt;kafka의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Consumer&lt;/code&gt;는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pull&lt;/code&gt; 모델을 기반으로 메시지 처리를 진행합니다. 즉, Broker가 Consumer에게 메시지를 전달하는 것이 아닌, Consumer가 필요할때, Broker로 부터 메시지를 가져와 처리하는 형태입니다.&lt;/p&gt;

&lt;p&gt;이러한 형태는 아래와 같은 장점이 존재합니다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;다양한 소비자의 처리 형태와 속도를 고려하지 않아도 된다.&lt;/p&gt;

    &lt;p&gt;반대의 경우인 Push모델에서는, Broker가 데이터 전송 속도를 제어하기 때문에, 다양한 메시지 스트림의 소비자를 다루기가 어렵지만, Pull 모델은 Consumer가 처리 가능한 때에 메시지를 가져와 처리하기 때문에 다양한 소비자를 다루기가 쉽습니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;불필요한 지연없이 일괄처리를 통해 성능향상 도모.&lt;/p&gt;

    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Push&lt;/code&gt; 모델의 경우에는, 요청을 즉시 보내거나, 더 많은 메시지를 한번에 처리하도록 하기 위해 Buffering을 할 수 있습니다. 하지만 이런 경우, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Consumer&lt;/code&gt;가 현재 메시지를 처리할 수 있음에도, 대기를 해야합니다. 그렇다고 전송 지연시간을 최소로 변경하면, 한번에 하나의 메시지만을 보내도록 하는것과 같으므로 매우 비효율적입니다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pull&lt;/code&gt; 모델의 경우, 마지막으로 처리된 메시지 이후의 메시지를 Consumer가 처리가능한 때에 모두 가져오기 때문에, 이 문제를 해결합니다. 따라서 불필요한 지연 없이 최적의 일괄처리를 할 수 있습니다.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;33-소비된-메시지-추적-commit과-offset&quot;&gt;3.3 소비된 메시지 추적 (Commit과 Offset)&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9912C33B5FC70CB319&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;

&lt;p&gt;메세지는 지정된 Topic에 전달됩니다. Topic은 다시 여러 Partition으로 나뉠 수도 있습니다. 위 그림에서 각 파티션의 한칸한칸은 로그라고 칭합니다. 또한 메시지는 로그에 순차적으로 append 됩니다. 그리고 이 메시지의 상대적인 위치를 offset이라고 칭합니다.&lt;/p&gt;

&lt;p&gt;메시징 시스템은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Broker&lt;/code&gt;에서 소비된 메시지에 대한 메타데이터를 유지합니다. 즉, 메시지가 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Consumer&lt;/code&gt;에게 전달되면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Broker&lt;/code&gt;는 이를 로컬에 기록하거나, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;소비자의 승인&lt;/code&gt;을 기다립니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commit과 Offset&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consumer의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poll()&lt;/code&gt;은 이전에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commit한 offset&lt;/code&gt;이 존재하면, 해당 offset 이후의 메시지를 읽어오게 됩니다. 또 읽어온 뒤, 마지막 offset을 commit을 합니다. 이어서 poll()이 실행되면 방금전 commit한 offset이후의 메시지를 읽어와 처리하게 됩니다.&lt;/p&gt;

&lt;p&gt;메시지 소비중에는 다음과 같은 문제들이 발생할 수도 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;소비된 메시지 기록시점&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Broker&lt;/code&gt;가 메시지를 네트워크를 통해 Consumer에게 전달할때 마다, 즉시, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;소비 된 것&lt;/code&gt;으로 기록하면, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Consumer&lt;/code&gt;가 메시지 처리를 실패하면 해당 메시지가 손실됩니다.&lt;/p&gt;

&lt;p&gt;이로 인해서, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Broker&lt;/code&gt;는 메시지가 소비되었음을 기록하기 위해서, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Consumer&lt;/code&gt;의 승인을 기다립니다. 하지만 이런식으로 메시지를 처리하게 되면 아래와 같은 문제점이 또 발생합니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;중복 메시지 전송과 멱등성&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;우선 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Consumer&lt;/code&gt;가 메시지를 성공적으로 처리하고, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;승인&lt;/code&gt;을 보내기전에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Broker&lt;/code&gt;가 실패하였다고 판단하고 다시 메시지를 보내게 되면, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Consumer&lt;/code&gt;는 같은 메시지를 두번 처리하게 됩니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;따라서, Consumer는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;멱등성&lt;/code&gt;을 고려하여야 합니다&lt;/strong&gt;. 즉, 같은 메시지를 특수한 상황에 의해 여러번 받아서 여러번 처리하더라도, 한번 처리한것과 같은 결과를 가지도록 설계해야 합니다.&lt;/p&gt;

&lt;h3 id=&quot;34-consumer-group&quot;&gt;3.4 Consumer Group&lt;/h3&gt;

&lt;p&gt;Consumer Group은 하나의 Topic을 구독하는 여러 Consumer들의 모음입니다. Topic을 구독하는 Consumer들을 Group화 하는 이유는, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;가용성&lt;/code&gt; 때문입니다. 하나의 Topic을 처리하는 Consumer가 1개인 것보다 여러개라면 당연히 가용성은 증가할 것입니다.&lt;/p&gt;

&lt;p&gt;아래에서 설명드릴 내용이지만, Consumer Group의 각 Consumer들은 하나의 Topic의 각기 다른 Partition의 내용만을 처리할 수 있는데요, 이를 통해서, Kafka는 메시지 처리 순서를 보장한다고 합니다. 이 때문에, 특정 Partition을 처리하던, Consumer가 처리 불가 상태가 된다면, 해당 Partition의 메시지를 처리할 수 없는 상태가 되어버립니다. 이때문에 Consumer Group이 필요합니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;가용성이란?&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;시스템이 서비스를 정상적으로 제공할 수 있는 상태&lt;/li&gt;
    &lt;li&gt;항상 서비스할 수 있는 시스템을 가용성이 높은 시스템이라고 한다.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;rebalance&quot;&gt;&lt;strong&gt;Rebalance&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;Partition을 담당하던 Consumer가 처리불가 상태가 되어버리면, Partition과 Consumer를 재조정하여, 남은 Consumer Group내의 Consumer들이 Partition을 적절하게 나누어 처리하게 됩니다.&lt;/p&gt;

&lt;p&gt;또한 Consumer Group내에서 Consumer들간에 Offset 정보를 공유하고 있기 때문에, 특정 Consumer가 처리불가 상태가 되었을때, 해당 Consumer가 처리한 마지막 Offset이후 부터 처리를 이어서 할 수 있습니다.&lt;/p&gt;

&lt;p&gt;이렇게 Partition을 나머지 Consumer들이 다시 나누어 처리하도록 하는 것을 Rebalance라고 하며, 이를 위해 Consumer Group이 필요합니다.&lt;/p&gt;

&lt;h4 id=&quot;consumer-group과-partition&quot;&gt;&lt;strong&gt;Consumer Group과 Partition&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/994AE9345FC70CBA1A&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;

&lt;p&gt;무조건, Consumer Group내의 Consumer들은 모두 각기 다른 Partition에 연결되어야 합니다. 이렇게 함으로써, Consumer의 &lt;strong&gt;메시지 처리 순서를 보장&lt;/strong&gt;하게 됩니다. 즉 위와 같은 그림의 형태로는 가능하지만.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/991D6F3E5FC70CC11A&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;

&lt;p&gt;이와 같은 형태로, 구성은 불가능 합니다.&lt;/p&gt;

&lt;p&gt;이해가 잘 안되신다면, 반대로 Consumer 그룹의 여러 Consumer가 Partition과 연결되어 메시지를 처리한다고 생각을 해보시면 됩니다. 이는 마치 하나의 공유자원을 여러 스레드가 사용하는 것과 같은 상황입니다.&lt;/p&gt;</content><author><name>김주현</name></author><category term="TIL" /><summary type="html">Kafka</summary></entry><entry><title type="html">2021년 10월 22일 TIL</title><link href="https://kjh-sun.github.io/til/20211022/" rel="alternate" type="text/html" title="2021년 10월 22일 TIL" /><published>2021-10-22T04:23:00+00:00</published><updated>2021-10-22T04:23:00+00:00</updated><id>https://kjh-sun.github.io/til/20211022</id><content type="html" xml:base="https://kjh-sun.github.io/til/20211022/">&lt;h2 id=&quot;builder-pattern-vs-java-beans-patterns&quot;&gt;Builder Pattern vs Java Beans Patterns&lt;/h2&gt;

&lt;p&gt;자바빈즈 패턴이란, &lt;strong&gt;매개변수가 없는 생성자로 객체를 만든 후, Setter 메서드들을 호출하여 원하는 매개변수의 값을 설정하는 방식이다.&lt;/strong&gt; 하지만 여기에는 심각한 단점이 있다. 생각해보면 default 생성자를 통해 객체만 생성하고 필요한 매개변수들을 setter 메소드로 값을 설정해준다고 할때 만약 설정해야할 매개변수의 수가 무수히 많다면? 우리는 그만큼의 Setter 메소드 호출을 해야한다. 또한 객체가 완전히 생성되기 전까지 일관성이 무너진 상태이다.&lt;/p&gt;

&lt;p&gt;일관성이 깨진 객체가 만들어지는것 자체도 런타임시 에러가 발생할 수 있게되고 디버깅도 어려워진다. 따라서 이러한 문제 때문에 자바빈즈 패턴에서는 클래스를 불변으로 만들 수 없고 스레드 안정성을 얻기 위해서 추가 작업을 해줘야하는 불편함이 있다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;생성과 표현의 분리를 위해서&lt;/li&gt;
  &lt;li&gt;불변객체로 인스턴스를 생성하는데 초점이 맞춰져 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;불변객체-사용의-이유&quot;&gt;불변객체 사용의 이유&lt;/h3&gt;

&lt;h3 id=&quot;-불변-객체immutable-object란-&quot;&gt;&lt;strong&gt;[ 불변 객체(Immutable Object)란? ]&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;불변 객체(Immutable Object)란 객체 생성 이후 내부의 상태가 변하지 않는 객체이다. 불변 객체는 read-only 메소드만을 제공하며, 객체의 내부 상태를 제공하는 메소드를 제공하지 않거나 제공하는 경우 방어적 복사(defensive-copy)를 통해 제공한다.&lt;/p&gt;

&lt;h3 id=&quot;-불변-객체immutable-object-및-final을-사용해야-하는-이유-&quot;&gt;&lt;strong&gt;[ 불변 객체(Immutable Object) 및 final을 사용해야 하는 이유 ]&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;실무를 진행하다보면 불변 객체와 final을 자주 사용하게 될 것이고, 자주 사용하지 않는다면 사용해야 한다. 불변 객체 또는 final을 사용해야 하는 이유는 다음과 같다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Thread-Safe하여 병렬 프로그래밍에 유용하며, 동기화를 고려하지 않아도 된다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;멀티 쓰레드 환경에서 동기화 문제가 발생하는 이유는 공유 자원에 동시에 쓰기(Write) 때문이다. 하지만 만약 공유 자원이 불변의 자원이라면 어떻게 될까? 더 이상 동기화를 고려하지 않아도 될 것이다. 왜냐하면 항상 동일한 값을 반환할 것이기 때문이다. 이는 안정성을 보장할 뿐만 아니라 동기화를 하지 않음으로써 성능상의 이점도 가져다준다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. 실패 원자적인(Failure Atomic) 메소드를 만들 수 있다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;가변 객체를 통해 어떠한 작업을 하는 도중 예외가 발생하면 해당 객체가 불안정한 상태에 빠질 수 있다. 그리고 불한정한 상태를 갖는 객체는 또 다른 에러를 유발할 수 있다. 하지만 불변 객체라면 어떠한 예외가 발생하여도 메소드 호출 전의 상태를 유지할 수 있을 것이다. 그리고 예외가 발생하여도 오류가 발생하지 않은 것 처럼 다음 로직을 처리할 수 있다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Cache나 Map의 또는 Set 등의 요소로 활용하기에 더욱 적합하다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;만약 캐시나 Map 또는 Set 등으로 사용되는 객체가 변경되었다면 이를 갱신하는 등의 작업을 추가로 해주어야 할 것이다. 하지만 객체가 불변이라면 한 번 데이터가 저장된 이후에 다른 부가 작업들을 고려하지 않아도 될 것이고, 이는 캐시나 다른 자료 구조를 사용하는데 용이하게 작용할 것이다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. 부수 효과(Side Effect)를 피해 오류가능성을 최소화할 수 있다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;부수 효과란 변수의 값이 변경되거나, 필드 값이 설정되는 등의 변화가 발생하는 효과를 의미한다. 만약 객체의 수정자(Setter)가 구현되어 있고, 여러 메소드에서 객체의 값이 변경된다면 객체를 예측하기 어려워질 것이다. 객체의 바뀐 상태를 파악하기 위해서는 메소드들을 살펴보아야 할 것이며 이러한 부분은 유지보수성을 상당히 떨어뜨린다. 그래서 이러한 부수효과가 없는 순수 함수들을 만드는 것이 상당히 중요한데, 객체가 불변이라면 어떻게 될까?&lt;/p&gt;

&lt;p&gt;불변 객체는 기본적으로 값의 수정이 불가능하기 때문에 변경 가능성이 적으며, 객체의 생성과 사용이 상당히 제한된다. 그렇기 때문에 메소드들은 자연스럽게 순수 함수들로 구성될 것이고, 다른 메소드가 호출되어도 객체의 상태가 유지되기 때문에 안전하게 객체를 다시 사용할 수 있다. 이러한 불변 객체는 오류를 줄여 유지보수성이 높은 코드를 작성하도록 도와줄 것이다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. 다른 사람이 작성한 함수를 예측가능하며 안전하게 사용할 수 있다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;일반적으로 개발을 하면 다른 사람들과 협업을 하면서 진행하게 된다. 불변성(Immutability)은 협업을 하는 과정에서도 도움을 주는데, 불변성이 보장된 함수라면 다른 사람이 개발한 함수를 위험없이 이용할 수 있다. 마찬가지로 다른 사람도 내가 작성한 메소드를 호출하여도, 값이 변하지 않음을 보장받을 수 있다. 그렇기에 우리는 다른 사람의 코드를 변경에 대한 불안없이 이용할 수 있다. 또한 불필요한 시간을 절약할 수도 있는데, 이에 대한 예제는 아래에서 자세히 살펴보도록 하자.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. 가비지 컬렉션의 성능을 높일 수 있다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;불변성(Immutability)을 활용하는 것은 많은 이점을 가져다주는데, 그 중에서 많은 사람들이 놓치는 것이 바로 GC의 성능을 높여준다는 것이다.&lt;/p&gt;

&lt;p&gt;불변의 객체는 한번 생성된 이후에 수정이 불가능한 객체로, Java에서는 final 키워드를 사용하여 불변의 객체를 생성할 수 있다. 이렇게 객체를 생성하기 위해서는 객체를 가지는 컨테이너도 존재한다는 것인데, 당연히 불변의 객체가 먼저 생성되어야 컨테이너가 이 객체를 참조할 수 있을 것이다. 즉, 컨테이너는 컨테이너가 참조하는 가장 젊은 객체들보다 더 젊다는 것(늦게 생성되었다는 것)이다. 이러한 점은 GC가 수행될 때, 가비지 컬렉터가 컨테이너 하위의 불변 객체들은 Skip할 수 있도록 도와준다. 왜냐하면 해당 컨테이너가 살아있다는 것은 하위의 불변 객체들 역시 처음에 할당된 그 상태로 참조되고 있다는 것을 의미하기 때문이다.&lt;/p&gt;

&lt;p&gt;Java에 대한 유명한 책인 Effective Java에는 다음과 같은 구문이 적혀있다. 이를 명심하고 개발하도록 하자.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;클래스들은 가변적이여야 하는 매우 타당한 이유가 있지 않는 한 반드시 불변으로 만들어야 한다. 만약 클래스를 불변으로 만드는 것이 불가능하다면, 가능한 변경 가능성을 최소화하라.&lt;/p&gt;
&lt;/blockquote&gt;</content><author><name>김주현</name></author><category term="TIL" /><summary type="html">Builder Pattern vs Java Beans Patterns</summary></entry><entry><title type="html">2021년 10월 21일 TIL</title><link href="https://kjh-sun.github.io/til/20211021/" rel="alternate" type="text/html" title="2021년 10월 21일 TIL" /><published>2021-10-21T04:23:00+00:00</published><updated>2021-10-21T04:23:00+00:00</updated><id>https://kjh-sun.github.io/til/20211021</id><content type="html" xml:base="https://kjh-sun.github.io/til/20211021/">&lt;h3 id=&quot;엔티티의-생명주기&quot;&gt;엔티티의 생명주기&lt;/h3&gt;

&lt;h3 id=&quot;비영속newtransient&quot;&gt;비영속(new/transient)&lt;/h3&gt;

&lt;p&gt;순수한 객체 상태이며, **영속성 컨텍스트와 관련이 없는 상태&lt;/p&gt;

&lt;h3 id=&quot;영속managed&quot;&gt;영속(managed)&lt;/h3&gt;

&lt;p&gt;EntityManager를 통해 엔티티를 영속성 컨텍스트에 저장되어 &lt;strong&gt;영속성 컨텍스트가 관리&lt;/strong&gt;하는 상태&lt;/p&gt;

&lt;h3 id=&quot;준영속detached&quot;&gt;준영속(detached)&lt;/h3&gt;

&lt;p&gt;영속성 컨텍스트에 저장되었다가 분리된(detached) 상태&lt;/p&gt;

&lt;h3 id=&quot;삭제removed&quot;&gt;삭제(removed)&lt;/h3&gt;

&lt;p&gt;엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제한 상태&lt;/p&gt;

&lt;h3 id=&quot;git-flow-vs-github-flow-vs-gitlab-flow&quot;&gt;Git Flow vs Github Flow vs Gitlab Flow&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Git flow
    &lt;ul&gt;
      &lt;li&gt;feature / develop / release / hotfix / master&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;GitHub Flow
    &lt;ul&gt;
      &lt;li&gt;master를 항상 배포하고, 그냥 마스터만 두고 쓰는 방식&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Gitlab Flow
    &lt;ul&gt;
      &lt;li&gt;Master / Pre-Production / Production&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;jvm&quot;&gt;JVM&lt;/h2&gt;

&lt;p&gt;참조 : https://1-7171771.tistory.com/125?category=865872&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnbSRB/btqUuNzlgfx/kMReJ5MuDRxKVU2dcT6Mf1/img.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;프로그램이 실행되면 JVM은 OS로 부터 이 프로그램이 필요로 하는 메모리를 할당받는다.&lt;/li&gt;
  &lt;li&gt;자바 컴파일러(javac)가 자바 코드(.java)를 읽어들여 자바 바이트코드(.class)로 변환한다.&lt;/li&gt;
  &lt;li&gt;Class Loader를 통해 class 파일들을 JVM으로 로딩한다.&lt;/li&gt;
  &lt;li&gt;로딩된 class 파일들은 실행엔진(Execution engine)을 통해 해석된다.&lt;/li&gt;
  &lt;li&gt;해석된 바이트 코드는 Runtime Data Areas에 배치되어 실질적으로 수행한다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpdtTT/btqUtXCay6r/ejvZRooY6uoKZzYPWSlJI0/img.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;클래스-로더-시스템-class-loader&quot;&gt;클래스 로더 시스템 (Class Loader)&lt;/h4&gt;

&lt;p&gt;JVM으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.class&lt;/code&gt;파일을 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈이다. 자바는 Runtime시에 동적으로 클래스를 로드(=로딩)한다. (클래스를 처음으로 &lt;strong&gt;참조&lt;/strong&gt;할 때, 해당 클래스를 로드하고 링크한다는 것이다.)&lt;/p&gt;

&lt;p&gt;쉽게말해서 자바 바이트코드를 읽어들여서 메모리에 적절하게 배치하는게 클래스 로더가 하는일이다.&lt;/p&gt;

&lt;p&gt;클래스 로더는 크게 &lt;strong&gt;로딩,링크,초기화&lt;/strong&gt; 세 가지로 나뉜다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;로딩 : 클래스를 읽어오는 과정&lt;/li&gt;
  &lt;li&gt;링크 : 객체를 연결하는 과정&lt;/li&gt;
  &lt;li&gt;초기화: static 값들 초기화 및 변수에 할당&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;실행-엔진execution-engine&quot;&gt;실행 엔진(Execution Engine)&lt;/h4&gt;

&lt;p&gt;클래스를 실행시키는 역할을 한다. 클래스 로더가 바이트 코드를 메모리에 배치시키는 역할을 한다면 실행 엔진은 이것을 실행시킨다.&lt;/p&gt;

&lt;p&gt;자바 컴파일러를 통해 변환된 바이트 코드는 기계가 바로 실행할 수 있는 언어가 아닌 &lt;strong&gt;인간이 이해하기 편한 언어에 가깝기 때문에&lt;/strong&gt; 아래 두 가지 방식을 통해 &lt;strong&gt;기계가 이해할 수 있는 언어로 변환&lt;/strong&gt;해준다.&lt;/p&gt;

&lt;p&gt;(앞으로는 인간이 이해하기 편한 언어를 네이티브 코드로 표현하겠다.)&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Interpreter(인터프리터)&lt;/p&gt;

    &lt;p&gt;실행 엔진은 자바 바이트 코드를 한줄씩 읽어서 기계가 이해할 수 있는 언어로 변환한다.&lt;/p&gt;

    &lt;p&gt;-&amp;gt; 한줄씩 수행하기 때문에 속도가 매우 느리다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;JIT(Just In Time)&lt;/p&gt;

    &lt;p&gt;인터프리터의 효율을 높이기 위해, 인터프리터가 반복되는 코드를 발견하면 JIT 컴파일러로 반복되는 코드를 모두 기계가 이해할 수 있는 언어인 네이티브 코드로 바꿔둔다. 그 다음부터 인터프리터는 네이티브코드로 컴파일된 코드를 바로 사용한다. 네이티브 코드는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 빠르게 수행될 수 있다.&lt;/p&gt;

    &lt;p&gt;JIT 컴파일러가 컴파일 하는 과정은 바이트 코드를 인터프리팅하는 것보다 훨씬 오래걸리므로 한번만 실행하는 코드라면 인터프리팅 하는 것이 유리하다.&lt;/p&gt;

    &lt;p&gt;-&amp;gt; 이 과정은 JVM이 내부적으로 해당 메서드의 실행 빈도를 체크하여 판단한다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;GC(Garbage Collector)&lt;/p&gt;

    &lt;p&gt;더이상 참조되지 않는 객체를 모아서 정리한다.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;jvm-메모리-영역&quot;&gt;JVM 메모리 영역&lt;/h3&gt;

&lt;h4 id=&quot;pc-register&quot;&gt;PC Register&lt;/h4&gt;

&lt;p&gt;Thread가 시작될 때 생성되며, 각 Thread별로 고유의 독립적인 공간을 가진다. Thread가 어떤 부분을 어떤 명령으로 실행해야 할 지에 대한 기록을 하는 부분으로 현재 수행중인 JVM명령의 주소를 갖는다.&lt;/p&gt;

&lt;h4 id=&quot;stack-스택-영역&quot;&gt;Stack (스택 영역)&lt;/h4&gt;

&lt;p&gt;프로그램 실행 과정에서 임시로 할당되었다가 메소드 호출이 종료됨과 동시에 소멸되는 데이터를 저장하기 위한 영역이다.&lt;/p&gt;

&lt;p&gt;메소드가 호출될 때 마다 메모리 안에 그 메서드만을 위한 공간인 스택 프레임이 생성되며, 그 안에는 메소드의 지역변수와 파라미터 변수가 포함되어 있다.&lt;/p&gt;

&lt;p&gt;메서드의 수행이 끝나면 스택 프레임 별로 삭제된다.&lt;/p&gt;

&lt;h4 id=&quot;native-method-stack&quot;&gt;Native method stack&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.class&lt;/code&gt; 가 아닌 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행시키는 영역으로, Java로 작성된 코드가 아닌 C 또는 C++로 작성된 코드들의 공간이다.&lt;/p&gt;

&lt;p&gt;JNI(Java Native Interace)를 통해 바이트 코드로 전환하여 저장하게 된다. JNI는 아래 코드처럼 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;native&lt;/code&gt; 키워드가 붙은 메소드를 호출한다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@HotSpotIntrinsicCandidate
public static native Thread currentThread();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;모든-thread가-공유하는-영역&quot;&gt;모든 Thread가 공유하는 영역&lt;/h3&gt;

&lt;h4 id=&quot;method-area메소드-영역&quot;&gt;Method Area(메소드 영역)&lt;/h4&gt;

&lt;p&gt;메소드 영역은 다른말로 클래스 영역 또는 스태틱 영역이라고도 불린다.&lt;/p&gt;

&lt;p&gt;클래스 수준의 정보(클래스 이름, 부모 클래스 이름, 메소드, 변수등)가 저장된다.&lt;/p&gt;

&lt;p&gt;클래스 정보를 처음 메모리 공간에 올릴 때 초기화 되는 대상을 저장하기 위한 메모리 공간이다. 대부분의 인스턴스 생성이 메소드 내에서 명령하고 호출하기 때문에 사실상 컴파일 된 바이트 코드의 대부분이 메소드 바이트코드이기 때문에 대부분의 바이트 코드가 모두 이 공간에 올라간다고 봐도 상관 없다.&lt;/p&gt;

&lt;p&gt;이 공간에는 추가적으로 &lt;strong&gt;Runtime Constant Pool&lt;/strong&gt; 이라는 별도의 영역이 존재하는데, 상수 자료형을 저장하여 참조하고 중복을 막아 효율적인 메모리를 관리하는 역할을 한다.&lt;/p&gt;

&lt;p&gt;메소드 영역은 &lt;strong&gt;GC의 관리 대상이다.&lt;/strong&gt;&lt;/p&gt;

&lt;h4 id=&quot;heap힙-영역&quot;&gt;heap(힙 영역)&lt;/h4&gt;

&lt;p&gt;객체를 저장하는 가상 메모리 공간으로, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new&lt;/code&gt;키워드로 생성된 객체와 배열을 저장하며 &lt;strong&gt;메소드 영역에 올라온 클래스들만 객체로 생성 가능하다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;힙 영역은 다음과 같이 세 영역으로 나뉜다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KSI0s/btqUzvq74Gm/Uh06IIzE1R5iYafIDUn9q0/img.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;</content><author><name>김주현</name></author><category term="TIL" /><summary type="html">엔티티의 생명주기</summary></entry><entry><title type="html">2021년 10월 14일 TIL</title><link href="https://kjh-sun.github.io/til/20211014/" rel="alternate" type="text/html" title="2021년 10월 14일 TIL" /><published>2021-10-14T04:23:00+00:00</published><updated>2021-10-14T04:23:00+00:00</updated><id>https://kjh-sun.github.io/til/20211014</id><content type="html" xml:base="https://kjh-sun.github.io/til/20211014/">&lt;h1 id=&quot;mapmerge&quot;&gt;Map.merge&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;putAll&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;putAll은 겹치는 키값이 있으면 파라미터로 전달된 map의 값으로 value가 바뀐다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;put&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Map에 삽입하는 메서드&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;putIfAbsent / computIfAbsent&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;h3 id=&quot;공통점&quot;&gt;공통점&lt;/h3&gt;

        &lt;ul&gt;
          &lt;li&gt;putIfAbsent와 computeIfAbsent는 둘 다 Map에 key가 없을 때만 적용되는 메소드이다.&lt;/li&gt;
        &lt;/ul&gt;

        &lt;h3 id=&quot;차이점&quot;&gt;차이점&lt;/h3&gt;

        &lt;ul&gt;
          &lt;li&gt;putIfAbsent는 value를 바로 가지지만 computeIfAbsent는 Function을 통한 계산된 value를 가진다.&lt;/li&gt;
          &lt;li&gt;putIfAbsent의 value에 메소드호출이 온다면 Map에 key값이 중복됐을 때, 값은 저장되지 않아도 메소드는 호출된다. -&amp;gt; 메소드 호출 비용이 비싸다면 성능이 저하된다.&lt;/li&gt;
          &lt;li&gt;computeIfAbsent의 Function은 key값이 중복됐을 때 실행되지 않고, key 값이 발견되지 않았을 때만 실행된다.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;computeIfPresent&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;키가 존재하는 경우에만 실행 BiFunction&amp;lt;T, U, R&amp;gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;compute&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;키 존재 유무에 관계없이 무조건 실행 BiFunction&amp;lt;T, U, R&amp;gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;merge&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;인자를 세개 받으며,
        &lt;ul&gt;
          &lt;li&gt;첫번째는 key&lt;/li&gt;
          &lt;li&gt;두번째는 key가 없을 경우 초기값&lt;/li&gt;
          &lt;li&gt;세번째는 key 존재 시 remapping할 BiFunction&amp;lt;T, U, R&amp;gt;
            &lt;ul&gt;
              &lt;li&gt;리턴값이 null이면 삭제&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;getOrDefault&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;key가 존재하면 value 반환, 없으면 지정한 default 반환&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;comparable-comparator-익명클래스&quot;&gt;Comparable Comparator (익명클래스)&lt;/h1&gt;

&lt;p&gt;둘 다 비교를 위한 인터페이스이나, Comparable은 비교의 주체가 자신이며, Comparator는 비교의 주체가 입력받는 매개변수 두개라는 차이점이 있다.(compare와 compareTo 메서드를 오버라이딩한다.)&lt;/p&gt;

&lt;h1 id=&quot;익명-클래스&quot;&gt;익명 클래스&lt;/h1&gt;

&lt;p&gt;한번만 객체로 사용하는데에 사용되는 클래스를 굳이 정의하지 않고 사용하는 것.&lt;/p&gt;

&lt;h1 id=&quot;함수형-프로그래밍&quot;&gt;함수형 프로그래밍&lt;/h1&gt;

&lt;p&gt;대입문이 없는 프로그래밍 - Clean Code&lt;/p&gt;

&lt;p&gt;Side Effect가 없는 순수 함수를 1급 객체로 간주하여, 파라미터로 넘기거나 반환값으로 사용할 수 있으며 참조 투명성을 지킬 수 있다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;순수 함수란?&lt;/p&gt;

  &lt;p&gt;Side Effect를 제거한 함수들을 의미한다.&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;Memory or I/O의 관점에서 Side Effect가 없는 함수&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;함수의 실행이 외부에 영향을 끼치지 않는 함수&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;Side Effect란?&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;/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;/li&gt;
    &lt;li&gt;
      &lt;p&gt;콘솔 또는 파일 I/O가 발생함&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;순수 함수를 사용했을 때의 장점?&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;함수 자체가 독립적이며, 부수 효과가 없기 때문에 Thread에 안전성을 보장받는다.&lt;/li&gt;
    &lt;li&gt;병렬처리를 동기화 없이 진행 가능&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;1급 객체란?&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;/blockquote&gt;</content><author><name>김주현</name></author><category term="TIL" /><summary type="html">Map.merge</summary></entry><entry><title type="html">2021년 10월 09일 TIL</title><link href="https://kjh-sun.github.io/til/20211009/" rel="alternate" type="text/html" title="2021년 10월 09일 TIL" /><published>2021-10-09T04:23:00+00:00</published><updated>2021-10-09T04:23:00+00:00</updated><id>https://kjh-sun.github.io/til/20211009</id><content type="html" xml:base="https://kjh-sun.github.io/til/20211009/">&lt;p&gt;이번 주말에는 프로젝트가 끝나고 조금 하고 싶던 공부를 해야겠다고 생각하여, 자료구조를 공부하기로 했다.&lt;/p&gt;

&lt;p&gt;문득 그런 생각이 들었었다. java의 Collections를 정말 많이 사용하고 있는데,(Map은 java에서 Collection 으로 분류하고 있지는 않지만, 일단은 이것도 포함해서) java에서 그 내부 동작이 구체적으로 어떻게 구현되어 있는지가 궁금했다.&lt;/p&gt;

&lt;p&gt;그래서 무작정 자바 내부 코드를 타고 들어가서 어떻게 구현되어있는지 보기로 했다.&lt;/p&gt;

&lt;p&gt;(번외 - 이 과정에서 굉장히 뜬금없이 Scanner와 BufferedReader의 속도 차이가 왜 발생하는지도 궁금해서 찾아보기도 했다.&lt;/p&gt;

&lt;p&gt;Scanner 내부에서 문자열을 입력받으면&lt;/p&gt;

&lt;p&gt;nextInt를 예로 들면, intergerPattern()이라는 메소드를 중간에 한번 거치게되는데, 그 메소드 내부에서 buildIntegerPatternString으로 들어간다.&lt;/p&gt;

&lt;p&gt;이후 그 안에서 정말 많은 정규 표현식을 검사하고 검사된 문자열을 반환하는데, 이 부분에서 성능 이슈가 발생하는 것으로 생각되었다.&lt;/p&gt;

&lt;p&gt;System.in이 inputStream 객체라는 것을 알게된 것도 재미있었다. 이게 Byte단위로 데이터를 받으니, inputStreamReader로 이 단위 데이터를 문자 단위로 싸고, 이것을 또 다시 BufferedReader로 싸서 한번에 전송하는 것이다.)&lt;/p&gt;

&lt;p&gt;결론부터 말하자면, 다른 것들보다 HashSet의 방식을 이해하는 것이 꽤 어려웠었다.&lt;/p&gt;

&lt;p&gt;다른 것들은 어느정도 기존에 알고 있던 것들이었다. Object[] 을 사용하는가 Node를 사용하는가 정도의 개념차이만 있었고, 대략적으로 이렇게 동작하는 구나를 이해한채로 읽으니 이해가 빨랐다.&lt;/p&gt;

&lt;p&gt;다만 조금 어려웠던 것은 HashMap 쪽이었다. 특히 어려웠던 부분이, resize()로 용적 크기를 재설정할 때 도대체 어떤 원리로 재배치를 하는 지 이해하기가 어려웠었는데, 한참 보고 인터넷도 찾아보니 그 원리를 알 수 있었다.&lt;/p&gt;

&lt;p&gt;그에 대한 내용은 내일 더 공부해보고 정리해보고자 한다!&lt;/p&gt;

&lt;p&gt;(10일 TIL에 이어서 계속)&lt;/p&gt;</content><author><name>김주현</name></author><category term="TIL" /><summary type="html">이번 주말에는 프로젝트가 끝나고 조금 하고 싶던 공부를 해야겠다고 생각하여, 자료구조를 공부하기로 했다.</summary></entry><entry><title type="html">2021년 10월 09일 TIL</title><link href="https://kjh-sun.github.io/til/20211010/" rel="alternate" type="text/html" title="2021년 10월 09일 TIL" /><published>2021-10-09T04:23:00+00:00</published><updated>2021-10-09T04:23:00+00:00</updated><id>https://kjh-sun.github.io/til/20211010</id><content type="html" xml:base="https://kjh-sun.github.io/til/20211010/">&lt;p&gt;https://d2.naver.com/helloworld/831311&lt;/p&gt;

&lt;p&gt;HashMap의 동작 원리에 대해서는 이 글을 참조하여 이해하였습니다.&lt;/p&gt;

&lt;p&gt;결국 핵심은, 용적을 리사이징할때 2^N으로 늘리는 것에 있었습니다.&lt;/p&gt;

&lt;p&gt;기존 크기가 4라고 가정하고, 커지는 크기가 8이라고 가정했을 때, 용적이 이렇게 2^N으로 늘어나면 배치될 수 있는 위치는 제자리이거나, 혹은 현재 자리에서 늘어난 용적만큼 이동한 인덱스의 위치입니다.&lt;/p&gt;

&lt;p&gt;이것을 이해하는데 조금 오랜 시간이 소요됐는데, 직접 나머지연산을 해보면 금방 그렇다는 사실을 알 수 있습니다.&lt;/p&gt;

&lt;p&gt;그렇다면 제자리에 배치되는 경우는 어떠한 경우이고, 현재 자리 + 늘어난 용적만큼 이동하는 경우는 어떤 경우인지에 대해서 고민해봐야하는데, 그것도 생각보다 간단하게 찾을 수 있었습니다.&lt;/p&gt;

&lt;p&gt;나머지만 보지않고 몫을 보면 됩니다.&lt;/p&gt;

&lt;p&gt;ex)&lt;/p&gt;

&lt;p&gt;hash value : 9&lt;/p&gt;

&lt;p&gt;기존 용적 : 4 -&amp;gt; 몫 2, index 1&lt;/p&gt;

&lt;p&gt;resize 후 용적 : 8 -&amp;gt; 몫 1 , index 1&lt;/p&gt;

&lt;p&gt;hash value : 5&lt;/p&gt;

&lt;p&gt;기존 용적 : 4 -&amp;gt; 몫 1, index 1&lt;/p&gt;

&lt;p&gt;resize 후 용적 : 8 -&amp;gt; 몫 0, index 5&lt;/p&gt;

&lt;p&gt;이것을 보면 기존 용적에서 몫이 홀수이면 인덱스가 증가하고, 짝수이면 그대로라는 사실을 알 수 있습니다.&lt;/p&gt;

&lt;p&gt;이것을 비트연산자로 표현하면, hash 값을 비트로 변환한 다음, 기존 용적에 해당하는 비트가 1이면 index를 증가시키고, 아니면 제자리에 두면 되는 것이다.&lt;/p&gt;

&lt;p&gt;세상엔 정말 천재가 많다는 것을 새삼 느꼈다. 이런 것들을 알게되면 정말 재밌고, 나도 언젠가 이런 로직을 고안할 수 있을까 하는 배움의 즐거움이 생기는 듯 하다!&lt;/p&gt;</content><author><name>김주현</name></author><category term="TIL" /><summary type="html">https://d2.naver.com/helloworld/831311</summary></entry><entry><title type="html">2021년 10월 08일 TIL</title><link href="https://kjh-sun.github.io/til/20211008/" rel="alternate" type="text/html" title="2021년 10월 08일 TIL" /><published>2021-10-08T04:23:00+00:00</published><updated>2021-10-08T04:23:00+00:00</updated><id>https://kjh-sun.github.io/til/20211008</id><content type="html" xml:base="https://kjh-sun.github.io/til/20211008/">&lt;p&gt;특화프로젝트 발표일&lt;/p&gt;

&lt;p&gt;발표는 무난하게 끝났다.&lt;/p&gt;

&lt;p&gt;어느정도 예상된 일이었지만, 이번엔 7주짜리 개발 프로젝트라고 해도 사실상 워킹데이가 4주도 되지 않았다.&lt;/p&gt;

&lt;p&gt;그렇다보니 발표하는 팀들 중에 배포에 실패한 팀들도 많았고, 목표했던 핵심 로직의 구현에 실패한 팀들도 많았다.&lt;/p&gt;

&lt;p&gt;우리는 그래도 나의 피와 땀과 눈물이 들어가서 최소한도의 발표용 기능정도는 구현이 된 것을 다행이라고 생각해야할까…&lt;/p&gt;

&lt;p&gt;오늘은 그동안 풀지 못했던 알고리즘 문제를 몇개 풀이하고 잠에 들까 한다.&lt;/p&gt;</content><author><name>김주현</name></author><category term="TIL" /><summary type="html">특화프로젝트 발표일</summary></entry><entry><title type="html">2021년 10월 07일 TIL</title><link href="https://kjh-sun.github.io/til/20211007/" rel="alternate" type="text/html" title="2021년 10월 07일 TIL" /><published>2021-10-07T04:23:00+00:00</published><updated>2021-10-07T04:23:00+00:00</updated><id>https://kjh-sun.github.io/til/20211007</id><content type="html" xml:base="https://kjh-sun.github.io/til/20211007/">&lt;p&gt;특화프로젝트 발표일 D-1&lt;/p&gt;

&lt;p&gt;최종적으로 목표했던 기능들의 구현에 성공했다. 개선해야할 점들이 많지만, 그럼에도 아무런 결과물도 완성하지 못하지 않을까 싶었던 이번 프로젝트도 어떻게든 해낸 점이 뿌듯한 것 같다. 취준 기간과 겹쳐서 프로젝트 내내 3~4시간을 간신히 잔 것 같은데, 고생이 보답받은 것 같아서 기쁘다.&lt;/p&gt;

&lt;p&gt;나의 경우에는, 이번 프로젝트에서 Kafka의 학습에 의의를 두고 진행한 프로젝트였다. LINE 기술블로그를 탐방하다가 우연히 마주친&lt;/p&gt;

&lt;p&gt;https://engineering.linecorp.com/ko/blog/line-shopping-platform-kafka-mongodb-kubernetes/&lt;/p&gt;

&lt;p&gt;글들을 보고 이번 프로젝트에서 이 기술을 한번 도입해보고, 학습해봐야겠다고 다짐했었다.&lt;/p&gt;

&lt;p&gt;원래도 Kafka에 대해서 익히 들어왔지만 도대체 그게 뭐길래 좋다고 하는거지? 정도의 생각만 갖고 있었고, 이게 주니어가 건드릴만한 영역의 기술일까? 아직 나는 자바도, SpringBoot에 대한 이해도 부족한 것 같은데 다른 것에 손을 벌리는 것이 맞는 행동일까라는 생각을 했지만 결국 이러한 것들도 도전이라고 생각했다. 나에게 있어서 도전은 삶의 가장 중요한 기치 중 하나이고, 이것이 나의 역량을 넘어서는 도전이어서 실패하더라도, 그 실패의 과정에서 얻는 것들이 또 귀한 경험이 되지 않을까 싶었다.&lt;/p&gt;

&lt;p&gt;플러스 알파로는, SSAFY에서 제공해주는 서버가 있을 때 시도해봐야하지 않겠냐는 생각도 머리 속에 있었다. 생각보다 Kafka를 돌리기 위해서 필요한 요구 스펙이 높았고, 최소 AWS 기준 Medium 사이즈의 EC2를 결제했어야할 것 같았는데, SSAFY 과정이 끝나고 나면 그것을 내 돈으로 결제하여 진행하기에는 조금 무리가 있지 않을까 싶었다.&lt;/p&gt;

&lt;p&gt;결론은 Kafka를 도입해보는 것을 시도하였고, 아무도 가르쳐주는 사람 없이(SSAFY에 Kafka에 대한 지식이 있는 사람이 전무했다) 공식 문서를 참조하고, 기술 블로그들을 참조하며 대략적인 아키텍처를 파악하고, 인프라를 구축하고&lt;/p&gt;

&lt;p&gt;(사실 이것에도 엄청나게 우여곡절이 많았다. 처음에는 Kafka 관련 인프라를 어떤 방식으로 구축해야할지 감이 오지 않았고, 어떻게 관리해야할지도 몰라서 Apache Kafka를 하나 생으로 다운받아 zookeeper랑 두개만 달랑 올려둔 채로 topic에 데이터를 넣었다 빼는 예제를 실행해보고, 그 다음에는, docker로 Kafka를 올리고, 그 다음에는 docker compose를 통해 Schema Registry와 기타 서버들을 묶어서 올렸다가, 나중에는 confluent platform을 통해 그것들을 일괄관리할 수 있다는 것을 알고 그간의 헤딩을 슬퍼하면서 confluent platform으로 수정했다.)&lt;/p&gt;

&lt;p&gt;그 와중에 HDFS에 Hive를 올려 데이터 처리를 해주겠다고 한 팀원이 그것을 실패했고, 못할 것 같다고 나에게 이야기를 해온 시점이 프로젝트 종료 2주전이었다. 내가 모든 API 서버와 Kafka 관련 업무를 맡고 있던 상황에서 청천벽력같은 이야기였고, (심지어 HDFS에 데이터를 적재할 방법도 마땅히 찾아주지 못한 상황이었다. )&lt;/p&gt;

&lt;p&gt;결국 그것도 내가 넘겨받아 Kafka Streams를 통해 데이터를 처리하고, Confluent의 HDFS 3 Sink Connector로 Kafka Connect를 통해 HDFS로 데이터를 적재했다.&lt;/p&gt;

&lt;p&gt;이것들을 처리한 시점이 일주일 전이다. 정말 밤잠을 쪼개고 밥을 걸러가며 처리했는데, 원래 이 시기에 LINE 블로그의 글처럼 K8S를 통해 배포해보고 싶었던 나에게는 정말 아까운 시간이었다.&lt;/p&gt;

&lt;p&gt;(다만 지금와서 생각해보면, frontend 쪽에서 next.js의 사용으로 인해 메모리 사용량을 꽤 많이 사용하고 있는 상황이어서, k8s로 올리는 것을 시도했으면 메모리가 터지지 않았을까 싶다. 현재도 꽤나 아슬아슬하게 메모리를 사용하고 있는 상황이어서, 우리의 상황으로는 그렇게까지 재현할 수는 없었을 듯 하다.)&lt;/p&gt;

&lt;p&gt;그래도 Kafka Streams를 사용해본 것은 굉장히 재미있는 경험이었다. 다만 Ksql을 제대로 이해하지 못한 채로 구현에 급급하여 예제만 확인하고 대충 이런 형식으로 굴러가는 것이겠구나, 정도로 작업한 것이라 코드가 너무 비효율적인 경향이 있다. 추후 더욱 공부가 필요할 듯 하다.&lt;/p&gt;</content><author><name>김주현</name></author><category term="TIL" /><summary type="html">특화프로젝트 발표일 D-1</summary></entry><entry><title type="html">2021년 10월 06일 TIL</title><link href="https://kjh-sun.github.io/til/20211006/" rel="alternate" type="text/html" title="2021년 10월 06일 TIL" /><published>2021-10-06T04:23:00+00:00</published><updated>2021-10-06T04:23:00+00:00</updated><id>https://kjh-sun.github.io/til/20211006</id><content type="html" xml:base="https://kjh-sun.github.io/til/20211006/">&lt;p&gt;특화프로젝트 발표일 D-2&lt;/p&gt;

&lt;p&gt;프론트쪽에서 추가적인 요구사항이 계속 나와서, 가장 빠르게 일을 쳐낼 수 있는 내가 가능하면 받아서 쳐내주고 있다. 굉장히 기묘하다. 분명 원하는 사항을 전부 다 맞춰서 돌려줬는데,&lt;/p&gt;

&lt;p&gt;이상하게 원하는 정보가 늘어난다 ㅎㅎ…어쩌면 이것이 주니어들이 모여서 진행하는 프로젝트의 숙명인가 싶다.&lt;/p&gt;

&lt;p&gt;테스트코드도 작성하고, 프로젝트 구조도 고민하며 프로젝트를 진행하고 싶은데 이번 특화 프로젝트는 중간에 연휴도 너무 길었고, 이래저래 취준으로 빠지는 사람들도 많아서&lt;/p&gt;

&lt;p&gt;도저히 많은 것들을 고민하며 프로젝트를 진행하기 어려운 프로젝트였다.&lt;/p&gt;

&lt;p&gt;나도 많은 것들을 진행하고 있지만, 가능하면 성실하게, 현재의 순간에 충실하며 살아가려고 노력한다. 나에게도 당장 앞선 면접 준비나 더 중요한 것이 있지만,&lt;/p&gt;

&lt;p&gt;적어도 싸피를 진행하는 동안(9시부터 6시)에는 프로젝트에 기여하며 프로젝트 팀원으로서의 책임감을 잊지 않으려고 노력한다.&lt;/p&gt;</content><author><name>김주현</name></author><category term="TIL" /><summary type="html">특화프로젝트 발표일 D-2</summary></entry></feed>