<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ju_yeong17.log</title>
        <link>https://velog.io/</link>
        <description>기록하는 개발자</description>
        <lastBuildDate>Tue, 06 Jan 2026 07:14:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ju_yeong17.log</title>
            <url>https://velog.velcdn.com/images/ju_yeong17/profile/6abd7e0f-772d-482d-842d-384e695a1512/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ju_yeong17.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ju_yeong17" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[모던 자바 인 액션] Chapter 12 새로운 날짜와 시간 API]]></title>
            <link>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-12-%EC%83%88%EB%A1%9C%EC%9A%B4-%EB%82%A0%EC%A7%9C%EC%99%80-%EC%8B%9C%EA%B0%84-API</link>
            <guid>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-12-%EC%83%88%EB%A1%9C%EC%9A%B4-%EB%82%A0%EC%A7%9C%EC%99%80-%EC%8B%9C%EA%B0%84-API</guid>
            <pubDate>Tue, 06 Jan 2026 07:14:56 GMT</pubDate>
            <description><![CDATA[<h2 id="121-localdate-localtime-duration-period-클래스"><strong>12.1 LocalDate, LocalTime, Duration, Period 클래스</strong></h2>
<h3 id="1211-localdate와-localtime"><strong>12.1.1 LocalDate와 LocalTime</strong></h3>
<ul>
<li>LocalDate 인스턴스는 시간을 제외한 날짜를 표현하는 불변 객체</li>
<li>LocalDate 객체는 어떤 시간대 정보도 포함하지 않음</li>
<li>팩토리 메소드 now()는 시스템 시계의 정보를 이용해 현재 날짜 정보를 얻음</li>
<li>LocalDate와 LocalTime은 게터 메서드를 제공</li>
</ul>
<pre><code class="language-java">LocalDate date = LocalDate.of(2017, 9, 21)
int year = date.getYear()
int month = date.getMonth()(
등등..
LocalTime time = LocalTime.of(13, 45, 20);// 13:45:20
time.getHour();// 13
time.getMinute();// 45
time.getSecond();// 20</code></pre>
<h3 id="1212-날짜와-시간-조합"><strong>12.1.2 날짜와 시간 조합</strong></h3>
<ul>
<li>LocalDateTime은 날짜와 시간을 모두 표현</li>
</ul>
<pre><code class="language-java">LocalDateTime dt1 = LocalDateTime.of(2017, Month.SEPTEMBER, 21, 13, 45, 20);
LocalDateTime dt2 = LocalDateTime.of(date, time);</code></pre>
<h3 id="1214-duration과-period-정의"><strong>12.1.4 Duration과 Period 정의</strong></h3>
<ul>
<li>Duration클래스는 between으로 두 시간 객체 사이의 지속시간을 만들 수 있음</li>
<li>Duration과 Period 클래스는 자신의 인스턴스를 만들 수 있도록 다양한 팩토리 메서드를 제공</li>
</ul>
<pre><code class="language-java">Duration threeMinutes = Duration.ofMinutes(3);
Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);

Period tenDays = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);</code></pre>
<h2 id="122-날짜-조정-파싱-포메팅"><strong>12.2 날짜 조정, 파싱, 포메팅</strong></h2>
<ul>
<li>withAttribute 메서드는 기존의 LocalDate를 바꾼 버전을 직접 간단하게 만들 수 있음</li>
<li>다음처럼 지정된 시간을 추가하거나 뺄 수 있음</li>
</ul>
<pre><code class="language-java">LocalDate date1 = LocalDate.of(2017, 9, 21);// 2017-09-21
LocalDate date2 = date1.withYear(2011);// 2011-09-21
LocalDate date3 = date2.withDayOfMonth(25);// 2011-09-25</code></pre>
<h3 id="1221-temporaladjusters-사용하기"><strong>12.2.1 TemporalAdjusters 사용하기</strong></h3>
<ul>
<li>날짜와 시간 API는 다양한 상황에서 사용할 수 있도록 TemporalAdjuster를 제공</li>
<li>필요한 기능이 없을 때는 쉽게 커스텀하여 구현</li>
</ul>
<pre><code class="language-java">LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY));
LocalDate date3 = date2.with(lastDayOfMonth());</code></pre>
<pre><code class="language-java">// 커스텀 구현
@FunctionalInterface
public interface TemporalAdjuster {
  Temporal adjustInto(Temporal temporal);
}</code></pre>
<h3 id="1222-날짜와-시간-객체-출력과-파싱"><strong>12.2.2 날짜와 시간 객체 출력과 파싱</strong></h3>
<ul>
<li>날짜와 시간 관련 작업에서 포매팅과 파싱은 필수</li>
<li>java.time.format 패키지가 이를 지원</li>
<li>가장 중요하게 알아야 할 클래스는 DateTimeFormatter</li>
<li>정적 팩토리 메서드와 상수를 이용해서 손쉽게 포매터를 만들 수 있음</li>
</ul>
<pre><code class="language-java">LocalDate date = LocalDate.of(2014, 3, 18);
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);// 20140318
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);// 2014-03-18</code></pre>
<pre><code class="language-java">DateTimeFormatter formatter = DateTimeFormatter.ofPatterm(&quot;dd/MM/yyyy&quot;);
LocalDate date = LocalDate.of(2014, 3, 18);
String formattedDate = date1.format(formatter);
LocalDate date2 = LocalDate.parse(formattedDate, formatter);</code></pre>
<ul>
<li>LocalDate의 format 메서드는 요청 형식의 패턴에 해당하는 문자열을 생성</li>
<li>그리고 정적 메서드 parse는 같은 포매터를 적용해서 생성된 문자열을 파싱함으로써 다시 날짜를 생성</li>
<li>DateTimeFormatterBuilder 클래스를 이용하면 원하는 포매터를 직접 만들 수 있음</li>
</ul>
<h2 id="123-다양한-시간대와-캘린더-활용-방법"><strong>12.3 다양한 시간대와 캘린더 활용 방법</strong></h2>
<h3 id="1231-시간대-사용하기"><strong>12.3.1 시간대 사용하기</strong></h3>
<ul>
<li>표준이 같은 지역을 묶어서 시간대(time zone) 규칙 집합을 정의</li>
<li>ZoneRules 클래스에는 약 40개 정도의 시간대가 있음</li>
<li>ZoneId의 getRules()를 이용해서 해당 시간대의 규정을 획득</li>
</ul>
<pre><code class="language-java">ZoneId romeZone = ZoneId.of(&quot;Europe/Rome&quot;);</code></pre>
<pre><code class="language-java">ZoneId zoneId = TimeZone.getDefault().toZoneId();</code></pre>
<ul>
<li>ZoneId는 LocalDate, LocalTime, LocalDateTime과 같이 ZonedDateTime 인스턴스로 변환할 수 있음</li>
</ul>
<pre><code class="language-java">LocalDate date = LocalDate.of(2014, 13, 18);
ZonedDateTime zdt = date.atStartOfDay(romeZone);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바 인 액션] Chapter 11 null 대신 Optional 클래스]]></title>
            <link>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-11-null-%EB%8C%80%EC%8B%A0-Optional-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-11-null-%EB%8C%80%EC%8B%A0-Optional-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Sun, 04 Jan 2026 00:52:30 GMT</pubDate>
            <description><![CDATA[<h2 id="111-값이-없는-상황을-어떻게-처리할까">11.1 <strong>값이 없는 상황을 어떻게 처리할까?</strong></h2>
<pre><code class="language-java">public String getCarInsuranceName(Person person) {
  return person.getCar().getInsurance().getName();
}</code></pre>
<ul>
<li>차를 소유하지 않은 사람도 많기 때문에 호출하게 되면 getInsurance는 null참조의 정보를 반환하려고 하고 런타임 에러가 발생될 것</li>
</ul>
<h3 id="1111-보수적인-자세로-nullpointerexception-줄이기">11.1.1 보수적인 자세로 NullPointerException 줄이기</h3>
<pre><code class="language-java">public String getCarInsuranceName(Person person) {
  if(person != null) {
    Car car = person.getCar();
    if(car != null) {
      Insurance insurance = car.getInsurance();
      if(insurance != null) {
        return insurance.getName();
      }
    }
  }
  return &quot;Unknown&quot;;
}</code></pre>
<ul>
<li>위 코드는 변수를 참조할때마다 null을 확인</li>
<li>따라서 중첩된 if가 추가되면서 코드 들여쓰기 수준이 증가</li>
<li>이와같은 반복 패턴 코드를 &#39;깊은 의심(deep doubt)&#39;라 부름</li>
<li>이를 반복하다보면 코드의 구조가 엉망이 되고 가독성도 떨어짐</li>
</ul>
<pre><code class="language-java">public String getCarInsuranceName(Person person) {
  if(person == null) {
    return &quot;Unknown&quot;;
  }
  Car car = person.getCar();
  if(car == null) {
    return &quot;Unknown&quot;;
  }
  Insurance insurance = car.getInsurance();
  if(insurance == null) {
    return &quot;Unknown&quot;;
  }
  return insurance.getName();
}</code></pre>
<ul>
<li>위 코드처럼 중첩 if 블록을 없앨 수 있지만 너무 많은 출구(return)이 생겼기 때문에 유지보수가 어려워지고 실수가 발생하기 쉬움</li>
</ul>
<h3 id="1112-null-때문에-발생하는-문제"><strong>11.1.2 null 때문에 발생하는 문제</strong></h3>
<ul>
<li>에러의 근원이다 : NullPointerException은 자바에서 가장 흔한 에러</li>
<li>코드를 어지럽힌다 : 중첩된 null 확인 코드로 가독성 저하</li>
<li>아무 의미가 없다 : null은 아무 의미도 표현하지 않음</li>
<li>자바 철학에 위배된다 : 자바는 개발자로부터 모든 포인터를 숨겼지만, null은 예외</li>
<li>형식 시스템에 구멍을 만든다 : null은 무형식이며 정보를 포함하고있지 않으므로 모든 참조 형식에 null을 할당할 수 있음 이렇게 할당된 null이 전파되면 애초에 null이 어떤 의미로 사용되었는지 알수 없음</li>
</ul>
<h3 id="1113-다른-언어는-null-대신-무얼-사용하나">11.1.3 다른 언어는 null 대신 무얼 사용하나?</h3>
<ul>
<li>그루비 같은 언어에서는 안전 내비게이션 연산자(?.)를 도입해서 null 문제를 해결</li>
</ul>
<p><code>def carInsuranceName = person?.car?.insurance?.name</code> </p>
<ul>
<li>null이 할당될 수 있는 값에 안전 네비게이션 연산자를 이용하면 null 참조 예외 걱정 없이 객체에 접근할 수 있음</li>
<li>이때 null인 참조가 있으면 결과로 null이 반환</li>
<li>하스켈, 스칼라 등의 함수형 언어는 다른 관점에서 null 문제에 접근</li>
<li>하스켈의 Maybe와 스칼라의 Optional은 주어진 형식의 값을 갖거나 아무 값도 갖지 않을 수 있는 구조를 가짐 따라서 null 참조 개념은 자연스럽게 사라짐</li>
</ul>
<h3 id="112-optional-클래스-소개"><strong>11.2 Optional 클래스 소개</strong></h3>
<ul>
<li>Optional은 선택형값을 캡슐화하는 클래스</li>
<li>값이 있으면 Optional 클래스는 값을 감싸고, 값이 없으면 Optional.empty 메서드로 Optional을 반환</li>
<li>null을 참조하면 NullPointerException이 발생하지만 Optional.empty()는 Optional 객체이므로 이를 다양한 방식으로 활용할 수 있음</li>
</ul>
<h2 id="113-optional-패턴-적용"><strong>11.3 Optional 패턴 적용</strong></h2>
<h3 id="1131-optional-객체-만들기"><strong>11.3.1 Optional 객체 만들기</strong></h3>
<p><strong>빈 Optional</strong></p>
<ul>
<li>Optional.empty로 빈 Optional 객체를 만들 수 있음</li>
</ul>
<p><strong>null이 아닌 값으로 Optional 만들기</strong></p>
<ul>
<li>정적 팩토리 메서드 Optional.of로 null이 아닌 값을 포함하는 Optional을 만들 수 있음</li>
</ul>
<p><code>Optional&lt;Car&gt; optCar = Optional.of(car);</code></p>
<ul>
<li>이제 car가 null이라면 즉시 NullPointerException이 발생</li>
</ul>
<p><strong>null값으로 Optional 만들기</strong></p>
<ul>
<li>정적 팩토리 메서드 Optional.ofNullable로 null 값을 저장할 수 있는 Optional을 만들 수 있음</li>
</ul>
<p><code>Optional&lt;Car&gt; optCar = Optional.ofNullable(car);</code></p>
<ul>
<li>이제 car가 null이면 빈 Optional 객체가 반환</li>
</ul>
<h3 id="1132-맵으로-optional의-값을-추출하고-변환하기">11.3.2 맵으로 Optional의 값을 추출하고 변환하기</h3>
<pre><code class="language-java">String name = null;
if(insurance != null) {
  name = insurance.getName();
}</code></pre>
<p>위의 코드를 다음과 같이 Optional로 사용할 수 있음</p>
<pre><code class="language-java">Optional&lt;Insurance&gt; optInsurance = Optional.ofNullable(insurance);
Optioanl&lt;String&gt; name = optInsurance.map(Insurance::getName);</code></pre>
<ul>
<li>Optional이 값을 포함하면 map의 인수로 제공된 함수가 값을 바꾸고 비어있으면 아무 일도 일어나지 않음</li>
</ul>
<h3 id="1133-flatmap으로-optional-객체-연결">11.3.3 flatMap으로 Optional 객체 연결</h3>
<pre><code class="language-java">public class Person {
  private Optional&lt;Car&gt; car;
  public Optional&lt;Car&gt; getCar() {
    return car;
  }
}</code></pre>
<ul>
<li>위 코드에서 optPerson의 형식은 Optional<Person>이므로 map 메서드를 호출 가능</li>
<li>하지만 getCar는 Optional<Car> 형식의 객체를 반환 즉 map 연산의 결과는 Optional&lt;Optional<Car>&gt; 형식의 객체가 되어 컴파일되지 않음</li>
<li>위 문제를 flatMap을 사용해 해결 flatMap은 인수로 받은 함수를 적용해서 생성된 각각의 스트림에서 콘텐츠만 평준화하여 남김</li>
</ul>
<p><strong>Optional로 자동차의 보험회사 이름 찾기</strong></p>
<pre><code class="language-java">public String getCarInsuranceName(Optional&lt;Person&gt; person) {
  return = person.flatMap(Person::getCar)
    .flatMap(car::getInsurance)
    .map(Insurance::getName)
    .orElse(&quot;Unknown&quot;);
}</code></pre>
<ul>
<li>null을 확인하느라 조건 분기문을 추가해서 코드를 복잡하게 만들지 않으면서도 쉽게 이해할 수 있는 코드를 완성</li>
<li>Optional을 인수로 받거나 Optioanl을 반환하는 메서드를 정의한다면 결과적으로 이 메서드가 빈 값을 받거나 빈 결과를 반환할 수 있음을 잘 문서화해서 제공하는 것과 같음</li>
</ul>
<h3 id="1134-optional-스트림-조작">11.3.4 Optional 스트림 조작</h3>
<pre><code class="language-java">public Set&lt;String&gt; getCarInsuranceNames(List&lt;Person&gt; persons) {
  return persons.stream()
    .map(Person::getCar) //Stream&lt;Optional&lt;Car&gt;&gt;
    .map(optCar -&gt; optCar.flatMap(Car::getInsurance)) //Stream&lt;Optional&lt;Insurance&gt;&gt;
    .map(optIns -&gt; OptIns.map(Insurance::getName)) //Stream&lt;Optional&lt;String&gt;&gt;
    .flatMap(Optional::stream) //Stream&lt;String&gt;
    .collect(toSet()); //Set&lt;String&gt;
}</code></pre>
<ul>
<li>스트림 요소를 조작하려면 변환, 필터 등의 일련의 긴 체인이 필요한데, Optional로 값이 감싸있으면 이 과정이 조금 더 복잡해짐</li>
</ul>
<h3 id="1135-디폴트-액션과-optional-언랩">11.3.5 디폴트 액션과 Optional 언랩</h3>
<p>Optional 클래스는 Optional 인스턴스에 포함된 값을 읽는 다양한 방법을 제공</p>
<ul>
<li>get()은 값을 읽는 가장 간단하면서 가장 안전하지 않은 메서드</li>
<li>래핑된 값이 있으면 해당 값을 반환하고 없으면 NoSuchElementException을 반환</li>
<li>orElse(T other) 메서드는 Optional이 값을 포함하지 않을 때 기본 값을 제공</li>
<li>orElseGet(Supplier&lt;? extends T&gt; other)은 orElse 메서드에 대응하는 게으른 버전의 메서드</li>
<li>Optional에 값이 없을때만 Supplier가 실행</li>
<li>디폴트 메서드를 만드는데 시간이 걸리거나 Optional이 비어있을 때만 기본값을 생성하고자 할 때 사용</li>
<li>orElseThrow(Supplier&lt;? extends X&gt; exceptionSupplier)는 Optional이 비어있을때 지정한 예외를 발생</li>
<li>ifPresent(Consumer&lt;? super T&gt; consumer)는 값이 존재할 때 인수로 넘겨준 동작을 실행</li>
<li>ifPresentOrElse(Consumer&lt;? super T&gt; action, Runnable emptyAction)은 Optional이 비어있을 때만 실행할 수 있는 Runnable을 인수로 받음</li>
</ul>
<h3 id="1136-두-optional-합치기">11.3.6 두 Optional 합치기</h3>
<pre><code class="language-java">public Insurance findCheapestInsurance(Person person, Car car) {
  return cheapestCompany;
}

public Optional&lt;Insurance&gt; nullSafeFindCheapestInsurance(
    Optional&lt;Person&gt; person, Optional&lt;Car&gt; car) {
  if (person.isPresent() &amp;&amp; car.isPresent()) {
    return Optional.of(findCheapestInsurance(person.get(), car.get()));
  } else {
    return Optional.empty();
  }
}</code></pre>
<ul>
<li>두 Optional을 인수로 받아서 둘 중 하나라도 비어있으면 빈 Optional<Insurance>를 반환</li>
<li>person과 car의 시그니처만으로 둘 다 아무 값도 반환하지 않을 수 있다는 정보를 명시적을 주지만, 구현 코드는 null 확인 코드와 크게 다르지 않음</li>
</ul>
<h3 id="1137-필터로-특정값-거르기">11.3.7 필터로 특정값 거르기</h3>
<pre><code class="language-java">Insurance insurance = ...;
if(insurance != null &amp;&amp; &quot;CambridgeInsurance&quot;.equals(insurance.getName())){
  System.out.println(&quot;ok&quot;);
}</code></pre>
<ul>
<li>위 코드처럼 객체의 메서드를 호출해서 프로퍼티를 확인해야 하는 경우 다음과 같이 재구현할 수 있음</li>
</ul>
<pre><code class="language-java">Optional&lt;Insurance&gt; optInsurance = ...;
optInsurance.filter(insurance -&gt; &quot;CambridgeInsurance&quot;.equals(insurance.getName()))
  .ifPresent(x -&gt; System.out.println(&quot;ok&quot;));</code></pre>
<h2 id="114-optional을-사용한-실용-예제">11.4 Optional을 사용한 실용 예제</h2>
<h3 id="1141-잠재적으로-null이-될-수-있는-대상을-optional로-감싸기">11.4.1 잠재적으로 null이 될 수 있는 대상을 Optional로 감싸기</h3>
<ul>
<li>Object value=map.get(&quot;key&quot;);<ul>
<li>null이 반환될 수 있는 코드를 Optional로 감싸서 개선</li>
</ul>
</li>
</ul>
<p><code>Optional&lt;Object&gt; value=Optional.ofNullable(map.get(&quot;key&quot;));</code></p>
<h3 id="1142-예외와-optional-클래스">11.4.2 예외와 Optional 클래스</h3>
<p>null을 확인할 때는 if문을 사용했지만 예외를 발생하시키는 메서드는 try/catch 블록을 사용</p>
<pre><code class="language-java">public static Optional&lt;Integer&gt; stringToInt(String s) {
  try {
    return Optional.of(Integer.parseInt(s));
  } catch (NumberFormatException e) {
    return Optional.empty();
  }
}</code></pre>
<ul>
<li>정수로 변환할 수 없는 문자열은 빈 Optional로 return하도록 구현</li>
</ul>
<h3 id="1143-기본형-optional을-사용하지-말아야-하는-이유">11.4.3 기본형 Optional을 사용하지 말아야 하는 이유</h3>
<ul>
<li>Optional도 기본형으로 특화된 OptionalInt, OptionalLong, OptionalDouble 등의 클래스를 제공</li>
<li>하지만 Optional의 최대 요소 수는 한개이므로 기본형 특화 Optional로 성능을 향상시킬 수 없음</li>
<li>또한 기본형 특화 Optional은 map, flatMap, filter등을 제공하지 않고, 생성한 결과를 다른 일반 Optional과 혼용할 수 없음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바 인 액션]Chapter 8 컬렉션 API 개선]]></title>
            <link>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98Chapter-8-%EC%BB%AC%EB%A0%89%EC%85%98-API-%EA%B0%9C%EC%84%A0</link>
            <guid>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98Chapter-8-%EC%BB%AC%EB%A0%89%EC%85%98-API-%EA%B0%9C%EC%84%A0</guid>
            <pubDate>Mon, 29 Dec 2025 13:54:33 GMT</pubDate>
            <description><![CDATA[<h2 id="81-컬렉션-팩토리">8.1 컬렉션 팩토리</h2>
<p>적은 요소 리스트 넣기</p>
<ul>
<li>List 만들어서 add 근데 새 문자열 저장하는데 많은 코드 필요</li>
<li>Arrays.asList() 팩토리 메서드 사용 하면 줄일 수 있음</li>
</ul>
<p>→ 고정 크기 리스트 요소 갱신 가능 , 새요소 추가 요소 삭제 불가</p>
<p>→ UnsupportedOperationException 예외 발생</p>
<ul>
<li>HashSet 사용 가능</li>
</ul>
<h3 id="811-리스트-팩토리">8.1.1 리스트 팩토리</h3>
<ul>
<li>List.of 팩토리 메소드를 이용하면 간단하게 리스트 만들 수 있음</li>
<li>요소 추가 불가능, 예외 발생</li>
<li>컬렉션이 의도치 않게 변하는 것을 막을 수 있음<ul>
<li>요소 자체가 변하는 것을 막을 수 있는 방법은 없음</li>
</ul>
</li>
<li>Collectors.toList() 컬렉터로 스트림을 리스트로 변환 가능<ul>
<li>데이터 처리 형식을 설정하거나 데이터를 변환할 필요가 없ㄷ아면 사용하기 간편한 팩토리 메서드를 이용할 것을 권장</li>
</ul>
</li>
</ul>
<h3 id="812-집합-팩토리">8.1.2 집합 팩토리</h3>
<p>Set<String> friends = Set.of(”~);</p>
<ul>
<li>바꿀 수 없는 집합</li>
</ul>
<h3 id="813--맵-팩토리">8.1.3  맵 팩토리</h3>
<ul>
<li>키와 값이 필요</li>
<li>Map.of 팩토리 메서드에 키와 값을 번갈아 제공하는 방법으로 맵을 만들 수 있음</li>
</ul>
<p><code>Map&lt;String,Integer&gt; ageOfFriends = Map.of(”Raphael”,30,”Olivia”,25);</code></p>
<ul>
<li>열개 이하의 키와 값 쌍을 가진 작읍 맵을 만들 때 유용</li>
<li>그 이상 Map.Entry&lt;K,V&gt; 객체를 인수로 받으며 가변 인수로 구현된 Map.ofEntries 팩토리 메서드를 이용하는 것이 좋음</li>
</ul>
<h2 id="82-리스트와-집합-처리">8.2 리스트와 집합 처리</h2>
<p>List,Set 인터페이스에 다음과 같은 메서드 추가</p>
<ul>
<li>removeIf<ul>
<li>프레디케이트를 만족하는 요소 제거</li>
</ul>
</li>
<li>replaceAll<ul>
<li>리스트에서 이용할 수 있는 기능으로 UnaryOperator 함수를 이용해 요소를 바꿈</li>
</ul>
</li>
<li>sort<ul>
<li>리스트 정렬</li>
</ul>
</li>
</ul>
<h3 id="821-removeif-메서드">8.2.1 removeIf 메서드</h3>
<ul>
<li>Iteraotr 객체, next(),hasNext()를 이용해 소스를 질의</li>
<li>Collection 객체 자체, remove()를 호출해 요소를 삭제</li>
</ul>
<p>반복자의 상태는 컬렉션의 상태와 서로 동기화 되지 않음</p>
<ul>
<li>Iterator 객체를 명시적으로 사용하고 그 객체의 remove() 메서드를 호출함으로써 문제 해결</li>
</ul>
<p>removeIf 메서드는 삭제할 요소를 가리키는 프레디 케이를 인수로 받기 때문에 위를 바꾸면 단순하게 해결가능</p>
<p>→ 삭제가 아니라 바꿔야하는 상황이 있는데 이때는 replaceAll 제공</p>
<h3 id="822-replaceall-메서드">8.2.2 replaceAll 메서드</h3>
<ul>
<li>List에 replaceAll 이용하며 리스트의 각 요소를 새로운 요소로 변경</li>
<li>일반적인 스트림</li>
</ul>
<pre><code class="language-java">referenceCodes.stream()
            .map(Strings::toUpperCase))
            .collect(toList());</code></pre>
<ul>
<li>이렇게 사용하면 새로운 컬렉션이 생성되고 기존 컬렉션 변경 X</li>
</ul>
<pre><code class="language-java">for (ListIterator&lt;String&gt; iterator = referenceCodes.listIterator(); iterator.hasNext();) {
iterator.hasNext();
String code = iterator.next();
iterator.set(code.toUpperCase());
}</code></pre>
<ul>
<li>Iterator와 혼용하여 사용하면 문제 일으킴</li>
</ul>
<pre><code class="language-java">referenceCodes.replaceAll(code -&gt; code.toUpperCase());</code></pre>
<p>이렇게 사용 가능</p>
<h2 id="83-맵처리">8.3 맵처리</h2>
<h3 id="831-foreach-메서드">8.3.1 forEach 메서드</h3>
<p>맵에서 Key, Value을 반복하며 확인하려면 Map.Entry&lt;K, V&gt;의 반복자를 활용</p>
<p><code>ageOfFriends.forEach((friend, age) -&gt; print(friend + &quot;is&quot; + age)))</code></p>
<h3 id="832-정렬-메서드">8.3.2 정렬 메서드</h3>
<ul>
<li>아래 두개 맵의 항목을 값을 또는 키를 기준으로 정렬가능</li>
<li>Entry.comparingByValue</li>
<li>Entry.comparingByKey</li>
</ul>
<h3 id="833-getordefault-메서드">8.3.3 getOrDefault 메서드</h3>
<p>기존에는 존재하지 않는 키로 값을 찾으려할때 NPE가 발생할 가능성이 있어서 Null 체크가 필요</p>
<ul>
<li>computeIfAbsent : 제공된 키에 해당하는 값이 존재하지 않으면(값이 없거나 Null) 제공된 키로 새로운 Value를 맵에 추가</li>
<li>computeIfPresent : 제공된 키가 존재하면 새로운 Value로 맵에 추가</li>
<li>compute : 제공된 키로 새 Value를 추가</li>
</ul>
<h3 id="834-계산-패턴">8.3.4 계산 패턴</h3>
<p>맵에 키가 존재하는지 여부에 따라 어떤 동작을 실행하고 결과를 저장해야하는 상황이 필요한 때가 있음</p>
<h3 id="835-삭제-패턴">8.3.5 삭제 패턴</h3>
<p>remove(key, value);</p>
<h3 id="836-교체-패턴">8.3.6 교체 패턴</h3>
<ul>
<li>맵의 항목을 변경하는데 사용할 수 있는 두 개의 메서드가 추가</li>
<li>replaceAll : BiFunction을 적용한 결과로 각 항목의 값을 교체 이전에 알려준 List.replaceAll과 비슷한 기능</li>
<li>replace : 키가 존재하면 맵의 값을 바꿈 키가 특정 값으로 매핑되었을때만 변경하도록 하는 오버로드된 버전도 있음</li>
</ul>
<h2 id="84-개선된-concurrenthashmap">8.4 개선된 ConcurrentHashMap</h2>
<h3 id="841-리듀스와-검색">8.4.1 리듀스와 검색</h3>
<ul>
<li>ConcurrentHashMap은 스트림에서 봤던것과 비슷한 종류의 세 가지 연산을 지원</li>
<li>forEach : 각 (key, value) 쌍에 주어진 액션을 실행</li>
<li>reduce : 모든 (key, value) 쌍을 제공된 리듀스 함수를 이용해 결과로 합침</li>
<li>search : 널이 아닌 값을 반환할 때 까지 각 (key, value)쌍에 함수를 적용</li>
<li>다음처럼 키에 함수 받기, 값, Map.Entry, (key, value)인수를 이용한 네 가지 연산 형태를 지원</li>
<li>키 값으로 연산 (forEach, reduce, search)</li>
<li>키로 연산(forEachKey, reduceKeys, searchKeys)</li>
<li>값으로 연산(forEachValue, reduceValues, searchValues)</li>
<li>Map.Entry 객체로 연산(forEachEntry, reduceEntries, searchEntries)</li>
</ul>
<h3 id="842-개수">8.4.2 개수</h3>
<ul>
<li>ConcurrentHashMap은 맵의 매핑 개수를 확인하는 mappingCount 메서드를 제공</li>
</ul>
<h3 id="843-집합뷰">8.4.3 집합뷰</h3>
<ul>
<li>ConcurrentHashMap 클래스는 ConcurrentHashMap을 집합 뷰로 반환하는 keySet이라는 새 메서드를 제공</li>
<li>맵을 바꾸면 집합도 바뀌고 반대로 집합을 바꾸면 맵도 영향 받음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바 인 액션] Chapter 7 병렬 데이터 처리와 성능]]></title>
            <link>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-7-%EB%B3%91%EB%A0%AC-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC%EC%99%80-%EC%84%B1%EB%8A%A5</link>
            <guid>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-7-%EB%B3%91%EB%A0%AC-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC%EC%99%80-%EC%84%B1%EB%8A%A5</guid>
            <pubDate>Wed, 24 Dec 2025 15:05:36 GMT</pubDate>
            <description><![CDATA[<p>자바 7</p>
<ul>
<li>더 쉽게 병렬화를 수행하면서 에러를 최소화할 수 있도록 포크 / 조인 프레임 워크 기능 제공</li>
</ul>
<h3 id="71-병렬-스트림">7.1 병렬 스트림</h3>
<p>병렬스트림</p>
<ul>
<li>컬렉션 parallelStream을 호출하면 하면 생성</li>
<li>각가의 스레드에서 처리할 수 있도록 스트림 요소를 여러 청크로 분할한 스트림</li>
<li>병렬 스트림 이용하면 모든 멀티코어 프로세서가 각각의 청크를 처리하도록 할당 할 수 있음</li>
</ul>
<p>ex)</p>
<ul>
<li>숫자 n을 인수로 받아서 1부터 n까지의 모든 숫자의 합계를 반환하는 메서드 구현한다고 가정</li>
<li>숫자로 이루어진 무한 스트림 만든 후 인수로 주어진 크기를 스트림을 제한, 두 숫자를 더하는 BinaryOperator로 리듀싱 작업</li>
</ul>
<pre><code class="language-java">public long sequentialSum(long n){
    return Stream.iterate(1L, i -&gt; i+1).limit(n).reduce(0L,Long::sum);
}</code></pre>
<h3 id="711-순차-스트림을-병렬-스트림으로-변환하기">7.1.1 순차 스트림을 병렬 스트림으로 변환하기</h3>
<ul>
<li>순차 스트림에 parallel 메서드를 호출하면 기존의 함수형 리듀싱 연산이 병렬로 처리</li>
</ul>
<pre><code class="language-java">public long parallelSum(long n){
    return Stream.iterate(1L, i -&gt; i+1).limit(n).parallel().reduce(0L,Long::sum);}</code></pre>
<ul>
<li>스트림이 여러 청크에 병렬로 수행</li>
</ul>
<h3 id="713-병렬-스트림의-올바른-사용법">7.1.3 병렬 스트림의 올바른 사용법</h3>
<ul>
<li>잘못 사용하면 발생하는 많은 문제는 공유된 상태를 바꾸는 알고리즘을 사용하기 때문에 일어남</li>
</ul>
<pre><code class="language-java">public long sideEffectSum(long n) {
    Accumulator accumulator = new Accumulator();
    LongStream.rangeClosed(1, n).forEach(accumulator::add);
    return accumulator.total;
}

public class Accumulator{
    public long total = 0;
    public void add(long value) {total += value;}
}
</code></pre>
<ul>
<li>본질적으로 순차 실행할 수 있도록 구현되어있어 병렬로 실행하면 참사</li>
<li>total을 접근할 때마다 데이터 레이스 문제 일어남</li>
<li>동기화로 문제 를 해결하다보면 결국 병렬화라는 특성이 없어져 버릴 것</li>
</ul>
<h3 id="714-병렬-스트림-효과적으로-사용하기">7.1.4 병렬 스트림 효과적으로 사용하기</h3>
<ul>
<li>확신이 서지 않으면 직접 측정하라<ul>
<li>무조건 병렬 스트림으로 바꾸는 것이 능ㅅ아는 아니다</li>
<li>언제나 병렬 스트림이 빠른 것이 아니기 때문</li>
</ul>
</li>
<li>박싱 주의<ul>
<li>자동 박싱과 언박싱은 성능을 크게 저하시킬 수 있는 요소</li>
<li>자바 8은 박싱 동작 피할 수 있도록 기본형 특화스트림 제공</li>
</ul>
</li>
<li>순차 스트림보다 병렬 스트림에서 성능이 떨어지는 연산이 있음<ul>
<li>limit, findFirst처럼 요소의 순서에 의존하는 연산을 병렬 스트림에서 수행하면 비싼 비용</li>
</ul>
</li>
<li>스트림에서 수행하는 전체 파이프라인 연산 비용 고려</li>
<li>소량의 데이터에서는 병렬 스트림 도움되지 않음</li>
<li>스트림을 구성하는 자료구조가 적절한지 확인</li>
<li>스트림의 특성과 파이프라인의 중간 연산이 스트림의 특성을 어떻게 바꾸는지에 따라 분해 과정의 성능이 달라질 수 있음</li>
<li>최종 연산의 병합과정 비용을 살펴봐라</li>
</ul>
<h2 id="72-포크조인-프레임-워크">7.2 포크/조인 프레임 워크</h2>
<ul>
<li>병렬화할 수 있는 작업을 재귀적으로 작은 작업으로 분할한 다음에 서브태스크 각각의 결과를 합쳐서 전체 결과를 만들도록 설계</li>
<li>서브태스트를 스레드 풀의 작업자 스레드에 분산 할당하는 ExecutorService 인터페이스 구현</li>
</ul>
<h3 id="721-recursivetask-활용">7.2.1 RecursiveTask 활용</h3>
<ul>
<li>스레드 풀을 이용하려면 RecursiveTask<R> 의 서브 클래스를 만들어야함</li>
<li>R은 병렬화 된 태스트가 생성하는 결과 형식 또는 결과가 없을때 형식은 RecursiveAction 형식</li>
<li>RecursiveTask를 정의하려면 추상 메서드 compute를 구현</li>
</ul>
<pre><code class="language-java">protected abstract R compute();</code></pre>
<p>// 의사코드
if(태스크가 충분히 작거나 더 이상 분할할 수 없으면) {
    순차적으로 태스크 계산
} else {
    태스크를 두 서브태스크로 분할
    태스크가 다시 서브태스크로 분할되도록 이 메서드를 재귀적으로 호출
    모든 서브태스크의 연산위 완료될 때까지 기다림
    각 서브태스크의 결과를 합침
}</p>
<h3 id="722-포크조인-프레임-워크를-제대로-사용하는-방법">7.2.2 포크/조인 프레임 워크를 제대로 사용하는 방법</h3>
<p>효과적인 방법</p>
<ul>
<li>join 메서드를 태스크에 호출하면 태스크가 생산되는 결과가 준비 될때까지 호출자를 블록시킴</li>
<li>두 서브태스크가 모두 시작된 다음에 Join 을 호출</li>
<li>그렇지 않으면 각각의 서브태스크가 다른 태스크가 끝나길 기다리는 일이 발생하며 원래 순차 알고리즘 보다 느리고 복잡한 프로그램이 될 수 있음</li>
<li>RecursiveTask내에서는 ForkJoinPool의 invoke 메서드를 사용하지 말아야함<ul>
<li>대신 compute나 fork 메서드 직접 호출</li>
</ul>
</li>
<li>fork  메서드를 호출해서 ForkJoinPool의 일정을 조절 가능</li>
</ul>
<h3 id="723-작업-훔치기">7.2.3 작업 훔치기</h3>
<p>작업을 훔치지 마세요.</p>
<p>작업 훔치기 기법</p>
<ul>
<li>ForkJoinPool의 모든 스레드를 거의 공정하게 분할</li>
<li>각각의 스레드는 자신에게 할당된 태스크를 포함하는 이중 연결 리스트를 참조하면서 작업이 끝날 때보다 자신에게 할당된 태스크를 더 빨리 처리 가능</li>
</ul>
<p>풀에 있는 작업자 스레드의 태스크를 재분배하고 균형을 맞출 때 작업 훔지기 알고리즘 사용</p>
<h2 id="73-spliterator-인터페이스">7.3 Spliterator 인터페이스</h2>
<p>Spliterator 라는 새로운 인터페이스 사용</p>
<p>Spliterator</p>
<ul>
<li>분할할 수 있는 반복자</li>
<li>소스의 요소 탐색 기능을 제공</li>
<li>병렬 작업에 특화</li>
</ul>
<pre><code class="language-java">public interface Spliteratro&lt;T&gt; {
    boolean tryAdvance(Consumer&lt;? super T&gt; action);  
    Spliterator&lt;T&gt; trySplit();  
    long estimateSize();  
    int characteristics(); 
}</code></pre>
<ul>
<li>T는 Spliterator에서 탐색하는 요소의 형식을 가르킴</li>
</ul>
<h3 id="731-분할-과정">7.3.1 분할 과정</h3>
<ol>
<li>첫번째 Spliterator 에 trySplit을 호출하면 두번째 Spliterator가 생성</li>
<li>두 개의 Spliterator에 trySplit를 다시 호출하면 네개의 Spliterator가 생성</li>
<li>trySplit의 결과가 Null이 될때까지 이과정 반복</li>
<li>Spliterator에 호출한 모든 trySplit의 결과가 null이면 재귀 분할 과정 종료</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바 인 액션] Chapter 6 스트림으로 데이터 수집]]></title>
            <link>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-6-%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9C%BC%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91</link>
            <guid>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-6-%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9C%BC%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91</guid>
            <pubDate>Mon, 22 Dec 2025 01:13:51 GMT</pubDate>
            <description><![CDATA[<p>중간 연산</p>
<ul>
<li>스트림의 요소를 소비 하지 않음</li>
</ul>
<p>최종 연산</p>
<ul>
<li>스트림의 요소를 소비함</li>
</ul>
<p>컬렉션, 컬렉터, collect이 헷갈리지 않게 주의하기</p>
<p>collect</p>
<ul>
<li>스트림의 최종 연산 메서드 중 하나</li>
</ul>
<p>collector</p>
<ul>
<li>Collect에서 필요한 메서드를 정의해놓은 인터페이스</li>
</ul>
<p>collectors</p>
<ul>
<li>Collector를 구현한 클래스들을 제공</li>
</ul>
<p>Collectors.toList()</p>
<ul>
<li>Collector를 반환</li>
<li>collect(Collectors.toList()) 하면 collect 메서드에 collector 인터페이스가 들어가는 것</li>
</ul>
<p>Collection</p>
<ul>
<li>Collection Framework에서 최상위 인터페이스</li>
</ul>
<p>Collection은 Iterable을 상속받는 인터페이스</p>
<p>Collections는 Object를 상속받는 클래스</p>
<p>헷갈릴만두;;;; </p>
<h2 id="61-컬렉터란-무엇인가">6.1 컬렉터란 무엇인가?</h2>
<p>함수형 프로그래밍</p>
<ul>
<li>무엇을 원하는지 직접 명시할 수 있어서 어떤 방법으로 이를 얻을지는 신경 쓸 필요 없음</li>
</ul>
<h3 id="611-고급-리듀싱-기능을-수행하는-컬렉터">6.1.1 고급 리듀싱 기능을 수행하는 컬렉터</h3>
<p>함수형 API 장점</p>
<ul>
<li>높은 수준의 조합성과 재사용성</li>
</ul>
<p>컬렉터 장점</p>
<ul>
<li>collect로 결과를 수집하는 과정을 간단하면서도 유연한 방식으로 정의</li>
<li>스트림에 collect를 호출하면 스트림의 요소에 리듀싱 연산이 수행</li>
</ul>
<p>collect</p>
<ul>
<li>리듀싱 연산을 이용해 스트림의 각 요소를 방문하면서 컬렉터가 작업을 처리</li>
</ul>
<h3 id="612-미리-정의된-컬렉터">6.1.2 미리 정의된 컬렉터</h3>
<p>Collectors에서 제공하는 메서드의 기능</p>
<ul>
<li>스트림 요소를 하나의 값으로 리듀스하고 요약</li>
<li>요소 그룹화</li>
<li>요소 분할</li>
</ul>
<h2 id="62-리듀싱과-요약">6.2 리듀싱과 요약</h2>
<p>컬렉터</p>
<ul>
<li>스트림의 모든 항목을 하나의 결과로 합칠 수 있음</li>
</ul>
<p>ex 1) counting() 이라는 팩토리 메서드가 반환하는 컬렉터로 메뉴에서 요리 수를 계산</p>
<pre><code class="language-java">long howManyDishes = menu.stream().collect(Collectors.counting());</code></pre>
<h3 id="621-스트림값에서-최댓값과-최솟값-검색">6.2.1 스트림값에서 최댓값과 최솟값 검색</h3>
<p>메뉴에서 칼로리 가장 높은 요리 찾기</p>
<ul>
<li>Collectors.maxBy, Collectors.minBy 두개 메서드를 이용해 계산 가능</li>
<li>두 컬렉터 스트림의 요소를 비교하는데 사용할 Comparator를 인수로 받음</li>
</ul>
<pre><code class="language-java">Comparator&lt;Dish&gt; dishCaloriesComparator = 
    Comparator.comparingInt(Dish::getCalories);
Optional&lt;Dish&gt; mostCalorieDish = menu.stream().collect(maxBy(dishCaloriesComparator));</code></pre>
<ul>
<li>스트림에 있는 객체의 숫자 필드의 합계나 평균 등을 반환하는 연산에도 리듀싱 기능이 자주 사용</li>
</ul>
<p>→ 요약 연산</p>
<h3 id="622-요약-연산">6.2.2 요약 연산</h3>
<p>Collectors 클래스</p>
<ul>
<li>Collectors.summingInt 라는 특별한 요약 팩토리 메서드 제공</li>
<li>summingInt<ul>
<li>객체를 int로 매핑하는 함수를 인수로 받음</li>
<li>인수로 전달된 함수는 객체를 int로 매핑한 컬렉터를 반환</li>
<li>collect 메서드로 전달되면 요약 작업을 수행</li>
</ul>
</li>
</ul>
<pre><code class="language-java">int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));</code></pre>
<ul>
<li>Collectors.summingLong, Collectors.summingDouble 메서드는 같은 방식으로 동작</li>
<li>종종 이들 중 두 개 이상의 연산을 한번에 수행할때 가 있음</li>
<li>이때는 팩토리 메서드 summarizingInt가 반환하는 컬렉터 사용 가능</li>
</ul>
<h3 id="623-문자열-연결">6.2.3 문자열 연결</h3>
<p>컬렉터 joining 팩토리 메서드</p>
<ul>
<li>스트림의 각 객체에 toString 메서드를 호출해서 추출한 모든 문자열을 하나의 문자열로 연결해서 반환</li>
</ul>
<p>메뉴의 모든 요리명을 연결</p>
<pre><code class="language-java">String shortMenu = menu.stream().map(Dish::getName).collect(joining());</code></pre>
<p>joining 메서드</p>
<ul>
<li>내부적으로 StringBuilder를 이용해 문자열을 하나로 만듦</li>
<li>Dish 클래스가 요리명을 반환하는 toString 메서드를 포함하고 있다면</li>
<li>String shortMenu = menu.stream().collect(joining()); 으로 생략 가능</li>
</ul>
<p>하지만 결과 문자열 해석 불가 </p>
<ul>
<li>두 요소 사이에 구분 문자열 연결 가능<ul>
<li>오버로드된 joining 팩토리 메서드</li>
</ul>
</li>
<li>String shortMenu = menu.stream().map(Dish::getName).collect(joining(”,”));</li>
</ul>
<h3 id="624-범용-리듀싱-요약-연산">6.2.4 범용 리듀싱 요약 연산</h3>
<p>모든 컬렉터 reducing 팩토리 메서드로 정의 가능</p>
<p>이거 대신 특화된 컬렉터 사용이유</p>
<ul>
<li>프로그래밍적 편의성 때문</li>
</ul>
<p>ex) reducing 메서드로 만들어진 컬렉터로도 모든 메뉴의 칼로리 합계 계산 가능</p>
<pre><code class="language-java">int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories,(i,j) -&gt;(i+j));</code></pre>
<p>reducing 인수를 세개 받음</p>
<ul>
<li>첫번째 인수 : 리듀싱 연산의 시작값 이거나 인수가 없을때 반환 값</li>
<li>두번째 인수 : 6.2.2절에서 요리를 칼로리 정수로 변환할 때 사용한 변환 함수</li>
<li>세번째 인수: 같은 종류의 두 항목을 하나의 값으로 더하는 BinaryOperator</li>
</ul>
<p><strong>컬렉션 프레임 워크 유연성: 같은 연산도 다양한 방식으로 수행 가능</strong></p>
<ul>
<li>reducing 컬렉터 사용하면 람다 표현식 대신 Integer 클래스의 sum 메서드 참조를 이용하면 코드 단순화 가능</li>
</ul>
<pre><code class="language-java">int totalCalories = menu.stream().collect(reducing(0,Dish::getCalories,Integer::sum));</code></pre>
<p>자신의 상황에 맞는 최적의 해법 선택</p>
<ul>
<li>하나의 연산을 다양한 방법으로 해결할 수 있음을 보여줌</li>
<li>스트림 인터페이스에서 직접 제공하는 메서드를 이용하는 것에 비해 컬렉터를 이용하는 코드가 더 복잡</li>
<li>복잡한 대신 재사용성과 커스터마이즈 가능성 제공</li>
<li>가장 일반적으로 문제에 특화된 해결책을 고르는 것이 바람직</li>
</ul>
<h2 id="63-그룹화">6.3 그룹화</h2>
<p>그룹화</p>
<ul>
<li>데이터 베이스에서 많이 수행되는 작업</li>
<li>까다롭고 할일이 많고 에러 많이 발생</li>
</ul>
<p>→ 자바 8의 함수형을 이용하면 가독성 있는 한 줄의 코드로 그룹화 구현 가능</p>
<p>ex) 메뉴를 그룹화 한다고 가정</p>
<p>고기를 포함하는 그룹, 생선을 포함하는 그룹, 나머지 그룹 </p>
<p>Collectors.groupingBy를 이용해 쉽게 메뉴 그룹화</p>
<pre><code class="language-java">Map&lt;Dish.Type, List&lt;Dish&gt;&gt; dishesByType = menu.stream().collect(groupingBy(Dish::getType));</code></pre>
<ul>
<li>분류 함수</li>
</ul>
<h3 id="631-그룹화된-요소-조작">6.3.1 그룹화된 요소 조작</h3>
<p>요소를 그룹화 한 다음에 결과 그룹의 요소를 조작하는 연산 필요</p>
<p>ex) 500칼로리 넘는 요리만 필터 가정</p>
<pre><code class="language-java">Map&lt;Dish.Type, List&lt;Dish&gt;&gt; caloricDishesByType = menu.stream()
.filter(dish -&gt; dish.getCalories() &gt; 500)
.collect(groupingBy(Dish::getType));</code></pre>
<p>위 처럼 해결할 수 있지만 FISH가 사라진다는 단점</p>
<pre><code class="language-java">Map&lt;Dish.Type, List&lt;Dish&gt;&gt; caloricDishesByType = menu.stream()
.collect(groupingBy(Dish::getType,filtering(dish -&gt; dish.getCalories() &gt; 500, toList())));</code></pre>
<ul>
<li>Collector 안으로 필터 프레디케이트를 이동함으로 문제 해결</li>
<li>filtering메서드는 Collectors 클래스의 또 다른 정적 팩토리 메서드로 프레디케이트를 인수로 받음</li>
<li>이 프레디케이트로 각 그룹의 요소와 필터링 된 요소를 재그룹화</li>
</ul>
<h3 id="632-다수준-그룹화">6.3.2 다수준 그룹화</h3>
<p>Collectors.groupingBy를 이용해 항목을 다수준으로 그룹화</p>
<ul>
<li>일반적인 분류 함수와 컬렉터를 인수로 받음</li>
<li>바깥쪽 groupingBy 메서드에 스트림의 항목을 분류할 두 번째 기준을 정의하는 내부 groupingBy를 전달 해서 두 수준으로 스트림의 항목을 그룹화</li>
</ul>
<pre><code class="language-java">Map&lt;Dish.Type, Map&lt;CaloricLevel, List&lt;Dish&gt;&gt; dishesByTypeCaloricLevel = 
menu.stream().collect(groupingBy(Dish::getType, groupingBy(dish -&gt; if(dish.getCalories() &lt;= 400) return CaloircLevel.DIET;
else if(dish.getCalories() &lt;= 700) return CaloricLevel.NORMAL; else return CaloricLevel.FAT;</code></pre>
<ul>
<li>다수준 그룹화 연산은 다양한 수즌ㅇ로 확장 가능</li>
<li>n 수준 그룹 화의 결과는 n수준 트리 구조로 표현되는 n 수준 맵이 됨</li>
<li>보통 groupingBy의 연산을 버킷 개념으로 생각</li>
</ul>
<h3 id="633-서브그룹으로-데이터-수집">6.3.3. 서브그룹으로 데이터 수집</h3>
<ul>
<li>첫번째 groupingBy 로 넘겨주는 컬렉터의 형식은 제한이 없음</li>
<li>분류 함수 한 개의 인수를 갖는 groupingBy(f)는 사실 groupingBy(f,toList()) 의 축약형</li>
</ul>
<pre><code class="language-java">Map&lt;Dish.Type, Optional&lt;Dish&gt;&gt; mostCaloricByType = menu.stream().collect(
groupingBy(Dish::getType, maxBy(comparingInt(Dish::getCalories))));</code></pre>
<p><strong>컬렉터 결과를 다른 형식에 적용하기</strong></p>
<ul>
<li>마지막 그룹화 연산에서 맵의 모든값을 Optional로 감쌀 필요 없으므로 삭제 가능</li>
<li>팩토리 메서드 Collectors.collectingAndThen으로 컬렉터가 반환 결과를 다른 형식으로 활용</li>
</ul>
<pre><code class="language-java">Map&lt;Dish.Type, Dish&gt; mostCaloricByType = menu.stream().collect(
groupingBy(Dish::getType, collectingAndThen(maxBy(comparingInt(Dish::getCalories)),Optional::get)));</code></pre>
<p>collectingAndThen</p>
<ul>
<li>적용할 컬렉터와 변환 함수를 인수로 받아 다른 컬렉터를 반환</li>
<li>반환되는 컬렉터는 기존 컬렉터의 래퍼 역할을 하며 collect의 마지막 과정에서 변환 함수로 자신의 반환하는 값을 매핑</li>
</ul>
<p>중첩 컬렉터 작동 가장 외부 계층에서 안쪽으로 </p>
<ul>
<li>컬렉터는 점선으로 표시 되어 있고 groupingBy는 가장 바깥쪽에 위치하면서 요리의 종류에 따라 메뉴 스트림을 세 개의 서브스트림으로 그룹화</li>
<li>groupingBy 컬렉터는 collectingAndThen 컬렉터를 감쌈 따라서 두번째 컬렉터는 그룹화된 세 개의 서브 스트림에 적용</li>
<li>collectingAndThen 컬렉터는 세 번째 컬렉터 maxBy를 감쌈</li>
<li>리듀싱 컬렉터가 서브스트림에 연산을 수행한 결과에 collectingAndThen의 Optional::get 변환 함수 적용</li>
<li>groupingBy 컬렉터가 반환하는 맵의 분류 키에 대응하는 세 값이 각각의 요리 형식에서 가장 높은 칼로리</li>
</ul>
<h2 id="64-분할">6.4 분할</h2>
<p>분할</p>
<ul>
<li>분할함수라 불리는 프레디케이트를 분류 함수로 사용하는 특수한 그룹화 기능</li>
<li>불리언을 반환하므로 맵의 키 형식은 Boolean</li>
<li>그룹화 맵은 최대 두 개의 그룹으로 분휴</li>
</ul>
<p>ex) 채식주의자 친구를 저녁에 초대 가정, 모든 요리를 채식 과 채식 아닌 것으로 분류</p>
<pre><code class="language-java">Map&lt;Boolean, List&lt;Dish&gt;&gt; partitionedMenu = menu.stream().collect(partitioningBy(Dish::isVegetarian));</code></pre>
<h3 id="641-분할의-장점">6.4.1 분할의 장점</h3>
<ul>
<li>분할 함수가 반환하는 참, 거짓 두 가지 요소의 스트림 리스트를 모두 유지한다는 것이 분할의 장점</li>
<li>컬렉터를 두번째 인수로 전달할 수 있는 오버로드 된 버전의 partitioningBy 메서드 있음</li>
</ul>
<pre><code class="language-java">Map&lt;Boolean, Map&lt;Dish.Type, List&lt;Dish&gt;&gt;&gt; vegetarianDishesByType = menu.stream().collect
(partitioningBy(Dish::isVegetarian, groupingBy(Dish::getType)));</code></pre>
<h2 id="65--collector-인터페이스">6.5  Collector 인터페이스</h2>
<ul>
<li>리듀싱 연산을 어떻게 구현할지 제공하는 메서드 집합으로 구성</li>
<li>Collector 인터페이스를 직접 구현해서 더 효율적으로 문제를 해결하는 컬렉터를 만드는 방법 살펴보자!!</li>
</ul>
<p>toList</p>
<ul>
<li>일상에서 자주 사용하는 컬렉터 중 하나</li>
<li>가장 구현하기 쉬운 컬렉터</li>
</ul>
<p>Collector 인터페이스의 시그니처와 다섯 개의 메서드 정의</p>
<pre><code class="language-java">public interface Collector&lt;T,A,R&gt;{
    Supplier&lt;A&gt; supplier();
    BiConsumer&lt;A,T&gt; accumulator();
    Function&lt;A,R&gt; finisher();
    BinaryOperator&lt;A&gt; combiner();
    Set&lt;Characteristics&gt; characteristics();
}</code></pre>
<ul>
<li>T는 수집될 스트림 항목의 제네릭 형식</li>
<li>A는 누적자, 즉 수집과정에서 중간 결과를 누적하는 객체의 형식</li>
<li>R은 수집 연산 결과 객체의 형식</li>
</ul>
<h3 id="651-collector-인터페이스의-메서드-살펴보기">6.5.1 Collector 인터페이스의 메서드 살펴보기</h3>
<ul>
<li>위에 네개의 메서드는 collect 메서드에서 실행하는 함수를 반환</li>
<li>다섯 번째 메서드 characteristics는 collect 메서드가 어떤 최적화를 이용해서 리듀싱 연산을 수행할 것인지 결정하도록 돕는 힌트 특성 집합을 제공</li>
</ul>
<p>Supplier 메서드: 새로운 결과 컨테이너 만들기</p>
<ul>
<li>빈 결과로 이루어진 Supplier 반환</li>
<li>supplier는 수집 과정에서 빈 누적자 인스턴스를 만드는 파라미터가 없는 함수</li>
</ul>
<p>accumulator 메서드: 결과 컨테이너에 요소 추가하기</p>
<ul>
<li>리듀싱 연산을 수행하는 함수를 반환</li>
<li>스트림에서 n 번째 요소를 탐색할 때 두 인수 , 즉 누적자 와 n 번째 요소를 함수에 적용</li>
<li>함수의 반환값은 void</li>
<li>요소를 탐색하면서 적용하는 함수에 의해 누적자 내부 상태가 바뀌므로 누적자가 어떤값일지 단정 못함</li>
<li>ToListCollector에서 accumlator가 반환하는 함수는 이미 탐색한 항목을 포함하는 리스트에 현재 항목을 추가하는 연산 수행</li>
</ul>
<pre><code class="language-java">public BiConsumer&lt;List&lt;T&gt; ,T&gt; accumulator(){
    return (list, item) -&gt; list.add(item);
}
//메서드 참조로 바꾸면 간결
public BiConsumer&lt;List&lt;T&gt;, T&gt; accumulator(){
    return List::add;
}</code></pre>
<p>finisher 메서드: 최종 변환값을 결과 컨테이너로 적용</p>
<ul>
<li>스트림 탐색을 끝내고 누적자 객체를 최종 결과로 변환하면서 누적 과정을 끝낼 때 호출할 함수를 반환</li>
<li>때로는 ToListCollector에서 볼 수 있는 것 처럼 누적자 객체가 이미 최종 결과인 상황도 있음</li>
<li>이럴때 변환 과정이 필요하지 않으므로 finisher 메서드는 항등 함수를 반환</li>
</ul>
<pre><code class="language-java">public Function&lt;List&lt;T&gt; , List&lt;T&gt;&gt; finisher(){
    return Function.identity();
}</code></pre>
<p>combiner 메서드: 두 결과 컨테이너 병합</p>
<ul>
<li>스트림의 서로 다른 서브파트를 병렬로 처리할 때 누적자가 이결과를 어떻게 처리할지 정의</li>
<li>스트림의 두 번째 서브파트에서 수집한 항목 리스트를 첫 번째 서브파트 결과 리스트의 뒤에 추가하면 됨</li>
</ul>
<pre><code class="language-java">public BinaryOperator&lt;List&lt;T&gt;&gt; combiner(){
    return(list1, list2) -&gt; {
            list1.addAll(list2);
            return list1;
        }
}</code></pre>
<p>Characteristics 메서드</p>
<ul>
<li>컬렉터의 연산을 정의하는 Characteristics형식의 불변 집합을 반환</li>
<li>스트림을 병렬로 리듀스 할 것인지 그리고 병렬로 리듀스한다면 어떤 최적화를 선택해야할지 힌트 제공</li>
<li>UNORDERED<ul>
<li>리듀싱 결과는 스트림 요소의 방문 순서나 누적 순서에 영향을 받지 않음</li>
</ul>
</li>
<li>CONCURRENT<ul>
<li>다중 스레드에서 accumulator 함수를 동시에 호출 가능</li>
<li>스트림의 병렬 리듀싱을 수행할 수 있음</li>
</ul>
</li>
<li>IDENTITY_FINISH<ul>
<li>반환하는 함수는 단순히 identity를 적용할 뿐이므로 이를 생략 가능</li>
<li>리듀싱 과정의 최종 결과로 누적자 객체를 바로 사용 가능</li>
</ul>
</li>
</ul>
<h3 id="652-응용하기">6.5.2 응용하기</h3>
<pre><code class="language-java">public class ToListCollector&lt;T&gt; implements Collector&lt;T, List&lt;T&gt;, List&lt;T&gt;&gt; {

    @Override
    public Supplier&lt;List&lt;T&gt;&gt; supplier() {
        return ArrayList::new;
    }

    @Override
    public BiConsumer&lt;List&lt;T&gt;, T&gt; accumulator() {
        return List::add;
    }

    @Override
    public BinaryOperator&lt;List&lt;T&gt;&gt; combiner() {
        return (list1, list2) -&gt; {
                list1.addAll(list2);
                return list1;            
        };
    }

    @Override
    public Function&lt;List&lt;T&gt;, List&lt;T&gt;&gt; finisher() {
        return Function.identity();
    }

    @Override
    public Set&lt;Characteristics&gt; characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(
                IDENTITY_FINISH, CONCURRENT));
    }
}
</code></pre>
<h2 id="66-커스텀-컬렉터를-구현해서-성능-개선하기">6.6 커스텀 컬렉터를 구현해서 성능 개선하기</h2>
<p>음.. 이건 알아서 해보는게 .. 맞는 부분인거같음;;</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바 인 액션] Chapter 5 스트림 활용]]></title>
            <link>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-5-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%ED%99%9C%EC%9A%A9</link>
            <guid>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-5-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%ED%99%9C%EC%9A%A9</guid>
            <pubDate>Fri, 19 Dec 2025 05:01:33 GMT</pubDate>
            <description><![CDATA[<h2 id="51-필터링">5.1 필터링</h2>
<p>스트림의 요소를 선택하는 방법에 대해 배움</p>
<h3 id="511-프레디케이트로-필터링">5.1.1 프레디케이트로 필터링</h3>
<p>filter 메서드 </p>
<ul>
<li>프레디케이트를 인수로 받아서 프레디케이트와 일치하는 모든 요소를 포함하는 스트림을 반환</li>
</ul>
<pre><code class="language-java">List&lt;Dish&gt; vegetarianMenu = menu.stream().filter(Dish::isVegetarian).collect(toList());</code></pre>
<h3 id="512-고유-요소-필터링">5.1.2 고유 요소 필터링</h3>
<p>고유 요소로 이루어진 스트림을 반환하는 distinct 메서드도 지원</p>
<p>고유 결정 여부</p>
<ul>
<li>스트림에서 만든 객체의 hashCode, equals로 결정</li>
</ul>
<p>ex) 리스트의 모든 짝수를 선택하고 중복을 필터링</p>
<pre><code class="language-java">List&lt;Integer&gt; numbers = Arrays.asList(1,2,1,3,3,2,4);
numbers.stream()
             .filter(i -&gt; i%2 ==0)
             .distinct()
             .forEach(System.out::println)</code></pre>
<h2 id="52-스트림-슬라이싱">5.2 스트림 슬라이싱</h2>
<h3 id="521-프레디케이트를-이용한-슬라이싱">5.2.1 프레디케이트를 이용한 슬라이싱</h3>
<p><strong>TakeWhile 활용</strong></p>
<pre><code class="language-java">List&lt;Dish&gt; filteredMenu = specialMenu.stream()
.filter(dish -&gt; dish.getCalories()&lt; 320)
.collect(toList());</code></pre>
<ul>
<li>filter 연산을 이용하면 전체 스트림을 반복하면서 각 요소에 프레디케이트를 적용</li>
</ul>
<p>→ 리스트가 이미 정렬되어 있다는 사실을 이용해 320 칼로리보다 크거나 같은 요리가 나왔을 때 반복 작업을 중단 할 수 있음</p>
<ul>
<li>큰 스트림에서 상당한 차이</li>
</ul>
<p>→ takeWhile 이용하면 무한 스트림을 포함한 모든 스트림에 프레디케이트를 적용해 스트림 슬라이스 가능</p>
<pre><code class="language-java">List&lt;Dish&gt; sliceMenu1 = specialMenu.stream()
.takeWhile(dish -&gt; dish.getCalories &lt;320)
.collect(toList());</code></pre>
<p><strong>DROPWHILE 활용</strong></p>
<p>나머지 요소를 선택하려면?
→dropWhile 사용</p>
<pre><code class="language-java">List&lt;Dish&gt; sliceMenu2 = specialMenu.stream()
.dropWhile(dish -&gt; dish.getCalories() &lt;320)
.collect(toList());</code></pre>
<ul>
<li>dropWhile은<ul>
<li>takeWhile 정반대의 작업 수행</li>
<li>프레디케이트가 처음으로 거짓이 되는 지점까지 발견된 요소를 버림</li>
<li>프레디케이트가 거짓이 되면 그 지점에서 작업을 중단하고 남은 모든 요소를 반환</li>
<li>무한한 남은 요소를 가진 무한 스트리미에서도 동작</li>
</ul>
</li>
</ul>
<h3 id="522-스트림-축소">5.2.2 스트림 축소</h3>
<p>스트림</p>
<ul>
<li>주어진 값 이하의 크기를 갖는 새로운 스트림을 반환하는 limit(n) 메서드를 지원</li>
<li>정렬 되어있으면 최대 요소 n개를 반환</li>
<li>정렬 안되어있으면 정렬되지 않은 상태로 반환</li>
</ul>
<pre><code class="language-java">List&lt;Dish&gt; dishes = specialMenu.stream()
.filter(dish -&gt; dish.getCalories() &gt; 300)
.limit(3)
.collect(toList());</code></pre>
<h3 id="523-요소-건너-뛰기">5.2.3 요소 건너 뛰기</h3>
<p>스트림</p>
<ul>
<li>처음 n개의 요소를 제외한 스트림을 반환하는 skip(n) 메서드를 지원</li>
<li>n개 이하의 요소를 포함하는 스트림에 skip(n)을 호출하면 빈 스트림이 반환</li>
<li>limit(n)과 skip(n)은 상호 보완적인 연산 수행</li>
</ul>
<p>ex) 300 칼로리 이상의 처음 두 요리를 건너뛴 다음에 300 칼로리가 넘는 나머지 요리를 반환</p>
<pre><code class="language-java">List&lt;Dish&gt; dishes = menu.stream()
.filter(d -&gt;&lt; d.getCalories() &gt; 300)
.skip(2)
.collect(toList());</code></pre>
<h2 id="53-매핑">5.3 매핑</h2>
<h3 id="531-스트림의-각-요소에-함수-적용하기">5.3.1 스트림의 각 요소에 함수 적용하기</h3>
<p>스트림</p>
<ul>
<li>함수를 인수로 받는 map 메서드를 지원</li>
<li>인수로 제공된 함수는 각 요소에 적용되며 함수를 적용한 결과가 새로운 요소 매핑</li>
<li>새로운 버전은 만든다</li>
<li>변환에 가까운 매핑</li>
</ul>
<p>ex) Dish::getName을 map 메서드로 전달해 스트림의 요리명 추출</p>
<pre><code class="language-java">List&lt;String&gt; dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());</code></pre>
<p>getName</p>
<ul>
<li>문자열 반환</li>
</ul>
<p>→ map 메서드 출력 스트림 Strem<String> 형식</p>
<h3 id="532-스트림-평면화">5.3.2 스트림 평면화</h3>
<p>고유 문자로 이루어진 리스트를 반환</p>
<p>ex) [”Hello”] → [”H”,”e”,”l”,”o”]</p>
<ul>
<li>리스트에 있는 각 단어를 문자로 매핑 후 중복된 문자를 필터링 하면 해결?!</li>
</ul>
<pre><code class="language-java">words.stream()
        .map(word -&gt; word.split(&quot;&quot;)
        .distint()
        .collect(toList());</code></pre>
<p>→ 반환 형식이 문제  </p>
<ul>
<li>기대 Stream<String> 현실 Stream&lt;String[]&gt;</li>
<li>해결 법 flatMap 사용하기</li>
</ul>
<p><strong>map 과 <a href="http://Arrays.Stream">Arrays.Stream</a> 활용</strong></p>
<ul>
<li>배열 스트림 대신 문자열 스트림 필요</li>
<li>문자열을 받아 스트림으로 만드는 Arrays.stream()</li>
</ul>
<p>→ 안됨 스트림 리스트가 만들어지면서 문제 해결이 안됨</p>
<p>flatMap 사용</p>
<pre><code class="language-java">List&lt;String&gt; uniqueCharacters= words.stream()
  .map(word -&gt; word.split(&quot;&quot;))
  .flatMap(Arrays::stream)
  .distinct()
  .collect(toList());</code></pre>
<p>flatMap</p>
<ul>
<li>배열을 스트림이 아니라 스트림의 콘텐츠로 매핑</li>
<li>하나의 평면화된 스트림 반환</li>
</ul>
<h2 id="54-검색과-매칭">5.4 검색과 매칭</h2>
<h3 id="541-프레디게이트가-적어도-한-요소와-일치하는-지-확인">5.4.1 프레디게이트가 적어도 한 요소와 일치하는 지 확인</h3>
<ul>
<li>anyMatch 메서드 이용</li>
<li>불리언을 반환</li>
</ul>
<h3 id="542-프레디케이트가-모든-요소와-일치하는지-검사">5.4.2 프레디케이트가 모든 요소와 일치하는지 검사</h3>
<ul>
<li>allMatch 메서드 사용</li>
<li>nonMatch<ul>
<li>allMatch와 반대 연산</li>
<li>프레디케이트와 일치하는 요소가 없는지 확인</li>
</ul>
</li>
</ul>
<p>→ 세 메서드는 스트림 쇼트서킷 기법 , 즉 자바의 &amp;&amp;, || 와 같은 연산</p>
<h3 id="543-요소검색">5.4.3 요소검색</h3>
<ul>
<li>findAny 메서드로 현재 스트림 에서 임의의 요소를 반환</li>
<li>다른 스트림연산과 연결해서 사용</li>
</ul>
<pre><code class="language-java">Optional&lt;Dish&gt; dish = menu.stream().filter(Dish::isVegetarian).findAny();</code></pre>
<p>Optional이란?</p>
<p>Optional<T></p>
<ul>
<li>값의 존재나 부재 여부를 표현하는 컨테이너 클래스</li>
<li>isPresent()<ul>
<li>Optional 이 값을 포함하면 참을 반환 , 포함하지 않으면 거짓</li>
</ul>
</li>
<li>ifPresent 는 값이 있으면 주어진 블록 수행</li>
<li>T get() 값이 존재하면 값을 반환 없으면 NoSuchElementException을 일으킴</li>
<li>T orElse(T other) 는 값이 있으면 값 반환 없으면 기본값 반환</li>
</ul>
<h3 id="544-첫-번째-요소-찾기">5.4.4 첫 번째 요소 찾기</h3>
<ul>
<li>리스트, 정렬된 연속 데이터로부터 생성된 일부 스트림에서 논리적인 아이템 순서가 정해져있을 수 있음</li>
</ul>
<h2 id="55-리듀싱">5.5 리듀싱</h2>
<p>리듀스 연산 이용</p>
<h3 id="551-요소의-합">5.5.1 요소의 합</h3>
<p>reduce를 이용해서 스트림의 모든 요소를 더할 수 있음</p>
<pre><code class="language-java">int sum = numbers.stream().reduce(0,(a,b) -&gt; a+b);</code></pre>
<ul>
<li>초깃값 0</li>
<li>두 요소를 조합해 새로운 값을 만드는 BinaryOperator<T></li>
<li>(a,b) → a* b를 넘겨주면 모든 요소에 곱셈 적용 가능</li>
</ul>
<p>메서드 참조 이용하면 더 간결</p>
<pre><code class="language-java">int sum = numbers.stream().reduce(0,Integer::sum);</code></pre>
<p><strong>초깃값 없음</strong></p>
<ul>
<li>초깃값을 받지 않도록 오버로드된 reduce도 있음</li>
<li>Optional 객체를 반환</li>
</ul>
<pre><code class="language-java">Optional&lt;Integer&gt; sum = numbers.stream().reduce((a,b) -&gt; (a+b));</code></pre>
<ul>
<li>합계가 없음을 가르킬 수 있도록 Optional 객체로 감싼 결과 반환</li>
</ul>
<h3 id="552-최댓값과-최솟값">5.5.2 최댓값과 최솟값</h3>
<ul>
<li><p>이때도 reduce 사용</p>
</li>
<li><p>방법</p>
<ul>
<li><p>두 인수를 받음</p>
<ul>
<li>초깃값</li>
<li>스트림의 두요소를 합쳐서 하나의 값으로 만드는데 사용할 람다</li>
</ul>
</li>
<li><p>두 요소에서 최댓값을 반환하는 람다만 있으면 구할 수 있음</p>
<p>→ reduce 연산은 새로운 값을 이용해서 스트림의 모든 요소를 소비할 때까지 람다를 반복수행해서 최댓값 생산</p>
</li>
</ul>
</li>
</ul>
<pre><code class="language-java">Optional&lt;Integer&gt; max = numbers.stream().reduce(Integer::max);</code></pre>
<h2 id="56-실전-연습">5.6 실전 연습</h2>
<p>알아서 다른곳에 풀겠다.</p>
<h2 id="57-숫자형-스트림">5.7 숫자형 스트림</h2>
<p>스트림 API </p>
<ul>
<li>숫자 스트림을 효율적으로 처리할 수 있도록 기본 특화 스트럼 제공</li>
</ul>
<h3 id="571-기본형-특화-스트림">5.7.1 기본형 특화 스트림</h3>
<ul>
<li>박싱 비용을 피할 수 있도록 int 요소에 특화된 IntStream, double 요소에 특화된 DoubleStream , long 요소에 특화된 LongStream 제공</li>
</ul>
<p>특화스트림</p>
<ul>
<li>오직 박싱 과정에서 일어나는 효율성과 관련 있으며 스트림에 추가 기능을 제공하지 않는다는 사실을 기억</li>
</ul>
<p>숫자 스트림으로 매핑 </p>
<ul>
<li>mapToInt, mapToDouble,mapToLong 세가지 메서드 많이 사용</li>
<li>map과 정확히 같은 기능을 수행 하지만, 특화된 스트림 반환</li>
</ul>
<pre><code class="language-java">int calories = menu.stream() // Stream&lt;Dish&gt; 반환
                                    .mapToInt(Dish::getCalories) //IntStream 반환
                                    .sum();</code></pre>
<ul>
<li>mapToInt<ul>
<li>모든 칼로리를 추출한 다음에 IntStream을 반환</li>
<li>sum 메서드를 이용해 칼로리 합계 계산 가능</li>
<li>스트림이 비어있으면 기본값 0을 반환</li>
</ul>
</li>
</ul>
<p>객체 스트림으로 복원하기</p>
<ul>
<li>숫자 스트림 만든 후 복원할 수 있을까?</li>
<li>IntStream은 기본형의 정수값만 들 수 있어서 IntStream의 map 연산은 int 를 인수로 받아서 int를 반환하는 람다를 인수로 받음</li>
<li>다른 값을 반환하고 싶으면?<ul>
<li>스트림 인터페이스에 정의된 일반적인 연산 사용해야함</li>
</ul>
</li>
</ul>
<p>기본값: OptionalInt</p>
<p>스트림에 요소가 없는 상화과 실제 최댓값이 0인 상황 구별 법</p>
<ul>
<li>값의 존재 여부를 알 수 있는 Optional 클래스 이용</li>
</ul>
<h3 id="572-숫자-범위">5.7.2 숫자 범위</h3>
<p>ex) 1에서 100 사이의 숫자를 생성 가정</p>
<ul>
<li>IntStream과 LongStream 에서 range, rangeClosed 정적 메서드 제공</li>
<li>두 메서드 첫번 째 인수 시작값, 두번째 인수 종료값 가짐</li>
<li>range은 시작값, 종료값이 결과에 포함 되지 않음 rangeClosed 는 포함됨</li>
</ul>
<pre><code class="language-java">IntStream evenNumbers= IntStream.rangeClosed(1,100)
//[1,100]의 범위를 나타냄</code></pre>
<h3 id="573-숫자-스트림-활용--피타고라스-수">5.7.3 숫자 스트림 활용 : 피타고라스 수</h3>
<p>수학이 자꾸 나오네;;</p>
<p><strong>세수 표현하기</strong> </p>
<ul>
<li>int 배열 사용<ul>
<li>new int[]{3,4,5} 표현가능</li>
</ul>
</li>
</ul>
<p><strong>좋은 필터링 조합</strong></p>
<ul>
<li>세 수 중 a,b 두 수만 제공했다고 가정</li>
<li>a<em>a + b</em>b 의 제곱근이 정수인지 확인할 수 있음</li>
<li>Math.sqrt(a<em>a + b</em>b) % 1 ==0;</li>
<li>filter ( b →Math.sqrt(a<em>a + b</em>b) % 1 ==0)</li>
</ul>
<p><strong>집합 생성</strong></p>
<p>필터를 이용해 좋은 조합을 갖는 a,b를 선택</p>
<p>세번 째 수 찾는 법</p>
<pre><code class="language-java">stream.filter( b →Math.sqrt(a*a + b*b) % 1 == 0)
            .map(b -&gt; new int[]{a,b,(int) Math.sqrt(a*a*+b*b)});</code></pre>
<p><strong>b값 생성</strong></p>
<pre><code class="language-java">Intstream.rangeClosed(1,100)
            .filter( b →Math.sqrt(a*a + b*b) % 1 == 0)
            .boxed()
            .map(b -&gt; new int[]{a,b,(int) Math.sqrt(a*a*+b*b)});</code></pre>
<ul>
<li>filter 연산 후 rangeClosed가 반환한 IntStream을 boxed를 이용해 Stream<Integer> 로 복원</li>
<li>map은 스트림의 각 요소를 int 배열로 반환</li>
</ul>
<p>a 값 생성</p>
<pre><code class="language-java">Stream&lt;intp[&gt; pythagoreanTriples = IntStream.rangeClosed(1,100).boxed()
  .flatMap( a-&gt; IntStream.rangeClosed(a,100)
  .filter(b -&gt; Math.sqrt(a*a +b*b) % 1 == 0)
  .mapToObj(b -&gt; new int[]{a,b,(int)Math.sqrt(a*a+bb*b)}))</code></pre>
<h2 id="58-스트림-만들기">5.8 스트림 만들기</h2>
<h3 id="581-값으로-스트림-만들기">5.8.1 값으로 스트림 만들기</h3>
<ul>
<li>임의의 수를 인수로 받는 정적 메서드 Stream.of를 이용해 스트림 만들 수 있음</li>
</ul>
<h3 id="582-null이-될-수-있는-객체로-스트림-만들기">5.8.2 null이 될 수 있는 객체로 스트림 만들기</h3>
<ul>
<li>System.getProperty는 제공된 키에 대응하는 속성이 없으면 널 반환</li>
<li>이런 메서드를 활용 하기 위해 널을 명시적으로 확인</li>
<li>flatMap과 함께 사용하면 더 유용하게 사용 가능</li>
</ul>
<h3 id="583-배열로-스트림-만들기">5.8.3 배열로 스트림 만들기</h3>
<ul>
<li>배열을 인수로 받는 정적 메서드 Arrays.stream을 이용해 스트림 만들 수 있음</li>
</ul>
<h3 id="584-파일로-스트림-만들기">5.8.4 파일로 스트림 만들기</h3>
<ul>
<li>파일을 처리하는 등의 I/O 연산에 사용하는 자바의 NIO API도 스트림 API를 활용할 수 있도록 업데이트</li>
</ul>
<h3 id="585-함수로-무한-스트림-만들기">5.8.5 함수로 무한 스트림 만들기</h3>
<ul>
<li>Stream.iterate와 Stream.generate를 제공</li>
<li>두 연산을 이용해 무한 스트림 즉 고정된 컬렉션에서 고정된 크기로 스트림을 만들었던 것과는 달리 크기가 고정되지 않은 스트림을 만들 수 있음</li>
<li>iterate와 generate에서 만든 스트림은 요청할때마다 주어진 함수를 이용해 값을 만듦</li>
</ul>
<p>→ 무제한 값 계산 가능 하지만 보통 limit(n) 함수를 사용해 연결</p>
<p>iterate 메서드</p>
<pre><code class="language-java">Stream.iterate(0,n-&gt;n+2)
            .limit(10)
            .forEach(System.out::println);</code></pre>
<ul>
<li>초깃값 과 람다를 인수로 받아 새로운 값 끊임없이 생산</li>
<li>무한 스트림 이런 스트림은 언바운드 스트림 이라고 표현</li>
</ul>
<p>generate 메서드</p>
<ul>
<li>iterate과 비슷하게 값을 계산하는 무한 스트림 만들 수 있음</li>
<li>생산된 각 값을 연속적으로 계산하진 않음</li>
<li>Supplier<T>를 인수로 받아 새로운 값을 생성</li>
</ul>
<pre><code class="language-java">//1번
List&lt;Transcation&gt; allTranscation = transactions.stream()
  .filter(transaction-&gt; transaction.getYear() ==2011)
  .sorted(comparing(Transcation::getValue))
  .collect(toList())
//2번                                                                                    
List&lt;Transcation&gt; allCity = transactions.stream()
  .map(transaction -&gt;transaction.getTrader().getCity())
  .distinct()
  .collect(toList());

List&lt;Trader&gt; allCam = transactions.stream()
  .map(Transcation::getTrader) 
  .filter(trader -&gt; trader.getCity().equals(&quot;Cambridge&quot;))
  .distinct()
  .sorted(comparing(Trader::getName))
  .collect(toList());
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바 인 액션] Chapter 4 스트림 소개]]></title>
            <link>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-4-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%EC%86%8C%EA%B0%9C</link>
            <guid>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-4-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%EC%86%8C%EA%B0%9C</guid>
            <pubDate>Thu, 18 Dec 2025 13:47:44 GMT</pubDate>
            <description><![CDATA[<h2 id="41-스트림이란-무엇인가">4.1 스트림이란 무엇인가</h2>
<p>스트림</p>
<ul>
<li>자바8 API에 새로 추가된 기능</li>
<li>선언형으로 컬렉션 데이터 처리<ul>
<li>컬렉션이란?<ul>
<li>많은 수의 데이터를 그 사용 목적에 적합한 자료구조로 묶어 하나로 그룹화한 객체</li>
<li>ArrayList, LinkedList,Vector,Stack, HashSet, TreeSet, TreeMap 등</li>
</ul>
</li>
</ul>
</li>
<li>데이터를 투명하게 병렬로 처리</li>
</ul>
<p>EX) 저칼로리의 요리명을 반환 후 칼로리를 기준으로 요리를 정렬</p>
<p>자바 7 코드</p>
<pre><code class="language-java">List&lt;Dish&gt; lowCaloricDishes = new ArrayList&lt;&gt;();
for(Dish dish: menu){
    if(dish.getCalories() &lt; 400){
        lowCaloricDishes.add(dish)
    }
}
Collections.sort(lowCaloricDishes, new Comparator&lt;Dish&gt;(){
    public int compare(Dish dish1, Dish dish2){
        return Integer.compare(dish1.getCalories(), dish2.getCalories());
    }});
    List&lt;String&gt; lowCaloricDishesName = new ArrayList&lt;&gt;();
    for(Dish dish: lowCaloricDishes){
        lowCaloricDishesName.add(dish.getName());
    }</code></pre>
<ul>
<li>lowCaloricDishes → 가비지 변수</li>
</ul>
<pre><code class="language-java">List&lt;String&gt; lowCaloricDishesName = menu.stream()
                                                                        .filter(d -&gt; d.getCalories() &lt;400)
                                                                        .sorted(comparing(Dish::getCalories))
                                                                        .map(Dish::getName)
                                                                .collect(toList()); //모든 요리명을 리스트에 저장

List&lt;String&gt; lowCaloricDishesName = menu.parallelStream()
                                                                    .filter( d -&gt; d.getCalories() &lt; 400)
                                                                    .sorted(comparing(Dishes::getCalories))
                                                                    .map(Dish::getName)
                                                                    .collect(toList());</code></pre>
<ul>
<li>parallelStream()로 바꾸면 멀티코어 아키텍처에서 병렬로 실행가능</li>
</ul>
<p>스트림 이득의 기능</p>
<ul>
<li>선언형으로 코드 구현 가능<ul>
<li>루프와 if 조건문 등의 제어 블록을 사용해서 어떻게 동작을 구현할지 지정할 필요 없이 저칼로리의 요리만 선택하라 같은 동작 수행을 지정</li>
</ul>
</li>
<li>filter, sorted, map, collect 같은 여러 빌딩 블록 연산을 연결해서 복잡한 데이터 처리 파이프라인 만들 수 있음</li>
<li>여러 연산을 연결해 가독성 명확성 유지</li>
</ul>
<p>고수준 빌딩 블록</p>
<ul>
<li>filer(또는 sorted, map, collect)</li>
<li>특정 스레딩 모델에 제한되지 않고 자유롭게 상황 사용 가능</li>
</ul>
<p>→ 데이터 처리 과정을 병렬화 하면 스레드와 락 걱정 필요 X</p>
<p>자바 8의 스트림 API 특징</p>
<ul>
<li>선언형<ul>
<li>더 간결하고 가독성 좋아짐</li>
</ul>
</li>
<li>조립할 수 있음<ul>
<li>유연성이 좋아짐</li>
</ul>
</li>
<li>병렬화<ul>
<li>성능이 좋아짐</li>
</ul>
</li>
</ul>
<h2 id="42-스트림-시작하기">4.2 스트림 시작하기</h2>
<p>스트림이란</p>
<ul>
<li>데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소</li>
<li>연속된 요소<ul>
<li>특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스 제공</li>
<li>표현 계산식이 주를 이룸</li>
<li>켈력션의 주제는 데이터고 스트림의 주제는 계산</li>
</ul>
</li>
<li>소스<ul>
<li>스트림은 컬렉션, 배열, I/O 자원등의 데이터 제공 소스로부터 데이터를 소비함</li>
<li>정렬된 컬렉션으로 스트림을 생성하면 그대로 유지</li>
</ul>
</li>
<li>데이터 처리 연산<ul>
<li>스트림은 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베이스 와 비슷한 연산 지원</li>
</ul>
</li>
<li>파이프라이닝<ul>
<li>스트림 연산끼리 연결해서 커다란 파이프라인을 구성할 수 있도록 스트림 자신을 반환</li>
</ul>
</li>
<li>내부 반복<ul>
<li>반복자를 이용해서 명시적으로 반복하는 컬렉션과 달리 스트림은 내부 반복 지원</li>
</ul>
</li>
</ul>
<p>예제</p>
<pre><code class="language-java">List&lt;String threeHighCaloricDishNames =
    menu.stream() //메뉴에서 스트림 얻기
            .filter(dish -&gt; dish.getCalories() &gt; 300)
            .map(Dish::getName)
            .limit(3)
            .collect(toList());</code></pre>
<p>데이터 소스는 메뉴 → 데이터 소시 연속된 요소 를 스트림에 제공 → 데이터 처리 연산 적용 → collect를 제외한 모든 연산은 서로 파이프라인 형성 할 수 있도록 스트림 반환</p>
<ul>
<li>filter<ul>
<li>람다를 인수로 받아 스트림에서 트특요소를 제외</li>
</ul>
</li>
<li>map<ul>
<li>람다를 이용해서 한 요소를 다른 요솔 변환하거나 정보를 추출</li>
</ul>
</li>
<li>limit<ul>
<li>정해진 개수 이상의 요소가 스트림에 저장되지 ㅁ소하게 슨트림 크기를 축소 truncate</li>
</ul>
</li>
<li>collect<ul>
<li>스트림을 다른 형식으로 변환</li>
</ul>
</li>
</ul>
<h2 id="43-스트림과-컬렉션">4.3 스트림과 컬렉션</h2>
<p>컬렉션 , 스트림 차이</p>
<ul>
<li>데이터를 언제 계산하느냐</li>
<li>컬렉션<ul>
<li>모든 값을 메모리에 저장하는 자료구조</li>
<li>모든 요소는 컬렉션에 추가하기 전에 계산되어야함</li>
<li>적극적으로 생성</li>
</ul>
</li>
<li>스트림<ul>
<li>요청할때만 요소를 계산 하는 고정된 자료구조</li>
<li>요청한 값만 스트림에서 추출한다는 것이 핵심</li>
<li>생산자와 소비자 관계 형성</li>
<li>게으르게 만들어지는 컬렉션</li>
</ul>
</li>
</ul>
<h3 id="431-딱-한번만-탐색">4.3.1 딱 한번만 탐색</h3>
<p>스트림</p>
<ul>
<li>한번만 탐색 가능</li>
<li>탐색된 스트림의 요소는 소비됨</li>
<li>다시 탐색하려면 초기 데이터 소스에서 새로운 스트림 만들어야함</li>
</ul>
<h3 id="432-외부-반복-내부-반복">4.3.2 외부 반복, 내부 반복</h3>
<p>외부반복</p>
<ul>
<li>컬렉션 인터페이스를 사용하려면 사용자가 직접 요소를 반복</li>
</ul>
<p>내부반복</p>
<ul>
<li>스트림 라이브러리는 내부 반복 사용</li>
<li>함수에 어떤 작업을 수행할지만 지정하면 모든 것이 알아서 처리</li>
</ul>
<pre><code class="language-java">//컬렉션: for-each 루프를 이용하는 외부반복
List&lt;String&gt; names = new ArrayList&lt;&gt;();
for(Dish dish:menu){
     names.add(dish.getName());
}
//스트림: 내부 반복
List&lt;String&gt; names = menu.stream()
                                            .map(Dish::getName)
                                            .collect(toList()); //파이프라인 실행함, 반복자 필요X                                    </code></pre>
<p>컬렉션</p>
<ul>
<li>외부적으로 반복 해서 컬렉션 항목을 하나씩 가져와서 처리 해야함</li>
</ul>
<p>내부 반복이 좋은 다른 두가지 이유</p>
<ul>
<li>투명하게 병렬로 처리하거나 더 최적화된 다양한 순서로 처리 가능</li>
</ul>
<p>스트림 라이브러리 내부반복</p>
<ul>
<li>데이터 표현과 하드웨어를 활용한 병렬성 구현으로 자동으로 선택</li>
<li>외부반복에서는 병렬성을 스스로 관리 해야함</li>
</ul>
<pre><code class="language-java">List&lt;String highCaloricDishes = menu.stream()
                                                                .filter(dish -&gt; dish.getCalories() &gt; 300)
                                                                .map(Dish::getName)
                                                                .collect(toList());</code></pre>
<h2 id="44-스트림-연산">4.4 스트림 연산</h2>
<pre><code class="language-java">List&lt;String threeHighCaloricDishNames =
    menu.stream() //메뉴에서 스트림 얻기
            .filter(dish -&gt; dish.getCalories() &gt; 300)
            .map(Dish::getName)
            .limit(3)
            .collect(toList());</code></pre>
<ul>
<li>filter,map,limit 서로 연결되어 파이프라인 형성</li>
<li>collect 파이프라인을 실행한 후 닫음</li>
</ul>
<p>연결할 수 있는 스트림 연산 </p>
<p>→ 중간 연산</p>
<p>스트림을 닫는 연산</p>
<p>→ 최종 연산</p>
<h3 id="441-중간-연산">4.4.1 중간 연산</h3>
<p>특징</p>
<ul>
<li>여러 중간 연산을 연결해서 질의를 만들 수 있음</li>
<li>단말 연산을 스트림 파이프라인에 실행하기 전까지는 아무 수행하지 않음</li>
<li>레이지</li>
<li>중간 연산을 합친 다음에 합쳐진 중간 연산을 최종 연산으로 한 번에 처리</li>
</ul>
<h3 id="442-최종-연산">4.4.2 최종 연산</h3>
<p>최종연산</p>
<ul>
<li>스트림 파이프라인에서 결과 도출</li>
<li>List, Integer, void등 스트림 이외의 결과 반환</li>
</ul>
<h3 id="443-스트림-이용하기">4.4.3 스트림 이용하기</h3>
<p>스트림 이용과정</p>
<ul>
<li>질의를 수행할 데이터 소스</li>
<li>스트림 파이프라인을 구성할 중간 연산 연결</li>
<li>스트림 파이프라인을 실행하고 결과를 만들 최종 연산</li>
</ul>
<p>스트림 파이프라인의 개념 == 빌더 패턴과 비슷</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바 인 액션] Chapter 3 람다 표현식]]></title>
            <link>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-3-%EB%9E%8C%EB%8B%A4-%ED%91%9C%ED%98%84%EC%8B%9D-hfow7hh2</link>
            <guid>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-3-%EB%9E%8C%EB%8B%A4-%ED%91%9C%ED%98%84%EC%8B%9D-hfow7hh2</guid>
            <pubDate>Wed, 17 Dec 2025 05:23:02 GMT</pubDate>
            <description><![CDATA[<h2 id="31-람다란-무엇인가">3.1 람다란 무엇인가?</h2>
<p>람다 표현식 </p>
<ul>
<li>메서드로 전달할 수 있는 익명 함수를 단순화한 것</li>
<li>이름은 없지만 파라미터 리스트, 바디, 반환형식, 발생할 수 있는 예외 리스트는 가질 수 있음</li>
</ul>
<p>특징</p>
<ul>
<li>익명<ul>
<li>보통의 메서드와 달리 이름이 없으므로 익명이라고 표현</li>
</ul>
</li>
<li>함수<ul>
<li>람다는 메서드처럼 특정 클래스에 종속되지 않으므로 함수라고 부름</li>
<li>하지만 메서드처럼 파라미터 리스트, 비디, 반환 형식, 가능한 예외 리스트 포함</li>
</ul>
</li>
<li>전달<ul>
<li>람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 잇음</li>
</ul>
</li>
<li>간결성<ul>
<li>익명 클래스처럼 많은 자질구레한 코드를 구현할 필요없음</li>
</ul>
</li>
</ul>
<p>람다 표현식</p>
<p>(Apple a1, Apple a2) → a.getWeight().compareTo(a2.getWeight());</p>
<p>-람다 파라미터 -         - 화살표- -람다 바디-</p>
<ul>
<li>파라미터 리스트<ul>
<li>Comparator 의 compare 메서드 파라미터</li>
</ul>
</li>
<li>화살표<ul>
<li>람다 파라미터 리스트와 바디 구분</li>
</ul>
</li>
<li>람다 바디<ul>
<li>두 사과의 무게를 비교 , 람다의 반환값에 해당하는 표현식</li>
</ul>
</li>
</ul>
<pre><code class="language-java"> //String 형식의 파라미터를 가지며 int를 반환 람다 표현식은 return이 함축 되어있어 안써도됨
(String s) -&gt; s.length() 
//Apple 형식의 파라미너 하나를 가지며 boolean을 반환
(Apple a) -&gt; a.getWeight() &gt; 150
//int 형식의 파리미터 두개를 가지며 리턴값은 없다 , 여러행을 포함해 쓸 수 있다.
(int x, int y) -&gt; { System.out.println(&quot;Result:&quot; ); System.out.println(x+y);}
//파라미터가 없으며 int 42 반환
() -&gt; 42
//Apple 형식의 파라미터 두개를 가지며 int를 반환
(Apple a1, Apple a2) → a.getWeight().compareTo(a2.getWeight());</code></pre>
<p>람다 문법</p>
<ol>
<li>() → {}  파리미터가 없고 void를 반환 pulbic void run() {}처럼 바디가 없는 메서드</li>
<li>() → “Raoul” 파라미터가 없고 문자열을 반환</li>
<li>() → { return “Mario”;} 파라미터가 없고 문자열을 반환하는 표현식(명시적으로 return 문사용)</li>
<li>(Integer i ) → { return “Alan”+i;} return은 흐름 제어문 {} 가 있어야함</li>
<li>(String s) → “Iron  Man” 구문이 아니라 표현식으로 {} 가 있으면 안됨</li>
</ol>
<p>예제</p>
<ol>
<li>불리언 표현식 (List<String> list) → list.isEmpty()</li>
<li>객체 생성 () → new Apple(10)</li>
<li>객체에서 소비 (Apple a) → { System.out.println(a.getWeight());}</li>
<li>객체에서 선택 /추출 (String s) → s.length()</li>
<li>두 값을 조합 (int a, int b) → a* b</li>
<li>두 객체 비교 (Apple a1. Apple a2) → a1.getWeight().compareTo(a2.getWeight())</li>
</ol>
<h3 id="32-어디에-어떻게-람다를-사용">3.2 어디에 어떻게 람다를 사용</h3>
<h3 id="321-함수형-인터페이스">3.2.1 함수형 인터페이스</h3>
<p>Predicate<T> 가 함수형 인터페이스 , 오직 하나의 추상 메서드만 지정하기 때문</p>
<pre><code class="language-java">public interface Predicate&lt;T&gt; {
 boolean test(T t)
}</code></pre>
<p>함수형 인터페이스는 정확히 하나의 추상 메서드를 지정하는 인터페이스</p>
<p>추가로 Comparator, Runnable 등이 있음</p>
<p>전체 표현식을 함수형 인터페이스의 인스턴스로 취급 가능</p>
<pre><code class="language-java">Runnable r1 = () -&gt; System.out.println(&quot;Hello Wolrd 1&quot;); // ㅏㄹㅁ다 사용
Runnable 41 = new Runnable(){ // 익명 클래스 사용
    public void run(){
     System,out.println(&quot;Hello Wolrd 2&quot;);
    }
};

public static void process(Runnable r){
 r.run();
}
process(r1); //Hello Wolrd 1 출력
process(r2); //Hello Wolrd 2 출력
process(() -&gt; System.out.println(&quot;Hello world 3&quot;)); // 직접 전달된 람다 표현식 </code></pre>
<h3 id="322-함수-디스크립터">3.2.2 함수 디스크립터</h3>
<p>함수형 인터페이스의 추상 메서드 시그니처 는 람다 표현식의 시그니처를 가르킴</p>
<p>람다 표현식의 시그니처를 서술하는 메서드를 함수 디스크립터 라고 부름</p>
<p>ex) Runnable 인터페이스의 유일한추상 메서드 run은 인수와 반환값이 없으므로 인수와 반환값이 없는 시그니처로 생각</p>
<p>() → void // 파라미터 리스트가 없고 void를 반환</p>
<p>(Apple, Apple) → int 는 두개의 인수를 받아 int를 반환</p>
<p>함수형 인터페이스를 인수로 받는 메서드에만 람다 표현식을 사용할 수 있을까 → 언어를 더 복잡하게 만들지 않기 위해</p>
<h2 id="33-람다-호라용-실행-어라운드-패턴">3.3 람다 호라용: 실행 어라운드 패턴</h2>
<p>자원 처리에 사용하는 순환패턴: 자원 열기 → 처리 → 자원 닫기 </p>
<p>설정과 정리 과정은 대부분 비슷 </p>
<p>→ 실제 자원을 처리하는 코드를 설정과 정리 두 과정이 둘러싸는 형태를 가짐</p>
<p>초기화/준비코드→ 작업 → 정리/ 마무리 코드 </p>
<p>→ 이걸 실행 어라운드 패턴 이라고 부름</p>
<p>예제, 자바 8에서 새로 추가된 trh-with-resources 구문, 자원을 명시적으로 닫을 필요가 없어 간결</p>
<pre><code class="language-java">public String porcessFile() throws IOException {
 try( BufferedReader br = new BufferedReader(new FileReader(&quot;data.txt&quot;)))}
  return br.readLing();
}
}  
</code></pre>
<h3 id="331-1단계--동작-파라미터화를-기억하라">3.3.1 1단계 : 동작 파라미터화를 기억하라</h3>
<p>혅내 코드는 파일에서 한번에 한줄말 읽을 수 있음 </p>
<p>한번데 두줄을 읽거나 가장 자주 사용되는 단어를 반환하려면?</p>
<p>→ 기존의 설정, 정리과정은 재사용하고 processFile 메서드만 다른 동작을 수행하도록 명령할 수 있으면 좋음</p>
<p>→ 동작을 파라미터화화 하는 것</p>
<p>processFile 메서드가BufferedReader를 이용해 다른 동작을 수해할 수 있도록 processFile 메서드로 동작을 전달</p>
<p>람다를 이용해 동작 전달 </p>
<p>한번에 두행을 읽게 하려면?</p>
<pre><code class="language-java">String result = processFile((BufferedReader br) -&gt; br.readLine() + br.readLine());</code></pre>
<h3 id="332-2단계-함수형-인터페이스를-이용해서-동작-전달">3.3.2 2단계: 함수형 인터페이스를 이용해서 동작 전달</h3>
<p>함수형 인터페이스 자리에 람다를 사용할 수 있음</p>
<p>BufferedReader → String과 IOException을 던질 수 있는 시그니처와 일치하는 함수형 인터페이스를 만들어함</p>
<pre><code class="language-java">@FunctinalInterface
public interface BufferedReaderProcessor{
 String process(BufferedReader b) throws IOException;
 }

 public String processFile(BufferedReaderProcessor p) thorws IOException{
 ...
 }</code></pre>
<h3 id="333-3단게-동작-실행">3.3.3 3단게: 동작 실행</h3>
<p>람다 표현식으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달할 수 있으면 전달된 코드는 함수형 인터페이스의 인스턴스로 전달된 코드와 같은 방식으로 처리</p>
<pre><code class="language-java">public String porcessFile(BufferedReaderProcessor p ) throws IOException {
 try( BufferedReader br = new BufferedReader(new FileReader(&quot;data.txt&quot;)))}
  return p.process(br);
}
}  
</code></pre>
<h3 id="334-4단계-람다-전달">3.3.4 4단계: 람다 전달</h3>
<p>이제 람다를 이용해 다양한 동작을 메서드로 전달 가능</p>
<pre><code class="language-java">String oneLine = processFile((BufferedReader br) -&gt; br.readLine());

String twoLines = processFile((BufferedReader br) -&gt; br.readLine() + br.readLine()); </code></pre>
<h2 id="34-함수형-인터페이스-사용">3.4 함수형 인터페이스 사용</h2>
<p>함수형 인터페이스는 오직 하나의 추상 메서드를 지정</p>
<p>함수형 인터페이스의 추상 메서드는 람다 표현식의 시그니처를 묘사함</p>
<ul>
<li>함수 디스크립터</li>
</ul>
<p>다양한 람다 표현식을 사용하려면 공통의 함수 디스크립터를 기술하는 함수형 인터페이스 집합이 필요</p>
<h3 id="341-predicate">3.4.1 Predicate</h3>
<p>java.util.function.Predicate<T> 인터페이스</p>
<ul>
<li>test라는 추상메서드를 정의 , test는 제너릭 형식 T의 객체를 인수로 받아 불리언을 반환</li>
<li>정의할 필요없이 바로 사용할 수 있음</li>
</ul>
<p>T 형식의 객체를 사용하는 불리언 표현식이 필요한 상황에서 Predicate 인터페이스를 사용할 수 있음</p>
<p>ex) String 객체를 인수로 받는 람다를 정의</p>
<pre><code class="language-java">@FunctionalInterface
public interface Predicate&lt;T&gt; {
 boolean test(T t);
}
public &lt;T&gt; List&lt;T&gt; filter(List&lt;T&gt; list, Predicate&lt;T&gt; p){
 List&lt;T&gt; results = new ArrayList&lt;&gt;();
 for(T t: list){
  if(p.test(t)){
   result.add(t);
  }
 }
 return results;
}
Predicate&lt;String nonEmptyStringPredicate = (String s) -&gt; !s.isEmpty();
List&lt;String&gt; nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);</code></pre>
<p>Predicate 인터페이스의 자바독 명세를 보면 and나 or 같은 메서드도 있음을 알 수 있음</p>
<h3 id="342-consumer">3.4.2 Consumer</h3>
<p>java.util.function.Consumer<T> 인터페이스는 제너릭 형식 T 객체를 받아서 void를 반환하는 accpet라는 추상 메서드를 정의</p>
<p>T 형식의 객체를 인수로 받아서 어떤 동작을 수행하고 싶을 때 Consumer 인터페이스를 사용할 수 있음</p>
<p>ex) Integer 리스트를 인수로 받아서 각 항목에 어떤 동작을 수행하는 forEach메서드를 정의할 때 Consumer를 활용할 수 있음</p>
<pre><code class="language-java">@FunctinalInterface
public interface Consumer&lt;T&gt;{
 void accept(T t);
}

public &lt;T&gt; void forEach(List&lt;T&gt; list, Consumer&lt;T&gt; c){
 for(T t:list){
  c.accpet(t);
 }
}
forEach( Arrays.asList(1,2,3,4,5),(Integer i ) -&gt; System.out.println(i)
);</code></pre>
<h3 id="343-function">3.4.3 Function</h3>
<p>java.util.function.Function&lt;T,R&gt; 인터페이스는 제네릭 형식 T를 인수로받아서 제네릭 형식 R 객체를 반환하는 추상 메서드 apply를 정의</p>
<p>입력을 출력으로 매핑하는 랃마를 정의할 때 Function인터페이스를 활용</p>
<p>ex) String 리스트를 인수로 받아 각 String의 길이를 포함하는 Integer리스트로 변환하는 map메서드를 정의하는 예제</p>
<pre><code class="language-java">@FunctionalInterface
public interface Function&lt;T,R&gt;{
 R apply(T t);
}
public &lt;T,R&gt; List&lt;R&gt; map(List&lt;T&gt; list,Function&lt;T,R&gt; f){
 List&lt;R&gt; result = new ArrayList&lt;&gt;();
 for(T t:list){
  result.add(f.apply(t));
 }
 return result;
}

//[7,2,6]
List&lt;Integer&gt; l = map(Arrays.asList(&quot;lambds&quot;,&quot;in&quot;,&quot;action&quot;),(String s) -&gt; s.length()
);</code></pre>
<p><strong>기본형 특화</strong></p>
<p>Predicate<T>, Consumer<T>,Function&lt;T,R&gt;</p>
<p>자바의 모든 형식은 참조형 아니면 기본형에 해당</p>
<p>제네릭 파라미터에는 참조형만 사용할 수 있음 제네릭의 내부 구현 때문에 어쩔 수 없는 일</p>
<p>자바에서는 기본형으로 참조형으로 변환하는 기능 제공 </p>
<p>→ 이 기능을 박싱, 반대 동작을 언박싱</p>
<p>박싱, 언박싱 자동으로 이루어지는 오토박싱</p>
<p>이런 변환 과정을 비용이 소모됨</p>
<p>박싱한 값은 기본형을 감싸는 래퍼며 힙에 저장됨</p>
<p>→ 박싱한 값은 메모리를 더 소미하며 기본형을 가져올 때도 메모리를 탐색하는 과정이 필요</p>
<p>자바 8에서는 기본형 입출력으로 사용하는 상황에서 오토박싱 동작을 피할 수 있도록 특별한 버전의 함수형 인터페이스를 제공</p>
<p>하지만 이런 변환 과정은 비용이 소모</p>
<p>자바 8에서는 기본형을 입출력으로 사용하는 상황에서 오토박싱 동작을 피할 수 있도록 함수형 인터페이스 제공</p>
<pre><code class="language-java">public interface IntPredicate{
 boolean test(int t);
}
IntPredicate evenNumbers = (int i) -&gt; i%2 ==0;
evenNumbers.test(1000); //참(박싱없음)
Predicate&lt;Integer&gt; oddNumbers = (Integer i) -&gt; i%2 !=0;
oddNumberse.test(1000); //거짓(박싱)</code></pre>
<h2 id="35-형식-검사-형식-추론-제약">3.5 형식 검사, 형식 추론, 제약</h2>
<p>람다가 어떤 함수형 인터페이스를 구현하는지의 정보가 포함되어 있진 않음</p>
<h3 id="341-형식-검사">3.4.1 형식 검사</h3>
<p>람다가 사용되는 콘텍스트를 이용해 람다의 형식을 추론할 수 있음</p>
<p>어떤 콘텍스트에서 기대되는 람다 표현식의 형식을 대상 형식이라고 부름</p>
<pre><code class="language-java">List&lt;Apple&gt; heavierThan150g = 
filter(inventory, (Apple apple) -&gt; apple.getWeight() &gt; 150);</code></pre>
<p>다음 순서로 형식 확인 과정 진행</p>
<ol>
<li>filter 메서드의 선언을 확인</li>
<li>filter 메서드는 두 번째 파라미터로 Predicate<Apple> 형식을 기대</li>
<li>Predicate<Apple> 은 test라는 한 개의 추상 메서드를 정의하는 함수형 인터페이스</li>
<li>test 메서드는 Apple을 받아 boolean을 반환하는 함수 디스크립터를 묘사</li>
<li>filter 메서드로 전달된 인수는 이와 같은 요구사항을 만족해야함</li>
</ol>
<p>람다 표현식이 예외를 던질 수 있다면 추상 메서드도 같이 예외를 던질 수 있도록 throws로 선언</p>
<h3 id="352-같은-람다-다른-함수형-이넡페이스">3.5.2 같은 람다, 다른 함수형 이넡페이스</h3>
<p>대상 형식 이라는 특징 때문에 같은 람다 표현식이더라도 호환되는 추상 메서드를 가진 다른 함수형 인터페이스로 사용 될 수 있음</p>
<p>ex) Callable 과 PrivilegedAction 인터페이스는 인수를 받지 않고 제네릭 형식 T를 반환하는 함수를 정의</p>
<pre><code class="language-java">Callable&lt;Integer&gt; c = () -&gt; 42; 
PrivilegedAction&lt;Integer&gt; p = () -&gt; 42;</code></pre>
<p>둘다 유효</p>
<h3 id="353-형식-추론">3.5.3 형식 추론</h3>
<p>자바 컴파일러는 대상 형식을 이용해서 함수 디스크립터를 알수 있으므로 컴파일러는 람다 시그니처도 추론 가능</p>
<p>→ 컴파일러는 람다 표현식의 파라미터 형식에 접근할 수 있으므로 람다 문법에서 이를 생략 가능</p>
<p>아래처럼 람다 파라미터 형식을 추론할 수 있음</p>
<pre><code class="language-java">List&lt;Apple&gt; greenApples = filter(inventory, apple -&gt; GREEN.equals(apple.getColor());
//파라미터 apple에 형식을 명시적으로 지정하지 않음</code></pre>
<p>상황에 따라 명시적으로 형식을 포함하는게 좋을 수도 있고 배제하는 것이 가독성을 향상시킬때도 음</p>
<p>정해진 규칙은 없기 때문에 스스로 향상 시킬 수 있는 코드를 결정</p>
<h3 id="354-지역변수-사용">3.5.4 지역변수 사용</h3>
<p>람다 표현식에서 익명 함수가 하는 것 처럼 자유 변수를 활용할 수 있음 </p>
<p>→ 람다 캡처링 </p>
<pre><code class="language-java">int portNumber = 1337;
Runnable r = () -&gt; System.out.println(portNumber);</code></pre>
<p>자유 변수에도 약간의 제약이 있음</p>
<p>람다는 인스턴스 변수와 정적 변수를 자유롭게 캡쳐할 수 있음</p>
<p>→ 지역변수는 명시적으로 final로 선언 되어있어야하거나 실질적으로 final로 선언된 변수와 똑같이 사용되어야함</p>
<p>즉 람다 표현식은 한 번만 할당할 수 있는 지역 변수를 캡처 가능</p>
<pre><code class="language-java">int portNumber = 1337;
Runnable r = () -&gt; System.out.println(portNumber);
portNumber = 31337; //에러 람다에서 참고하는 지역 변수는 final로 선언되건 final 취급</code></pre>
<p><strong>지역 변수 제약</strong></p>
<p>제약이 필요한 이유</p>
<p>내부적으로 인스턴스 변수와 지역 변수는 태생부터 다름</p>
<p>인스턴스 변수는 힙에 저장되는 반면 지역 변수는 스택에 위치</p>
<p>람다에서 지역 변수에 바로 접근할 수 있다는 가정하에 람다가 스레드에서 실행되면 변수를 할당한 스레드가 사라져서 변수 할당이 해제되었는데도 람다를 실행하는 스레드에서는 해당 변수에 접글하려할 수 있음</p>
<p>자바 구현에서는 원래 변수에 접근을 허용하는 것이 아니라 자유 지역 변수의 복사본을 제공</p>
<p>→ 복사본의 값이 바뀌지 않아야하므로 지역 변수에는 한번만 값을 할당해야한다는 제약이 생김</p>
<p>이 제약 때문에 외부 변수를 변화시키는 일반적인 명령형 프로그램 패턴에 제동 걸 수 있음</p>
<h2 id="36-메서드-참조">3.6 메서드 참조</h2>
<p>메서드 참조를 이용하면 기존의 메서드 정의를 재활용해서 람다처럼 전달 할 수 있음</p>
<p>→ 람다 표현식보다 메서드 ㅊ암조를 사용하는 것이 더 가독성 좋을 때가 있음</p>
<pre><code class="language-java">inventory.sort((Apple a1,Apple a2) -&gt; a1.getWeight().compareTo(a2.getWeight()));

inventory.sort(comparing(Apple::getWeight)//메서드 참조와 java.util.Comparator.comparing 활용한 코드
//(Apple a) → a.getWeight() 축약</code></pre>
<h3 id="361-요약">3.6.1 요약</h3>
<p>메서드 참조</p>
<ul>
<li>특정 메서드만을 호출하는 람다의 축약형</li>
<li>예) 람다가 이 메서드를 직접 호출해 라고 명령 시 메서드를 어떻게 호출해야 하는지 설명을 참조하기 보다 메서드명을 직접 참조하는 것이 편리</li>
<li>메서드 참조 이용하면 기존 메서드 구현으로 람다 표현식을 만들 수 있음</li>
<li>가독성 향상</li>
<li>메서드명 앞에 구분자(::)를 붙임</li>
<li>괄호는 필요 없음</li>
</ul>
<p>ex)</p>
<pre><code class="language-java">(Apple apple) -&gt; apple.getWeight() =&gt; Apple::getWeight
() -&gt;  Thread.currentThread().dumpStack() =&gt; Thread.currentThread()::dumpStack
(str,i) -&gt; str.substring(i) =&gt; String::substring
(String s) -&gt; System.out.println(s) =&gt; System.out::println
(String s) -&gt; this.isValidName(s) =&gt; this::isValidName
//뭐야 책을 굉장히 보기 어렵게 해놨다;;;;</code></pre>
<p><strong>메서드 참조 만드는 법</strong></p>
<ol>
<li>정적 메서드 참조<ol>
<li>Integer의 parseInt Integer::parseInt</li>
</ol>
</li>
<li>다양한 형식의 인스턴스 메서드 참조<ol>
<li>String의 length 메서드는 String::length로 표현</li>
</ol>
</li>
<li>기존 객체의 인스턴스 메서드 참조<ol>
<li>Transcation객체를 할당 받은 expensvieTranscation지역 변수가 있고</li>
<li>Transaction객체에는 getValue가 있다면 expensiveTransaction::getValue 로 표현</li>
</ol>
</li>
</ol>
<p>컴파일러는 람다 표현식의 형식을 검사하던 방식과 비슷한 과정으로 메서드 참조가 주어진 함수형 인터페이스와 호환하는지 확인 </p>
<p>→ 메서드 참조는 콘텍스트의 형식과 일치해야함</p>
<h3 id="362-생성자-참조">3.6.2 생성자 참조</h3>
<p>ClassName::new 처럼 클래스명과 new 키워드를 이용해 기존 생성자의 참조를 만들 수 있음</p>
<p>ex) 인수가 없는 생성자 즉 Supplierd의 () → Apple 과 같은 시그니처를 갖는 생성자가 있다고 가정</p>
<pre><code class="language-java">Supplier&lt;Apple&gt; c1 = Apple::new;
Apple a1 = c1.get(); //Supplier의 get 메서드를 호출해서 새로운 Apple 객체를 만들 수 있음
//아래와 같은 코드
Supplier&lt;Apple&gt; c1 = () -&gt; new Apple(); //람다 표현식은 디폴트 생성자를 가진 Apple을 만듦
Apple a1 = c1.get(); //Supplier의 get 메서드를 호출해서 새로운 Apple 객체를 만들 수 있음</code></pre>
<p>Apple(Integer weight)라는 시그니처를 갖는 생성자는 Function 인터페이스와 같음</p>
<pre><code class="language-java">Function&lt;Integer,Apple&gt; c2= Apple::new;
Apple a2 = c2.apply(110);

Function&lt;Integer,Apple&gt; c2= (weight) -&gt; new Apple(weight);
Apple a2 = c2.apply(110);</code></pre>
<p>인스턴스화 하지 않고도 생성자에 접근할 수 있는 기능을 다양한 상황에 응용 가능</p>
<h2 id="37-람다-메서드-참조-활용하기">3.7 람다, 메서드 참조 활용하기</h2>
<h3 id="371-1단계--코드-전달">3.7.1 1단계 : 코드 전달</h3>
<p>sort 메서드에 정렬 전략을 전달 할 수 있나?</p>
<p>void sort(Comparator&lt;? super E&gt; c)</p>
<p>Comparator 객체를 인수로 받아 두 사과를 비교</p>
<p>객체 안에 동작을 포함시키는 방식으로 다양한 전략을 전달 </p>
<p>sort의 동작은 파라미터화 됨</p>
<p>→ sort에 전달된 정렬 전략 에 따라 sort의 동작 달라짐</p>
<pre><code class="language-java">public class AppleComparator implements Comparator&lt;Apple&gt;{
 public int compare(Apple a1, Apple a2)
{ return a1.getWeight().compareTo(a2.getWeight());
}
inventory sort(new AppleComparator());</code></pre>
<h3 id="372-2단계-익명클래스-사용">3.7.2 2단계: 익명클래스 사용</h3>
<pre><code class="language-java">inventory.sort(newComparator&lt;Apple&gt;(){
 public int compare(Apple a1, Apple a2)
{ return a1.getWeight().compareTo(a2.getWeight());
});</code></pre>
<h3 id="373-3단계-람다-표현식-사용">3.7.3 3단계: 람다 표현식 사용</h3>
<pre><code class="language-java">inventory.sort((Apple a1, Apple a2) -&gt; a1.getWeigth().compareTo(a2.getWeight()));
//형식 추론을 통해 아래와 같이 더 줄일 수 있음
inventory.sort((a1,a2) -&gt; a1.getWeight().compareTo(a2.getWeight()));</code></pre>
<p>가독성 더 향상</p>
<p>Comparator 는 comparable 키를 추출해서 Comparator 객체로 만드는 Function 함수를 인수로 받는 정적 메서드 comparing 포함</p>
<pre><code class="language-java">Comparator&lt;Apple&gt; c = Comparator.comparing((Apple a) -&gt; a.getWeight());

import static java.util.Comparator.comparing;
inventory.sort(comparing(apple -&gt; apple.getWeight())));</code></pre>
<h3 id="374-4단계-메서드-참조-사용">3.7.4 4단계: 메서드 참조 사용</h3>
<p>메서드 참조 이용하면 더 깔끔하고 간소하게 전달가능</p>
<pre><code class="language-java">inventory.srot(comparing(Apple::getWeight));</code></pre>
<h2 id="38-랃마-표현식을-조립할-수-있는-유용한-메서드">3.8 랃마 표현식을 조립할 수 있는 유용한 메서드</h2>
<h3 id="381-comparator-조합">3.8.1 Comparator 조합</h3>
<p>Comparator<Apple> c = Comparator.comparing(Apple::getWeight);</p>
<p><strong>역정렬</strong></p>
<p>inventory.srot(comparing(Apple::getWeight).reversed());</p>
<p>Comperator 연결</p>
<p>무게가 같은 두 사과가 존재할때 어떤 사과부터 나열&quot;?</p>
<p>→ 비교 결과를 더 다듬을 수 있는 두번째 Comparator를 만들 수 있음</p>
<pre><code class="language-java">inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getCountry));</code></pre>
<h3 id="382-predicate-조합">3.8.2 Predicate 조합</h3>
<p>Predicate 인퍼테이스는 복잡한 프레디케이트를 만들 수 있도록 netgate, and, or 세가지 메서드 제공 </p>
<pre><code class="language-java">Predicate&lt;Apple&gt; notRedApple = redApple.netgate();

Predicate&lt;Apple&gt; redAndHeavyApple = redApple.and(apple -&gt; apple.getWeight() &gt; 150);</code></pre>
<h3 id="383-function-조합">3.8.3 Function 조합</h3>
<p>Function 인터페이스는 Function 인스턴스를 반환하는 andThen,compose 두가지 디폴트 메서드를 제공</p>
<p>andThen 메서드는 주어진 함수를 먼저 적용한 결과를 다른 함수의 입력으로 전달하는 함수를 반환</p>
<p>compose 메서드는 인수로 주어진 함수를 먼저 실행한 다음에 그 결과를 외부 함수의 인수로 제공</p>
<h3 id="39-비슷한-수학-개념">3.9 비슷한 수학 개념</h3>
<h3 id="수학을-좋아하지-않으니-건너-뛰겠다">수학을 좋아하지 않으니 건너 뛰겠다.</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바 인 액션] Chapter 2 동작 파라미터화 코드 전달하기 ]]></title>
            <link>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-2-%EB%8F%99%EC%9E%91-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%ED%99%94-%EC%BD%94%EB%93%9C-%EC%A0%84%EB%8B%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@ju_yeong17/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94-%EC%9D%B8-%EC%95%A1%EC%85%98-Chapter-2-%EB%8F%99%EC%9E%91-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%ED%99%94-%EC%BD%94%EB%93%9C-%EC%A0%84%EB%8B%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 16 Dec 2025 05:37:28 GMT</pubDate>
            <description><![CDATA[<p>장기적인 관점에서 유지보수가 쉬워야 함</p>
<p>동작 파라미터화를 이용하면 자주 바뀌는 요구사항에 효과적으로 대응할 수 있음</p>
<p>동작 파라미터화 </p>
<ul>
<li>아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록</li>
<li>나중에 호출</li>
</ul>
<h2 id="21-변화하는-요구사항에-대응하기">2.1 변화하는 요구사항에 대응하기</h2>
<p>변화에 대응하는 코드를 구현하는 것은 어려운 일</p>
<p>모범 사례</p>
<p>기존의 농장 재고 목록 애플리케이션에 리스트에서 녹색 사과만 필터링 하는 기능 추가 한다고 가정</p>
<h3 id="211-첫번째-시도--녹색-사과-필터링">2.1.1 첫번째 시도 : 녹색 사과 필터링</h3>
<pre><code class="language-java">enum Color{RED,GREEN}

public static List&lt;Apple&gt; filterGreenApples(List&lt;Apple&gt; inventory){
    List&lt;Apple result = new ArrayList&lt;&gt;();
    for ( Apple apple: inventory){
        if(GREEN.equals(apple.getColor()){ // 녹색 사과만 필터링
            result.add(apple);
        }
    }
    return result;    
}</code></pre>
<p>이렇게 녹색 사과만 필터링을 할 수 있음</p>
<p>근데 갑자기 빨간색 사과를 필터링 하고 싶게 되면 메서드를 복사해서 새로운 메서드를 만들고 if 문의 조건을 빨간사과르 바꾸는 방법을 선택 할 수 있음</p>
<p>하지만 더 다양한 색을 필터링 할때 적절하게 대응 할 수 없음</p>
<p>→ 이렇게 반복될때는 코드를 추상화하는 좋은 규칙이 있음</p>
<h3 id="212-두번-째-시도-색을-파라미터화">2.1.2 두번 째 시도: 색을 파라미터화</h3>
<pre><code class="language-java">public static List&lt;Apple&gt; filterApplesByColor(List&lt;Apple&gt; inventory, Color color)
{
    List&lt;Apple&gt; result = new ArrayList&lt;&gt;();
    for ( Apple apple: inventory){
        if ( apple.getColor().equal(color)){
            result.add(apple);
        }
    }
    return result;
}</code></pre>
<p>이렇게 하면 </p>
<p>List<Apple> greendApples = filterApplesByColor(inventory , GREEN);</p>
<p>이런식으로 호출 할 수 있음</p>
<p>하지만 이때 무게로도 판단하고 싶다고 하면 또 다시 메서드를 복사하고 조건만 수정해 중복된 메서드가 생길 것</p>
<h3 id="213-세번째-시도-가능한-모든-속성으로-필터링">2.1.3 세번째 시도: 가능한 모든 속성으로 필터링</h3>
<pre><code class="language-java">public static List&lt;Apple&gt; filterApplesByColor(List&lt;Apple&gt; inventory, Color color, int weight, boolean flag))
{
    List&lt;Apple&gt; result = new ArrayList&lt;&gt;();
    for ( Apple apple: inventory){
        if ((flag &amp;&amp; appel.getColor().equals(color)) || 
            (!gloag &amp;&amp; apple.getWeigth() &gt; weight){
            result.add(apple);
        }
    }
    return result;
}</code></pre>
<p>이렇게 되면 형편 없는 코드가 됨</p>
<p>이걸 해결하기 위해 동작 파라미터화를 이용</p>
<h2 id="22-동작-파라미터화">2.2 동작 파라미터화</h2>
<p>참 거짓을 반환하는 함수를 프레디케이트</p>
<p>선택 조건을 결정하는 인터페이스 정의</p>
<pre><code class="language-java">public interface ApplePredicate{
    boolean test (Apple apple);
}

public class AppleHeavyWeightPredicate implements ApplePredicate{
    public boolean test(Apple apple){
        return apple.getWegith()&gt;150;
    }
}</code></pre>
<p>조건에 따라 filter 메서드가 다르게 동작할 것이라고 예상</p>
<p>→ 이를 전략 디자인 패턴</p>
<p>전략 디자인 패턴</p>
<ul>
<li>각 알고리즘을 캡슐화하는 알고리즘 패밀리를 정의해둔 다음에 런타임에 알고리즘을 선택하는 기법</li>
<li>ApplePredicate가 알고리즘 패밀리 AppleHeavyWeightPReciate 전략</li>
</ul>
<p>ApplePredicate는 어떻게 다양한 동작 수행?</p>
<ul>
<li>filterApples엣거 ApplePredicate 객체를 받아 애플의 조건을 검사하도록 메서드 수정 필요</li>
<li>동작 파라미터화<ul>
<li>메서드가 다양한 동작을 받아서 내부적으로 다양한 동작을 수행</li>
</ul>
</li>
</ul>
<p>filterApples메서드가 ApplePredicate 객체를 인수로 받도록 수정</p>
<ul>
<li>filterApples 메서드 내부에서 컬렉션을 반복하는 로직과 컬렉션의 각 요소에 적용할 동작을 분리할 수 있다는 점에서 이득</li>
</ul>
<h3 id="221-네번째-시도-추상적-조건으로-필터링">2.2.1 네번째 시도: 추상적 조건으로 필터링</h3>
<pre><code class="language-java">public static List&lt;Apple&gt; filterApples(List&lt;Apple&gt; inventory,ApplePrdicate p)
{
    List&lt;Apple&gt; result = new ArrayList&lt;&gt;();
    for ( Apple apple: inventory){
        if ( p.test(apple){ //프레디케이트 객체로 사과 검사 조건을 캡슐화
            result.add(apple);
        }
    }
    return result;
}</code></pre>
<p>이제 필요한 대로 다양한 ApplePredicate를 만들어서 filterApples 메서드로 전달 가능</p>
<p>ex ) 150 그램이 넘는 빨간 사과를 검색해달라고 부탁</p>
<pre><code class="language-java">public class AppleRedAndHeavyPredicate implements ApplePredicate{
    public boolean test(Apple apple){
        return RED.equlas(apple.getColor())
            &amp;&amp; apple.getWegith()&gt; 150;
    }
}</code></pre>
<p>한개의 파라미터, 다양한 동작</p>
<p>컬렉션 탐색 로직과 각 항목에 적용할 동작을 분리할 수 잇다는 것이 동작 파라미터화 의 강점</p>
<h2 id="23-복잡한-과정-간소화">2.3 복잡한 과정 간소화</h2>
<p>위에처럼 한다면 로직과 관련없는 코드가 많이 추가 됨</p>
<p>자바에서는 클래스의 선언과 인스턴스화를 동시에 수행할 수 있도록 익명 클래스 기법 제공</p>
<ul>
<li>모든것을 해결하진 않지만 코드 양 줄일 수 잇음</li>
</ul>
<h3 id="231-익명-클래스">2.3.1 익명 클래스</h3>
<p>익명 클래스</p>
<ul>
<li>자바의 지역 클래스와 비슷한 개념</li>
<li>이름이 없는 클래스</li>
<li>클래스 선언과 인스턴스화를 동시에 할 수 있다.</li>
</ul>
<h3 id="232-다섯번째-시도-익명-클래스-사용">2.3.2 다섯번째 시도 익명 클래스 사용</h3>
<pre><code class="language-java">List&lt;Apple&gt; redApples = filterApples(inventory, new ApplePredicate(){
    public boolean test(Apple apple){
        return RED.equals(apple.getColor());
    }
});</code></pre>
<p>부족한점</p>
<ol>
<li>익명클래스는 여전히 많은 공간 차지</li>
<li>익명 클래스의 사용에 익숙하지 않음</li>
</ol>
<h3 id="233-여섯-번째-시도-람다-표현식-사용">2.3.3 여섯 번째 시도: 람다 표현식 사용</h3>
<pre><code class="language-java">List&lt;Apple&gt; result = filterApples(inventory, (Apple apple) -&gt; RED.equals(apple.getColor()));</code></pre>
<h3 id="234-일곱-번째-시도-리스트-형식으로-추상화">2.3.4 일곱 번째 시도: 리스트 형식으로 추상화</h3>
<pre><code class="language-java">public interface Predicate&lt;T&gt;{
 boolean test(T t);
}

public static &lt;T&gt; List&lt;T&gt; filter(List&lt;T&gt; list, Predicate&lt;T&gt; p ){
    List&lt;T&gt; result = new ArrayList&lt;&gt;();
    for(T e: list){
        if(p.test(e)){
            result.add(e);
        }
    }
    return result;
}</code></pre>
<p>이제 바나나, 오렌지 , 정수, 문자열등의 리스트에 필터 메서드 사용 가능</p>
<h2 id="24-실전-예제">2.4 실전 예제</h2>
<h3 id="241-comparator-로-정렬">2.4.1 Comparator 로 정렬</h3>
<p>컬렉션 정렬은 반복되는 프로그래밍 작업</p>
<p>자바 8 List 에는 sort 기능 포함</p>
<p>java.util.Comparator 객체를 이용해서 sort의 동작 파라미터화</p>
<pre><code class="language-java">//java.util.Comparator
public interface Comparator&lt;t&gt;{
 int c ompar(T o1, To2);
}</code></pre>
<p>sort 메서드의 동작 다양화</p>
<p>ex) 익명 클래스를 이용해서 무게가 적은 순서로 목록에서 사과를 정렬</p>
<pre><code class="language-java">inventory.sort(new Comparator&lt;Apple&gt;(){
 public int compare(Apple a1, Apple a2){
     return a1.getWright().commjpareTo(a2.getWeight());
 }
});
//람다 사용하면
inventory.sort((Apple a1, Apple a2) -&gt; a1.getWeight().compareTo(a2.getWeight()));</code></pre>
<h3 id="242-runnable로-코드-블록-실행하기">2.4.2 Runnable로 코드 블록 실행하기</h3>
<p>자바 스레드를 이용하면 병렬로 코드 블록을 실행할 수 있음</p>
<p>자바 8까지는 Thread 생성자에 객체만을 전달 할 수 있었으므로 보통 결과를 반환하지 않는 void run 메소드를 포함하는 익명 클래스가 Runnable 인터페이스를 구현 하도록 하는 것이 일반적</p>
<pre><code class="language-java">public interface Runnable{
 void run();
}

Thread t = new Thread(new Runnable(){
 public void run()}{
  System.out.println(&quot;Hello world&quot;);
  }
 });

 //람다
 Thread t= new Thread(() -&gt; Sysmtem.out.println(&quot;Hello World&quot;));</code></pre>
<h3 id="243-callable-을-결과로-반환하기">2.4.3 Callable 을 결과로 반환하기</h3>
<p>ExecutorService 인터페이스</p>
<ul>
<li>태스크 제출과 실행 과정의 연관성을 끊어줌</li>
<li>태스트를 스레드 풀로 보내고 결과를 Future로 저장</li>
<li>Runnable 의 업그레이드 버전</li>
</ul>
<h3 id="244-gui-이벤트처리">2.4.4 GUI 이벤트처리</h3>
<p>변화에 대응할 수 있는 유연한 코드 필요 </p>
<p>모든 동작에 반응 할 수 있기 때문</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GraphQL 이란?]]></title>
            <link>https://velog.io/@ju_yeong17/GraphQL-%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@ju_yeong17/GraphQL-%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Sun, 31 Aug 2025 06:57:05 GMT</pubDate>
            <description><![CDATA[<p>GraphQL 을 쓰는 일이 생길 것 같아서 가볍게 무엇인지 알아보았다.</p>
<h2 id="📌-graphql-이란">📌 GraphQL 이란?</h2>
<p>GraphQL은 페이스북에서 만든 쿼리 언어이다. SQL과 마찬가지로 쿼리 언어라고 볼 수 있다.
SQL과는 언어적 구조 차이, 실전에서 쓰이는 방식이 매우 크다. 
SQL은 데이터베이스 시스템에 저장된 데이터를 효율적으로 가져오는 것이 목적이고 주로 백엔드 시스템에서 작성하고 호출한다.
GraphQL은 웹 클라이언트가 데이터를 서버로부터 효율적으로 가져오는 것이 목적이고 주로 클라이언트 시스템에서 작성하고 호출한다.</p>
<p>GraphQL 예시</p>
<pre><code class="language-graphql">{
  hero {
    name
    friends {
      name
    }
  }
}</code></pre>
<h2 id="📌-restapi-vs-graphql">📌 RestAPI vs GraphQL</h2>
<p>RestAPI는 URl, Method 등을 조합하기 때문에 다양한 Endpoint가 존재 하지만 GraphQL은 하나의 Endpoint만 존재한다. 또한 GraphQL API에서는 불러오는 데이터의 종류를 쿼리 조합을 통해 결정 예를 들어, Rest API에서는 각 Endpoint 마다 데이터 베이스 SQL 쿼리가 달라지는 반면, GraphQL API는 GraphQL 스키마의 타입마다 데이터 베이스 SQL 쿼리가 달라진다.
<img src="https://velog.velcdn.com/images/ju_yeong17/post/d5e31b78-ac0f-484a-93c5-fa3b40f034dc/image.png" alt=""></p>
<h2 id="📌-graphql-구조">📌 GraphQL 구조</h2>
<p>쿼리/뮤테이션 
<img src="https://velog.velcdn.com/images/ju_yeong17/post/7cdc5197-6e57-4fed-a5db-0f5ef8f5fb49/image.png" alt=""></p>
<p>GraphQL 쿼리문 우측은 응답데이터 형식이다.
GraphQL은 쿼리와 뮤테이션으로 나누는데 쿼리는 데이터 읽는 데 사용하고 뮤테이션은 데이터를 변조하는데 사용한다. </p>
<p>GraphQL은 프론트엔드와의 협업 방식에 영향을 준다. 
기존에는 백엔드에서 전달해주는 API 요청, 응답 형식에 의존했지만 GraphQL을 사용한 방식은 의존도가 많이 사라지게 된다. </p>
<h2 id="📌-리졸버resolver">📌 리졸버(resolver)</h2>
<p>데이터 베이스 사용시, 데이터를 가져오기 위해 SQL을 작성했고 데이터베이스 어플리케이션을 사용해 데이터를 가져오는 구체적인 과정이 구현 되어있었다. 하지만 GraphQL에서는 데이터 가져오는 구체적인 과정을 직접 구현해야한다. 
GraphQL 쿼리문 파싱은 대부분 라이브러리에서 처리하지만 GraphQL에서 데이터를 가져오는 구체적인 과정은 resolver가 담당하고 이를 직접 구현해야한다.</p>
<p>GraphQL 쿼리에서는 각각의 필드마다 함수가 하나씩 존재한다고 생각하면 된다. 각각의 함수를 리졸버 라고 한다.</p>
<h2 id="📌-장점">📌 장점</h2>
<ol>
<li>효율적인 데이터 로딩</li>
</ol>
<ul>
<li>클라이언트가 필요한 데이터만을 정확히 요청할 수 있어서 네트워크 사용을 최적화 할 수 있고 오버페칭과 언더페칭을 방지</li>
</ul>
<ol start="2">
<li>강력한 타이핑 시스템</li>
</ol>
<ul>
<li>GraphQL 스키마는 API의 모든 객체 타입을 명확하게 정의한다. API를 문서화 하는 동시에 클라이언트와 서버 간의 계약으로 작용하여 개발 중 버그 발생 가능성을 줄여준다.</li>
</ul>
<ol start="3">
<li>요청과 응답의 유연성</li>
</ol>
<ul>
<li>하나의 요청으로 여러 리소스를 가져오거나 복잡한 데이터 구조를 한번에 조립할 수 있다.</li>
</ul>
<ol start="4">
<li>실시간 데이터</li>
</ol>
<ul>
<li>GraphQL서버를 Websockets와 연결하여 구독을 통해 실시간으로 데이터 변경을 클라이언트에 푸시할 수 있다.</li>
</ul>
<h2 id="📌-단점">📌 단점</h2>
<ol>
<li>쿼리 복잡성</li>
</ol>
<ul>
<li>복잡한 쿼리는 서버에 큰 부담을 줄 수 있다. N+1 문제를 야기할 수 있다.</li>
</ul>
<ol start="2">
<li>캐싱의 어려움</li>
</ol>
<ul>
<li>HTTP 캐싱은 URL 기반으므로 REST API에서는 자연스럽게 지원된다. 그러나 GraphQL은 대개 하나의 엔드포인트를 사용하므로 , 캐싱이 더 복잡해질 수 있다.</li>
</ul>
<ol start="3">
<li>파일 업로드</li>
</ol>
<ul>
<li>GraphQL 자체는 파일 업로드를 지원하지 않는다. 따라서 파일 업로드를 구현하려면 멀티파트폼 요청을 처리하거나별더의 라이브러리를 사용해야한다.</li>
</ul>
<ol start="4">
<li>학습 곡선</li>
</ol>
<ul>
<li>GraphQL은 새로운 개념과 다양한 요소를 도입함으로써 상당한 학습 곡선을 가진다.</li>
</ul>
<hr>
<p>GraphQL이 그냥 무엇인지에 대한 개념을 알게 된 것 같다. 직접 사용하고 만드려면 조금 더 깊이 공부해야할 것 같다. 어려운 친구다 !!!!!</p>
<p>[참고 자료]
<a href="https://tech.kakao.com/posts/364">https://tech.kakao.com/posts/364</a>
<a href="https://ian-info.tistory.com/18">https://ian-info.tistory.com/18</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[주니어 백엔드 개발자가 반드시 알아야 할 실무 지식] 4장]]></title>
            <link>https://velog.io/@ju_yeong17/%EC%A3%BC%EB%8B%88%EC%96%B4-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%B0%98%EB%93%9C%EC%8B%9C-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%EC%8B%A4%EB%AC%B4-%EC%A7%80%EC%8B%9D-4%EC%9E%A5</link>
            <guid>https://velog.io/@ju_yeong17/%EC%A3%BC%EB%8B%88%EC%96%B4-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%B0%98%EB%93%9C%EC%8B%9C-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%EC%8B%A4%EB%AC%B4-%EC%A7%80%EC%8B%9D-4%EC%9E%A5</guid>
            <pubDate>Wed, 20 Aug 2025 14:37:54 GMT</pubDate>
            <description><![CDATA[<h1 id="외부-연동-문제일-때-살펴봐야-할-것들">외부 연동 문제일 때 살펴봐야 할 것들</h1>
<h2 id="📌-우리는-문제가-없는데"><strong>📌</strong> 우리는 문제가 없는데</h2>
<p>연동하는 서비스에 장애가 발생 시 우리 서비스도 영향을 받음</p>
<h2 id="📌-타임아웃"><strong>📌</strong> 타임아웃</h2>
<ul>
<li>연동 서비스를 호출할때 타임 아웃을 적절히 설정하지 않으면 서비스 품질 나빠질 수 있음</li>
<li>타임아웃을 걸지 않으면 대기가 계속 쌓이기 때문에 서버가 받는 부하가 배가 됨</li>
<li>반응 없는 무한 대기보다는 에러 화면이라도 보는게 나음</li>
</ul>
<p><strong>2가지 타임아웃, 연결 타임아웃, 읽기 타임 아웃</strong></p>
<p>네트워크 연결시도 단계</p>
<ul>
<li>연결에는 시간이 걸림</li>
<li>대기 시간도 함께 증가</li>
<li>연결 타임아웃을 설정해 연결 대기 시간을 제한</li>
</ul>
<p>연결이 되면 요청을 전송하고 응답 기다림</p>
<ul>
<li>이때 응답을 받기까지 시간이 걸리면 또 대기 시간 문제 발생</li>
</ul>
<p>→ 읽기 타임아웃 설정</p>
<h2 id="📌-재시도"><strong>📌</strong> 재시도</h2>
<p><strong>재시도 가능 조건</strong></p>
<p>항상 재시도를 할 수 있는건 아님</p>
<p>가능 조건</p>
<ol>
<li>단순 조회기능</li>
<li>연결 타임아웃<ol>
<li>읽기 타임아웃은 재시도 할때 주의해야함 <ol>
<li>이미 연동 서비스가 요청을 처리하고 있는 중이기 때문에 포인트가 중복 차감되는 데이터 문제가 생길 수 있음</li>
</ol>
</li>
</ol>
</li>
<li>멱등성을 가진 변경 기능<ol>
<li>멱등성 : 연산을 여러번 적용해도 결과가 달라지지 않는 성질</li>
</ol>
</li>
</ol>
<p><strong>재시도 횟수와 간격</strong></p>
<p>재시도 할때 횟수와 간격 을 결정해야함</p>
<ul>
<li>재시도를 무한정 할 수 없음</li>
<li>1~2번 정도 재시도 적당</li>
<li>간격 중요</li>
</ul>
<p><strong>재시도 폭풍 안티패턴</strong></p>
<ul>
<li>연동 서비스에는 더 큰 부하를 줄 수 있음</li>
</ul>
<p>→ 연동 서비스의 성능 상황도 고려</p>
<h2 id="📌-동시-요청-제한"><strong>📌</strong> 동시 요청 제한</h2>
<p>연동 서비스 임계치 이상 요청을 보낼때 성능 저하 문제를 완화하는 방법</p>
<ul>
<li>일정 수준이상으로 요청을 보내지 않는 것</li>
</ul>
<h2 id="📌-서킷-브레이커"><strong>📌</strong> 서킷 브레이커</h2>
<p>서킷 브레이커는 누전 차단기와 비슷하게 동작</p>
<ul>
<li>과도한 오류가 발생하면 연동을 중지시키고 바로 에러 응답</li>
<li>닫힘, 열림, 반 열림 3가지 상태 갖음</li>
<li>닫힘 상태로 시작<ul>
<li>모든 요청을 연동 서비스에 전달</li>
<li>오류 발생시 지정한 임계치 초과</li>
</ul>
</li>
<li>초과하면 열림 상태<ul>
<li>연동 요청은 수행하지 않고 바로 에러 응답 리턴</li>
<li>지정된 시간 동안 유지</li>
</ul>
</li>
<li>시간 지나면 반 열림 상태<ul>
<li>일부 요청에 한해 연동 시도</li>
<li>이 기간 동안 연동에 성공하면 닫힘 상태로 복귀</li>
<li>실패하면 다시 열림 상태로 전환</li>
</ul>
</li>
</ul>
<h2 id="📌-외부-연동과-db-연동"><strong>📌</strong> 외부 연동과 DB 연동</h2>
<p><strong>외부 연동과 트랜잭션 처리</strong></p>
<p>오류 발생 시 DB 트랜잭션을 어떻게 처리할 지 알맞게 판단해야함</p>
<p>흔히 발생하는 상황</p>
<ul>
<li>외부 연동 실패했을때 트랜잭션을 롤백<ul>
<li>변경한 데이터 DB에 반영되지 않음</li>
<li>롤백을 통해 DB 데이터에 이상이 생기는 것을 방지</li>
</ul>
</li>
<li>외부 연동은 성공했지만 디비 연동에 실패해 트랜잭션<ul>
<li>취소 API를 통해 이전 상태로 되돌리는 것이 필요</li>
<li>이 경우 성공 API를 호출해도 의미 없음</li>
</ul>
</li>
</ul>
<p>읽기 타임아웃이 발생해 트래잭션을 롤백할 때는 외부 서비스가 성공 했을 가능성도 염두</p>
<p>두가지 검토</p>
<ul>
<li>일정 주기로 두 시스템의 데이터가 일치하는 지 확인하고 보정</li>
<li>성공확인 API를 호출하는 방식<ul>
<li>성공 응답오면 트랜잭션 지속</li>
<li>실패 응답 오면 트랜잭션 롤백</li>
<li>취소 API 호출</li>
</ul>
</li>
</ul>
<p><strong>외부 연동이 느려질 때 DB 커넥션 풀 문제</strong></p>
<p>외부 연동이 느려지면서 발생하는 커넥션 풀 부족 현상</p>
<p>DB 연동과 무관하게 외부 연동을 실행할 수 있따면 DB 커넥션을 사용하기 전이나 후에 외부 연동을 시도하는 방안도 고려 가능</p>
<p>→ 커넥션 풀이 포화되는 상황 방지</p>
<h2 id="📌-http-커넥션-풀"><strong>📌</strong> HTTP 커넥션 풀</h2>
<p>DB 커넥션 풀이 DB 연결에 걸리는 시간을 줄여 성능을 높이는 것 처럼 HTTP 연결도 커넥션 풀을 사용하면 연결 시간을 줄일 수 있어 응답 속도 향상에 도움</p>
<p>3가지 고려</p>
<ul>
<li>HTTP 커넥션 풀의 크기<ul>
<li>연동할 서비스의 성능에 따라 결정</li>
<li>성능 미 고려 하고 무턱대고 커넥션 풀 크기 늘리면 순간적으로 트래팩이 몰릴 때 연동 서비스의 응답 시간이 급격히 느려질 수 있음</li>
</ul>
</li>
<li>풀에서 HTTP 커넥션을 가져올 때까지 대기하는 시간<ul>
<li>대기 시간이 길어지면 응답 시간도 길어지므로 대기 시간은 수 초 이내의 짧은 시간으로 설정하는 것이 좋음</li>
</ul>
</li>
<li>HTTP 커넥션을 유지할 시간<ul>
<li>무한정 유지 되지 않음</li>
<li>연동 서비스에 맞춰 유지 시간을 적절히 설정</li>
</ul>
</li>
</ul>
<h2 id="📌-연동-서비스-이중화"><strong>📌</strong> 연동 서비스 이중화</h2>
<p>연동 서비스 이중화할지 여부 결정 고려 할거 2가지</p>
<ul>
<li>해당 기능이 서비스의 핵심인지 여부</li>
<li>이중화 비용이 감당 가능한 수준인지<h2 id="📌-우리는-문제가-없는데-1"><strong>📌</strong> 우리는 문제가 없는데</h2>
</li>
</ul>
<p>연동하는 서비스에 장애가 발생 시 우리 서비스도 영향을 받음</p>
<h2 id="📌-타임아웃-1"><strong>📌</strong> 타임아웃</h2>
<ul>
<li>연동 서비스를 호출할때 타임 아웃을 적절히 설정하지 않으면 서비스 품질 나빠질 수 있음</li>
<li>타임아웃을 걸지 않으면 대기가 계속 쌓이기 때문에 서버가 받는 부하가 배가 됨</li>
<li>반응 없는 무한 대기보다는 에러 화면이라도 보는게 나음</li>
</ul>
<p><strong>2가지 타임아웃, 연결 타임아웃, 읽기 타임 아웃</strong></p>
<p>네트워크 연결시도 단계</p>
<ul>
<li>연결에는 시간이 걸림</li>
<li>대기 시간도 함께 증가</li>
<li>연결 타임아웃을 설정해 연결 대기 시간을 제한</li>
</ul>
<p>연결이 되면 요청을 전송하고 응답 기다림</p>
<ul>
<li>이때 응답을 받기까지 시간이 걸리면 또 대기 시간 문제 발생</li>
</ul>
<p>→ 읽기 타임아웃 설정</p>
<h2 id="📌-재시도-1"><strong>📌</strong> 재시도</h2>
<p><strong>재시도 가능 조건</strong></p>
<p>항상 재시도를 할 수 있는건 아님</p>
<p>가능 조건</p>
<ol>
<li>단순 조회기능</li>
<li>연결 타임아웃<ol>
<li>읽기 타임아웃은 재시도 할때 주의해야함 <ol>
<li>이미 연동 서비스가 요청을 처리하고 있는 중이기 때문에 포인트가 중복 차감되는 데이터 문제가 생길 수 있음</li>
</ol>
</li>
</ol>
</li>
<li>멱등성을 가진 변경 기능<ol>
<li>멱등성 : 연산을 여러번 적용해도 결과가 달라지지 않는 성질</li>
</ol>
</li>
</ol>
<p><strong>재시도 횟수와 간격</strong></p>
<p>재시도 할때 횟수와 간격 을 결정해야함</p>
<ul>
<li>재시도를 무한정 할 수 없음</li>
<li>1~2번 정도 재시도 적당</li>
<li>간격 중요</li>
</ul>
<p><strong>재시도 폭풍 안티패턴</strong></p>
<ul>
<li>연동 서비스에는 더 큰 부하를 줄 수 있음</li>
</ul>
<p>→ 연동 서비스의 성능 상황도 고려</p>
<h2 id="📌-동시-요청-제한-1"><strong>📌</strong> 동시 요청 제한</h2>
<p>연동 서비스 임계치 이상 요청을 보낼때 성능 저하 문제를 완화하는 방법</p>
<ul>
<li>일정 수준이상으로 요청을 보내지 않는 것</li>
</ul>
<h2 id="📌-서킷-브레이커-1"><strong>📌</strong> 서킷 브레이커</h2>
<p>서킷 브레이커는 누전 차단기와 비슷하게 동작</p>
<ul>
<li>과도한 오류가 발생하면 연동을 중지시키고 바로 에러 응답</li>
<li>닫힘, 열림, 반 열림 3가지 상태 갖음</li>
<li>닫힘 상태로 시작<ul>
<li>모든 요청을 연동 서비스에 전달</li>
<li>오류 발생시 지정한 임계치 초과</li>
</ul>
</li>
<li>초과하면 열림 상태<ul>
<li>연동 요청은 수행하지 않고 바로 에러 응답 리턴</li>
<li>지정된 시간 동안 유지</li>
</ul>
</li>
<li>시간 지나면 반 열림 상태<ul>
<li>일부 요청에 한해 연동 시도</li>
<li>이 기간 동안 연동에 성공하면 닫힘 상태로 복귀</li>
<li>실패하면 다시 열림 상태로 전환</li>
</ul>
</li>
</ul>
<h2 id="📌-외부-연동과-db-연동-1"><strong>📌</strong> 외부 연동과 DB 연동</h2>
<p><strong>외부 연동과 트랜잭션 처리</strong></p>
<p>오류 발생 시 DB 트랜잭션을 어떻게 처리할 지 알맞게 판단해야함</p>
<p>흔히 발생하는 상황</p>
<ul>
<li>외부 연동 실패했을때 트랜잭션을 롤백<ul>
<li>변경한 데이터 DB에 반영되지 않음</li>
<li>롤백을 통해 DB 데이터에 이상이 생기는 것을 방지</li>
</ul>
</li>
<li>외부 연동은 성공했지만 디비 연동에 실패해 트랜잭션<ul>
<li>취소 API를 통해 이전 상태로 되돌리는 것이 필요</li>
<li>이 경우 성공 API를 호출해도 의미 없음</li>
</ul>
</li>
</ul>
<p>읽기 타임아웃이 발생해 트래잭션을 롤백할 때는 외부 서비스가 성공 했을 가능성도 염두</p>
<p>두가지 검토</p>
<ul>
<li>일정 주기로 두 시스템의 데이터가 일치하는 지 확인하고 보정</li>
<li>성공확인 API를 호출하는 방식<ul>
<li>성공 응답오면 트랜잭션 지속</li>
<li>실패 응답 오면 트랜잭션 롤백</li>
<li>취소 API 호출</li>
</ul>
</li>
</ul>
<p><strong>외부 연동이 느려질 때 DB 커넥션 풀 문제</strong></p>
<p>외부 연동이 느려지면서 발생하는 커넥션 풀 부족 현상</p>
<p>DB 연동과 무관하게 외부 연동을 실행할 수 있따면 DB 커넥션을 사용하기 전이나 후에 외부 연동을 시도하는 방안도 고려 가능</p>
<p>→ 커넥션 풀이 포화되는 상황 방지</p>
<h2 id="📌-http-커넥션-풀-1"><strong>📌</strong> HTTP 커넥션 풀</h2>
<p>DB 커넥션 풀이 DB 연결에 걸리는 시간을 줄여 성능을 높이는 것 처럼 HTTP 연결도 커넥션 풀을 사용하면 연결 시간을 줄일 수 있어 응답 속도 향상에 도움</p>
<p>3가지 고려</p>
<ul>
<li>HTTP 커넥션 풀의 크기<ul>
<li>연동할 서비스의 성능에 따라 결정</li>
<li>성능 미 고려 하고 무턱대고 커넥션 풀 크기 늘리면 순간적으로 트래팩이 몰릴 때 연동 서비스의 응답 시간이 급격히 느려질 수 있음</li>
</ul>
</li>
<li>풀에서 HTTP 커넥션을 가져올 때까지 대기하는 시간<ul>
<li>대기 시간이 길어지면 응답 시간도 길어지므로 대기 시간은 수 초 이내의 짧은 시간으로 설정하는 것이 좋음</li>
</ul>
</li>
<li>HTTP 커넥션을 유지할 시간<ul>
<li>무한정 유지 되지 않음</li>
<li>연동 서비스에 맞춰 유지 시간을 적절히 설정</li>
</ul>
</li>
</ul>
<h2 id="📌-연동-서비스-이중화-1"><strong>📌</strong> 연동 서비스 이중화</h2>
<p>연동 서비스 이중화할지 여부 결정 고려 할거 2가지</p>
<ul>
<li>해당 기능이 서비스의 핵심인지 여부</li>
<li>이중화 비용이 감당 가능한 수준인지</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[주니어 백엔드 개발자가 반드시 알아야 할 실무 지식] 3장]]></title>
            <link>https://velog.io/@ju_yeong17/%EC%A3%BC%EB%8B%88%EC%96%B4-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%B0%98%EB%93%9C%EC%8B%9C-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%EC%8B%A4%EB%AC%B4-%EC%A7%80%EC%8B%9D-3%EC%9E%A5</link>
            <guid>https://velog.io/@ju_yeong17/%EC%A3%BC%EB%8B%88%EC%96%B4-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%B0%98%EB%93%9C%EC%8B%9C-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%EC%8B%A4%EB%AC%B4-%EC%A7%80%EC%8B%9D-3%EC%9E%A5</guid>
            <pubDate>Mon, 11 Aug 2025 14:39:28 GMT</pubDate>
            <description><![CDATA[<h2 id="📌-성능에-핵심인-db">📌 성능에 핵심인 DB</h2>
<p>H 서비스는 특정 시간에 응답 시간이 길어져 불만을 받았음</p>
<p>→ DB 풀 스캔이 문제가 됨</p>
<p>Db 성능은 연동하는 모든 서버 성능에 영향을 줌 </p>
<p>→ DB 성능 문제를 충분히 줄이거나 없앨 수 있음</p>
<h2 id="📌-조회-트래픽을-고려한-인덱스-설계">📌 조회 트래픽을 고려한 인덱스 설계</h2>
<p>DB 테이블 설계시 조회 기능, 트래픽 규모 고려 해야함</p>
<p>풀 스캔이 발생하지 않도록 하려면 조회 패턴을 기준으로 인덱스 설계 필요</p>
<p><strong>단일 인덱스와 복합 인덱스</strong></p>
<p>데이터가 많을 경우 단일 인덱스가 아닌 복합 인덱스로 조회하는게 좋음</p>
<p><strong>선택도를 고려한 인덱스 칼럼 선택</strong></p>
<p>인덱스를 생성할 때 선택도가 높은 칼럼을 골라야함</p>
<ul>
<li>선택도는 인덱스에서 특정 칼럼의 고유한 값 비율을 나타냄</li>
<li>선택도가 높으면 고유한 값이 많음</li>
<li>조회 효율이 높아짐</li>
</ul>
<p><strong>커버링 인덱스 활용하기</strong></p>
<p>커버링 인덱스</p>
<ul>
<li>특정 쿼리를 실행하는데 필요한 칼럼을 모두 포함하는 인덱스</li>
<li>쿼리 실행 효율을 높일 수 있음</li>
</ul>
<p><strong>인덱스는 필요한 만큼만 만들기</strong></p>
<p>복합 인덱스가 효과를 발휘하려면 데이터 개수가 조회 성능에 영향을 줄 만큼 많아야함</p>
<p>효과가 적은 인덱스를 추가하면 오히려 성능이 나빠질 수 있음</p>
<h2 id="📌-몇-가지-조회-성능-개선하는-방법">📌 몇 가지 조회 성능 개선하는 방법</h2>
<p><strong>미리 집계하기</strong></p>
<p>집계 데이터를 미리 계산해서 별도 칼럼에 저장 하는 방법</p>
<p><strong>페이지 기준 목록 조회 대신 ID 기준 목록 조회 방식 사용하기</strong></p>
<p><strong>조회 범위를 시간 기준으로 제한하기</strong></p>
<p>조회 범위를 시간 기준으로 제한 하는 것</p>
<ul>
<li>사용하기 적당한 예는 뉴스 기사 목록</li>
</ul>
<p>최신 데이터만 조회하는 것</p>
<ul>
<li>DB 성능 향상</li>
<li>DB는 성능을 높이기 위해 메모리 캐시 사용하는데 최신 데이터일 수록 메모리 캐시에 적재될 확률이 높음</li>
</ul>
<p><strong>전체 개수 세지 않기</strong></p>
<p>목록을 표시하는 기능은 전체 개수를 함께 표시하는 경우가 많은데 성능 문제를 향상 시키려면 전체 개수를 표시 하지 않는 것이 좋음</p>
<p><strong>오래된 데이터 삭제 및 분리 보관하기</strong></p>
<p>데이터 증가폭을 낮추는 방법 중 하나는 과거 데이터를 삭제하는 것</p>
<ul>
<li>삭제하면 데이터 개수를 일정하게 유지할 수 있음</li>
<li>성능 또한 일정 수준으로 유지</li>
</ul>
<p><strong>DB 장비 확장하기</strong></p>
<p>우선 DB 장비를 수직으로 확장해 서비스를 가능한 상태로 유지하고 개선 시간을 벌어야함</p>
<ul>
<li>클라우드 사용하면 짧은 시간안에 성능 높일 수 있음</li>
<li>더빠른 CPU , 더 많은 CPU</li>
</ul>
<p>수평 확장도 고려</p>
<ul>
<li>DB가 처리할 수 있는 트래픽을 늘릴 수 있음</li>
</ul>
<p><strong>별도 캐시 서버 구성하기</strong></p>
<p>여러 이유로 DB가 트래픽을 처리하는데 어려움이 있다면 별도의 캐시 서버를 구성하는 것도 고려</p>
<ul>
<li>대규모 트래픽이 발생하는 서비스는 기본적으로 캐시 서버 를 사용</li>
<li>대규모가 아니여도 캐시 서버를 잘 활용하면 적은 비용으로 더많은 트래픽을 처리 가능</li>
</ul>
<h2 id="📌-알아두면-좋을-몇가지-주의사항">📌 알아두면 좋을 몇가지 주의사항</h2>
<p><strong>쿼리 타임아웃</strong></p>
<p>응답 시간은 처리량에 큰 영향을 줌</p>
<ul>
<li>동시 접속자가 많은때 응답 시간이 길어지면 재시도로 인한 서버 부하가 증가함</li>
<li>그래서 쿼리 실행 시간을 제한을 함</li>
</ul>
<p>쿼리 타임아웃</p>
<ul>
<li>서비스와 기능의 특성에 따라 다르게 설정</li>
</ul>
<p><strong>상태 변경 기능은 복제 DB에서 조회하지 않기</strong> </p>
<p>주 DB- 복제 DB 구조를 사용할때 변경은 주 DB 조회는 복제 DB를 사용</p>
<p>이를 잘못 이해해 SELECT 쿼리를 무조건 복제 하는 경우 </p>
<ul>
<li>순간적으로 데이터 가 일치 하지 않는 문제 발생<ul>
<li>데이터 복제에는 지연이 발생하기 때문</li>
</ul>
</li>
<li>트랜잭션 문제 발생<ul>
<li>데이터 복제는 트랜잭션 커밋 시점에 이뤄짐</li>
</ul>
</li>
</ul>
<p><strong>배치 쿼리 실행 시간 증가</strong></p>
<p>배치 프로그램</p>
<ul>
<li>데이터를 일괄로 조회하거나 집계하거나 생성하는 작업</li>
<li>메모리 를 많이 사용하거나 특정 임계점을 넘겨 실행 시간이 예측할 수 없을 만큼 길어질 수 있음<ul>
<li>예방을 위해 쿼리의 실행시간을 지속적으로 추적</li>
<li>춪겅르 통해 쿼리 실행시간이 갑자기 큰 폭으로 증가했는지를 감지</li>
<li>문제가 되는 쿼리를 발견하면 원인을 찾아 해결할 수 있음</li>
</ul>
</li>
</ul>
<p>가장 빠른 해결책</p>
<ul>
<li>DB 장비의 사양 높이기<ul>
<li>다른 방법도 함께 고려<ul>
<li>커버링 인덱스 활용</li>
<li>데이터 일정 크기로 나눠처리</li>
</ul>
</li>
</ul>
</li>
<li>데이터를 일정 크기로 나눠 처리하는 것도 해결책</li>
</ul>
<p><strong>타입이 다른 칼럼 조인 주의</strong></p>
<p>칼럼의 타입이 서로 다르면 실제로 인덱스를 활용하지 못할 수 있음</p>
<p><strong>테이블 변경은 신중하게</strong></p>
<p>데이터가 많은 테이블에 새로운 칼럼을 추가하거나 기존 열거 타입 칼럼을 변경할 때는 매우 주의가 필요</p>
<p>서비스가 장시간 중단되는 상황 발생</p>
<p>이유</p>
<ul>
<li>Mysql은 테이블 변경시 새 테이블 생성 후 원본 테이블 데이터 복사한 뒤 복사가 완료되면 새테이블로 대체<ul>
<li>복사과정에는 작업이 허용되지 않아 복사 시간만큼 서비스 중단</li>
</ul>
</li>
</ul>
<p><strong>DB 최대 연결 개수</strong></p>
<p>DB 서버 자원에는 여유가 있지만 API 서버에서 DB에 연결되지 않는다면 DB에 설정된 최대 연결 개수를 확인해야함</p>
<h2 id="📌-실패와-트랜잭션-고려하기">📌 실패와 트랜잭션 고려하기</h2>
<p>서버 초심자가 놓치는 것중 하나는 DB 트랜잭션을 고려하지 않는것</p>
<ul>
<li>고려하지 않으면 데이터 일관성 문제 생김</li>
</ul>
<p>자주 발생하는 실수</p>
<ul>
<li>트랜잭션 없이 여러 데이터를 수정</li>
<li>트랜잭션 없이 수정하면 오류가 났을때 데이터가 꼬이는 문제 발생</li>
<li>트랜잭션 사용하면 전체 트랜잭션 롤백 함</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[주니어 백엔드 개발자가 반드시 알아야 할 실무 지식] 1장, 2장]]></title>
            <link>https://velog.io/@ju_yeong17/%EC%A3%BC%EB%8B%88%EC%96%B4-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%B0%98%EB%93%9C%EC%8B%9C-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%EC%8B%A4%EB%AC%B4-%EC%A7%80%EC%8B%9D-1%EC%9E%A5-2%EC%9E%A5</link>
            <guid>https://velog.io/@ju_yeong17/%EC%A3%BC%EB%8B%88%EC%96%B4-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%B0%98%EB%93%9C%EC%8B%9C-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%EC%8B%A4%EB%AC%B4-%EC%A7%80%EC%8B%9D-1%EC%9E%A5-2%EC%9E%A5</guid>
            <pubDate>Thu, 12 Jun 2025 14:41:44 GMT</pubDate>
            <description><![CDATA[<h2 id="1장-들어가며">1장 들어가며</h2>
<h3 id="📌-코딩을-할-수-있게-된-것일뿐"><strong>📌 코딩을 할 수 있게 된 것일뿐</strong></h3>
<p>커넥션을 닫지 않고 최대 시간을 설정해두지 않아서 오류가 생긴 사례를 통해
기초 부족으로 인한 실수를 하지 않도록 하기 위해 만든 책임을 알린다.</p>
<h2 id="2장-느려진-서비스-어디부터-봐야할까">2장 느려진 서비스, 어디부터 봐야할까</h2>
<h3 id="📌-처리량과-응답-시간"><strong>📌 처리량과 응답 시간</strong></h3>
<p>서버 성능과 관련있는 중요한 지표 2가지</p>
<p>응답 시간</p>
<ul>
<li>사용자의 요청을 처리하는데 걸리는 시간</li>
<li>API 요청을 처리하는데 걸리는 전체 시간은<ul>
<li>API 요청 - SQL 실행, 응답 생성 등 - API 응답</li>
</ul>
</li>
<li>두가지로 나누어 측정<ul>
<li>TTFB<ul>
<li>응답 데이터 중 첫번째 바이트가 도착할때까지 걸린 시간</li>
</ul>
</li>
<li>TTLB<ul>
<li>응답 데이터의 마지막 바이트가 도착할 때까지 걸린 시간</li>
</ul>
</li>
<li>응답 크기가 작다면 둘 차이는 크게 없지만 데이터가 크거나 네트워크가 느리면 차이가 크게 남</li>
</ul>
</li>
<li>응답 시간을 줄일때 DB 연동과 API 연동 시간에 집중한다.</li>
</ul>
<p>처리량</p>
<ul>
<li>단위 시간당 시스템을 처리하는 작업량을 의미한다</li>
<li>TPS, RPS로 나타냄<ul>
<li>TPS 는 초당 트랜잭션 수를 의미</li>
<li>RPS 는 초당 요청 수를 의미</li>
<li>이 책에선 TPS 사용</li>
</ul>
</li>
<li>최대 TPS를초과하면 서버는 초과한 요청을 나중에 처리<ul>
<li>응답 시간을 줄이기 위한 방법<ul>
<li>서버가 동시에 처리할 수 있는 요청의 수 를 늘려 대기시간 줄이기</li>
<li>처리시간 시간 자체를 줄여 대기 시간 줄이기</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="📌-서버-성능-개선-기초"><strong>📌 서버 성능 개선 기초</strong></h3>
<p>병목 지점</p>
<p>성능 문제는 사용자가 늘면서 점차 나타남 </p>
<p>트래픽이 늘고 데이터가 쌓이면서 간헐적으로 응답 시간이 느려지는 현상</p>
<p>주된 이유</p>
<ul>
<li>시스템이 수용할 수 있는 최대 TPS를 초과하는 트래픽이 유입되기 때문</li>
</ul>
<p>TPS를 높이려면 먼저 성능 문제가 발생하는 지점을 찾아야함</p>
<ul>
<li>처리시간이 오래 걸리는 작업을 식별</li>
<li>응답 시간이 길어지면서 발생하는 경우가 많음</li>
<li>모니터링 도구가 유용 없다면 로그라도 남겨야함</li>
</ul>
<p>필자 경험상 성능 문제는 주로 디비나 외부 API를 연동하는 과정에서 발생</p>
<h3 id="📌-수직-확장과-수평-확장"><strong>📌 수직 확장과 수평 확장</strong></h3>
<p>원인 찾은 후 개선안 도출</p>
<ul>
<li>급한 불부터 끄고 근본적인 해결책 모색해야함</li>
</ul>
<p>급한 불 끄는 법</p>
<ul>
<li>수직확장 (스케일 업)<ul>
<li>CPU,메모리 , 디스크 등의 자원을 증가시키는 것</li>
<li>비용이 많이듬</li>
</ul>
</li>
<li>수평 확장<ul>
<li>서버를 추가로 투입</li>
<li>무턱대고 서버를 추가해서는 안됨 병목 지점 파악 중요</li>
</ul>
</li>
</ul>
<h3 id="📌-db-커넥션-풀"><strong>📌 DB 커넥션 풀</strong></h3>
<p>DB를 사용하려면</p>
<ol>
<li>디비 연결 </li>
<li>쿼리 실행</li>
<li>사용 후 연결 종료</li>
</ol>
<p>3 단계를 거친다.</p>
<p>서버와 DB는 네트워크 통신을 통해 연결</p>
<ul>
<li>연결하는 시간때문에 매 요청마다 연결 종료 하면 처리량이 급격하게 떨어짐</li>
</ul>
<p>→ 이문제 해결을 위해 DB 커넥션 풀 사용</p>
<p>DB 커넥션풀</p>
<ul>
<li>DB에 연결된 커넥션을 미리 생성해서 보관</li>
<li>이미 연결된 커넥션을 재사용 → 응답 시간 줄어듬</li>
</ul>
<p>커넥션 풀은 다양한 설정 제공</p>
<ul>
<li>커넥션 풀 크기</li>
<li>풀에 커넥션이 없을때 커넥션을 구할 때까지 대기할 시간</li>
<li>커넥션의 유지시간</li>
</ul>
<h3 id="📌-커넥션의-풀-크기"><strong>📌 커넥션의 풀 크기</strong></h3>
<ul>
<li>미리 샐성해둘 커넥션 개수를 지정하는 설정</li>
<li>가장 중요</li>
<li>DB 연결 관리하는 DB 커넥션 풀 크기를 잘못 설정하면 성능 큰 영향</li>
<li>최대 , 최소 크리 설정 할 수 있음</li>
<li>무턱대고 늘리면 안됨 디비 상태보고 늘려야함</li>
</ul>
<h3 id="📌-커넥션-대기-시간"><strong>📌 커넥션 대기 시간</strong></h3>
<ul>
<li>풀에 사용할 수 있는 커넥션이 없을 때 커넥션을 얻기 위해 기다릴 수 있는 최대 시간</li>
<li>지정된 대기 시간 안에 커넥션을 구하지 못하면 DB 연결 실패 에러</li>
<li>커넥션을 얻기 위해 대기샇는 시간만큼 응답 시간도 길어짐 → 응답시간이 중요한 서비스는 커넥션 대기 시간을 가능한 짧게 설정해야함</li>
<li>짧게 설정했을때 모두 사용 중일 경우 일시적 오류와 같은 에러 응답을 보여줄 수 있음</li>
<li>에러가 부정적으로 보이지만 무응답 보다는 에러를 반환하는게 나음 → 서버 부하 증가 방지 할수있음</li>
</ul>
<h3 id="📌-최대-유휴-시간-유효성-검사-최대-유지-시간"><strong>📌 최대 유휴 시간, 유효성 검사 ,최대 유지 시간</strong></h3>
<ul>
<li>커넥션이 사용되지 않는 시간이 길어지면 연결이 끊길 수 있음</li>
<li>연결 끊김으로 인해 발생하는 에러를 방지 하기 위해<ul>
<li>최대 유휴 시간 지정<ul>
<li>커넥션을 풀에 유지할 수 있는 최대 시간을 의미</li>
</ul>
</li>
<li>유효성 검사 지원<ul>
<li>커넥션이 정상적으로 사용할 수 있는 상태인지 여부를 확인하는 절차</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>최대 유휴시간과 최대 유지 시간을 무한대로 설정하지 않는게 좋음</p>
<h3 id="📌-서버-캐시"><strong>📌 서버 캐시</strong></h3>
<ul>
<li>DB 서버를 확장하려면 비용이 많이듬</li>
<li>서버를 확장하지 않고 캐시 사용 고려</li>
<li>캐시는 데이터를 읽는 속도가 빠름<ul>
<li>자주 조회되는 데이터를 캐시에 보관하면 응답 시간을 줄일 수 있음</li>
</ul>
</li>
</ul>
<h3 id="📌-적중률과-삭제-규칙"><strong>📌 적중률과 삭제 규칙</strong></h3>
<p>적중률</p>
<ul>
<li>캐시가 얼마나 효율적으로 사용 되는지 판단</li>
<li>캐시에 존재한 건수/캐시에 조회를 시도한 건수</li>
<li>적중률이 높을 수록 디비 와의 연동이 줄어들고 응답 시간 감소 , 처리량 증가, 디비 부하 감소로 이어짐</li>
<li>높이는 방법<ul>
<li>캐시에 최대한 많은 데이터를 저장하는 것</li>
<li>모든 데이터를 무작정 저장할 순 없음<ul>
<li>메모리 자원을 사용하기 때문</li>
<li>메모리 용량 한계</li>
</ul>
</li>
<li>삭제할 대상 선택<ul>
<li>LRU → 가장 오래전에 사용된 데이터 제거</li>
<li>LFU → 가장 적게 사용된 데이터 유지</li>
<li>FIFO → 먼저 추가된 데이터를 먼저 삭제</li>
</ul>
</li>
<li>캐시가 가득차 있지 않아도 오래된 데이터는 미리 삭제 하는게 좋음</li>
<li>유효시간을 설정하는 방식도 함께 사용</li>
</ul>
</li>
</ul>
<h3 id="📌-로컬-캐시와-리모트-캐시"><strong>📌</strong> 로컬 캐시와 리모트 캐시</h3>
<p>캐시 종류 두개</p>
<ul>
<li>로컬 캐시<ul>
<li>서버 프로세스와 동일한 메모리를 캐시 저장소로 사용</li>
<li>장점<ul>
<li>빠르다</li>
<li>구조가 단순</li>
</ul>
</li>
<li>단점<ul>
<li>저장할 수 있는 데이터 크기에 제한</li>
<li>물리적 한계</li>
<li>서버 프로세스 재시작하면 메모리에 존재하면 캐시 데이터 모두 삭제 일시적으로 캐시 효율이 떨어짐</li>
</ul>
</li>
</ul>
</li>
<li>리모트 캐시<ul>
<li>별도 프로세스를 캐시 저장소로 사용</li>
<li>장점<ul>
<li>캐시 크기를 유연하게 확장</li>
<li>레디스<ul>
<li>캐시에 보관할 수 잇는 데이터 규모를 늘릴 수 있음</li>
<li>재시작되더라도 레디스에 저장된 캐시데이터는 유지</li>
</ul>
</li>
<li>캐시 효율 업</li>
</ul>
</li>
<li>단점<ul>
<li>속도</li>
<li>별도의 서버 장비와 프로세스 필요 → 세스템 구조 복잡</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>→ 상황이나 용도에 맞게 선택</p>
<p>캐시에 보관할 데이터 규모가 작고 변경 빈도가 낮다면 로컬 캐시</p>
<p>데이터 규모가 크다면 리모트 캐시</p>
<p>배포 빈도가 높은 서비스라면 리모트 캐시 사용</p>
<h3 id="📌-캐시-사전-적재"><strong>📌</strong> 캐시 사전 적재</h3>
<p>트래픽이 순간적으로 급증하면 캐시에 데이터를 미리 저장하는 것도 고려</p>
<h3 id="📌-캐시-무효화"><strong>📌</strong> 캐시 무효화</h3>
<ul>
<li>유효하지 않은 데이터를 적절한 시점에 캐시에서 삭제</li>
<li>원본이 바뀌면 그에 맞춰 캐시도 변경하거나 삭제</li>
<li>데이터 특성에 따라 무효화 시점 달리 설정</li>
</ul>
<p>변경에 민감한 데이터는 리모트 캐시 </p>
<p>변경에 민감하지 않고 데이터 크기가 작ㄷ아면 캐시의 유효 시간을 설정하여 주기적으로 갱신</p>
<h3 id="📌-가비지-컬렉터와-메모리-사용"><strong>📌</strong> 가비지 컬렉터와 메모리 사용</h3>
<p>가비지 컬렉터</p>
<ul>
<li>사용하는 언어는 사용이 끝난 객체를 힙 메모리에서 바로 삭제하지 않고 정해진 규칙에 따라 사용하지 않는 메모리를 찾아서 반환</li>
<li>개발자가 메모리를 직접 고나리해야하는 부담 줄여줌</li>
<li>보안 이슈를 줄여줌</li>
<li>응답시간에 영향</li>
<li>실행 되는 동안 애플리케이션의 실행이 일시 중지</li>
<li>메모리양과 객체가 많을수록 GC 실행시간은 길어짐</li>
</ul>
<p>메모리 사용을 줄이면 GC 시간도 줄어들 가능성 높아짐</p>
<p>—&gt; 메모리 부족 에러가 발생할 수 있으니 조정해야함</p>
<p>한번에 대량 객체를 생성하는 것도 주의</p>
<ul>
<li>조회 범위 제한</li>
</ul>
<p>파인 다운로드와 같은 기능을 구현할 때는 스트림 활용</p>
<ul>
<li>파일 처리 과정에서 필요한 메모리 크기를 줄일 수 있음</li>
</ul>
<h3 id="📌-응답-데이터-압축"><strong>📌</strong> 응답 데이터 압축</h3>
<p>데이터 전송 시간 영향 받는것</p>
<ul>
<li>네트워크 속도</li>
<li>전송 데이터 크기</li>
</ul>
<p>응답 데이터를 압축해서 전송</p>
<ul>
<li>비용에도 영향</li>
<li>이미 압축된 데이터는 다시 압축해도 효과 없음 → 텍스트형식의 데이터에 압축 적용</li>
<li>방화벽이 해제해 응답 할 수 있음 → 방화벽 설정도 확인</li>
</ul>
<h3 id="📌-정적-자원과-브라우저-캐시"><strong>📌</strong> 정적 자원과 브라우저 캐시</h3>
<p>동적자원</p>
<ul>
<li>브라우저가 요청할 때마다 결과가 바뀌는 데이터</li>
<li>제품 목록 HTML, 제품 상세 JSON 응답</li>
</ul>
<p>정적 자원</p>
<ul>
<li>같은 URL에 대해 같은 데이터를 응답하는 콘텐츠로 이미지,JS, CSS</li>
<li>전체 트래픽에서 상당한 비중을 차지</li>
</ul>
<p>클라이언트 캐시 활용</p>
<ul>
<li>서버가 전송하는 트래픽을 줄이면서 브라우저가 더 빠르게 화면에 표시 할 수 있는 방법</li>
</ul>
<h3 id="📌-정적-자원과-cdn"><strong>📌</strong> 정적 자원과 CDN</h3>
<p>브라우저 캐시</p>
<ul>
<li>네트워크 트래픽 줄일 수 있지만 여전히 문제 발생 할 수 있음</li>
<li>브라우저 단위로 동작<ul>
<li>동시에 많은 사용자가 접속하면 순간적으로 많은 양의 이미지, JS, CSS 전송</li>
<li>네트워크 포화되어 응답 느려짐</li>
</ul>
</li>
</ul>
<p>→ 해결 방법 CDN(content Delivery Network, 콘텐츠 전송 네트워크)</p>
<p>CDN</p>
<ul>
<li><p>대표적으로 Amazon CloudFront, Akamai, cloudflare 등</p>
</li>
<li><p>콘텐츠를 제공하기 위한 별도의 네트워크</p>
</li>
<li><p>흐름</p>
<ul>
<li><p>사용자 CDN이 제공하는  URL 접속 → CDN 서버에 요청한 콘텐츠가 없으면 오리진 서버에서 읽어와 제공 → 캐시에 보관</p>
<p>→ 동일한 콘텐츠 들어오면 캐시에 보관한 데이터를 응답, 나머지는 CDN이 맡음</p>
<p>→ 트래픽을 상당히 줄일 수 있음</p>
</li>
</ul>
</li>
<li><p>여러 지역에 서버를 둠</p>
</li>
<li><p>가까운 곳에 위치한 서버에 연결해서 콘텐츠를 다운로드</p>
</li>
</ul>
<h3 id="📌-대기-처리"><strong>📌</strong> 대기 처리</h3>
<p>순간적으로 폭증 할때 트래픽 처리</p>
<ul>
<li>서버,디비 미리 증설<ul>
<li>디비 증설 문제점<ul>
<li>비용</li>
<li>DB 성능을 높여 놓으면 다시 DB 성능을 줄이기 쉽지 않다.</li>
</ul>
</li>
</ul>
</li>
<li>무작정 늘리기 보다는 수용할 수 있는 수준의 트래픽만 받아들이고 나머지는 대기 처리</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클린코드] 16, 17장 ]]></title>
            <link>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-16-17%EC%9E%A5-hv7zda4e</link>
            <guid>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-16-17%EC%9E%A5-hv7zda4e</guid>
            <pubDate>Sun, 25 May 2025 14:04:59 GMT</pubDate>
            <description><![CDATA[<p>마지막이다!</p>
<h2 id="16장-serialdate-리팩토링">16장 SerialDate 리팩토링</h2>
<ul>
<li>SerialDate는 날짜를 표현하는 자바 클래스이다.</li>
<li>이장 또한 책에 있는 코드와 함께 보면 좋을 것 같다</li>
</ul>
<h2 id="17장-냄새와-휴리스틱">17장 냄새와 휴리스틱</h2>
<h3 id="📌-주석">📌 주석</h3>
<p>C1 : 부적절한 정보</p>
<ul>
<li>다른 시스템에 저장할 정보는 주석으로 적절하지 못하다.</li>
<li>주석은 코드와 설계에 기술적인 설명을 부연하는 수단이다.</li>
</ul>
<p>C2: 쓸모 없는 주석</p>
<ul>
<li>주석은 빨리 낡는다.</li>
<li>쓸모 없어질 주서은 아예 달지 않는 편이 좋다.</li>
</ul>
<p>C3: 중복된 주석</p>
<ul>
<li>코드만으로 충분한데 구구절절 설명하는 주석이 중복된 주석이다.</li>
</ul>
<p>C4: 성의 없는 주석</p>
<ul>
<li>작성할 가치가 있는 주석은 잘 작성할 가치도 있다.</li>
<li>단어를 신중하게 선택한다.</li>
<li>문법과 구두점을 올바로 사용한다.</li>
</ul>
<p>C5: 주석 처리된 코드</p>
<ul>
<li>주석으로 처리된 코드를 발견하면 즉각 지워버려라!</li>
<li>걱정할 필요 없다. 소스 코드 관리 시스템이 기억하니까.</li>
<li>누군가 정말로 필요하다면 이전 버전을 가져오면 된다.</li>
</ul>
<h3 id="📌-환경">📌 환경</h3>
<p>E1: 여러 단계로 빌드해야 한다</p>
<ul>
<li>빌드는 간단히 한 단계로 끝나야 한다.</li>
</ul>
<p>E2: 여러 단계로 테스트해야 한다</p>
<ul>
<li>모든 단위테스트는 한 명령으로 돌려야한다.</li>
<li>모든 테스트를 한 번에 실행하는 능력은 아주 근본적이고 아주 중요하므로 빠르고, 쉽고 명백해야 한다.</li>
</ul>
<h3 id="📌-함수">📌 함수</h3>
<p>F1: 너무 많은 인수</p>
<ul>
<li>함수에서 인수 개수는 작을수록 좋다.</li>
<li>아예 없으면 가장 좋다.</li>
</ul>
<p>F2: 출력인수</p>
<ul>
<li>출력 인수는 직관을 정면으로 위배한다.</li>
<li>함수에서 뭔가의 상태를 변경해야 한다면 함수가 속한 객체의 상태를 변경한다.</li>
</ul>
<p>F3: 플래그 인수</p>
<ul>
<li>boolean 인수는 함수가 여러 기능을 수행한다는 명백한 증거다.</li>
<li>플래그 인수는 혼란을 초래 하므로 피해야 마땅하다.</li>
</ul>
<p>F4: 죽은 함수</p>
<ul>
<li>아무도 호출하지 않는 함수는 삭제한다.</li>
<li>죽은 코드는 낭비다. 과감히 삭제하라.</li>
</ul>
<h3 id="📌-일반">📌 일반</h3>
<p>G1: 한 소스 파일에 여러 언어를 사용한다</p>
<ul>
<li>좋게 말하자면 혼란스럽고 나쁘게 말하자면 조잡하다.</li>
<li>이상적으로는 소스 파일 하나에 언어 하나만 사용한 방식이 가장 좋다.</li>
</ul>
<p>G2: 당연한 동작을 구현하지 않는다</p>
<ul>
<li>최소 놀람의 원칙에 의거해함수나 클래스는 프로그래머가 당연하게 여길만한 동작과 기능을 제공해야 한다.</li>
<li>당연한 동작을 구현하지 않으면 코드를 읽거나 사용하는 사람이 더 이상 함수 이름만으로 함수 기능을 직관적으로 예상하기 어렵다.</li>
</ul>
<p>G3: 경계를 올바로 처리하지 않는다</p>
<ul>
<li>모든 경계 조건을 찾아내고, 모든 경계 조건을 테스트하고 테스트케이스를 작성 하라.</li>
</ul>
<p>G4: 안전 절차 무시</p>
<ul>
<li>안전 절차를 무시하면 위험하다.</li>
<li>실패하는 테스트 케이스를 일단 제겨두고 나중으로 미루는 태도는 신용카드가 공짜 돈이라는 생각만큼 위험하다.</li>
</ul>
<p>G5: 중복</p>
<ul>
<li>코드에서 중복을 발견할 때마다 추상화할 기회로 간주하라.</li>
<li>중복된 코드를 하위 루틴이나 다른 클래스로 분리하라.</li>
<li>이렇듯 추상화로 중복을 정리하면 설계 언어의 어휘가 늘어난다.</li>
</ul>
<p>G6: 추상화 수준이 올바르지 못하다</p>
<ul>
<li>추상화는 저차원 상세 개념에서 고차원 일반 개념을 분리한다.</li>
<li>기초 클래스는 구현 정보에 무지해야 마땅하다.</li>
<li>고차원 개념과 저차원 개념을 섞어서는 안된다.</li>
<li>잘못된 추상화 수준은 거짓말이나 꼼수로 해결하지 못한다.</li>
</ul>
<p>G7: 기초 클래스가 파생 클래스에 의존한다</p>
<ul>
<li>개념을 기초 클래스와 파생 클래스로 나누는 가장 흔한 이유는 고차원 기초 클래스 개념을저차원 파생 클래스 개념으로부터 분리해 독립성을 보장하기 위해서다.</li>
<li>그러므로 기초 클래스가 파생 클래스를 사용한다면 뭔가 문제가 있다는 말이다.</li>
<li>일반적으로 기초 클래스는 파생 클래스를 아예 몰라야 마땅하다.</li>
<li>이렇게 구현하면 변경이 시스템에 미치는 영향이 아주 작아지므로 현장에서 시스템을 유지보수 하기가 한결 수월하게 된다.</li>
</ul>
<p>G8: 과도한 정보</p>
<ul>
<li>잘 정의된 모듈은 인터페이스가 아주 작다. 하지만 작은 인터페이스로도 많은 동작이 가능하다.</li>
<li>부실하게 정의된 모듈은 인터페이스가 구질구질하다. 그래서 간단한 동작 하나에도 온갖 인터페이스가 필요하다.</li>
<li>잘 정의된 인터페이스는 많은 함수를 제공하지 않는다. 그래서 결합도가 낮다.</li>
</ul>
<p>G9: 죽은 코드</p>
<ul>
<li>죽은 코드란 실행되지 않는 코드를 가리킨다.</li>
<li>발견시 시스템에서 제거하라.</li>
</ul>
<p>G10: 수직 분리</p>
<ul>
<li>변수와 함수는 사용되는 위치에 가깝게 정의한다.</li>
<li>자역 변수는 처음으로 사용하기 직전에 선언하며 수직으로 가까운 곳에 위치해야 한다.</li>
<li>비공개 함수는 처음으로 호출한 직후에 정의한다.</li>
<li>비공개 함수는 정의하는 위치와 호출하는 위치를 가깝게 유지한다.</li>
<li>비공개 함수는 처음으로 호출되는 위치를 찾은 후 조금만 아래로 내려가면 쉽게눈에 띄어야한다.</li>
</ul>
<p>G11: 일관성 부족</p>
<ul>
<li>어떤 개념을 특정 방식으로 구현했다면 유사한 개념도 같은 방식으로 구현하라.</li>
<li>표기법은 신중하게 선택하며, 일단 선택한 표기법은 신중하게 따른다.</li>
</ul>
<p>G12: 잡동사니</p>
<ul>
<li>비어 있는 기본 생성자가 왜 필요한가? 쓸데없이 코드만 복잡하게 만든다.</li>
<li>소스 파일은 언제나 깔끔하게 정리하라! 잡동사니를 없애라!<ul>
<li>사용하지 않는 변수</li>
<li>아무도 호출하지 않는 함수</li>
<li>정보를 제공하지 못하는 주석</li>
</ul>
</li>
</ul>
<p>G13: 인위적 결합</p>
<ul>
<li>서로 무관한 개념을 인위적으로 결합하지 않는다.</li>
<li>일반적인 enum은 특정 클래스에 속할 이유가 없다.</li>
<li>범용 static 함수도 마찬가지로 특정 클래스에 속할 이유가 없다.</li>
<li>함수, 상수, 변수를 선언할 때는 시간을 들여 올바른 위치를 고민한다.</li>
<li>그저 당장 편한곳에 선언하고 내버려두면 안 된다.</li>
</ul>
<p>G14: 기능 욕심</p>
<ul>
<li>클래스 메서드는 다른 클래스의 변수와 함수에 관심을 가져서는 안 된다.</li>
<li>다른 객체의 참초자와 변경자를 사용해 그 객체를 조작하면 그 객체 클래스 범위를 욕심내는 탓이다.</li>
</ul>
<p>G15: 선택자 인수</p>
<ul>
<li>선택자 인수는 큰 함수를 작은 함수 여럿으로 쪼개지 않으려는 게으름의 소산이다.</li>
<li>일반적으로, 인수를 넘겨 동작을 선택하는 대신 새로운 함수를 만드는 편이 좋다.</li>
</ul>
<p>G16: 모호한 의도</p>
<ul>
<li>코드를 짤 때는 의도를 최대한 분명히 밝힌다.</li>
<li>행을 바꾸지 않고 표현한 수식, 헝가리식 표기법, 매직 번호 등은 모두 저자의 의도를 흐린다.</li>
<li>독자에게 의도를 분명히 표현하도록 시간을 투자할 가치가 있다.</li>
</ul>
<p>G17: 잘못 지운 책임</p>
<ul>
<li>소프트웨어 개발자가 내리는 가장 중요한 결정 중 하나가 코드를 배치하는 위치다.</li>
<li>때로는 개발자가 ‘영리하게’ 기능을 배치한다. 독자에게 직관적인 위치가 아니라 개발자에게 편한 함수에 배치한다.</li>
</ul>
<p>G18: 부적절한 static 함수</p>
<ul>
<li>메서드를 소유하는 객체에서 가져오는 정보가 아니고 인수에서만 정보를 가져와야한다.</li>
<li>일반적으로 static 함수보다 인스턴스 함수가 더 좋다.</li>
<li>조금이라도 의심스럽다면 인스턴스 함수로 정의한다.</li>
<li>반드시 static 함수로 정의해야겠다면 재정의할 가능성은 없는지 꼼꼼히 따져본다.</li>
</ul>
<p>G19: 서술적 변수</p>
<ul>
<li>프로그램 가독성을 높이는 가장 효과적인 방법<ul>
<li>계산을 여러 단계로 나눈다.</li>
<li>중간 값으로 서술적인 변수 이름을 사용.</li>
</ul>
</li>
<li>서술적인 변수 이름은 많이 써도 괜찮다.</li>
<li>일반적으로는 많을수록 더 좋다.</li>
</ul>
<p>G20: 이름과 기능이 일치하는 함수</p>
<ul>
<li>이름만으로 분명하지 않기에 구현을 살피거나 문서를 뒤적여야 한다면더 좋은 이름으로 바꾸거나 아니면 더 좋은 이름을 붙이기 쉽도록 기능을 정리해야한다.</li>
</ul>
<p>G21: 알고리즘을 이해하라</p>
<ul>
<li>대다수 괴상한 코드는 사람들이 알고리즘을 충분히 이해하지 않은 채 코드를 구현한 탓이다.</li>
<li>구현이 끝났다고 선언하기 전에 함수가 돌아가는 방식을 확실히 이해하는지 확인하라.</li>
<li>작성자가 알고리즘이 올바르다는 사실을 알아야 한다.</li>
</ul>
<p>G22: 논리적 의존성은 물리적으로 드러내라</p>
<ul>
<li>한 모듈이 다른 모률에 의존한다면 물리적인 의존성도 있어야 한다.</li>
<li>논리적인 의존성만으로는 부족하다.</li>
<li>의존하는 모든 정보를 명시적으로 요청하는 편이좋다.</li>
</ul>
<p>G23: if/else 혹은 switch/case 보다는 다형성을 사용하라</p>
<ul>
<li>선택 유형 하나에는 switch 문을 한 번만 사용한다.</li>
<li>같은 선택을 수행하는 다른 코드에서는 다형성 객체를 생성해 switch 문을 대신한다.</li>
</ul>
<p>G24: 표준 표기법을 따르라</p>
<ul>
<li>팀은 업계 표준에 기반한 구현 표준을 따라야 한다.</li>
<li>표준을 설명하는 문서는 코드 자체로 충분해야 하며 별도 문서를 만들 필요는 없어야 한다.</li>
<li>팀이 정한 표준은 팀원 모두가 따라야 한다.</li>
<li>모두가 동의한 위치에 넣는다는 사실이 중요하다.</li>
<li>이 사실을 이해할 정도로 팀원들이 성숙해야 한다.</li>
</ul>
<p>G25:매직 숫자는 명명된 상수로 교체하라</p>
<ul>
<li>매직숫자는 상수로 변경하라.</li>
<li>어떤 상수는 이해하기 쉬우므로, 코드 자체가 자명하다면, 상수 뒤로 숨길 필요가 없다.</li>
</ul>
<p>G26: 정확하라</p>
<ul>
<li>검색 결과 중 첫 번째 결과만 유일한 결과로 간주하는 행동은 순진하다.</li>
<li>코드에서 뭔가를 결정할 때는 정확히 결정한다.</li>
<li>결정을 내리는 이유, 예외 처리 방법을 분명히 알아야한다.</li>
<li>호출하는 함수가 null을 반환할지도 모른다면 null을 반드시 점검한다.</li>
</ul>
<p>G27: 관례보다 구조를 사용하라</p>
<ul>
<li>설계 결정을 강제할 때는 규칙보다 관례를 사용한다.</li>
<li>명명 관례도 좋지만 구조 자체로 강제하면 더 좋다.</li>
<li>enum 변수가 멋진 switch/case 문보다 추상 메서드가 있는 기초 클래스가 더 좋다.</li>
</ul>
<p>G28: 조건을 캡슐화하라</p>
<ul>
<li>조건의 의도를 분명히 밝히는 함수로 표현하라.</li>
</ul>
<p>G29: 부정 조건은 피하라</p>
<ul>
<li>가능하면 긍정 조건으로 표현한다.</li>
</ul>
<p>G30: 함수는 한 가지만 해야 한다</p>
<ul>
<li>함수를 짜다보면 한 함수 안에 여러 단락을 이어 일련의 작업을 수행하고픈 유혹에 빠진다.</li>
<li>한 가지만 수행하는 좀 더 작은 함수 여럿으로 나눠야 마땅하다.</li>
</ul>
<p>G31: 숨겨진 시간적인 결합</p>
<ul>
<li>시간적인 결합이 필요하다, 하지만 시간적인 결합을 숨겨서는 안 된다.</li>
<li>함수를 짤 때는 함수 인수를 적절히 배치해 함수가 호출되는 순서를 명백히 드러낸다.</li>
</ul>
<p>G32: 일관성을 유지하라</p>
<ul>
<li>코드 구조를 잡을 때는 이유를 고민해야한다.</li>
<li>그리고 그 이유를 코드 구조로 명백히 표현해야한다.</li>
<li>시스템 전반에 걸쳐 구조가 일관성이 있다면 남들도 일관성을 따르고 보존한다.</li>
</ul>
<p>G33: 경계 조건을 캡슐화하라</p>
<ul>
<li>경계 조건은 빼먹거나 놓치기 십상이다.</li>
<li>경계 조건은 한 곳에서 별도로 처리한다.</li>
<li>코드 여기저기에서 처리하지 않는다.</li>
</ul>
<p>G34: 함수는 추상화 수준을 한 단계만 내려가야 한다</p>
<ul>
<li>함수내 모든 문장은 추상화 수준이 동일해야한다.</li>
<li>그리고 그 추상화 수준은 함수 이름이 의미하는 작업보다 한 단계만 낮아야 한다.</li>
<li>개념은 아주 간단하지만 인간은 추상화 수준을 뒤섞는 능력이 너무나도 뛰어나다.</li>
<li>추상화 수준 분리는 리팩터링을 수행하는 가장 중요한 이유 중 하나다.</li>
</ul>
<p>G35: 설정 정보는 최상위 단계에 둬라</p>
<ul>
<li>추상화 최상위 단계에 둬야 할 기본값 상수나 설정 관련 상수를 저차원 함수에 숨기면 안된다.</li>
<li>대신 고차원 함수에서 저차원 함수를 호출할 때 인수로 넘긴다.</li>
</ul>
<p>G36: 추이적 탐색을 피하라</p>
<ul>
<li>일반적으로 한 모듈은 주변 모듈을 모를수록 좋다.</li>
<li>이를 디미터의 법칙이라 부른다.</li>
<li>실용주의 프로그래머들은 부끄럼 타는 코드 작성 이라고도 한다.</li>
<li>무엇이라 부르든 요지는 자신이 직접 사용하는 모듈만 알아야 한다는 뜻이다.</li>
</ul>
<h3 id="자바">자바</h3>
<p>J1: 긴 import 목록을 피하고 와일드카드를 사용하라</p>
<ul>
<li>패키지에서 클래스를 둘 이상 사용한다면 와일드카드를 사용해 패키지 전체를 가져오라.</li>
<li>와일드카드 import 문은 때로 이름 충돌이나 모호성을 초래한다.</li>
<li>다소 번거롭지만 자주 발생하지 않으므로 여전히 와일드카드 import 문이 명시적인 import 문보다 좋다.</li>
</ul>
<p>J2: 상수는 상속하지 않는다</p>
<ul>
<li>어떤프로그래머는 상수를 인터페이스에 넣은 다음 그 인터페이스를 상속해 해당 상수를 사용한다.</li>
<li>참으로 끔직한 관행이다! 상수를 상속 계층 맨 위에 숨겨놨다.</li>
<li>언어의 범위 규칙을 속이는 행위다.</li>
<li>대신 static import 를사용하라.</li>
</ul>
<p>J3: 상수 대 Enum</p>
<ul>
<li>자바 5는 enum을 제공한다.</li>
<li>마음껏 활용하라! public static final int라는 옛날 기교를 더 이상 사용할 필요가 없다.</li>
</ul>
<h3 id="📌-이름">📌 이름</h3>
<p>N1: 서술적인 이름을 사용하라</p>
<ul>
<li>이름은 성급하게 정하지 않는다. 서술적인 이름을 신중하게 고른다.</li>
<li>신중하게 선택한 이름은 추가 설명을 포함한 코드보다 강력하다.</li>
</ul>
<p>N2: 적절한 추상화 수준에서 이름을 선택하라</p>
<ul>
<li>구현을 드러내는 이름은 피하라.</li>
<li>작업 대상 클래스나 함수가 위치하는 추상화 수준을 반영하는 이름을 선택하라.</li>
</ul>
<p>N3: 가능하다면 표준 명명법을 사용하라</p>
<ul>
<li>기존 명명법을 사용하는 이름은 이해하기 더 쉽다.</li>
<li>DECORATOR 패턴을 활용 한다면 장식하는 클래스 이름에 Decorator 라는 단어를 사용해야 한다.</li>
<li>ToString 과 같이 관례가 존재하는 이름은 관례를 따른다.</li>
<li>프로젝트에 유효한 의미가 담긴 이름을 많이 사용할수록 독자가 코드를 이해하기 쉬워진다.</li>
</ul>
<p>N4: 명확한 이름</p>
<ul>
<li>함수나 변수의 목적을 명확히 밝히는 이름을 선택한다.</li>
<li>길더라도 명확하고 서술적으로 지어야 한다.</li>
</ul>
<p>N5: 긴 범위는 긴 이름을 사용하라</p>
<ul>
<li>이름 길이는 범위 길이에 비례해야 한다.</li>
<li>범위가 작으면 아주 짧은 이름을 사용해도 괜찮다.</li>
<li>하지만 범위가 길어지면 긴 이름을 사용한다.</li>
<li>이름 범위가 길수록 이름을 정확하고 길게 짓는다.</li>
</ul>
<p>N6: 인코딩을 피하라</p>
<ul>
<li>이름에 유형 정보나 범위 정보를 넣어서는 안 된다.</li>
</ul>
<p>N7: 이름으로 부수 효과를 설명하라</p>
<ul>
<li>함수, 변수, 클래스가 하는 일을 모두 기술하는 이름을 사용한다.</li>
<li>실제로 여러 작업을 수행하는 함수에다 동사 하나만 달랑 사용하면 곤란하다.</li>
</ul>
<h3 id="📌-테스트">📌 테스트</h3>
<p>T1: 불충분한 테스트</p>
<ul>
<li>테스트 케이스는 몇 개나 만들어야 충분할까?</li>
<li>잠재적으로 깨질 만한 부분을 모두 테스트해야 한다.</li>
<li>테스트 케이스가 확인하지 않는 조건이나 검증하지 않는 계산이 있다면 그 테스트는 불완전하다.</li>
</ul>
<p>T2: 커버리지 도구를 사용하라</p>
<ul>
<li>커버리지 도구는 테스트가 빠뜨리는 공백을 알려준다.</li>
<li>대다수 IDE는 테스트 커버리지를 시각적으로 표현한다.</li>
</ul>
<p>T3: 사소한 테스트를 건너뛰지 마라</p>
<ul>
<li>사소한 테스트는 짜기 쉽다.</li>
</ul>
<p>T4: 무시한 테스트는 모호함을 뜻한다</p>
<ul>
<li>때로는 요구사항이 불분명하기에 프로그램이 돌아가는 방식을 확신하기 어렵다.</li>
<li>선택 기준은 모호함이 존재하는 테스트 케이스가 컴파일이 가능한지 불가능한지에 달려있다.</li>
</ul>
<p>T5: 경계 조건을 테스트하라</p>
<ul>
<li>경계 조건은 각별히 신경 써서 테스트한다.</li>
<li>알고리즘의 중앙 조건은 올바로 놓고 경계 조건에서 실수하는 경우가 흔하다.</li>
</ul>
<p>T6: 버그 주변은 철저히 테스트하라</p>
<ul>
<li>버그는 서로 모이는 경향이 있다.</li>
<li>한 함수에서 버그를 발견했다면 그 함수를 철저히 테스트하는 편이 좋다.</li>
<li>십중팔구 다른 버그도 발견하리라.</li>
</ul>
<p>T7: 실패 패턴을 살펴라</p>
<ul>
<li>때로는 테스트 케이스가 실패하는 패턴으로 문제를 진단할 수 있다.</li>
<li>테스트 케이스를 최대한 꼼꼼히 짜라는 이유도 여기에 있다.</li>
<li>합리적인 순서로 정렬된 꼼꼼한 테스트 케이스는 실패 패턴을 드러낸다.</li>
</ul>
<p>T8: 테스트 커버리지 패턴을 살펴라</p>
<ul>
<li>통과하는 테스트가 실행하거나 실행하지 않는 코드를 살펴보면 실패하는 테스트 케이스의 실패 원인이 드러난다.</li>
</ul>
<p>T9: 테스트는 빨라야 한다</p>
<ul>
<li>느린 테스트 케이스는 실행하지 않게 된다.</li>
<li>일정이 촉박하면 느린 테스트 케이스를 제일 먼저 건너뛴다.</li>
<li>그러므로 테스트 케이스가 빨리 돌아가게 최대한 노력한다.</li>
</ul>
<h3 id="📌-후기">📌 후기</h3>
<p>드디어 끝났다 
수학의 정석 처럼 계속해서 읽어야 내가 제대로 사용할 수 있을 것 같았다.
이해는 되지만 직접 사용해봐야 지속적으로 클린한 코드를 만들 수 있을 것 같다.
모두 읽어보면 좋을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클린코드] 14장, 15장]]></title>
            <link>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-14%EC%9E%A5-15%EC%9E%A5-r4wpvdyd</link>
            <guid>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-14%EC%9E%A5-15%EC%9E%A5-r4wpvdyd</guid>
            <pubDate>Sun, 11 May 2025 12:35:13 GMT</pubDate>
            <description><![CDATA[<p>이 두장은 음 두개로 나누기엔 내용이 적어서 한번에 쓴다.</p>
<h2 id="제-14장-점진적인-개선"><strong>제 14장 점진적인 개선</strong></h2>
<p>깨끗한 코드를 짜려면 먼저 지저분한 코드를 짠 뒤 정리해야 한다</p>
<p>점진적으로 개선하다</p>
<p>프로그램을 망치는 가장 좋은 방법 중 하나는 개선이라는 이름 아래 구조를 크게 뒤집는 행위다. 어떤 프로그램은 그저 그런 개선에서 켤코 회복하지 못한다 개선전과 똑같이 프로그램을 돌리기 어렵기 때문이다.</p>
<p>그래서 테스트 주도 개발이라는 기법을 사용했다.</p>
<p>변경 전 후 에 시스템이 똑같이 돌아간다는 사실을 확인할려면 언제든 실행이 가능한 자동홛된 테스트 슈트가 필요하다.</p>
<p>소프트웨어 설계는 분할만 잘해도 품질이 크게 높아진다.</p>
<h2 id="제-15장-junit-들여다보기"><strong>제 15장 JUnit 들여다보기</strong></h2>
<p>저자들은 우수한 모듈을 만들었다. 하지만 세상에 개선이 불필요한 모듈을 없다 코드를 처음보다 조금 더 깨끗하게 만드는 책음은 우리 모두에게 있다.</p>
<hr>
<blockquote>
<p>14,15 장에서는 대부분 코드로 보여주면서 설명하기 때문에 직접 책을 보는 것이 좋다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클린코드] 13장 동시성]]></title>
            <link>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-13%EC%9E%A5-%EB%8F%99%EC%8B%9C%EC%84%B1</link>
            <guid>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-13%EC%9E%A5-%EB%8F%99%EC%8B%9C%EC%84%B1</guid>
            <pubDate>Sun, 27 Apr 2025 13:31:10 GMT</pubDate>
            <description><![CDATA[<p>동시성과 깔끔한 코드는 양립하기 어렵다</p>
<h3 id="📌-동시성이-필요한-이유">📌 동시성이 필요한 이유</h3>
<p>동시성은 결합을 없애는 전략ㅇ이다 즉 무엇 과 언제를 분리하는 전략이다.</p>
<p>스레드가 하나인 프로그램은 무엇과 언제가 서로 밀접하다.</p>
<p>그래서 호출 스택을 살펴보면 프로그램 상태가 곧바로 드러난다. </p>
<p>무엇과 언제를 분리하면 애플리케이션 구조와 효율이 극적으로 나아진다.</p>
<p><em>미신과 오해</em></p>
<p>동시성을 항상 성능을 높여준다.</p>
<ul>
<li>동시성은 때로 성능을 높여준다. 대기 시간이 아주 길어 여러 스레드가 프로세서를 공유할 수 있건, 여러 프로세서가 동시에 처리할 독립적인 계산이 충분히 많은 경우에만 성능이 높아진다.</li>
</ul>
<p>동시성을 구현해도 설계는 변하지 않는다.</p>
<ul>
<li>단일 스레드 시스템과 다중 스레드 시스템은 설계가 판이하게 다르다. 일반적으로 무엇과 언제를 분리하면 시스템 구조가 크게 달라진다.</li>
</ul>
<p>웹 또는 EJB 컨테이너를 사용하면 동시성을 이해할 팔요가 없다.</p>
<ul>
<li>실제로는 컨테이너가 어떻게 동작하는지 어떻게 동시수정 데드락 등과 같은 문제를 피할 수 있는지 알아야만 한다.</li>
</ul>
<p><em>동시성과 관련된 타당한 생각</em></p>
<ol>
<li>동시성은 다소 부하가 유발한다. 성능 측면에서 부하가 걸리며 코드도 더 짜야한다.</li>
<li>동시성은 복잡하다. 간단한 문제라도 동시성은 복잡하다.</li>
<li>일반적으로 동시성 버그는 재현하기 어렵다 그래서 진짜 결함으로 간주되지 않고 일회성 문제로 여겨 무시하기쉽다.</li>
<li>동시성을 구현하려면 흔히 근본적인 설계 전략을 재고해야한다.</li>
</ol>
<h3 id="📌-동시성-방어-원칙">📌 동시성 방어 원칙</h3>
<p><em>단일 책임원칙</em></p>
<ul>
<li>동시성 관련 코드는 다른 코드와 분리해야한다는 뜻이다.</li>
<li>고려해야하는 몇가지<ul>
<li>동시성 코드는 독자적인 개발, 변경 , 조율 주기가 있다.</li>
<li>동시성 코드에는 독자적인 난관이 있다. 다른 코드에서 겪는 난관과 다르며 훨씬 어렵다.</li>
<li>잘못 구현한 동시성 코드는 별의별 방식으로 실패한다. 주변에 있는 다른 코드가 발목 잡지 않더라도 동시성 하나만으로도 충분히 어렵다.</li>
</ul>
</li>
</ul>
<p><em>따름 정리 : 자료 범위를 제한하라</em></p>
<p>객체를 사용하는 코드 내 임계영역을 synchronized 키워드로 보호하라고 권장한다.</p>
<p>자료를 캡슐화 하라 공유 자료를 최대한 줄여라</p>
<p><em>따름 정리 : 자료 사본을 사용하라</em></p>
<p>공유 자료를 줄이려면 처음부터 공유하지 않는 방법이 제일 좋다. </p>
<p>공유 객체를 피하는 방법이 있다면 코드가 문제를 일으킬 가능성도 아주 낮아진다.</p>
<p><em>따름 정리: 스레드는 가능한 독립적으로 구현하라</em></p>
<p>자신만의 세상에 존재하는 스레드를 구현하라. 즉 다른 스데르와 자료를 공유하지 않는다. </p>
<p>모든 정보는 비공유 출처에서 가져오며 로컬 변수에 저장한다.</p>
<p>독자적인 스레드로 가능하면 다른 프로세서에서 돌려도 괜찮도록 자료를 독립적인 단위로 분할하라</p>
<h3 id="📌-라이브러리를-이해하라">📌 라이브러리를 이해하라</h3>
<p>언어가 제공하는 클래스를 검토하라</p>
<h3 id="📌-실행-모델을-이해하라">📌 실행 모델을 이해하라</h3>
<p>다중 스레드 애플리케이션을 분류하는 방식은 여러가지다.</p>
<p><em>생산자- 소비자</em></p>
<p>하나 이상 생산자 스레드가 정보를 생성해 버퍼나 대기열에 넣는다.
하나 이상 소비자 스레드가 대기열에서 정보를 가져와 사용한다.</p>
<p>여기서 사용하는 대기열은 한정된 자원이다.</p>
<p>잘못하면 생산자 스레드와 소비자 스레드가 둘 다 진행 가능함에도 불구하고 동시에 서로 에게서 시그널을 기다릴 가능성이 존재한다.</p>
<p><em>읽기-쓰기</em></p>
<p>읽기 스레드를 위한 주된 정보원으로 공유 자원을 사용하지만, 쓰기 스레드가 이 공유 자원을 이따금 갱신한다고 하면 처리율이 문제의 핵심이다.</p>
<p>읽기 스레드의 요구와 쓰기 스레드의 오규를 적절히 만족시켜 처리율도 적당히 높이고 기아도 방지하는 해법이 필요하다.</p>
<p>간단한 전략은 읽기 스레드가 없을때까지 갱신을 원하는 쓰기 스레드가 버퍼를 기다리는 방법이다.</p>
<p><em>식사하는 철학자들</em> </p>
<p>유명한 철학자들 문제를 각 알고리즘을 공부하고 해법을 직접 구현해보라</p>
<h3 id="📌-동기화하는-메서드-사이에-존재하는-의존성을-이해하라">📌 동기화하는 메서드 사이에 존재하는 의존성을 이해하라</h3>
<p>동기화하는 메서드 사이에 의존성이 존재하면 동시성 코드에 찾아내기 어려운 버그가 생긴다.</p>
<p>공유 객체 하나에는 메서드 하나만 사용하라</p>
<p>여러 메서드가 필요한 상황이 생긴다. 그럴때 세가지 방법을 사용하라</p>
<ol>
<li>클라이언트에서 잠금</li>
<li>서버에서 잠금</li>
<li>연결 서버 </li>
</ol>
<h3 id="📌-동기화하는-부분을-작게-만들어라">📌 동기화하는 부분을 작게 만들어라</h3>
<p>자바에서 synchronized 키워드를 사용하면 락을 설정한다. 같은 락으로 감싼 모든 코드 영역은 한번에 한 스레드만 실행 가능하다. </p>
<p>필요 이상으로 임계 영역 크기를 키우면 스레드 간에 경쟁이 늘어나고 프로그램 성능이 떨어진다.</p>
<h3 id="📌-올바른-종료-코드를-구현하기-어렵다">📌 올바른 종료 코드를 구현하기 어렵다</h3>
<p>깔끔하게 종료하는 코드를 구현하는 곳에서 발생하는 문제가 데드락이다.</p>
<p>종료 코드를 개발 초기부터 고민하고 동작하게 초기부터 구현하라. 생각보다 오래 걸린다. 생각보다 어려우므로 이미 나온 알고리즘을 검토하라</p>
<h3 id="📌-스레드-코드-테스트하기">📌 스레드 코드 테스트하기</h3>
<p>코드가 올바르다고 증명하기 는 현실적으로 불가능하다. 테스트가 정확성을 보장하지 않지만 충분한 테스트는 위험을 낮춘다. </p>
<p>문제를 노출하는 테스트 케이스를 작성하라. 프로그램 설정과 시스템 설정과 부하를 바꿔가며 자주 돌려라 테스트가 실패하면 원인을 추적하라. 다시 돌렸더니 통과하더라는 이유로 그냥 넘어가면 절대로 안된다.
고</p>
<p>구체적인 지침을 제시</p>
<ol>
<li>말이 안 되는 실패는 잠정적인 스레드 문제로 취급하라.</li>
<li>다중 스레드를 고려하지 않은 순차 코드부터 제대로 돌게 만들자</li>
<li>다중 스레드를 쓰는 코드 부분을 다양한 환경에 쉽게 끼워 넣을 수 있도록 스레드 코드를 구현하라</li>
<li>다중 스레드를 쓰는 코드 부분을 상황에 맞춰 조정할 수 있게 작성하라.</li>
<li>프로세서 수보다 많은 스레드를 돌려보라</li>
<li>다른 플랫폼에서 돌려보라 </li>
<li>코드에 보조 코드를 넣어 돌려라 강제로 실패를 일으키게 해보라</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클린코드] 12장 창발성]]></title>
            <link>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-12%EC%9E%A5-%EC%B0%BD%EB%B0%9C%EC%84%B1</link>
            <guid>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-12%EC%9E%A5-%EC%B0%BD%EB%B0%9C%EC%84%B1</guid>
            <pubDate>Sun, 27 Apr 2025 13:03:06 GMT</pubDate>
            <description><![CDATA[<p>창발성이 뭐지 </p>
<hr>
<h3 id="📌-창발적-설계로-깔끔한-코드를-구현하자">📌 창발적 설계로 깔끔한 코드를 구현하자</h3>
<p>우리들 대다수는 켄트 백이 제시한 단순한 설계 규칙 네가지가 소프트웨어 설계 품질을 크게 높여준다고 믿는다.</p>
<p>켄트 백은 다음 규칙을 따르면 설계가 단순하다고 말한다.</p>
<ol>
<li>모든 테스트를 실행한다.</li>
<li>중복을 없앤다</li>
<li>프로그래머 의도를 표현한다.</li>
<li>클래스와 메서드의 수를 최소로 줄인다.</li>
</ol>
<h3 id="📌-단순한-사례-규칙-1-모든-테스트를-실행한다">📌 단순한 사례 규칙 1: 모든 테스트를 실행한다.</h3>
<p>먼저 설계는 의도한 대로 돌아가는 시스템을 내놓아야한다. 테스트를 철저히 거쳐 모든 테스트 케이스를 항상 통과하는 시스템은 테스트가 가능한 시스템이다.</p>
<p>테스트가 가능한 시스템을 만들려고 애쓰면 설계 품질이 더불어 높아진다.</p>
<p>테스트 케이스를 만들고 게속 돌려라 라는 간단하고 단순한 규칙을 따르면 시스템은 낮은 결합도와 높은 응집력이라는 객체 지향 방법론이 지향하는 목표를 저절로 달성한다.</p>
<h3 id="📌-단순한-설계-규칙-24-리팩토링">📌 단순한 설계 규칙 2~4: 리팩토링</h3>
<p>테스트 케이스를 모두 작성했다면 점진적으로 리팩터링 해나간다.</p>
<p>코드를 정리하면서 시스템이 깨질까 걱정할 필요가 없다. 테스트 케이스가 있으니까!</p>
<h3 id="📌-중복을-없애라">📌 중복을 없애라</h3>
<p>비슷한 코드는 더 비슷하게 고쳐주면 리팩터링이 쉬워진다.</p>
<h3 id="📌-표현하라">📌 표현하라</h3>
<p>코드는 개발자의 의도를 분명히 표현해야한다. 개발자가 코드를 명백하게 짤수록 다른 사람이 그 코드를 이해하기 쉬워진다. 그래야 결함이 줄어들고 유지보수 비용이 적게 든다.</p>
<p>우선 좋은 이름을 선택한다.</p>
<p>둘째 함수와 클래스 크기를 가능한 줄인다.</p>
<p>세째 표준 명칭을 사용한다.</p>
<p>네째 단위 테스트 케이스를 꼼꼼히 작성한다. </p>
<p>표현력을 높이는 가장 중요한 방법은 노력이다.</p>
<h3 id="📌-클래스와-메서드-수를-최소로-줄여라">📌 클래스와 메서드 수를 최소로 줄여라</h3>
<p>함수와 클래스 크기를 작세 유지하면서 동시에 시스템 크기도 작세 유지하는 데 있다. 하지만 이 규칙은 간단한 설계 규칙 네 개 중 우선순위가 가장 낮다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클린코드] 11장 시스템]]></title>
            <link>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-11%EC%9E%A5-%EC%8B%9C%EC%8A%A4%ED%85%9C-ku496d9m</link>
            <guid>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-11%EC%9E%A5-%EC%8B%9C%EC%8A%A4%ED%85%9C-ku496d9m</guid>
            <pubDate>Sun, 20 Apr 2025 09:16:44 GMT</pubDate>
            <description><![CDATA[<h3 id="📌-시스템-제작과-시스템-사용을-분리하라">📌 시스템 제작과 시스템 사용을 분리하라</h3>
<p>제작은 사용과 아주 다르다는 사실을 명심한다.</p>
<p>소프트웨어 시스템은 런타임 로직을 분리해야한다.</p>
<p>시작 단계는 모든 애플리케이션이 풀어야할 관심사다. 관심사 분리는 우리 분야에서 가장 오래되고 가장 중요한 설계 기법중 하나다.</p>
<p>체계적이고 탄탄한 시스템을 만들고 싶다면 흔히 쓰는 좀스럽고 손쉬운 기법으로 모듈성을 깨서는 절대로 안된다.</p>
<p><em>Main 분리</em></p>
<p>시스템 생성과 시스템 사용을 분리하는 방법이다.</p>
<p><em>의존성 주입</em></p>
<p>사용과 제작을 분리하는 강력한 메커니즘 하나가 의존성 주입이다.</p>
<p>의존성 주입은 제어 역전 기법을 의존성 관리에 적용한 메커니즘이다.</p>
<p>제어 역전에서는 한 객체가 맡은 보조 책임을 새로운 객체에게 전적으로 떠넘기다. 새로운 객체는 넘겨받은 책임만 맡으므로 단일 책임 원칙을 지키게 된다.</p>
<p>호출되는 객체는 실제로 반환되는 객체의 유형을 제어하지 않는다. 대신 호출하는 객체는 의존성을 능동적으로 해결한다.</p>
<p>진정한 의존성 주입은 클래스가 의존성을 해결하려 시도하지 않으며 수동적이다. 대신에 의존성을 주입하는 방법으로 설정자 메서드나 생성자 인수를 제공한다.</p>
<h3 id="📌-확장">📌 확장</h3>
<p>깨끗한 코드는 코드 수준에서 시스템을 조정하고 확장하기 쉽게 만든다.</p>
<p>소프트웨어 시스템은 물리적인 시스템과 다르다. 관심사를 적절히 분리해 관리한다면 소프트웨어 아키텍처는 점진적으로 발전할 수 있다.</p>
<p><em>횡단 관심사</em></p>
<p>EJB2 아키텍처는 일부 영역에서 관심사를 거의 완벽하게 분리한다.</p>
<p>영속성과 같은 관심사는 애플리케이션의 자연스러운 객체 경계를 넘나드는 경향이 잇다.</p>
<p>AOP는 횡단 관심사에 대처해 모듈성을 확보하는 일반적인 방법론이다. </p>
<p>AOP에서 관점이라는 모듈 구성 개념은 특정 관심사를 지원하려면 시스템에서 특정 지점들이 동작하는 방식을 일관성 있게 바꿔야한다라고 명시한다.</p>
<h3 id="📌-자바-프록시">📌 자바 프록시</h3>
<p>단순한 상황에 적합하다. 개별 객체나 클래스에서 메서드 호출을 감싸는 경우가 좋은 예다</p>
<p>프록시를 사용하면 깨끗한 코드를 작성하기 어렵다</p>
<h3 id="📌-순수-자바-aop-프레임-워크">📌 순수 자바 AOP 프레임 워크</h3>
<p>다행스럽게도 대부분의 프록시 코드는 판박이라 도구로 자동화할 수 있다.</p>
<p>순수 자바 관점을 구현하는 스프링 AOP, JBoss AOP 등과 같은 여러 자바 프레임워크는 내부적으로 프록시를 사용한다.</p>
<p>스프링은 비즈니스 논리를 POJO로 구현한다. POJO는 순수하게 도메인에 초점을 맞춘다.</p>
<p>프로그래머는 설정 파일이나 API를 사용해 필수적인 애플리케이션 기반 구조를 구현한다.</p>
<h3 id="📌-aspectj-관점">📌 AspectJ 관점</h3>
<p>관심사를 관점으로 분리하는 가장 강력한 도구는 AspectJ 언어다.</p>
<p>언어 차원에서 관점을 모듈화 구성으로 지원하는 자바 언어 확장이다. </p>
<p>관점을 분리하는 강력하고 풍부한 도구 집합을 제공하긴 하지만, 새 도구를 사용하고 새 언어 문법과 사용법을 익혀야 한다는 단점이 있다.</p>
<h3 id="📌-테스트-주도-시스템-아키텍처-구축">📌 테스트 주도 시스템 아키텍처 구축</h3>
<p>코드 수준에서 아키텍처 관심사를 분리할 수 있다면 진정한 테스트 주도 아키텍처 구축이 가능해진다.</p>
<p>아주 단순하면서도 멋지게 분리된 아키텍처로 소프트웨어 프로젝트를 진행해 결과물을 재빨리 출시한 후, 기반 구조를 추가하며 조금씩 확장해 나가도 괜찮다는 말이다. 그렇다고 아무 방향 없이 프로젝트에 뛰어들어도 좋다는 소리는 아니다.</p>
<p>시스템의 일반적인 구조도 생각해야한다.</p>
<h3 id="📌-의사-결정을-최적화하라">📌 의사 결정을 최적화하라</h3>
<p>모듈을 나누고 관심사를 분리하면 지엽적인 관리와 결정이 가능해진다.</p>
<p>가장 적합한 사람에게 책임을 맡기면 가장 좋다. 우리는 때때로 가능한 마지막 순간까지 결정을 미루는 방법이 최선이라는 사실을 까먹곤한다.</p>
<ul>
<li>게으른 것이 아닌 최대한 정보를 모아 최선의 결정을 내리기 위해서</li>
</ul>
<h3 id="📌-명백한-가치가-있을-때-표준을-현명하게-사용하라">📌 명백한 가치가 있을 때 표준을 현명하게 사용하라</h3>
<p>아주 과장되게 포장된 표준에 집착하는 바람에 고객 가치가 뒷전으로 밀려난 사례를 많이 봤다.</p>
<h3 id="📌-시스템은-도메인-특화-언어가-필요하다">📌 시스템은 도메인 특화 언어가 필요하다</h3>
<p>DSL은 간단한 스크립트 언어나 표준 언어로 구현한 API를 가르킨다.</p>
<p>DSL로 짠 코드는 도메인 전문가가 작성한 구조적인 산문처럼 읽힌다.</p>
<p>좋은 DSL은 도메인 개념과 그 개념을 구현한 코드 사이에 존재하는 의사소통 간극을 줄여준다.</p>
<h3 id="📌--결론">📌  결론</h3>
<p>시스템 역시 깨끗해야한다. 깨끗하지 못한 아키텍처는 도메인 논리를 흐리며 기민성을 떨어뜨린다.</p>
<p>시스템을 설계하든 개별 모듈을 설계하든, 실제로 돌아가는 가장 단순한 수단을 사용해야한다는 사실을 명심하자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클린코드] 10장 클래스]]></title>
            <link>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-10%EC%9E%A5-%ED%81%B4%EB%9E%98%EC%8A%A4-qe5411ye</link>
            <guid>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-10%EC%9E%A5-%ED%81%B4%EB%9E%98%EC%8A%A4-qe5411ye</guid>
            <pubDate>Sun, 20 Apr 2025 09:16:05 GMT</pubDate>
            <description><![CDATA[<h3 id="📌-클래스-체계">📌 클래스 체계</h3>
<p>클래스를 정의하는 표준 자바 관례에 따르면 가장 먼저 변수 목록이 나온다.</p>
<p>정적 공개 상수가 있다면 맨처음에 나오고 정적 비공개 변수가 나오면 비공개 인스턴스 변수가 나온다. 공개 변수가 핑료한 경우는 거의 없다.</p>
<p>프로그램은 신문 기사처럼 릭힌다.</p>
<p><em>캡슐화</em> </p>
<p>변수와 유틸리티 함수는 가능한 공개하지 않는 편이 낫지만 반드시 숨겨야 한다는 법칙도 없다</p>
<p>하지만 비공개 상태를 유지한 온갖 방법을 강구해야한다.</p>
<p>캡슐화를 풀어주는 결정은 언제나 초후의 수단이다.</p>
<h3 id="📌-클래스는-작아야한다">📌 클래스는 작아야한다.</h3>
<p>클래스는 작아야한다. </p>
<p>클래스 이름은 해당 클래스 책임으 기술해야 한다. 실제로 작명은 클래스 크기를 줄이는 첫 번째 관문이다.</p>
<p><em>단일책임 원칙(SRP)</em></p>
<p>클래스는 책임 즉 변경할 이유가 하나여야한다.</p>
<p><em>응집도</em></p>
<p>클래스는 인스턴스 변수가 작아야한다. 각 클래스 메서드는 클래스 인스턴스 변수를 하나 이상 사용해야하낟.</p>
<p><em>응집도를 유지하면 작은 클래스 여럿이 나온다.</em></p>
<p>큰 함수를 작은 함수 여럿으로 나누기만 해도 클래스 수가 많아진다.</p>
<h3 id="📌-변경하기-쉬운-클래스">📌 변경하기 쉬운 클래스</h3>
<p>대다수 시스템은 지속적인 변경이 가해진다. 깨끗한 시스템은 클래스를 체계적으로 정리해 변경에 수반하는 위험을 낮춘다.</p>
<p><em>변경으로부터 격리</em></p>
<p>인터페이스와 추상 클래스를 사용해 구현이 미치는 영향을 격리한다.</p>
<p>상세한 구현에 의존하는 코드는 테스트가 어렵다.</p>
<p>테스트가 가능할 정도로 시스템의 결합도를 낮추면 유연성과 재사용성도 더욱 높아진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클린코드] 9장 단위 테스트]]></title>
            <link>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-9%EC%9E%A5-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@ju_yeong17/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-9%EC%9E%A5-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Mon, 14 Apr 2025 04:07:45 GMT</pubDate>
            <description><![CDATA[<p>애자일과 TDD 덕택에 단위 테스트를 자동화하는 프로그래머들이 많아졌다.</p>
<h3 id="📌-tdd-법칙-세가지">📌 TDD 법칙 세가지</h3>
<ol>
<li>실패하는 단위테스트를 작성할 때까지 실제 코드를 작성하지 않는다.</li>
<li>컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.</li>
<li>현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다</li>
</ol>
<p>세가지 규칙을 따르면 개발과 테스트가 대락 30초 주기로 묶인다.</p>
<p>방대한 테스트 코드는 심각한 관리 문제를 유발하기도 한다.</p>
<h3 id="📌-깨끗한-테스트-코드-유지하기">📌 깨끗한 테스트 코드 유지하기</h3>
<p>테스트 코드는 실제 코드 못지 않게 중요하다. 실제 코드 못지 않게 깨끗하게 짜야한다.</p>
<p><em>테스트는 유연성, 유지보수성, 재사용성을 제공한다.</em></p>
<p>테스트 코드를 깨끗하게 유지 하니 않으면 결국 잃어버린다. 그리고 테스트 케이스가 없으면 실제 코드를 유연하게 만드는 버팀목도 사라진다.</p>
<p>코드에 유연성 , 유지보수성, 재사용성을 제공하는 버팀목이 바로 단위 테스트이다.</p>
<h3 id="📌-깨끗한-테스트-코드">📌 깨끗한 테스트 코드</h3>
<p>깨끗한 테스트 코드를 만들려면 세가지가 필요하다</p>
<p>가독성, 가독성, 가독성</p>
<p>가독성은 실제 코드보다 테스트 코드에 더더욱 중요하다.</p>
<p><em>도메인에 특화된 테스트 언어</em></p>
<p>테스트를 구현하는 당사자와 나중에 테스트를 읽어볼 독자를 도와주는 테스트언어</p>
<p><em>이중 표준</em></p>
<p>테스트 API 코드에 적용하는 표준은 실제 코드에 적용하는 표준과 확실히 다르다.</p>
<p>단순하고 간결하고 표현력이 풍부해야하지만 실제 코드만큼 효율적일 필요는 없다.</p>
<h3 id="📌-테스트당-assert-하나">📌 테스트당 assert 하나</h3>
<p>assert 문이 단 하나인 함수는 결론이 하나라서 코드를 이해하기 쉽고 빠르다.</p>
<p><em>테스트당 개념하나</em></p>
<p>테스트 함수마다 한 개념만 테스트하라는 규칙이 더 낫다</p>
<p>테스트 함수 하나에 개념하나만 테스트하라</p>
<h3 id="📌-first">📌 FIRST</h3>
<p>깨끗한 테스트는 다섯가지 규칙을 따른다.</p>
<p>빠르게(FAST)</p>
<ul>
<li>테스트는 빨라야한다. 빨리 돌아야한다는 말이다.</li>
</ul>
<p>독립적으로(Independent)</p>
<ul>
<li>각 테스트는 서로 의존하면 안된다. 한 테스트가 다음 테스트가 실행될 환경을 준비해서는 안된다. 각 테스트는 독립적으로 그리고 어떤 순서로 실행해도 괜찮아야한다.</li>
</ul>
<p>반복가능하게 (Repeatable)</p>
<ul>
<li>테스트는 어떤 환경에서도 반복 가능해야한다.</li>
</ul>
<p>자가검증하는 (self-validating)</p>
<ul>
<li>테스트는 부울 값으로 결과를 내야한다.</li>
</ul>
<p>적시에 (timely) </p>
<ul>
<li>테스트는 적시에 작성해야한다. 단위 테스트는 테스트 하려는 실제 코드를 구현하기 직전에 구현한다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>