<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>GO 22!</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 21 Jul 2024 12:52:26 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>GO 22!</title>
            <url>https://velog.velcdn.com/images/22_/profile/850127dd-64b4-4145-af8e-1e64ed985654/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. GO 22!. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/22_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[github action ]]></title>
            <link>https://velog.io/@22_/github-action</link>
            <guid>https://velog.io/@22_/github-action</guid>
            <pubDate>Sun, 21 Jul 2024 12:52:26 GMT</pubDate>
            <description><![CDATA[<h3 id="name">name</h3>
<pre><code>name: learn-github-actions</code></pre><p>-&gt; Actions 탭에 나타날 이름이다. (optional)</p>
<h3 id="run-name">run-name</h3>
<pre><code>run-name: ${{ github.actor }} is learning GitHub Actions</code></pre><p>-&gt; Actions 탭을 클릭하면 나오는 워크플로 실행 목록에 표시되는 이름 (optional)</p>
<h3 id="on">on</h3>
<pre><code>on: [push]</code></pre><p>-&gt; 워크 플로우에 대한 트리거를 지정한다. push, branch 등 여러 이벤트로 트리거할 수 있다. </p>
<ul>
<li>cron 을 통해 스케줄링할 수 도 있다.</li>
<li>input 내용을 받을 수 있다. <pre><code>on:
workflow_dispatch:
  inputs:
    build_id:
      required: true
      type: string
    deploy_target:
      required: true
      type: string
    perform_deploy:
      required: true
      type: boolean
</code></pre></li>
</ul>
<p>jobs:
  deploy:
    runs-on: ubuntu-latest
    if: ${{ inputs.perform_deploy }}
    steps:
      - name: Deploy build to target
        run: echo &quot;Deploying build:${{ inputs.build_id }} to target:${{ inputs.deploy_target }}&quot;</p>
<p>```</p>
<p>=&gt; 이 예제에서 워크플로우는 &#39;workflow_dispatch&#39; 이벤트에 의해서 트리거 된다. 워크플로우에 전달된 build_id, deployt_target, perform_deploy 의 입력값을 가져오기 위해 <code>input</code> 을 사용한다. </p>
<h3 id="permissions">permissions</h3>
<h3 id="ref">REF</h3>
<p><a href="https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions#understanding-the-workflow-file">https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions#understanding-the-workflow-file</a>
<a href="https://docs.github.com/en/actions/learn-github-actions/contexts#example-contents-of-the-inputs-context">https://docs.github.com/en/actions/learn-github-actions/contexts#example-contents-of-the-inputs-context</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[kotlin constructor]]></title>
            <link>https://velog.io/@22_/kotlin-constructor</link>
            <guid>https://velog.io/@22_/kotlin-constructor</guid>
            <pubDate>Wed, 10 Jul 2024 12:46:32 GMT</pubDate>
            <description><![CDATA[<h3 id="primary-constructor">Primary Constructor</h3>
<ul>
<li><p>primary constructor 에는 어떠한 code 도 들어갈 수 없다. 초기화하는 코드를를 넣고 싶은 경우 init() 을 사용해야한다. </p>
</li>
<li><p>kotlin init() 생성자와 property 선언 및 초기화는 같은 우선순위를 가져 위에서부터 선언된 순서대로 수행된다. </p>
</li>
</ul>
<pre><code>kotlin

class InitOrderDemo(name: String) {
    val firstProperty = &quot;First property: $name&quot;.also(::println)

    init {
        println(&quot;First initializer block that prints $name&quot;)
    }

    val secondProperty = &quot;Second property: ${name.length}&quot;.also(::println)

    init {
        println(&quot;Second initializer block that prints ${name.length}&quot;)
    }
}
</code></pre><pre><code>First property: hello
First initializer block that prints hello
Second property: 5
Second initializer block that prints 5

</code></pre><h3 id="secondary-constructor">Secondary Constructor</h3>
<ul>
<li>prefix 로 constructor 를 사용해야한다. </li>
<li>기본 생성자가 있는 경우는 this를 사용하여 위임해야한다. </li>
<li><code>init</code> 은 기본 생성자의 일부이기 때문에, secondary constructor 이전에 실행된다. </li>
</ul>
<pre><code>class Constructors {
    init {
        println(&quot;Init block&quot;)
    }

    constructor(i: Int) {
        println(&quot;Constructor $i&quot;)
    }
}</code></pre><pre><code>Init block
Constructor 1
</code></pre><ul>
<li>primary constructor 가 없더라도 먼저 실행된다. </li>
</ul>
<h3 id="reference">REFERENCE</h3>
<p><a href="https://kotlinlang.org/docs/classes.html#class-members">https://kotlinlang.org/docs/classes.html#class-members</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Project Reactor]]></title>
            <link>https://velog.io/@22_/Project-Reactor</link>
            <guid>https://velog.io/@22_/Project-Reactor</guid>
            <pubDate>Sat, 11 May 2024 13:07:00 GMT</pubDate>
            <description><![CDATA[<h3 id="reactor란-">Reactor란 ?</h3>
<ul>
<li>Spring WebFlux 기반의 리액티브 애플리케이션을 제작하기 위한 핵심 역할이다. </li>
</ul>
<h3 id="특징">특징</h3>
<ul>
<li>리액티브 스트림즈의 구현체이다.</li>
<li>Non-Blocking </li>
<li>Flux[N] : Reactor 의 Publisher 타입 중 하나이다. N 개의 데이터를 emit 한다는 것이다. </li>
<li>Mono[0|1] : Reactor 의 Publisher 타입 중 하나이다. </li>
<li>Well-suited for microservices: 마이크로 서비스 기반 시스템이 적합한 시스템 중 하나이다. </li>
<li>Backpressure-ready network: Reactor 는 Publisher 로부터 전달받은 데이터를 처리하는 데 과부하가 걸리지 않도록 제어하는 backpressure 를 지원한다. </li>
</ul>
<h3 id="구성요소">구성요소</h3>
<ul>
<li>아주 간단하지만 핵심 구성요소를 갖고 있다. <pre><code class="language-java">public class reactor {
  public static void main(String[] args) {
      // Publisher 이다. &quot;Hello&quot;, 1. Publisher : 데이터를 생성해서 제공하고, 
      Flux&lt;String&gt; sequence = Flux.just(&quot;Hello&quot;, &quot;Reactor&quot;);
      sequence.map(data -&gt; data.toLowerCase()) // 2. 데이터를 가공하고,
          .subscribe(data -&gt; System.out.println(data)); // 3. Subscriber : 전달받은 데이터를 처리한다.   
  }
}</code></pre>
</li>
</ul>
<p>출처 : 스프링으로 시작하는 리액티브 프로그래밍 챕터5</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액티브 프로그래밍을 위한 사전지식 ]]></title>
            <link>https://velog.io/@22_/%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%82%AC%EC%A0%84%EC%A7%80%EC%8B%9D</link>
            <guid>https://velog.io/@22_/%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%82%AC%EC%A0%84%EC%A7%80%EC%8B%9D</guid>
            <pubDate>Sat, 13 Apr 2024 05:45:54 GMT</pubDate>
            <description><![CDATA[<p>함수형 프로그래밍 ?</p>
<ul>
<li>java8부터 람다 표현식이 도입되면서 함수형 프로그래밍 기법을 사용할 수 있게 되었다.</li>
</ul>
<h3 id="함수형-인터페이스-functional-interface">함수형 인터페이스 Functional Interface</h3>
<ul>
<li><p>기존 인터페이스와 달리 단 하나의 추상 메서드만 정의되어 있다.
왜 이름을 굳이 함수형 인터페이스라고 부를까?</p>
</li>
<li><p>함수형 프로그래밍 세계에서는 함수를 값으로 취급한다. 즉, 어떤 함수를 호출할 때 함수 자체를 파라미터를 전달 할 수 있다. </p>
<pre><code class="language-java">Collections.sort(cryptoCurrencies, new Comparator&lt;CryptoCurrency&gt;() {
          @Override
          public int compare(CryptoCurrency o1, CryptoCurrency o2) {
              return o1.getUnit().name().compareTo(o2.getUnit().name());
          }
      });</code></pre>
<p>-&gt; 함수형 인터페이스인 Comperator를 사용.. </p>
<p>Comperator 인터페이스는 default 메서드가 여러개 있지만 추상 메서드 하나만 정의되어 있으므로 함수형 인터페이스이다. 
@FunctionalInterface 어노테이션으로 명시적으로 지정되어있다. </p>
</li>
</ul>
<p>그런데, 인터페이스 자체를 익명 구현객체로 전달하는 방식이 코드자체가 너무 길어서 지저분하게 보인다. 
  -&gt; 람다 표현식을 사용한다. </p>
<h3 id="람다-표현식">람다 표현식</h3>
<ul>
<li>javascript 의 화살표 함수와 비슷한 형태로 java 에서 함수를 값으로 취급하기 위해 생겨난 간결한 형태의 표현식이다. 
(String a, String b) -&gt; a.equals(b)</li>
<li><blockquote>
<p>람다 파라미터       -&gt; 화살표   -&gt; 람다 body</p>
</blockquote>
</li>
<li>람다 표현식은 단 하나의 추상메서드를 가지는 인터페이스, 즉 함수형 인터페이스를 구현한 클래스의 메서드 구현을 단순화한 표현식이다. </li>
<li><ul>
<li>람다파라미터 : 함수형 인터페이스에 정의된 추상 메서드의 파라미터</li>
</ul>
</li>
<li><ul>
<li>람다 body : 추상 메서드에서 구현되는 메서드 body<pre><code>Collections.sort(cryptoCurrencies, (o1, o2) -&gt; o1.getUnit().name().compareTo(o2.getUnit().name()));</code></pre></li>
<li>위에 보다 훨씬 깔끔하다!!</li>
</ul>
</li>
</ul>
<p>헷갈리면 안될 것! </p>
<ul>
<li>함수형 인터페이스의 추상 메서드를 람다 표현식으로 작성해서 메서드의 파라미터로 전달한다. <ul>
<li>이 말의 의미는 &quot;메서드 자체를 파라미터로 전달하는 것&quot; 이 아니다. 바로, 함수형 인터페이스를 구현한 클래스의 인스턴스를 람다 표현식으로 작성해서 전달하는 것람다 표현식은 함수형 인터페이스를 구현한 클래스의 객체이다! </li>
</ul>
</li>
<li>람다표현식은 외부에서 정의된 변수도 사용할 수 있다. as 람다 캡처링    </li>
</ul>
  <br>

<h3 id="메서드-레퍼런스">메서드 레퍼런스</h3>
<ul>
<li><p>람다표현식 보다 좀 더 간결하게 함수형 인터페이스를 작성할 수 있다. </p>
</li>
<li><p>메서드의 이름만 사용하여 메서드 레퍼런스를 표현합니다. </p>
<pre><code>(Car car) -&gt; car.getCarName() = Car::getCarName</code></pre><br>



</li>
</ul>
<h3 id="함수-디스크립터">함수 디스크립터</h3>
<ul>
<li>함수 디스크립터란 일반화된 람다 표현식을 통해서 이 함수형 인터페이스가 어떤 파라미터를 가지고, 어떤 값을 리턴하는지 설명해주는 역할을 한다. </li>
<li>Predicate<T> : 서술하다, 단정하다. 파라미터가 조건을 만족하는지 검사한다는 의미이다. <ul>
<li>T -&gt; boolean : T 타입의 람다 파라미터와 boolean 타입의 값을 리턴.</li>
</ul>
</li>
<li>Consumer<T> : 리턴값이 없고 데이터를 소비한다라는 의미 <ul>
<li>T -&gt; void. : T타입의 파라미터가 있고 리턴값은 없다. 일정 작업을 수행한 후 결과 값을 리턴할 필요가 없는 경우, 사용(배치)</li>
</ul>
</li>
<li>Function&lt;T,R&gt; : 함수 내에서 어떤 처리 과정을 거친 후에 그 결과로 특정 타입의 값을 반환한다. <ul>
<li>T -&gt; R : T 타입의 파라미터를 가지며, R 타입을 리턴한다. </li>
</ul>
</li>
<li>Supplier<T> : 어떤 값이 필요할 때 데이터를 제공하는 용도로 사용한다. 즉, 어떤 데이터를 제공할지에 대한 동작을 정의한다. <ul>
<li>() -&gt; T : 추상 메서드가 파라미터를 갖지 않으며 리턴으로 T타입의 값만 리턴한다. </li>
</ul>
</li>
</ul>
  <br>
  Ref 스프링으로 시작하는 리액티브 프로그래밍 챕터4
]]></description>
        </item>
        <item>
            <title><![CDATA[Blocking I/O, Non-Blocking I/O]]></title>
            <link>https://velog.io/@22_/Blocking-IO-Non-Blocking-IO</link>
            <guid>https://velog.io/@22_/Blocking-IO-Non-Blocking-IO</guid>
            <pubDate>Mon, 01 Apr 2024 13:12:41 GMT</pubDate>
            <description><![CDATA[<h3 id="io-란-">I/O 란 ?</h3>
<ul>
<li>운영체제 측면에서 컴퓨터 시스템이 외부의 입출력 장치들과 데이터를 주고받는 것을 의미한다. </li>
</ul>
<p>웹 애플리케이션 측면에서는 ?</p>
<ul>
<li>파일 I/O, DB I/O ,, 네트워크 I/O!</li>
</ul>
<h3 id="blocking-io">Blocking I/O?</h3>
<ul>
<li>하나의 스레드가 I/O 에 의해서 차단되어 대기하는 것.</li>
<li>문제점을 보완하기 위해 멀티스레딩 기법으로 차단된 그 시간을 효율적으로 쓸 수 있다. </li>
<li>하지만 멀티스레딩도 문제가 있다. </li>
</ul>
<p>멀티스레딩의 문제 </p>
<ul>
<li><p>JVM 의 디폴트 스택 사이즈는 64bit =&gt; 1024 KB 으로, 64,000명이 동시 접속을 한다면 64GB 정도의 메모리가 추가로 필요하게 된다. </p>
</li>
<li><p>Java 웹 어플리케이션은 요청당 하나의 스레드를 할당한다. 만약에! 또 다른 작업을 처리하기 위해 스레드를 추가로 할당하게 된다면(멀티스레딩) 과다한 메모리 사용으로 오버헤드가 발생할 수 있다.</p>
</li>
<li><p>대량의 요청이 발생하게 된 경우 스레드 풀에 유휴 스레드가 없을 경우, 스레드 풀에서 응답지연이 발생할 수 있다. </p>
</li>
</ul>
<h3 id="non-blocking-io">Non-Blocking I/O</h3>
<ul>
<li>Blocking I/O 와 반대로 스레드가 차단되지 않는다!</li>
<li>차단되지 않기 때문에 하나의 스레드로 많은 수의 요청을 처리할 수 있다. </li>
<li>더 적은 수의 스레드를 사용하기 때문에 Blocking I/O에서 멀티스레딩을 사용할 때의 문제점들이 생기지 않는다. </li>
<li>단점,, 스레드 내부에 cpu 를 많이 사용하는 작업이 포함된 경우 성능에 악영향을 준다. (왜지? 이건 어떤 경우에도 그렇지 않나..) </li>
<li>Blocking I/O 요소가 포함된 경우 Non-Blocking 의 이점이 없다.</li>
</ul>
<h3 id="spring-framework--blocking-io-non-blocking-io">Spring Framework , Blocking I/O, Non-Blocking I/O</h3>
<ul>
<li>Spring MVC 는 Blocking I/O 를 사용한다.</li>
<li>Spring WebFlux 는 Netty 같은 비동기 Non-Blocking I/O 기반의 서버 엔진을 사용함으로써 적은 수의 스레드로 많은 수의 요청을 처리한다. 
즉, 적은 컴퓨팅 파워로 고성능의 애플리케이션을 운영할 수 있다. </li>
</ul>
<h3 id="non-blocking-io-방식의-통신이-적합한-시스템은">Non-Blocking I/O 방식의 통신이 적합한 시스템은?</h3>
<ul>
<li>고려해야할 것: 학습 난이도, 리액티브 프로그래밍 경험이 있는 개발 인력을 확보할 수 있는가 ? </li>
<li>대량의 요청 트래픽이 발생하는 시스템 : 서버 증설이나 VM 확장 등을 통해 트래픽을 분산할 수 있겠지만 그만큼의 높은 비용을 지불해야 한다. </li>
<li>마이크로 서비스 기반 시스템 : MSA 특성상 서비스들 간에 많은 수의 I/O 가 발생한다. 따라서 특정 서비스들 간의 통신에서 Blocking 으로 인한 응답 지연이 발생하게 된다면 해당 서비스뿐만 아니라 연쇄 작용으로 다른 서비스들에도 영향을 미칠 가능성이 높다. (MSA 에는 반드시 필요하다고 할 수 있다!)</li>
<li>스트리밍 또는 실시간 시스템 : 무한한 데이터 스트림을 전달받아서 효율적으로 처리할 수 있다. </li>
</ul>
<br>
<br>

<h3 id="reference">Reference</h3>
<p>스프링으로 시작하는 리액티브 프로그래밍 chater3</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[hibernate 자동 entity 중복제거를 피하는 방법]]></title>
            <link>https://velog.io/@22_/hibernate-%EC%9E%90%EB%8F%99-entity-%EC%A4%91%EB%B3%B5%EC%A0%9C%EA%B1%B0</link>
            <guid>https://velog.io/@22_/hibernate-%EC%9E%90%EB%8F%99-entity-%EC%A4%91%EB%B3%B5%EC%A0%9C%EA%B1%B0</guid>
            <pubDate>Thu, 28 Mar 2024 01:03:00 GMT</pubDate>
            <description><![CDATA[<h2 id="when">WHEN?</h2>
<p>회사에서 중복된 엔티티들을 조회하여 각 entity 의 counting 을 하는 작업을 하려고 했습니다. 
<strong>그런데 엔티티들이 다 중복이 제거돼서 리턴됐다?!</strong></p>
<h3 id="예시">예시</h3>
<p>User와 Record 엔티티가 있습니다. 1:N 관계입니다.
User가 Record 에 저장된 것만큼? User를 조회하고 싶습니다. </p>
<pre><code class="language-java"># querydsl 

    fun findUsersEntity(): List&lt;UserEntity&gt; {
        return from(QUserEntity.userEntity)
            .leftjoin(QRecordEntity.recordEntity)
            .on(QUserEntity.userEntity.id.eq(QRecordEntity.recordEntity.fkId))
            .fetch()
    }
</code></pre>
<pre><code class="language-sql"># sql 

select
        ue1_0.id,
        ue1_0.name
    from
        user ue1_0
    left join
        record re1_0
            on ue1_0.id=re1_0.fk_id;</code></pre>
<ul>
<li>같은 쿼리로 보여 응답값도 같은 것으로 예측되지만, 다른 응답값을 가져온다. </li>
</ul>
<p align="center">
<img src="https://velog.velcdn.com/images/22_/post/8e3be877-9c88-4ad1-9634-913dff587fc7/image.png"/>
api 응답
</p> 

<p align="center">
<img src="https://velog.velcdn.com/images/22_/post/9ca4deae-ee66-4a7c-84c1-9cd499d071f0/image.png"/>
sql 리턴
</p> 


<blockquote>
<p>기대했던 응답값은 sql 값과 같습니다. 같은 쿼리이지만 왜 api 응답과 쿼리의 결과는 다른걸까요?</p>
</blockquote>
<p><br><br></p>
<h2 id="why">WHY?</h2>
<h3 id="하이버네이트-5">하이버네이트 5</h3>
<ul>
<li>일대다 조인하여 부모만 select 하면, 자식 수 만큼 부모 엔티티가 늘어나는 이슈가 있었습니다. 그걸 해결하기 위해 JPQL 에 distinct 를 붙였습니다.</li>
<li>하지만 SQL distinct 는 그냥 select 보다 연산이 많아 성능적으로 불리합니다 (실행계획 참조)</li>
<li><code>org.hibernate.jpa.QueryHints.HINT_PASS_DISTINCT_THROUGH</code>  옵션을 false 로 주면 SQL distinct 쿼리가 발생하지 않고 하이버네이트가 어플리케이션에서 중복제거를 해줍니다.</li>
</ul>
<h3 id="하이버네이트-6">하이버네이트 6</h3>
<ul>
<li>SQL distinct 쿼리를 만들지 않고 어플리케이션에서 중복제거를 하도록 바뀌었습니다. (하이버네이트 6.0 마이그레이션 가이드에서는 다르게 설명되어있어서 확인 필요)</li>
<li>HINT_PASS_DISTINCT_THROUGH 옵션이 아예 없어졌습니다.</li>
</ul>
<p><br><br></p>
<h2 id="how">HOW?</h2>
<p><img src="https://velog.velcdn.com/images/22_/post/da35d98c-3d07-4279-8c0c-d24837520894/image.png" alt="">
<code>org.hibernate.sql.results.spi.ListResultsConsumer&lt;R&gt;</code></p>
<h4 id="중복제거가-되는-과정">중복제거가 되는 과정</h4>
<ul>
<li>1: 리턴타입이 Entity 이고 UniqueSemantic.ALLOW 가 True면 EntityResults를 사용합니다.</li>
<li>2: addUnique() 를 하기 때문에, 여기서 중복제거가 됩니다.</li>
</ul>
<h3 id="방법1--uniquesemanticallow-설정을-바꿀-수-없을까-">방법1 : UniqueSemantic.ALLOW 설정을 바꿀 수 없을까 ?</h3>
<p><img src="https://velog.velcdn.com/images/22_/post/b3190995-cab9-4ef8-bc6a-66e9661605d5/image.png" alt="">
<code>org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan&lt;R&gt;</code> </p>
<ul>
<li>처음 쿼리를 요청할 때 각 쿼리에 맞는 <code>UniqueSemantic</code> 값이 세팅됩니다. </li>
<li>제 요구사항에는 중복된 결과가 응답돼서 유니크하지 않기 때문에 ALLOW 가 세팅됩니다.</li>
<li>만약 findByName() 같은 쿼리를 사용한다면 NONE 이 세팅됩니다. </li>
</ul>
<p>이 값은 SQM 이 판단하기 때문에 제가 옵션을 세팅할 수 없었습니다. </p>
<blockquote>
<p>SQM 이란? Semantic Query Model 로 하이버네이트 6부터 도입됐습니다.
  It is the new entity query parser that addresses both JPQL and Criteria API.</p>
</blockquote>
<p><br><br></p>
<h2 id="해결">해결</h2>
<h3 id="방법2--result-가-entity-가-아닌-경우">방법2 : result 가 entity 가 아닌 경우</h3>
<ul>
<li>1: 1에서 False 이기 때문에 Results를 사용합니다.</li>
<li>3: add() 를 합니다. 중복제거가 되지 않기 때문에 중복된 데이터를 리턴합니다.</li>
</ul>
<h2 id="reference">Reference</h2>
<p><a href="https://in.relation.to/2016/08/04/introducing-distinct-pass-through-query-hint/">https://in.relation.to/2016/08/04/introducing-distinct-pass-through-query-hint/</a>
<a href="https://docs.jboss.org/hibernate/orm/6.0/migration-guide/migration-guide.html#query-sqm-distinct">https://docs.jboss.org/hibernate/orm/6.0/migration-guide/migration-guide.html#query-sqm-distinct</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stream API 와 OOM ]]></title>
            <link>https://velog.io/@22_/Stream-API-%EC%99%80-OOM</link>
            <guid>https://velog.io/@22_/Stream-API-%EC%99%80-OOM</guid>
            <pubDate>Sun, 21 Jan 2024 05:26:06 GMT</pubDate>
            <description><![CDATA[<p>Stream API는 함수형 프로그래밍 스타일을 지원하며, 중간 연산과 최종 연산을 사용하여 데이터를 스트림으로 처리하여 대용량 데이터를 효율적으로 처리할 수 있다.</p>
<p>Stream API가 OOM를 해결하는 데 도움이 되는 특징은 다음과 같다.</p>
<p>Lazy Evaluation
중간 연산들은 실제로 데이터를 처리하지 않고, 최종 연산이 호출될 때에만 데이터를 처리하기 때문에 대용량 데이터를 한 번에 모두 메모리에 적재하지 않고 처리할 수 있다</p>
<ul>
<li>however!!!!!!! 출력 시점에 엄청나게 큰 리스트의 계산을 한번에 수행해야 하기때문에 메모리 오버헤드가 부담될 수 있다. </li>
</ul>
<p>Pipelining
Stream API는 연속적인 연산들을 파이프라이닝하여 데이터를 순차적으로 처리하기 때문에 중간 연산들에 대한 결과를 임시적으로 저장하지 않고도 메모리 사용을 최적화할 수 있다</p>
<p>Parallel Processing</p>
<p> <a href="https://medium.com/@goinhacker/lazy-evaluation%EA%B3%BC-%EB%A9%94%EB%AA%A8%EB%A6%AC-c6789ac2173c">https://medium.com/@goinhacker/lazy-evaluation%EA%B3%BC-%EB%A9%94%EB%AA%A8%EB%A6%AC-c6789ac2173c</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Covering Index(Using index)]]></title>
            <link>https://velog.io/@22_/Covering-IndexUsing-index</link>
            <guid>https://velog.io/@22_/Covering-IndexUsing-index</guid>
            <pubDate>Sun, 17 Dec 2023 05:06:53 GMT</pubDate>
            <description><![CDATA[<p>실행계획 분석에 Extra 컬럼에 &quot;Using index&quot; 가 표시될 수 있다.
이 경우는 <strong>데이터 파일을 전혀 읽지 않고 인덱스만 읽어서 쿼리를 모두 처리할 수 있을 때</strong> 표시된다.</p>
<p>인덱스를 이용해 처리하는 쿼리에서 가장 큰 부하를 차지하는 부분은 인덱스 검색에서 일치하는 키 값들의 레코드를 읽기 위해 <strong>데이터 파일을 검색</strong> 하는 작업이다. 최악의 경우, 인덱스를 통해 검색된 결과 레코드 한 건 한 건마다 디스크를 한 번씩 읽어야 할 수도 있다.</p>
<pre><code class="language-sql">// first_name 이 인덱스가 되어있는 경우
explain #1
select * from members where first_name between &#39;angel&#39; and &#39;sunny&#39;;

explain #2
select first_name from members where first_name between &#39;angel&#39; and &#39;sunny&#39;;</code></pre>
<ul>
<li><p>#1 인 경우는 각 레코드의 컬럼의 값을 얻기 위해 각 레코드가 저장된 데이터 페이지를 조건절 만큼을 읽어야 한다. 이런 경우는 인덱스를 사용하는 것보다 풀 테이블 스캔으로 처리하는 편이 더 효율적으로 판단했기 때문이다. 
<img src="https://velog.velcdn.com/images/22_/post/2de59f42-45dd-4a71-a8ff-1f3dd0d83faa/image.png" alt=""></p>
</li>
<li><p>#2 이 쿼리에서는 members 테이블의 모든 컬럼으로 조회하지 않고 first_name 만 사용했다. 필요한 칼럼이 모두 인덱스에 있으므로 데이터 파일을 읽을 필요가 없다. 
=&gt; 인덱스만으로 처리되는 것을 <strong>커버링 인덱스</strong> 라고 한다. </p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/22_/post/62ef3d37-1f79-4ea7-9236-1709c41e8173/image.png" alt=""></p>
<br/>

<h3 id="innodb-일석이조">InnoDb 일석이조</h3>
<p>InnoDB 는 first_name 으로 인덱스로 설정해도 결국 인덱스에 PK 값이 저장된다. 아래와 같이 id 가 조회 필드에 추가돼도 Extra 필드는 Using index 로 나온다.</p>
<pre><code class="language-sql">select first_name, id from members where first_name between &#39;angel&#39; and &#39;sunny&#39;;</code></pre>
<ul>
<li>InnoDB 의 세컨더리 인덱스에는 데이터 레코드를 찾기 위한 주소를 사용하기 위해 PK 를 저장하느느 것이지만, 추가 컬럼을 하나 더 가지는 인덱스의 효과를 동시에 얻을 수 있다.</li>
</ul>
<br/>

<h3 id="typeindex--extra--using-index">type:index != Extra : Using index</h3>
<ul>
<li>쿼리 실행계획 분석에서 접근 방법(type)이 eq_ref, ref, range, index_merge, index 등과 같이 인덱스를 사용하는 실행 계획에서는 모두 Extra 컬럼에 &quot;Using index&quot; 가 표시 될 수 있다. </li>
<li>인덱스 풀 스캔(index) 와 혼동할 때가 자주 있지만, 성능상 반대되는 개념이라고도 할 수 있다. 
  -&gt; 같은 인덱스 풀 스캔이어도 커버링 인덱스인 경우가 훨씨 빠르게 처리된다. </li>
</ul>
<br/>

<h3 id="참고">참고</h3>
<p>Real MySQL 8.0 1</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Lombok 주의사항]]></title>
            <link>https://velog.io/@22_/Lombok-%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD</link>
            <guid>https://velog.io/@22_/Lombok-%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD</guid>
            <pubDate>Wed, 13 Dec 2023 13:50:42 GMT</pubDate>
            <description><![CDATA[<p>Lombok 은 보일러 플레이트 코드를 감소시켜 코드 양을 줄일 수 있는 Java 의 강력한 라이브러리이다. 
하지만 자동으로 해주는 것이 많은 만큼 주의 사항도 있다. </p>
<h3 id="allargsconstructor-requiredargsconstructor">@AllArgsConstructor @RequiredArgsConstructor</h3>
<ul>
<li>두 어노테이션은 어노테이션 선언 하나로 편리하게 생성자를 만들어주는 기능이다. </li>
<li>치명적인 문제가 한 가지 있다. 바로 선언된 필드의 순서가 바뀌면 자동으로 생성자의 순서도 바뀐다. 두 필드의 타입이 같은 경우 순서가 바뀌어도 에러가 발생하지 않기 때문에 알 수 없다.<pre><code class="language-java">@AllArgsConstructor
public class Square { // 두 필드의 순서가 바뀌면, 생성자의 순서도 바뀐다.
  private int width;
  private int height;
}
</code></pre>
</li>
</ul>
<pre><code>
- 위의 치명적인 이유 때문에 두 어노테이션은 지양해야 한다.
&lt;br/&gt;
&lt;br/&gt;

### @Builder
- 위에 두 어노테이션 대신 Builder 패턴을 쉽게 사용할 수 있는 @Builder 사용을 추천하기도 한다.
- @Builder 는 클래스와 생성자에 어노테이션을 사용할 수 있다. 클래스에 사용하는 경우 @AllArgsConstructor 를 사용하기 때문에 지양해야한다.
&gt; Finally, applying @Builder to a class is as if you added @AllArgsConstructor(access = AccessLevel.PACKAGE) to the class and applied the @Builder annotation to this all-args-constructor. This only works if you haven&#39;t written any explicit constructors yourself. If you do have an explicit constructor, put the @Builder annotation on the constructor instead of on the class. Note that if you put both `@Value` and `@Builder` on a class, the package-private constructor that `@Builder` wants to generate &#39;wins&#39; and suppresses the constructor that `@Value` wants to make.
https://projectlombok.org/features/Builder

- 메서드에 사용하는 경우 또한 주의해야한다. @Builder 를 사용해서 생성자를 사용하는 경우 실수로 필드를 세팅하지 않는 경우, 의도치 않은 null 이 들어갈 수 있다. (클래스도 같은 문제가 있다.)

```java
@Getter
public class Person {

    private final String lastName;
    private final String firstName;

    @Builder
    public Person(String lastName, String firstName) {
        this.lastName = lastName;
        this.firstName = firstName;
    }
}</code></pre><pre><code class="language-java">Person kim = Person.builder()
                .firstName(&quot;민수&quot;)
                .build();

        System.out.println(kim.getFirstName());
        System.out.println(kim.getLastName());

&gt; 민수
&gt; null</code></pre>
<ul>
<li>private final 로 선언한 두 필드 중 lastName 에 값을 세팅하지 않으면 null 이 출력된다. 아래 공식 문서에 설명대로 세팅되지 않은 필드는 <code>0 / null / false</code> 로 세팅된다. </li>
</ul>
<blockquote>
<p>If a certain field/parameter is never set during a build session, then it always gets 0 / null / false. If you&#39;ve put @Builder on a class (and not a method or constructor) you can instead specify the default directly on the field, and annotate the field with @Builder.Default:
@Builder.Default private final long created = System.currentTimeMillis();
<a href="https://projectlombok.org/features/Builder">https://projectlombok.org/features/Builder</a></p>
</blockquote>
<ul>
<li><p>@Builder 가 클래스에 있고, 필드에 컬렉션이 있는 경우에도 주의사항이 있다.</p>
<pre><code class="language-java">@Getter
@Builder
public class Person {

  private final String lastName;
  private final String firstName;
  private List&lt;String&gt; cars = new ArrayList&lt;&gt;();
}</code></pre>
<pre><code class="language-java">Person kim = Person.builder()
                     .firstName(&quot;김&quot;)
                  .build();
</code></pre>
</li>
</ul>
<p>System.out.println(kim.getCars());</p>
<blockquote>
<p>null</p>
</blockquote>
<pre><code>- 초기화가 됐다고 생각하지만, null 이 리턴된다. @Builder 를 사용할 때 컬렉션에 @Singular 를 선언하지 않으면 null 이 리턴된다. @Singular 를 사용해야 빈 리스트가 할당된다. 
- 이런 경우 @Builder.Default, @Signular, private final 셋 중 하나를 컬렉션에 선언하면 초기화 된다. 


&lt;br/&gt;
&lt;br/&gt;

### 참고
https://projectlombok.org/features/Builder
https://johngrib.github.io/wiki/pattern/builder/#fn:lombok-builder
https://kwonnam.pe.kr/wiki/java/lombok/pitfall
https://velog.io/@nawhew/%EB%A1%AC%EB%B3%B5-Builder-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%9E%90%EB%B0%94-%EC%BB%AC%EB%A0%89%EC%85%98%EC%97%90-null%EC%9D%B4-%EC%84%B8%ED%8C%85%EB%90%98%EB%8A%94-%EA%B2%BD%EC%9A%B0
https://stackoverflow.com/questions/32824821/lombok-builder-not-initializing-collections
























</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[MVCC]]></title>
            <link>https://velog.io/@22_/MVCC</link>
            <guid>https://velog.io/@22_/MVCC</guid>
            <pubDate>Tue, 12 Dec 2023 06:52:25 GMT</pubDate>
            <description><![CDATA[<h3 id="multi-version-concurrency-control">Multi Version Concurrency Control</h3>
<ul>
<li>여기서 Multi Version 은 하나의 레코드에 대해 여러 개의 버전이 동시에 관리된다는 의미이다. </li>
<li>MVCC 는 레코드 레벨의 트랜잭션을 지원하는 DBMS 가 제공하는 기능이고, 가장 큰 목적은 잠금을 사용하지 않는 일관된 읽기를 제공하는 것이다. </li>
<li>MySql 의 Inno DB 는 undo log 를 이용해 repetable read 를 구현한다.</li>
</ul>
<br/>


<h3 id="동작방법-mysql">동작방법 (MySQL)</h3>
<ul>
<li>Isolation level 이 Read_Committed 인 경우
<code>update member set firtst_name = &#39;김&#39; where id=1;</code></li>
<li>update 전에 first_name 이 &#39;박&#39;이라면 &#39;박&#39; 이 mysql 메모리의 undo log 에 복사된다. </li>
<li>commit 여부와 관계없이 Inno DB 의 버퍼풀은 새로운 값인 &#39;김&#39;으로 update 된다. </li>
</ul>
<p>Q. 이 상황에서 commit or rollback 전, 다른 사용자가 <code>select * from member where id=1</code> 을 요청하면 어디에 있는 데이터를 조회할까?
A. MySQL 서버의 isolation 시스템 변수에 따라 다르다. </p>
<ul>
<li>read_uncommitted 라면 InnoDB 버퍼 풀이나 데이터 파일로부터 변경되지 않은 데이터를 읽는다. read_commited 이상에서는 undo 영역의 데이터를 반환한다. </li>
</ul>
<blockquote>
<p>이처럼 MVCC 는 하나의 레코드에 대해 2개의 버전이 유지되고, 필요에 따라 어느 데이터가 보여지는지 달라지는 구조다. </p>
</blockquote>
<p>Q. undo 영역은  언제 제거가 될까?
A. 데이터가 commit 이 됐다고 undo 영역의 백업 데이터가 바로 삭제되는 것은 아니다. undo 영역을 필요로 하는 트랜잭션이 더는 없을 때 비로소 삭제된다. </p>
<ul>
<li>예를 들면, 위에 id 가 1인 row 의 update 가 commit 이 됐다. 그랬다고 undo 영역에 id 가 1인 &#39;김&#39; 이 바로 제거되지 않는다. 다른 트랜잭션이 repetable read 레벨에서 undo 영역의 &#39;김&#39;을 조회하고 있을 수 있기 때문이다.</li>
</ul>
<br/>

<h3 id="트랜잭션이-길어지면-안되는-이유">트랜잭션이 길어지면 안되는 이유</h3>
<ul>
<li>예전 버전의 데이터는 무한히 많아질 수 있어, undo 영역에 저장되는 공간이 많아질 수 있다. 트랜잭션이 길어지면 undo 에서 관리하는 예전 데이터가 삭제되지 못하고 오랫동안 관리돼야 하고, 저장 공간이 많이 증가되기 때문이다. </li>
</ul>
<br/>

<h3 id="mvcc-장단점">MVCC 장단점</h3>
<p><strong>장점</strong></p>
<ul>
<li><p>증가된 동시성 : MVCC는 데이터에 대한 깨끗한 스냅샷을 제공하여 여러 트랜잭션이 동시에 실행될 수 있도록 합니다. 이는 락 경합을 감소시켜 트랜잭션이 불필요하게 차단되는 것을 방지한다.</p>
</li>
<li><p>최적화된 성능 : 명시적인 락이 필요하지 않기 때문에 MVCC는 빠른 읽기 및 쓰기 작업이 가능하다. 특히 높은 동시성 환경에서 성능이 최적화된다.</p>
</li>
<li><p>향상된 격리 : MVCC는 각 트랜잭션의 시작 시간에 맞춰진 스냅샷을 제공함으로써 동시에 실행되는 트랜잭션 간에 격리를 제공한다. 이는 트랜잭션이 독립적으로 작동하고 일관되게 데이터를 다룰 수 있도록 한다.</p>
</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li><p>증가된 저장 공간 사용량 : MVCC는 각 레코드의 여러 버전을 유지해야 하므로 저장 공간 사용량이 증가할 수 있다. 그러나 이 오버헤드는 저장 및 가비지 컬렉션 메커니즘을 최적화함으로써 수용 가능한 수준으로 감소시킬 수 있다.</p>
</li>
<li><p>복잡한 가비지 컬렉션 : MVCC가 생성한 레코드 버전을 관리하기 위해 정교한 가비지 컬렉션 메커니즘이 필요하다. 이로 인해 데이터베이스 시스템에서 특히 높은 트랜잭션 환경에서 일부 복잡성이 발생할 수 있다.</p>
</li>
<li><p>특정 상황에서 일관성 보장 감소 : MVCC는 특정 상황에서 일관성 보장이 감소할 수 있다. 이러한 상황은 일반적으로 읽기-쓰기 충돌이나 데이터베이스 시스템이 read-committed 및 read-uncommitted와 같은 덜 강력한 일관성 보장을 제공하는 isolation level 을 사용하는 경우에 발생할 수 있다. 그러나 이는 일반적으로 MVCC의 다양한 이점을 고려할 때 허용할 수 있는 트레이드오프 이다. (상황에 따라 isolation level 을 잘 설정해야한다.)</p>
</li>
</ul>
<h3 id="참고">참고</h3>
<p>Real MySQL 8.0 1 
<a href="https://appmaster.io/blog/mvcc-in-relational-databases">https://appmaster.io/blog/mvcc-in-relational-databases</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[String matches() method]]></title>
            <link>https://velog.io/@22_/String-matches-method</link>
            <guid>https://velog.io/@22_/String-matches-method</guid>
            <pubDate>Thu, 07 Dec 2023 12:00:30 GMT</pubDate>
            <description><![CDATA[<p>&quot;joy12***3&quot; 이라는 마스킹된 id 가 있다. 이런 경우, &quot;joy12kkk3&quot; 일수도 &quot;joy12aaa3&quot; 이 될 수도 있다. 이러한 여러 가지 경우의 수를 String <code>matches</code> 메서드로 쉽게 찾을 수 있다. </p>
<h3 id="matches"><em>matches()</em></h3>
<p>이 메서드는 주어진 정규 표현식과 일치하는지 여부를 나타낸다. str.matches(regex) 형식의 이 메서드 호출은 정확히 Pattern.matches(regex, str) 표현식과 동일한 결과를 나타낸다.</p>
<pre><code class="language-java">String id =&quot;joy12***3&quot;.replace(&#39;*&#39;, &#39;.&#39;); // 마스킹된 ***을 .으로 바꾼다.
System.out.println(&quot;joy120003&quot;.matches(id)); // true </code></pre>
<p>정규식 
<code>.</code>   =&gt; 어떤 문자 하나를 나타낸다. 
<code>.*</code>  =&gt; 모든 문자(0개 이상)이 올 수 있다.</p>
<pre><code class="language-java">String Str = &quot;Welcome to Korea!!!&quot;;

System.out.println(Str.matches(&quot;.*Korea.*&quot;)); // true
System.out.println(Str.matches(&quot;Korea&quot;));     // false
System.out.println(Str.matches(&quot;.Korea.&quot;));      // false
System.out.println(Str.matches(&quot;Korea(.*)&quot;)); // true</code></pre>
<h3 id="참고">참고</h3>
<p><a href="https://www.tutorialspoint.com/java/java_string_matches.htm">https://www.tutorialspoint.com/java/java_string_matches.htm</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Annotation Processor]]></title>
            <link>https://velog.io/@22_/Annotation-Processor</link>
            <guid>https://velog.io/@22_/Annotation-Processor</guid>
            <pubDate>Thu, 07 Dec 2023 01:39:48 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>Annotation Processor 는 소스 코드를 분석하여 특정 어노테이션이 적용된 요소들을 찾고, 이에 대한 추가적인 코드생성이나 변경을 수행한다. 
덕분에 개발자는 반복적이거나 일상적인 작업을 자동화하고, 코드의 가독성을 향상시키며, 코드의 일관성을 유지할 수 있다.</p>
</li>
<li><p>Processor 인터페이스를 상속받아 구현한다. process 메서드를 구현해야한다. </p>
</li>
<li><p>QueryDSL, Json, Lombok 등 여러 기술들에서 사용된다. </p>
</li>
</ul>
<p>예를 들어 lombok의 @getter 를 사용하면 getXXX 메서드는 java 파일에 생기는 것이 아닌 클래스 파일에 있다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/22_/post/36336654-8917-4315-a790-9d64561655ef/image.png" alt="text" width="number" /> @getter 어노테이션을 사용한 Person 클래스의 class file
</p>


<p>java 의 컴파일 단계는 3가지 단계로 진행된다.
<img src="https://velog.velcdn.com/images/22_/post/f93198e8-f2e1-4a6f-9d12-0d7e416bd14f/image.png" alt="">
This process is handled by the JavaCompiler class.</p>
<ol>
<li>All the source files specified on the command line are read, parsed into syntax trees, and then all externally visible definitions are entered into the compiler&#39;s symbol tables.</li>
<li>All appropriate annotation processors are called. If any annotation processors generate any new source or class files, the compilation is restarted, until no new files are created.</li>
<li>Finally, the syntax trees created by the parser are analyzed and translated into class files. During the course of the analysis, references to additional classes may be found. The compiler will check the source and class path for these classes; if they are found on the source path, those files will be compiled as well, although they will not be subject to annotation processing.</li>
</ol>
<p>Annotation Processor 는 위 사진에서 2단계에서 진행된다. 컴파일러는 java 코드를 ast 트리 형태로 파싱하여 구문 분석을 한다. 그리고 그 트리를 기반으로 class file 을 생성하는데 2단계에서 코드를 추가한다. </p>
<p>2단계에서는 적절한 annotation processor 들이 호출된다. 만약 annotation processor 중 어떤 것이라도 새로운 소스 파일이나 클래스 파일을 생성한다면, 컴파일은 해당 파일이 생성되지 않을 때까지 다시 시작된다.</p>
<p>개념적으로 어노테이션 프로세스는 컴파일 전에 수행되는 예비 단계다. 
이 예비 단계는 일련의 라운드로 구성되어 각 라운드는 소스 파일을 구문 분석하고 입력한 후 적절한 어노테이션 프로세서를 결정하고 호출한다. 초기 라운드 이후에는 어노테이션 프로세서 중 어떤 것이라도 새로운 소스 파일이나 클래스 파일을 생성하여 최종 컴파일에 포함되어야 할 경우 추가 라운드가 수행된다. 최종적으로 필요한 모든 라운드가 완료되면 실제 컴파일이 수행된다.</p>
<p>보통 annotation proccessor 를 사용하면 새로운 클래스를 만드는데, lombok 은 process 메서드에서 제공하는 파라미터만 사용하는 게 아니고 ast를 수정가능한 타입으로 downcasting 해 사용하여 클래스를 수정한다. </p>
<h3 id="예제">예제</h3>
<ul>
<li><p>lombok 같은 annotation processor 생성 방법 (ast에 직접 접근한다.)
  <a href="https://www.happykoo.net/@happykoo/posts/255">https://www.happykoo.net/@happykoo/posts/255</a></p>
</li>
<li><p>일반적인 annotation processor 생성 방법
<a href="https://catsbi.oopy.io/78cee801-bb9c-44af-ad1f-dffc5a541101">https://catsbi.oopy.io/78cee801-bb9c-44af-ad1f-dffc5a541101</a></p>
</li>
</ul>
<h3 id="참고">참고</h3>
<p><a href="https://openjdk.org/groups/compiler/doc/compilation-overview/index.html">https://openjdk.org/groups/compiler/doc/compilation-overview/index.html</a>
<a href="https://medium.com/yanoljacloud-tech/java-%EC%BD%94%EB%93%9C%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9E%90%EB%8F%99%EC%9C%BC%EB%A1%9C-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A7%88%EA%B9%8C-2-2-438f14e94aa8">https://medium.com/yanoljacloud-tech/java-%EC%BD%94%EB%93%9C%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9E%90%EB%8F%99%EC%9C%BC%EB%A1%9C-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A7%88%EA%B9%8C-2-2-438f14e94aa8</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Safe Navigation Operator]]></title>
            <link>https://velog.io/@22_/Safe-Navigation-Operator</link>
            <guid>https://velog.io/@22_/Safe-Navigation-Operator</guid>
            <pubDate>Wed, 06 Dec 2023 00:32:25 GMT</pubDate>
            <description><![CDATA[<p>thymeleaf 에서 사용하는 문법을 보고 찾아보게 됐다.</p>
<p><code>th:price=&quot;${item?.price}&quot;</code></p>
<p>⇒ item 에 price 가 있으면 price 값이 나오고, 만약 null 이면 NPE 를 던지지 않고, null 을 리턴한다.</p>
<p>위에 문법은 SpEL의 Safe Navigation Operator 를 사용한 것이다.
&quot;Spring Expression Language(“SpEL”)는 객체 그래프를 런타임에서 조회하고 조작하는 강력한 표현 언어이다. 
Safe Navigation Operator 는 SpEL 의 사용방법 중 하나이다.</p>
<p>Safe Navigation Operator 는 NullPointerException을 피하기 위해 사용되며, 그 기원은 Groovy 언어에 있다. 
일반적으로 객체에 대한 참조가 있는 경우 해당 객체의 메서드나 속성에 액세스하기 전에 null인지 확인해야 할 수 있다. 
이를 방지하기 위해 Safe Navigation Operator 는 예외를 던지지 않고 대신에 null을 반환한다. </p>
<pre><code class="language-java">    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

        Car hyundai = new Car(&quot;Hyundai&quot;);
        hyundai.setPlaceOfBirth(new PlaceOfBirth(&quot;Seoul&quot;));

        String city = parser.parseExpression(&quot;placeOfBirth?.city&quot;).getValue(context, hyundai, String.class);
        System.out.println(city);  // Seoul

        hyundai.setPlaceOfBirth(null);
        city = parser.parseExpression(&quot;placeOfBirth?.city&quot;).getValue(context, hyundai, String.class);
        System.out.println(city);  // null - NullPointerException 발생하지 않음.

    }

    private static class Car {
        String name;
        public PlaceOfBirth placeOfBirth;

        public Car(String name) {
            this.name = name;
        }

        public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) {
            this.placeOfBirth = placeOfBirth;
        }
    }

    private static class PlaceOfBirth {
        public String city;

        public PlaceOfBirth(String city) {
            this.city = city;
        }
    }</code></pre>
<p>  <a href="https://docs.spring.io/spring-framework/reference/core/expressions/language-ref/operator-safe-navigation.html">https://docs.spring.io/spring-framework/reference/core/expressions/language-ref/operator-safe-navigation.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[spring batch 에서 특정 job 을 실행하는 방법]]></title>
            <link>https://velog.io/@22_/%ED%8A%B9%EC%A0%95job%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@22_/%ED%8A%B9%EC%A0%95job%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 03 Dec 2023 04:42:11 GMT</pubDate>
            <description><![CDATA[<p>스프링 부트 어플리케이션을 실행하면 모든 잡이 실행된다.
특정 잡을 실행하기 위해서는 아래와 같이 설정을 해야한다. </p>
<p>spring.batch.job.names = {job name} or ${job.name:NONE}// program argument
or
-Dspring.batch.job.names=myjob</p>
<p><a href="https://stackoverflow.com/questions/25122103/how-to-select-which-spring-batch-job-to-run-based-on-application-argument-spri">https://stackoverflow.com/questions/25122103/how-to-select-which-spring-batch-job-to-run-based-on-application-argument-spri</a></p>
<p><a href="https://docs.spring.io/spring-boot/docs/2.7.18/reference/html/howto.html#howto.batch.running-jobs-on-startup">https://docs.spring.io/spring-boot/docs/2.7.18/reference/html/howto.html#howto.batch.running-jobs-on-startup</a></p>
<p><a href="https://jojoldu.tistory.com/328">https://jojoldu.tistory.com/328</a></p>
]]></description>
        </item>
    </channel>
</rss>