<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://linkeverything.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://linkeverything.github.io/" rel="alternate" type="text/html" /><updated>2026-04-13T08:02:09+00:00</updated><id>https://linkeverything.github.io/feed.xml</id><title type="html">Link Everything</title><subtitle></subtitle><entry><title type="html">GitHub pages에 Google Codelab 만들기 How to make a Google codelab in Github pages</title><link href="https://linkeverything.github.io/codelab/github%20pages/how-to-make-a-codelab/" rel="alternate" type="text/html" title="GitHub pages에 Google Codelab 만들기 How to make a Google codelab in Github pages" /><published>2025-01-03T00:00:00+00:00</published><updated>2025-01-03T00:00:00+00:00</updated><id>https://linkeverything.github.io/codelab/github%20pages/how-to-make-a-codelab</id><content type="html" xml:base="https://linkeverything.github.io/codelab/github%20pages/how-to-make-a-codelab/"><![CDATA[<p>Google Codelab 은 절차적인 설명을 가독성 높게 제공하는 매우 유용한 도구입니다. 
남은 시간 표시, 남은 스텝 표기, 보던 페이지 그대로 반영 등등 기본적으로 제공하는 기능들이 많고,
markdown 을 활용하기 때문에 가이드 문서를 만드는 것이 매우 편리한 도구입니다.</p>

<p class="notice--danger"><strong>이 문서의 내용 자체도 Google Codelab 으로 구성되어 있으며, 해당 
<a href="https://linkeverything.github.io/resource.codelabs/how-to-make-a-codelab/">Codelab 페이지</a>에서 확인 가능합니다.</strong></p>

<p class="notice--danger"><strong>This article has same contents for the codelab, you can check the codelab in 
<a href="https://linkeverything.github.io/resource.codelabs/how-to-make-a-codelab/">this page</a>.</strong></p>

<h2 id="introduction">Introduction</h2>
<p>This codelab page describe the process that making <code class="language-plaintext highlighter-rouge">codelab page</code> in <code class="language-plaintext highlighter-rouge">Github pages</code>.</p>

<p class="notice--info"><strong>GitHub Pages</strong> is a feature offered by GitHub that allows you to host static websites directly from a GitHub repository. It’s commonly used to publish personal projects, portfolios, documentation, or even blogs.</p>

<p class="notice--info"><strong>Google Codelab</strong> is an online platform provided by Google that offers step-by-step, interactive tutorials designed to teach developers how to use Google technologies and tools effectively. These tutorials cover a wide range of topics, including web development, Android app development, machine learning, cloud computing, and more.</p>

<h3 id="why-use-google-codelabs">Why Use Google Codelabs?</h3>
<ul>
  <li><strong>Beginner-Friendly</strong>: They simplify complex technologies into easy-to-follow steps.</li>
  <li><strong>Hands-On Learning</strong>: Practical examples help you understand real-world applications of the technology.</li>
  <li><strong>Frequent Updates</strong>: Google keeps adding and updating content to reflect the latest tools and features.</li>
  <li><strong>Global Accessibility</strong>: Tutorials are accessible online to developers worldwide.</li>
</ul>

<h2 id="pre-requisites">Pre-requisites</h2>

<p>In local machine, <code class="language-plaintext highlighter-rouge">brew</code> should be installed.</p>

<p class="notice--info"><em>To install Homebrew on macOS, follow these steps:</em></p>

<blockquote>
  <h4 id="step-1-open-terminal">Step 1: Open Terminal</h4>
  <ol>
    <li>Press <strong>Command(⌘) + Space</strong> to open Spotlight Search.</li>
    <li>Type <code class="language-plaintext highlighter-rouge">Terminal</code> and press Enter.</li>
  </ol>

  <h4 id="step-2-install-xcode-command-line-tools">Step 2: Install Xcode Command Line Tools</h4>
  <p>Before installing Homebrew, ensure that the Xcode Command Line Tools are installed:</p>
  <ol>
    <li>Run the following command:
      <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xcode-select <span class="nt">--install</span>
</code></pre></div>      </div>
    </li>
    <li>A dialog box will appear. Click <strong>Install</strong> to proceed.</li>
    <li>Wait for the installation to complete.</li>
  </ol>

  <h4 id="step-3-install-homebrew">Step 3: Install Homebrew</h4>
  <ol>
    <li>Run the following command in Terminal:
      <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/bin/bash <span class="nt">-c</span> <span class="s2">"</span><span class="si">$(</span>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="si">)</span><span class="s2">"</span>
</code></pre></div>      </div>
    </li>
    <li>The script will prompt you to confirm the installation and provide your admin password. Enter it when prompted.</li>
  </ol>

  <h4 id="step-4-add-homebrew-to-your-path-if-not-automatically-added">Step 4: Add Homebrew to Your PATH (if not automatically added)</h4>
  <p>Depending on your shell, you may need to update your PATH:</p>
  <ol>
    <li>For <strong>zsh</strong> (default in macOS Catalina and later):
      <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s1">'eval "$(/opt/homebrew/bin/brew shellenv)"'</span> <span class="o">&gt;&gt;</span> ~/.zprofile
<span class="nb">eval</span> <span class="s2">"</span><span class="si">$(</span>/opt/homebrew/bin/brew shellenv<span class="si">)</span><span class="s2">"</span>
</code></pre></div>      </div>
    </li>
    <li>For <strong>bash</strong>:
      <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s1">'eval "$(/opt/homebrew/bin/brew shellenv)"'</span> <span class="o">&gt;&gt;</span> ~/.bash_profile
<span class="nb">eval</span> <span class="s2">"</span><span class="si">$(</span>/opt/homebrew/bin/brew shellenv<span class="si">)</span><span class="s2">"</span>
</code></pre></div>      </div>
    </li>
  </ol>

  <h4 id="step-5-verify-installation">Step 5: Verify Installation</h4>
  <ol>
    <li>Run the following command to check if Homebrew is installed correctly:
      <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nt">--version</span>
</code></pre></div>      </div>
      <p>This will display the installed version of Homebrew.</p>
    </li>
  </ol>

  <h4 id="step-6-update-homebrew">Step 6: Update Homebrew</h4>
  <p>To ensure everything is up to date, run:</p>
  <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew update
</code></pre></div>  </div>

  <p>You’re now ready to use Homebrew! 🎉</p>
</blockquote>

<h2 id="step-1-install-go-and-claat">Step 1: Install <code class="language-plaintext highlighter-rouge">go</code> and <code class="language-plaintext highlighter-rouge">claat</code></h2>

<p><code class="language-plaintext highlighter-rouge">googlecodelab</code> will be deployed and served by(using) <code class="language-plaintext highlighter-rouge">claat</code>, and <code class="language-plaintext highlighter-rouge">claat</code> can be installed by <code class="language-plaintext highlighter-rouge">go</code> lang.
So, first of all, install <code class="language-plaintext highlighter-rouge">go</code> using <code class="language-plaintext highlighter-rouge">brew</code> and then, install <code class="language-plaintext highlighter-rouge">claat</code> using <code class="language-plaintext highlighter-rouge">go</code>.</p>

<p>Install <code class="language-plaintext highlighter-rouge">go</code>:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>go
</code></pre></div></div>

<p>When <code class="language-plaintext highlighter-rouge">brew</code> install <code class="language-plaintext highlighter-rouge">go</code> lang, it add the <code class="language-plaintext highlighter-rouge">go</code> binary to the <code class="language-plaintext highlighter-rouge">PATH</code>,
but if it does not add binary to the <code class="language-plaintext highlighter-rouge">PATH</code>, add it manually.</p>

<p>Add <code class="language-plaintext highlighter-rouge">go</code> binary to <code class="language-plaintext highlighter-rouge">PATH</code>:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s1">'export PATH=$PATH:~/go/bin'</span> <span class="o">&gt;&gt;</span> ~/.zshrc
</code></pre></div></div>
<p>Refresh <code class="language-plaintext highlighter-rouge">.zshrc</code> file to activate <code class="language-plaintext highlighter-rouge">PATH</code>:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source</span> ~/.zshrc
</code></pre></div></div>

<p>Then, install <code class="language-plaintext highlighter-rouge">claat</code>:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>go <span class="nb">install </span>github.com/googlecodelabs/tools/claat@latest
</code></pre></div></div>
<p>To check <code class="language-plaintext highlighter-rouge">claat</code> installed successfully:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>claat <span class="nt">--help</span>
</code></pre></div></div>

<h2 id="step-2-create-a-markdown-md-file">Step 2: create a markdown <code class="language-plaintext highlighter-rouge">.md</code> file</h2>

<p>Duration: 0:05:00</p>

<p>Make a markdown file(<code class="language-plaintext highlighter-rouge">sample.md</code>) like:</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>summary: Title of the Codelab
id: codelab-id
categories: Web, Beginner
status: Published

<span class="gh"># Title of the Codelab</span>

<span class="gu">## Introduction</span>
Brief introduction of the blog/tutorial.

<span class="gu">## Step 1: Do Something</span>
Instructions for step 1.

<span class="gu">## Step 2: Do Something Else</span>
Instructions for step 2.
</code></pre></div></div>

<p class="notice--info">Because this md path can be a web root path, so make the <code class="language-plaintext highlighter-rouge">.md</code> file in git repository root path.</p>

<h2 id="step-3-export-the-contents-and-check-in-local">Step 3: export the contents and check in local</h2>

<h3 id="export-the-markdown-file-by-claat">Export the markdown file by claat</h3>

<p>In the same directory with markdown file:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>claat <span class="nb">export </span>sample.md
</code></pre></div></div>

<p>claat create a <code class="language-plaintext highlighter-rouge">codelab-id</code> directory in the same path. (id based directory.)</p>

<p class="notice--info">When using images in markdown file, those are included in img folder in exported directory.</p>

<p class="notice--warning">the <code class="language-plaintext highlighter-rouge">codelab-id</code> will be a folder that can represent as a web path <code class="language-plaintext highlighter-rouge">/codelab-id</code>. 
So, if you want to change the web path, you should change the <code class="language-plaintext highlighter-rouge">id</code> value in <code class="language-plaintext highlighter-rouge">.md</code> file.</p>

<h3 id="check-in-local">Check in local</h3>

<p>After export the file, codelab can be check in local:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>claat serve
</code></pre></div></div>

<p>When run this command, the default browser is open.
If you type this command in same directory for <code class="language-plaintext highlighter-rouge">.md</code> file, you can see as below:</p>

<p><img src="/assets/images/posts/dev/common/2025-01-03-how-to-codelab/claat-serve.png" alt="" /></p>

<p>You can check the codelab with clicking the <code class="language-plaintext highlighter-rouge">codelab-id</code> path.</p>

<h2 id="step-4-push-to-github-pages">Step 4: push to github pages</h2>

<p>Copy the <code class="language-plaintext highlighter-rouge">codelab-id</code> directory to the github repo’s <code class="language-plaintext highlighter-rouge">docs</code> folder.</p>

<p>Then, in the settings in the repository, enable pages as below:</p>

<p><img src="/assets/images/posts/dev/common/2025-01-03-how-to-codelab/img.png" alt="" /></p>

<p>For this codelab, the directory structure is:</p>

<p><img src="/assets/images/posts/dev/common/2025-01-03-how-to-codelab/img-1.png" alt="" /></p>

<h2 id="step-5-check-in-the-browser">Step 5: Check in the browser</h2>

<p>Finally, we can check the codelab in:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://&lt;username&gt;.github.io/&lt;repo&gt;/codelab-id/
</code></pre></div></div>

<h2 id="tips-and-tricks-syntax">Tips and Tricks: Syntax</h2>

<blockquote class="notice--warning">
  <p>If you’re using Windows make sure to set your text editor to use UNIX line endings!</p>
</blockquote>

<h3 id="open-your-markdown-file">Open your markdown file</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vim &lt;name-of-codelab&gt;.md
</code></pre></div></div>

<h4 id="header-metadata-auto-generated">Header metadata [Auto generated]</h4>

<p>Update the headers metadata in your markdown file and change the values appropriately.
Guidelines are available below the sample headers.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>author: Author Name
summary: Summary of your codelab that is human readable
id: unique-codelab-identifier
tags: workshop,iguide
categories: Java,Spring
environments: Web
status: Published
feedback link: A link where users can go to provide feedback (e.g. the git repo or issue page)
</code></pre></div></div>

<p>Metadata consists of key-value pairs of the form “key: value”. Keys cannot
contain colons, and separate metadata fields must be separated by blank lines.
At present, values must all be on one line. All metadata must come before the
title. Any arbitrary keys and values may be used; however, only the following
will be understood by the renderer:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Author</code>: Author name or git username</li>
  <li><code class="language-plaintext highlighter-rouge">Summary</code>: A human-readable summary of the codelab. Defaults to blank</li>
  <li><code class="language-plaintext highlighter-rouge">Id</code>: An identifier composed of lowercase letters ideally describing the content of the codelab. This field should be unique among codelabs. This will be in the URL of the codelab</li>
  <li><code class="language-plaintext highlighter-rouge">Tags</code>: Leave “workshop” if creating a Developer workshop or “iguide” if creating an integration guide. Remove both if neither. Note: this is used for the “Filter by Type” feature on the <a href="solace.dev/codelabs">landing page</a></li>
  <li><code class="language-plaintext highlighter-rouge">Categories</code>: A comma-separated list of the topics or technologies the codelab covers. Include items such as language(s) and protocol(s) used. The first one is used to create a new “Filter by category” feature on the <a href="solace.dev/codelabs">landing page</a> and the styling of the category. The remaining will be used for the filtering.
    <ul>
      <li>Note that the list of available categories can be found in the main <a href="https://github.com/SolaceDev/solace-dev-codelabs-site/blob/master/site/app/styles/_categories.scss#L152-L181">site repo</a></li>
      <li>The current list is (case insensitive): <code class="language-plaintext highlighter-rouge">[amqp, boomi, codelab, java, jms, kafka, mqtt, rest, solace, spring, kubernetes, javascript, helm, azure, opentelemetry, mulesoft, rabbitmq, keda, apama, hermesjms, flink, nifi, nagios, jboss, weblogic, websphere, webspherelib, spark, sap, terraform]</code></li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">Environments</code>: Leave as “Web”</li>
  <li><code class="language-plaintext highlighter-rouge">Status</code>: The publication status of the codelab. Valid values are:
    <ul>
      <li>Draft: Codelab is not finished.</li>
      <li>Published: Codelab is finished and visible.</li>
      <li>Deprecated: Codelab is considered stale and should not be widely advertised.</li>
      <li>Hidden: Codelab is not shown in index.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">Feedback Link</code>: A link to send users to if they wish to leave feedback on the codelab. Link to git repo where code for the tutorial will live.</li>
</ul>

<h4 id="add-the-title">Add the Title</h4>

<p>Next add your title using a single ‘#’ character</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Title of codelab
</code></pre></div></div>

<h4 id="add-sections--durations">Add Sections &amp; Durations</h4>

<p>Then for each section use Header 2 or ‘##’ &amp; specify q duration beneath for time remaining calculations</p>

<p class="notice--info">Duration is in the following time format: <code class="language-plaintext highlighter-rouge">hh:mm:ss</code></p>

<p>Example</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## Section 1</span>
Duration: 0:10:00

<span class="c">## Section 2</span>
Duration: 0:05:00
</code></pre></div></div>

<h4 id="add-section-content">Add Section Content</h4>

<p>Now that we have 2 sections to our titled codelab let’s go ahead and add some content to each section.
Make up your own or copy &amp; paste the example below:</p>

<p>Copy into section 1 (Below Duration and above Section 2):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>### Info Boxes
Plain Text followed by green &amp; yellow info boxes

&gt; aside negative
&gt; This will appear in a yellow info box.
&gt; This is line two of the negative block
&gt; ```
&gt; this is a code block
&gt; with multiple lines
&gt; ```

&gt; aside positive
&gt; This will appear in a green info box.

You created info boxes!

### Bullets
Plain Text followed by bullets
* Hello
* Codelab
* World

You created bullets!

### Numbered List
1. List
1. Using
1. Numbers

You created a numbered list!
</code></pre></div></div>
<p>Copy into section 2 (Below Duration):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>### Add a Link
Add a link!
[Example of a Link](https://www.google.com)

### Add an Image
Add an image!
![image_caption](https://solace.com/wp-content/uploads/2019/09/goodbye_otter_v3.gif)

### Embed an iframe
![https://codepen.io/tzoght/embed/yRNZaP](https://en.wikipedia.org/wiki/File:Example.jpg "Try Me Publisher")
</code></pre></div></div>

<p>More Markdown Parser examples can be found <a href="https://github.com/googlecodelabs/tools/tree/master/claat/parser/md">here</a>.</p>

<h2 id="참고자료-및-출처">참고자료 및 출처</h2>

<ul>
  <li><a href="https://codelabs.solace.dev/codelabs/codelab-4-codelab/?index=..%2F..index#0">https://codelabs.solace.dev/codelabs/codelab-4-codelab/?index=..%2F..index#0</a></li>
</ul>]]></content><author><name>Park Jong hun</name></author><category term="Codelab" /><category term="github pages" /><category term="codelab" /><category term="github pages" /><summary type="html"><![CDATA[Google Codelab 은 절차적인 설명을 가독성 높게 제공하는 매우 유용한 도구입니다. 남은 시간 표시, 남은 스텝 표기, 보던 페이지 그대로 반영 등등 기본적으로 제공하는 기능들이 많고, markdown 을 활용하기 때문에 가이드 문서를 만드는 것이 매우 편리한 도구입니다.]]></summary></entry><entry><title type="html">Spring Boot에서의 조건에 따른 Bean 생성 Conditional Beans with Spring boot</title><link href="https://linkeverything.github.io/springboot/Spring-conditional-annotation/" rel="alternate" type="text/html" title="Spring Boot에서의 조건에 따른 Bean 생성 Conditional Beans with Spring boot" /><published>2024-12-26T00:00:00+00:00</published><updated>2024-12-26T00:00:00+00:00</updated><id>https://linkeverything.github.io/springboot/Spring-conditional-annotation</id><content type="html" xml:base="https://linkeverything.github.io/springboot/Spring-conditional-annotation/"><![CDATA[<p>Spring Boot 앱을 빌드할 때, 어떤 조건이 충족될 때만 애플리케이션 컨텍스트에 Bean이나
모듈을 로드하고 싶을 때가 있습니다. 테스트 중에 일부 Bean을 비활성화하거나 런타임 환경에서
특정 속성에 반응하는 경우입니다.</p>

<p>Spring은 애플리케이션 컨텍스트의 일부에 적용할 사용자 정의 조건을 정의할 수 있는
<code class="language-plaintext highlighter-rouge">@Conditional</code> 이라는 annotation을 도입했습니다. Spring Boot는 이를 기반으로
구축되어 일부 사전 정의된 조건을 제공하므로 직접 구현할 필요가 없습니다.</p>

<p>이 튜토리얼에서는 Conditional로 로드된 Bean이 왜 필요한지 설명하는 몇 가지 사용 사례를 살펴보겠습니다.
그런 다음 조건을 적용하는 방법과 Spring Boot가 제공하는 조건을 살펴보겠습니다.
마무리로 사용자 지정 조건도 구현하겠습니다.</p>

<blockquote>
  <p>참고자료가 되는 원본 출처로 가 보면, 샘플 코드도 제공하고 있으니 참고하세요.</p>
</blockquote>

<h2 id="conditional-bean-이-필요한-이유는-무엇인가요">Conditional Bean 이 필요한 이유는 무엇인가요?</h2>

<p>Spring 애플리케이션 컨텍스트에는 런타임에 애플리케이션에 필요한 모든 Bean을 구성하는
객체 그래프가 들어 있습니다. Spring의 <code class="language-plaintext highlighter-rouge">@Conditional</code>annotation을 사용하면 특정 Bean이
해당 객체 그래프에 포함되는 조건을 정의할 수 있습니다.</p>

<h3 id="특정-조건-하에서-콩을-포함하거나-제외해야-하는-이유는-무엇입니까">특정 조건 하에서 콩을 포함하거나 제외해야 하는 이유는 무엇입니까?</h3>

<p>제 경험상 가장 흔한 사용 사례는 특정 Bean이 테스트 환경에서 작동하지 않는다는 것입니다.
테스트 중에 사용할 수 없는 원격 시스템이나 애플리케이션 서버에 연결해야 할 수도 있습니다.
따라서 테스트 중에 이러한 Bean을 제외하거나 대체하기 위해 테스트를 모듈화 하고자 합니다.</p>

<p>또 다른 사용 사례는 특정 횡단적 관심사를 활성화하거나 비활성화하려는 경우입니다.
보안을 구성하는 모듈을 빌드했다고 가정해 보겠습니다. 개발자 테스트 중에 사용자 이름과
비밀번호를 매번 입력하고 싶지 않으므로 스위치를 뒤집어 로컬 테스트를 위해 전체 보안 모듈을
비활성화합니다.</p>

<p>또한, 우리는 특정 Bean을 작동할 수 없는 외부 리소스가 있는 경우 에만 로드하고 싶을 수 있습니다.
예를 들어, <code class="language-plaintext highlighter-rouge">logback.xml</code>클래스 경로에서 파일이 발견된 경우에만 Logback 로거를 구성하고
싶습니다.</p>

<p>다음 토론에서 몇 가지 더 많은 사용 사례를 살펴보겠습니다.</p>

<h2 id="조건에-따른-bean-선언">조건에 따른 Bean 선언</h2>

<p>Spring Bean을 정의하는 모든 곳에서 선택적으로 조건을 추가할 수 있습니다. 이 조건이
충족되는 경우에만 Bean이 애플리케이션 컨텍스트에 추가됩니다. 조건을 선언하려면
아래에 <code class="language-plaintext highlighter-rouge">@Conditional...</code> 설명된 annotation 중 하나를 사용할 수 있습니다.</p>

<p>하지만 먼저, 특정 Spring Bean에 조건을 적용하는 방법부터 살펴보겠습니다.</p>

<h3 id="conditional-bean">Conditional @Bean</h3>

<p>단일 @Bean정의에 조건을 추가하는 경우 조건이 충족되는 경우에만 이 Bean이 로드됩니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="kd">class</span> <span class="nc">ConditionalBeanConfiguration</span> <span class="o">{</span>

    <span class="nd">@Bean</span>
    <span class="nd">@Conditional</span><span class="o">...</span> <span class="c1">// &lt;--</span>

    <span class="nc">ConditionalBean</span> <span class="nf">conditionalBean</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="k">new</span> <span class="nf">ConditionalBean</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="conditional-configuration">Conditional @Configuration</h3>

<p>Spring에 조건을 추가하면 @Configuration이 구성에 포함된 모든 Bean은 조건이
충족되는 경우에만 로드됩니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Configuration</span>
<span class="nd">@Conditional</span><span class="o">...</span> <span class="c1">// &lt;--</span>

<span class="kd">class</span> <span class="nc">ConditionalConfiguration</span> <span class="o">{</span>

    <span class="nd">@Bean</span>
    <span class="nc">Bean</span> <span class="nf">bean</span><span class="o">()</span> <span class="o">{</span>
        <span class="o">...</span>
    <span class="o">}</span>

    <span class="o">;</span>

<span class="o">}</span>
</code></pre></div></div>

<h3 id="conditional-component">Conditional @Component</h3>

<p>마지막으로 @component, @Service, @Repository, 또는 @Controller 등과 같은
stereotype annotation 중 하나로 선언된 모든 Bean에 조건을 추가할 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Component</span>
<span class="nd">@Conditional</span><span class="o">...</span> <span class="c1">// &lt;--</span>

<span class="kd">class</span> <span class="nc">ConditionalComponent</span> <span class="o">{</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="pre-defined-conditions">Pre-defined Conditions</h2>

<p>Spring Boot는 바로 사용할 수 있는 미리 정의된 <code class="language-plaintext highlighter-rouge">@ConditionalOn...</code> annotation을
제공합니다. 각각을 차례로 살펴보겠습니다.</p>

<h3 id="conditionalonproperty">@ConditionalOnProperty</h3>

<p>이 <code class="language-plaintext highlighter-rouge">@ConditionalOnProperty</code> annotation은 제 경험상 Spring Boot 프로젝트에서
가장 일반적으로 사용되는 Conditional annotation입니다. 특정 환경 속성에 따라 Bean을 Conditional로
로드할 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnProperty</span><span class="o">(</span>
        <span class="n">value</span> <span class="o">=</span> <span class="s">"module.enabled"</span><span class="o">,</span>
        <span class="n">havingValue</span> <span class="o">=</span> <span class="s">"true"</span><span class="o">,</span>
        <span class="n">matchIfMissing</span> <span class="o">=</span> <span class="kc">true</span><span class="o">)</span>
<span class="kd">class</span> <span class="nc">CrossCuttingConcernModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<p>CrossCuttingConcernModule은 <code class="language-plaintext highlighter-rouge">module.enabled</code> 값이 있고,<code class="language-plaintext highlighter-rouge">true</code>인 경우에만
로드됩니다. 속성이 전혀 설정되지 않은 경우에도 <code class="language-plaintext highlighter-rouge">matchIfMissing</code>를 <code class="language-plaintext highlighter-rouge">true</code>로 정의했기
때문에 로드됩니다. 이런 식으로, 우리는 달리 결정할 때까지 기본적으로 로드되는 모듈을
만들었습니다.</p>

<p>같은 방식으로 보안이나 스케줄링과 같은 교차적 문제에 대해 다른 모듈을 만들어 특정 (테스트)
환경에서 비활성화할 수도 있습니다.</p>

<h3 id="conditionalonexpression">@ConditionalOnExpression</h3>

<p>여러 속성에 기반한 더 복잡한 조건이 있는 경우 <code class="language-plaintext highlighter-rouge">@ConditionalOnExpression</code>을
사용할 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnExpression</span><span class="o">(</span>
        <span class="s">"${module.enabled:true} and ${module.submodule.enabled:true}"</span>
<span class="o">)</span>
<span class="kd">class</span> <span class="nc">SubModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<p>SubModule은 <code class="language-plaintext highlighter-rouge">module.enable</code>속성과 <code class="language-plaintext highlighter-rouge">module.submodule.enabled</code>두 속성 모두에
값이 있는 경우에만 로드됩니다. 속성에 추가하여 속성이 설정되지 않은 경우 기본값으로
사용하라고 Spring에 알립니다. Spring Expression Language 의 전체 확장을 사용할
수 있습니다.</p>

<p>이 방법을 사용하면 부모 모듈이 비활성화되면 비활성화되어야 하는 하위 모듈을 만들 수 있지만,
부모 모듈이 활성화되면 비활성화될 수도 있습니다.</p>

<h3 id="conditionalonbean">@ConditionalOnBean</h3>

<p>때로는 애플리케이션 컨텍스트에서 특정 다른 Bean을 사용할 수 있는 경우에만 Bean을
로드하고 싶을 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnBean</span><span class="o">(</span><span class="nc">OtherModule</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">class</span> <span class="nc">DependantModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<p>DependantModule은 애플리케이션 컨텍스트에 OtherModule 클래스의 Bean이 있는
경우에만 로드됩니다. Bean 클래스 대신 Bean 이름을 정의할 수도 있습니다.</p>

<p>이런 식으로, 예를 들어 특정 모듈 간의 종속성을 정의할 수 있습니다. 한 모듈은 다른 모듈의
특정 Bean이 사용 가능한 경우에만 로드됩니다.</p>

<h3 id="conditionalonmissingbean">@ConditionalOnMissingBean</h3>

<p>마찬가지로, 특정 다른 Bean이 애플리케이션 컨텍스트에 없는 경우에만 Bean을 로드하려는
경우 <code class="language-plaintext highlighter-rouge">@ConditionalOnMissingBean</code>을 사용할 수 있습니다 .</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="kd">class</span> <span class="nc">OnMissingBeanModule</span> <span class="o">{</span>

    <span class="nd">@Bean</span>
    <span class="nd">@ConditionalOnMissingBean</span>
    <span class="nc">DataSource</span> <span class="nf">dataSource</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="k">new</span> <span class="nf">InMemoryDataSource</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>이 예에서 우리는 이미 사용 가능한 데이터 소스가 없는 경우에만 애플리케이션 컨텍스트에
메모리 내 데이터 소스를 주입합니다. 이는 Spring Boot가 테스트 컨텍스트에서 메모리
내 데이터베이스를 제공하기 위해 내부적으로 수행하는 작업과 매우 유사합니다.</p>

<h3 id="conditionalonresource">@ConditionalOnResource</h3>

<p>클래스 경로에서 특정 리소스를 사용할 수 있다는 사실에 따라 Bean을 로드하려는 경우
<code class="language-plaintext highlighter-rouge">@ConditionalOnResource</code>을 사용할 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnResource</span><span class="o">(</span><span class="n">resources</span> <span class="o">=</span> <span class="s">"/logback.xml"</span><span class="o">)</span>
<span class="kd">class</span> <span class="nc">LogbackModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<p>LogbackModule클래스 경로에서 logback configuration 파일이 발견된 경우에만
로드됩니다. 이런 식으로, 해당 구성 파일이 발견된 경우에만 로드되는 유사한 모듈을 만들
수 있습니다.</p>

<h2 id="other-conditions">Other Conditions</h2>

<p>위에서 설명한 Conditional annotation은 Spring Boot 애플리케이션에서 사용할 수
있는 일반적인 annotation입니다. Spring Boot는 더 많은 Conditional annotation
을 제공합니다. 그러나 이러한 annotation은 그렇게 일반적이지 않으며 일부는 애플리케이션
개발보다는 프레임워크 개발에 더 적합합니다(Spring Boot는 이러한 annotation 중 일부를
비밀리에 많이 사용합니다). 따라서 여기서는 간략하게 살펴보겠습니다.</p>

<h3 id="conditionalonclass">@ConditionalOnClass</h3>

<p>특정 클래스가 클래스 경로에 있는 경우에만 Bean을 로드합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnClass</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"this.clazz.does.not.Exist"</span><span class="o">)</span>
<span class="kd">class</span> <span class="nc">OnClassModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="conditionalonmissingclass">@ConditionalOnMissingClass</h3>

<p>특정 클래스가 클래스 경로에 없는 경우에만 Bean을 로드합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnMissingClass</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"this.clazz.does.not.Exist"</span><span class="o">)</span>
<span class="kd">class</span> <span class="nc">OnMissingClassModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="conditionalonjndi">@ConditionalOnJndi</h3>

<p>JNDI를 통해 특정 리소스를 사용할 수 있는 경우에만 Bean을 로드합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnJndi</span><span class="o">(</span><span class="s">"java:comp/env/foo"</span><span class="o">)</span>
<span class="kd">class</span> <span class="nc">OnJndiModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="conditionalonjava">@ConditionalOnJava</h3>

<p>특정 버전의 Java를 실행하는 경우에만 Bean을 로드합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnJava</span><span class="o">(</span><span class="nc">JavaVersion</span><span class="o">.</span><span class="na">EIGHT</span><span class="o">)</span>
<span class="kd">class</span> <span class="nc">OnJavaModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="conditionalonsinglecandidate">@ConditionalOnSingleCandidate</h3>

<p><code class="language-plaintext highlighter-rouge">@ConditionalOnBean</code>과 유사하지만 주어진 Bean 클래스에 대한 단일 후보가 결정된
경우에만 Bean을 로드합니다. auto-congifurations 외부에 사용 사례가 없을
가능성이 큽니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnSingleCandidate</span><span class="o">(</span><span class="nc">DataSource</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">class</span> <span class="nc">OnSingleCandidateModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="conditionalonwebapplication">@ConditionalOnWebApplication</h3>

<p>웹 애플리케이션 내부에서 실행하는 경우에만 Bean을 로드합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnWebApplication</span>
<span class="kd">class</span> <span class="nc">OnWebApplicationModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="conditionalonnotwebapplication">@ConditionalOnNotWebApplication</h3>

<p>웹 애플리케이션 내부에서 실행 하지 않는 경우에만 Bean을 로드합니다 .</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnNotWebApplication</span>
<span class="kd">class</span> <span class="nc">OnNotWebApplicationModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="conditionaloncloudplatform">@ConditionalOnCloudPlatform</h3>

<p>특정 클라우드 플랫폼에서 실행하는 경우에만 Bean을 로드합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nd">@Configuration</span>
<span class="nd">@ConditionalOnCloudPlatform</span><span class="o">(</span><span class="nc">CloudPlatform</span><span class="o">.</span><span class="na">CLOUD_FOUNDRY</span><span class="o">)</span>
<span class="kd">class</span> <span class="nc">OnCloudPlatformModule</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="custom-conditions">Custom Conditions</h2>

<p>Conditional annotation 외에도 우리는 우리만의 조건 annotation을 만들고 논리 연산자를 사용해 여러 조건을 결합할 수 있습니다.</p>

<h3 id="custom-condition의-정의">Custom Condition의 정의</h3>

<p>운영 체제와 네이티브하게 통신하는 Spring Bean이 있다고 가정해 보겠습니다.
이러한 Bean은 해당 운영 체제에서 애플리케이션을 실행하는 경우에만 로드해야 합니다.</p>

<p>코드를 유닉스 머신에서 실행하는 경우에만 Bean을 로드하는 조건을 구현해 보겠습니다.
이를 위해 Spring의 Condition 인터페이스를 구현합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">OnUnixCondition</span> <span class="kd">implements</span> <span class="nc">Condition</span> <span class="o">{</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">matches</span><span class="o">(</span>
            <span class="nc">ConditionContext</span> <span class="n">context</span><span class="o">,</span>
            <span class="nc">AnnotatedTypeMetadata</span> <span class="n">metadata</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="nc">SystemUtils</span><span class="o">.</span><span class="na">IS_OS_LINUX</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>우리는 단순히 Apache Commons의 <code class="language-plaintext highlighter-rouge">SystemUtils</code>클래스를 사용하여 유닉스 계열
시스템에서 실행 중인지 확인합니다. 필요하다면 현재 애플리케이션 컨텍스트
(ConditionContext) 또는 annotation이 달린 클래스(AnnotatedTypeMetadata)에
대한 정보를 사용하는 보다 정교한 로직을 포함할 수 있습니다.</p>

<p>이제 이 조건을 Spring의 <code class="language-plaintext highlighter-rouge">@Conditional</code> annotation과 함께 사용할 준비가 되었습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Bean</span>
<span class="nd">@Conditional</span><span class="o">(</span><span class="nc">OnUnixCondition</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="nc">UnixBean</span> <span class="nf">unixBean</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="k">new</span> <span class="nf">UnixBean</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="or로-조건-결합">OR로 조건 결합</h3>
<p>논리적 “OR” 연산자를 사용하여 여러 조건을 단일 조건으로 결합하려면 
<code class="language-plaintext highlighter-rouge">AnyNestedCondition</code>을 확장할 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">OnWindowsOrUnixCondition</span> <span class="kd">extends</span> <span class="nc">AnyNestedCondition</span> <span class="o">{</span>

    <span class="nc">OnWindowsOrUnixCondition</span><span class="o">()</span> <span class="o">{</span>
        <span class="kd">super</span><span class="o">(</span><span class="nc">ConfigurationPhase</span><span class="o">.</span><span class="na">REGISTER_BEAN</span><span class="o">);</span>
    <span class="o">}</span>
    
    <span class="nd">@Conditional</span><span class="o">(</span><span class="nc">OnWindowsCondition</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
    <span class="kd">static</span> <span class="kd">class</span> <span class="nc">OnWindows</span> <span class="o">{}</span>
    
    <span class="nd">@Conditional</span><span class="o">(</span><span class="nc">OnUnixCondition</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
    <span class="kd">static</span> <span class="kd">class</span> <span class="nc">OnUnix</span> <span class="o">{}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>여기서는 애플리케이션이 Windows나 Unix에서 실행되는지 여부에 관계없이 충족되는 조건을
생성했습니다.</p>

<p><code class="language-plaintext highlighter-rouge">AnyNestedCondition</code>부모 클래스는 메서드에 대한 <code class="language-plaintext highlighter-rouge">@Conditional</code>annotation을 
평가하고 <code class="language-plaintext highlighter-rouge">OR</code> 연산자를 사용하여 이를 결합합니다.</p>

<p>이 조건은 다른 조건과 마찬가지로 사용할 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Bean</span>
<span class="nd">@Conditional</span><span class="o">(</span><span class="nc">OnWindowsOrUnixCondition</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="nc">WindowsOrUnixBean</span> <span class="nf">windowsOrUnixBean</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="k">new</span> <span class="nf">WindowsOrUnixBean</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>

<blockquote>
  <h4 id="anynestedcondition-이나-allnestedconditions가-동작하지-않나요"><code class="language-plaintext highlighter-rouge">AnyNestedCondition</code> 이나 <code class="language-plaintext highlighter-rouge">AllNestedConditions</code>가 동작하지 않나요?</h4>
  <p><code class="language-plaintext highlighter-rouge">super()</code>에 전달된 <code class="language-plaintext highlighter-rouge">ConfigurationPhase</code> 매개변수를 확인합니다. 결합된 조건을
<code class="language-plaintext highlighter-rouge">@Configuration</code> Bean에 적용하려면 <code class="language-plaintext highlighter-rouge">PARSE_CONFIGURATION</code> 값을 사용합니다. 
조건을 간단한 Bean에 적용하려면 위의 예시처럼 <code class="language-plaintext highlighter-rouge">REGISTER_BEAN</code>을 사용합니다. 
Spring Boot는 애플리케이션 컨텍스트 시작 시 적절한 시기에 조건을 적용할 수 있도록
이러한 구분을 해야 합니다.</p>
</blockquote>

<h3 id="and를-사용한-조건-결합">AND를 사용한 조건 결합</h3>
<p>조건을 <code class="language-plaintext highlighter-rouge">AND</code> 논리와 결합하려면 단일 Bean에 여러 개의 <code class="language-plaintext highlighter-rouge">@Conditional...</code> annotation
을 사용하면 됩니다. 이러한 annotation은 논리적 “AND” 연산자와 자동으로 결합되므로
적어도 하나의 조건이 실패하면 Bean이 로드되지 않습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Bean</span>
<span class="nd">@ConditionalOnUnix</span>
<span class="nd">@Conditional</span><span class="o">(</span><span class="nc">OnWindowsCondition</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="nc">WindowsAndUnixBean</span> <span class="nf">windowsAndUnixBean</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="k">new</span> <span class="nf">WindowsAndUnixBean</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>

<p>누군가가 내가 모르는 Windows/Unix 하이브리드를 만들지 않는 한, 이 Bean은 반드시
로드되어서는 안 됩니다.</p>

<p><code class="language-plaintext highlighter-rouge">@Conditional</code>annotation은 단일 메서드나 클래스에서 두 번 이상 사용할 수 없습니다.
따라서 이런 방식으로 여러 annotation을 결합하려면 이러한 제한이 없는 사용자 지정 
<code class="language-plaintext highlighter-rouge">@ConditionalOn...</code>annotation을 사용해야 합니다. 아래에서 <code class="language-plaintext highlighter-rouge">@ConditionalOnUnix</code>
annotation을 만드는 방법을 살펴보겠습니다.</p>

<p>또는 AND를 사용하여 조건을 단일 <code class="language-plaintext highlighter-rouge">@Conditional</code>annotation으로 결합하려면 
Spring Boot의 <code class="language-plaintext highlighter-rouge">AllNestedConditions</code>클래스를 확장할 수 있으며, 이는 위에서 설명한
<code class="language-plaintext highlighter-rouge">AnyNestedConditions</code>과 정확히 동일하게 작동합니다.</p>

<h3 id="not을-사용한-조건-결합">NOT을 사용한 조건 결합</h3>
<p><code class="language-plaintext highlighter-rouge">AnyNestedCondition</code> 및 <code class="language-plaintext highlighter-rouge">AllNestedConditions</code>과 유사하게, 결합된 조건이 
하나도 일치하지 않는 경우에만 Bean을 로드하도록 <code class="language-plaintext highlighter-rouge">NoneNestedCondition</code>을 확장할 
수 있습니다.</p>

<h3 id="사용자-정의-conditionalon-annotation-정의">사용자 정의 @ConditionalOn… annotation 정의</h3>
<p>우리는 어떤 조건에 대해서도 custom annotation을 만들 수 있습니다. 우리는 단순히 
이 annotation에 다음과 같이 메타 <code class="language-plaintext highlighter-rouge">@Conditional</code>annotation을 달면 됩니다:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Target</span><span class="o">({</span> <span class="nc">ElementType</span><span class="o">.</span><span class="na">TYPE</span><span class="o">,</span> <span class="nc">ElementType</span><span class="o">.</span><span class="na">METHOD</span> <span class="o">})</span>
<span class="nd">@Retention</span><span class="o">(</span><span class="nc">RetentionPolicy</span><span class="o">.</span><span class="na">RUNTIME</span><span class="o">)</span>
<span class="nd">@Documented</span>
<span class="nd">@Conditional</span><span class="o">(</span><span class="nc">OnLinuxCondition</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="nd">@interface</span> <span class="nc">ConditionalOnUnix</span> <span class="o">{}</span>
</code></pre></div></div>

<p>우리가 새로운 annotation으로 Bean에 annotation을 달 때 Spring은 이 메타 
annotation을 평가할 것입니다:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Bean</span>
<span class="nd">@ConditionalOnUnix</span>
<span class="nc">LinuxBean</span> <span class="nf">linuxBean</span><span class="o">(){</span>
    <span class="k">return</span> <span class="k">new</span> <span class="nf">LinuxBean</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="결론">결론</h2>
<p>@Conditional annotation과 Custom <code class="language-plaintext highlighter-rouge">@Conditional...</code> annotation을 
생성할 수 있는 기능을 통해, Spring은 이미 우리에게 애플리케이션 컨텍스트의 내용을 
제어할 수 있는 많은 기능을 제공합니다.</p>

<p>Spring Boot는 편리한 <code class="language-plaintext highlighter-rouge">@ConditionalOn...</code> annotation을 최상단에 추가하여
사용할 수 있게 해 주고, <code class="language-plaintext highlighter-rouge">AllNestedConditions</code>, <code class="language-plaintext highlighter-rouge">AnyNestedCondition</code>또는 
<code class="language-plaintext highlighter-rouge">NoneNestedCondition</code>을 사용하여 조건을 결합할 수 있도록 하여 이를
기반 으로 구축됩니다. 이러한 도구를 사용하면 프로덕션 코드와 테스트를 모듈화할 수 있습니다.</p>

<p>하지만 권한에는 책임이 따르므로 조건문으로 애플리케이션 컨텍스트를 가득 채우지 않도록
주의해야 합니다. 그렇지 않으면 언제 무엇이 로드되는지 추적할 수 없게 됩니다.</p>

<h2 id="참고자료-및-출처">참고자료 및 출처</h2>

<ul>
  <li><a href="https://reflectoring.io/spring-boot-conditionals/">https://reflectoring.io/spring-boot-conditionals/</a></li>
</ul>]]></content><author><name>Park Jong hun</name></author><category term="springboot" /><category term="multiple" /><category term="bean" /><category term="conditional" /><summary type="html"><![CDATA[Spring Boot 앱을 빌드할 때, 어떤 조건이 충족될 때만 애플리케이션 컨텍스트에 Bean이나 모듈을 로드하고 싶을 때가 있습니다. 테스트 중에 일부 Bean을 비활성화하거나 런타임 환경에서 특정 속성에 반응하는 경우입니다.]]></summary></entry><entry><title type="html">Kotlin 의 apply, with, let, also, run에 대해 Kotlin Scoping Functions apply vs. with, let, also, and run</title><link href="https://linkeverything.github.io/kotlin/apply-with-let-also-run/" rel="alternate" type="text/html" title="Kotlin 의 apply, with, let, also, run에 대해 Kotlin Scoping Functions apply vs. with, let, also, and run" /><published>2024-12-26T00:00:00+00:00</published><updated>2024-12-25T00:00:00+00:00</updated><id>https://linkeverything.github.io/kotlin/apply-with-let-also-run</id><content type="html" xml:base="https://linkeverything.github.io/kotlin/apply-with-let-also-run/"><![CDATA[<p>함수형 프로그래밍은 Kotlin의 구문과 Kotlin의 표준 라이브러리의 다양한 함수에서 강력히
지지되고 지원됩니다. 이 게시물에서는 <code class="language-plaintext highlighter-rouge">apply</code>, <code class="language-plaintext highlighter-rouge">with</code>, <code class="language-plaintext highlighter-rouge">let</code>, <code class="language-plaintext highlighter-rouge">also</code>, <code class="language-plaintext highlighter-rouge">run</code>과 같은 다섯
가지 고차 함수를 살펴보겠습니다.</p>

<p><img src="/assets/images/posts/dev/kotlin/2024-12-26-apply-with-let-also-run/1_t3hR3BuuWySMGdcN5SNhXg.webp" alt="" /></p>

<p>이 다섯 가지 함수를 배울 때는 두 가지를 기억해야 합니다. 사용법 과 사용 시기입니다.
유사한 성격 때문에 처음에는 약간 중복되는 것처럼 보일 수 있습니다.</p>

<p>이 게시물에서는 먼저 이 다섯 가지 범위 지정 함수의 공통점을 살펴보고,
그 다음 차이점을 살펴보겠습니다. 마지막으로, 언제 이 함수를 사용해야 하는지에 대한
규칙에 대해 알아보겠습니다.</p>

<h2 id="그들은-무엇을-하나요">그들은 무엇을 하나요?</h2>

<p>이 다섯 가지 함수는 기본적으로 매우 유사한 일을 합니다. receiver parameter와
code block을 받은 다음 제공된 receiver에서 제공된 code block을 실행하는
범위 지정 함수(scoping function)입니다.</p>

<p>먼저 그 함수 중 하나와 어떻게 작동하는지 살펴보겠습니다. <code class="language-plaintext highlighter-rouge">with</code> 함수는 기본적으로
다음과 같이 정의됩니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">inline</span> <span class="k">fun</span> <span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">R</span><span class="p">&gt;</span> <span class="nf">with</span><span class="p">(</span><span class="n">receiver</span><span class="p">:</span> <span class="nc">T</span><span class="p">,</span> <span class="n">block</span><span class="p">:</span> <span class="nc">T</span><span class="p">.()</span> <span class="p">-&gt;</span> <span class="nc">R</span><span class="p">):</span> <span class="nc">R</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">receiver</span><span class="p">.</span><span class="nf">block</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>이를 사용하면 코드를 더 간결하게 만들 수 있습니다.
먼저 범위 지정 함수를 사용하지 않는 일반적인 코드를 살펴보겠습니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Person</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="py">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span>
    <span class="kd">var</span> <span class="py">age</span><span class="p">:</span> <span class="nc">Int</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span>
<span class="p">}</span>

<span class="kd">val</span> <span class="py">person</span><span class="p">:</span> <span class="nc">Person</span> <span class="p">=</span> <span class="nf">getPerson</span><span class="p">()</span>
<span class="nf">print</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">age</span><span class="p">)</span>
</code></pre></div></div>

<p>다음 code snippet은 위의 code snippet과 동일하지만,
person 변수 의 중복을 제거하기 위해 <code class="language-plaintext highlighter-rouge">with()</code> 범위 함수를 사용합니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">person</span><span class="p">:</span> <span class="nc">Person</span> <span class="p">=</span> <span class="nf">getPerson</span><span class="p">()</span>
<span class="nf">with</span><span class="p">(</span><span class="n">person</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">age</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>좋아요! 하지만, 그럼 왜 함수가 5개나 필요한 걸까요? 아래를 봅시다!</p>

<h2 id="apply-with-let-also-및-run의-차이점"><code class="language-plaintext highlighter-rouge">apply</code>, <code class="language-plaintext highlighter-rouge">with</code>, <code class="language-plaintext highlighter-rouge">let</code>, <code class="language-plaintext highlighter-rouge">also</code> 및 <code class="language-plaintext highlighter-rouge">run</code>의 차이점</h2>

<p>이러한 함수는 매우 유사한 일을 하지만, 서명(signature)과 구현(implementation)에
중요한 차이점이 있습니다. 이러한 차이점은 <strong>어떻게</strong> 사용해야 하는지를 지시합니다.</p>

<p><code class="language-plaintext highlighter-rouge">with()</code> 함수를 다른 함수 중 하나인 <code class="language-plaintext highlighter-rouge">also()</code> 함수의 서명과 구현과 비교해 보겠습니다.
이 함수는 기본적으로 다음과 같이 정의되어 있습니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">inline</span> <span class="k">fun</span> <span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">R</span><span class="p">&gt;</span> <span class="nf">with</span><span class="p">(</span><span class="n">receiver</span><span class="p">:</span> <span class="nc">T</span><span class="p">,</span> <span class="n">block</span><span class="p">:</span> <span class="nc">T</span><span class="p">.()</span> <span class="p">-&gt;</span> <span class="nc">R</span><span class="p">):</span> <span class="nc">R</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">receiver</span><span class="p">.</span><span class="nf">block</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">inline</span> <span class="k">fun</span> <span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;</span> <span class="nc">T</span><span class="p">.</span><span class="nf">also</span><span class="p">(</span><span class="n">block</span><span class="p">:</span> <span class="p">(</span><span class="nc">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">):</span> <span class="nc">T</span> <span class="p">{</span>
    <span class="nf">block</span><span class="p">(</span><span class="k">this</span><span class="p">)</span>
    <span class="k">return</span> <span class="k">this</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">with()</code> 함수 와 <code class="language-plaintext highlighter-rouge">also()</code> 함수는 3가지 면에서 다릅니다.</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">with()</code> 의 경우에는 receiver 인수가 명시적 매개변수 T 로 제공되지만,
<code class="language-plaintext highlighter-rouge">also()</code> 의 경우에는 암시적 receiver T 로 제공됩니다.</li>
  <li>블록 인수는 <code class="language-plaintext highlighter-rouge">with()</code> 의 경우 암묵적 receiver T를 갖는 함수로 정의되지만,
<code class="language-plaintext highlighter-rouge">also()</code> 의 경우에는 명시적 인수 T를 갖습니다.</li>
  <li><code class="language-plaintext highlighter-rouge">with()</code> 함수 는 블록 인수를 실행하여 반환되는 내용을 반환하는 반면,
<code class="language-plaintext highlighter-rouge">also()</code> 함수는 receiver로 제공된 동일한 객체를 반환합니다.</li>
</ol>

<p>이러한 3가지 차이점 때문에 <code class="language-plaintext highlighter-rouge">also()</code> 함수는 다른 방식으로 사용해야 합니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">person</span><span class="p">:</span> <span class="nc">Person</span> <span class="p">=</span> <span class="nf">getPerson</span><span class="p">().</span><span class="nf">also</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">name</span><span class="p">)</span> <span class="nf">print</span> <span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">age</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>이 코드 snippet은 <code class="language-plaintext highlighter-rouge">getPerson()</code> 함수를 사용하여 사람을 검색하고,
person 변수에 할당합니다. 그렇게 하기 전에 <code class="language-plaintext highlighter-rouge">also()</code> 함수는 검색된 사람의 이름과 나이를
print합니다.</p>

<p>다른 함수인 <code class="language-plaintext highlighter-rouge">apply</code>, <code class="language-plaintext highlighter-rouge">let</code>, <code class="language-plaintext highlighter-rouge">run</code>은 어떨까요?
모두 위에 표시된 3가지 차이점 중 한 가지에 대해서 다릅니다.</p>

<ul>
  <li>명시적 receiver parameter vs. 암시적 receiver</li>
  <li>명시적 parameter vs. 암묵적 receiver로 블록 인수에 제공됨</li>
  <li>receiver를 반환하는 것 vs. 블록이 반환하는 것을 반환하는 것</li>
</ul>

<p>5가지 함수의 정의는 다음과 같습니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">inline</span> <span class="k">fun</span> <span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">R</span><span class="p">&gt;</span> <span class="nf">with</span><span class="p">(</span><span class="n">receiver</span><span class="p">:</span> <span class="nc">T</span><span class="p">,</span> <span class="n">block</span><span class="p">:</span> <span class="nc">T</span><span class="p">.()</span> <span class="p">-&gt;</span> <span class="nc">R</span><span class="p">):</span> <span class="nc">R</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">receiver</span><span class="p">.</span><span class="nf">block</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">inline</span> <span class="k">fun</span> <span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;</span> <span class="nc">T</span><span class="p">.</span><span class="nf">also</span><span class="p">(</span><span class="n">block</span><span class="p">:</span> <span class="p">(</span><span class="nc">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">):</span> <span class="nc">T</span> <span class="p">{</span>
    <span class="nf">block</span><span class="p">(</span><span class="k">this</span><span class="p">)</span>
    <span class="k">return</span> <span class="k">this</span>
<span class="p">}</span>
<span class="k">inline</span> <span class="k">fun</span> <span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;</span> <span class="nc">T</span><span class="p">.</span><span class="nf">apply</span><span class="p">(</span><span class="n">block</span><span class="p">:</span> <span class="nc">T</span><span class="p">.()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">):</span> <span class="nc">T</span> <span class="p">{</span>
    <span class="nf">block</span><span class="p">()</span>
    <span class="k">return</span> <span class="k">this</span>
<span class="p">}</span>
<span class="k">inline</span> <span class="k">fun</span> <span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">R</span><span class="p">&gt;</span> <span class="nc">T</span><span class="p">.</span><span class="nf">let</span><span class="p">(</span><span class="err">블록</span><span class="p">:</span> <span class="p">(</span><span class="nc">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">R</span><span class="p">):</span> <span class="nc">R</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nf">block</span><span class="p">(</span><span class="k">this</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">inline</span> <span class="k">fun</span> <span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">R</span><span class="p">&gt;</span> <span class="nc">T</span><span class="p">.</span><span class="nf">run</span><span class="p">(</span><span class="err">블록</span><span class="p">:</span> <span class="nc">T</span><span class="p">.()</span> <span class="p">-&gt;</span> <span class="nc">R</span><span class="p">):</span> <span class="nc">R</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nf">block</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>이러한 함수를 배울 때, 함수의 정의를 기억하기 어려울 수 있습니다.
<a href="https://docs.google.com/spreadsheets/d/1P2gMRuu36pSDW4fdwE-fLN9fcA_ZboIU2Q5VtgixBNo/edit?source=post_page-----816e4efb75f5--------------------------------&amp;gid=0#gid=0">Kotlin 표준 범위 함수</a>
스프레드시트는 행렬에서 차이점을 보여줍니다. 필요할 때마다 인쇄하여 참조하는 것이 좋습니다.</p>

<h2 id="apply-with-let-also-또는-run을-사용하는-경우"><code class="language-plaintext highlighter-rouge">apply</code>, <code class="language-plaintext highlighter-rouge">with</code>, <code class="language-plaintext highlighter-rouge">let</code>, <code class="language-plaintext highlighter-rouge">also</code> 또는 <code class="language-plaintext highlighter-rouge">run</code>을 사용하는 경우</h2>

<p>우리는 이제 이 다섯 가지 함수가 어떻게 다른지 알고 있습니다. 하지만 우리는 여전히
어떤 범위 지정 함수를 언제 사용해야 하는지 모릅니다. 이들은 본질적으로 매우 유사하며
종종 상호 교환이 가능합니다.</p>

<p>공식 Kotlin 문서 에 정의된 이 다섯 가지 함수에 대한 여러 가지 모범 사례와 규칙이 있습니다.
이러한 규칙을 배우면 더 관용적인 코드를 작성할 수 있으며 다른 개발자의 코드 의도를 더 빨리
이해하는 데 도움이 됩니다.</p>

<h3 id="apply-사용에-대한-규칙"><code class="language-plaintext highlighter-rouge">apply</code> 사용에 대한 규칙</h3>

<p>블록 내에서 receiver의 어떤 함수에도 접근하지 않고 동일한 receiver를 반환하려는 경우 <code class="language-plaintext highlighter-rouge">apply()</code>
함수를 사용합니다. 이는 새 객체를 초기화할 때 가장 자주 발생합니다. 다음 snippet은
예를 보여줍니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">peter</span> <span class="p">=</span> <span class="nc">Person</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="c1">// only access properties in apply block!</span>
    <span class="n">name</span> <span class="p">=</span> <span class="s">"Peter"</span>
    <span class="n">age</span> <span class="p">=</span> <span class="mi">18</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">apply()</code> 없이 동일한 코드는 다음과 같습니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">clark</span> <span class="p">=</span> <span class="nc">Person</span><span class="p">()</span>
<span class="n">clark</span><span class="p">.</span><span class="n">name</span> <span class="p">=</span> <span class="s">"Clark"</span>
<span class="n">clark</span><span class="p">.</span><span class="n">age</span> <span class="p">=</span> <span class="mi">18</span>
</code></pre></div></div>

<h3 id="also-사용에-대한-규칙"><code class="language-plaintext highlighter-rouge">also</code> 사용에 대한 규칙</h3>

<p>블록이 receiver 매개변수에 전혀 접근하지 않거나 receiver 매개변수를 변형하지 않는 경우
<code class="language-plaintext highlighter-rouge">also()</code> 함수를 사용합니다. 블록이 다른 값을 반환해야 하는 경우 <code class="language-plaintext highlighter-rouge">also()</code> 를 사용하지
마세요. 예를 들어, 객체에 대한 일부 부작용을 실행하거나 속성에 할당하기 전에 데이터를
검증할 때 매우 유용합니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Book</span><span class="p">(</span><span class="kd">val</span> <span class="py">author</span><span class="p">:</span> <span class="nc">Person</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">author</span> <span class="p">=</span> <span class="n">author</span><span class="p">.</span><span class="nf">also</span> <span class="p">{</span>
        <span class="nf">requireNotNull</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">age</span><span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">also()</code> 없이 동일한 코드는 다음과 같습니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Book</span><span class="p">(</span><span class="kd">val</span> <span class="py">author</span><span class="p">:</span> <span class="nc">Person</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">init</span> <span class="p">{</span>
        <span class="nf">requireNotNull</span><span class="p">(</span><span class="n">author</span><span class="p">.</span><span class="n">age</span><span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">author</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="let-사용-규칙"><code class="language-plaintext highlighter-rouge">let</code> 사용 규칙</h3>

<p>다음의 경우 모두 <code class="language-plaintext highlighter-rouge">let()</code> 함수를 사용하세요.</p>

<ul>
  <li>주어진 값이 null이 아닌 경우 코드를 실행</li>
  <li>null 허용 객체를 다른 null 허용 객체로 변환</li>
  <li>단일 로컬 변수의 범위를 제한</li>
</ul>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">getNullablePerson</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
    <span class="c1">// only executed when not-null</span>
    <span class="nf">promote</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">val</span> <span class="py">driversLicence</span><span class="p">:</span> <span class="nc">Licence</span><span class="p">?</span> <span class="p">=</span> <span class="nf">getNullablePerson</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
    <span class="c1">// convert nullable person to nullable driversLicence</span>
    <span class="n">licenceService</span><span class="p">.</span><span class="nf">getDriversLicence</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">val</span> <span class="py">person</span><span class="p">:</span> <span class="nc">Person</span> <span class="p">=</span> <span class="nf">getPerson</span><span class="p">()</span>
<span class="nf">getPersonDao</span><span class="p">().</span><span class="nf">let</span> <span class="p">{</span> <span class="n">dao</span> <span class="p">-&gt;</span>
    <span class="c1">// scope of dao variable is limited to this block</span>
    <span class="n">dao</span><span class="p">.</span><span class="nf">insert</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">let()</code> 없이 동일한 코드는 다음과 같습니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">person</span><span class="p">:</span> <span class="nc">Person</span><span class="p">?</span> <span class="p">=</span> <span class="nf">getPromotablePerson</span><span class="p">()</span>
<span class="k">if</span> <span class="p">(</span><span class="n">person</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">promote</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">val</span> <span class="py">driver</span><span class="p">:</span> <span class="nc">Person</span><span class="p">?</span> <span class="p">=</span> <span class="nf">getDriver</span><span class="p">()</span>
<span class="kd">val</span> <span class="py">driversLicence</span><span class="p">:</span> <span class="nc">Licence</span><span class="p">?</span> <span class="p">=</span> <span class="k">if</span> <span class="p">(</span><span class="n">driver</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="k">null</span> <span class="k">else</span>
    <span class="n">licenceService</span><span class="p">.</span><span class="nf">getDriversLicence</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
<span class="kd">val</span> <span class="py">person</span><span class="p">:</span> <span class="nc">Person</span> <span class="p">=</span> <span class="nf">getPerson</span><span class="p">()</span>
<span class="kd">val</span> <span class="py">personDao</span><span class="p">:</span> <span class="nc">PersonDao</span> <span class="p">=</span> <span class="nf">getPersonDao</span><span class="p">()</span>
<span class="n">personDao</span><span class="p">.</span><span class="nf">insert</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="with-사용-규칙"><code class="language-plaintext highlighter-rouge">with</code> 사용 규칙</h3>

<p><code class="language-plaintext highlighter-rouge">with()</code>는 null이 불가능한 receiver에서만 사용하고, 결과가 필요하지 않을 때
사용합니다. 예를 들어:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">person</span><span class="p">:</span> <span class="nc">Person</span> <span class="p">=</span> <span class="nf">getPerson</span><span class="p">()</span>
<span class="nf">with</span><span class="p">(</span><span class="n">person</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">age</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">with()</code> 없이 동일한 코드는 다음과 같습니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">person</span><span class="p">:</span> <span class="nc">Person</span> <span class="p">=</span> <span class="nf">getPerson</span><span class="p">()</span>
<span class="nf">print</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">age</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="run-사용에-대한-규칙"><code class="language-plaintext highlighter-rouge">run</code> 사용에 대한 규칙</h3>

<p>어떤 값을 계산하거나 여러 로컬 변수의 범위를 제한하려면 <code class="language-plaintext highlighter-rouge">run()</code> 함수를 사용하세요.
명시적 매개변수를 암묵적 receiver로 변환하려면 <code class="language-plaintext highlighter-rouge">run()</code>도 사용하세요.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">inserted</span><span class="p">:</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="nf">run</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">person</span><span class="p">:</span> <span class="nc">Person</span> <span class="p">=</span> <span class="nf">getPerson</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">personDao</span><span class="p">:</span> <span class="nc">PersonDao</span> <span class="p">=</span> <span class="nf">getPersonDao</span><span class="p">()</span>
    <span class="n">personDao</span><span class="p">.</span><span class="nf">insert</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">fun</span> <span class="nf">printAge</span><span class="p">(</span><span class="n">person</span><span class="p">:</span> <span class="nc">Person</span><span class="p">)</span> <span class="p">=</span> <span class="n">person</span><span class="p">.</span><span class="nf">run</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">age</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">run()</code>이 없는 동일한 코드는 다음과 같습니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">person</span><span class="p">:</span> <span class="nc">Person</span> <span class="p">=</span> <span class="nf">getPerson</span><span class="p">()</span>
<span class="kd">val</span> <span class="py">personDao</span><span class="p">:</span> <span class="nc">PersonDao</span> <span class="p">=</span> <span class="nf">getPersonDao</span><span class="p">()</span>
<span class="kd">val</span> <span class="py">inserted</span><span class="p">:</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="n">personDao</span><span class="p">.</span><span class="nf">insert</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
<span class="k">fun</span> <span class="nf">printAge</span><span class="p">(</span><span class="n">person</span><span class="p">:</span> <span class="nc">Person</span><span class="p">)</span> <span class="p">=</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">age</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="여러-범위-기능-결합">여러 범위 기능 결합</h3>

<p>이전 섹션에서는 코드 가독성을 개선하기 위해 범위 지정 함수를 격리하여 사용하는 방법을
보여주었습니다. 동일한 코드 블록 내에서 여러 범위 지정 함수를 결합하는 것이 종종
유혹적입니다.</p>

<p>범위 지정 함수가 중첩되면 코드가 빠르게 혼란스러워질 수 있습니다. 원칙적으로
receiver 인수를 람다 블록의 receiver에 바인딩하는 범위 지정 함수(apply, run, with)를
중첩하지 않도록 합니다. 다른 범위 지정 함수(let, also)를 중첩할 때는 람다 블록의
매개변수에 대한 명시적 이름을 제공합니다. 즉, 해당 범위 지정 함수를 중첩할 때 암시적
매개변수 it을 사용하지 마세요.</p>

<p>중첩 외에도 범위 지정 함수는 호출 체인에 결합될 수도 있습니다. 중첩과 달리 이런 방식으로
범위 지정 함수를 결합할 때 가독성 페널티가 없습니다. 오히려 가독성 개선이 훨씬 더
커질 것입니다.</p>

<p>이 글의 마무리로, 호출 체인에서 범위 지정 함수를 결합하는 몇 가지 예를 살펴보겠습니다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">fun</span> <span class="nf">insert</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="nc">User</span><span class="p">)</span> <span class="p">=</span> <span class="nc">SqlBuilder</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="nf">append</span><span class="p">(</span><span class="s">"INSERT INTO user (email, name, age) VALUES "</span><span class="p">)</span>
    <span class="nf">append</span><span class="p">(</span><span class="s">"(?"</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">email</span><span class="p">)</span>
    <span class="nf">append</span><span class="p">(</span><span class="s">",?"</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
    <span class="nf">append</span><span class="p">(</span><span class="s">",?)"</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">age</span><span class="p">)</span>
<span class="p">}.</span><span class="nf">also</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"Executing SQL update: $it."</span><span class="p">)</span>
<span class="p">}.</span><span class="nf">run</span> <span class="p">{</span>
    <span class="n">jdbc</span><span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="k">this</span><span class="p">)</span> <span class="p">&gt;</span> <span class="mi">0</span>
<span class="p">}</span>
</code></pre></div></div>

<p>위의 snippet은 데이터베이스에 사용자를 삽입하기 위한 dao 함수를 보여줍니다. 
Kotlin의 표현식 본문 구문을 사용하면서도 구현 내에서 우려 사항을 분리합니다. 
즉, SQL 준비, SQL 로깅, SQL 실행입니다. 마지막에 이 함수는 삽입의 성공을 
나타내는 Boolean을 반환합니다.</p>

<h2 id="참고자료-및-출처">참고자료 및 출처</h2>

<ul>
  <li><a href="https://medium.com/@fatihcoskun/kotlin-scoping-functions-apply-vs-with-let-also-run-816e4efb75f5">https://medium.com/@fatihcoskun/kotlin-scoping-functions-apply-vs-with-let-also-run-816e4efb75f5</a></li>
</ul>]]></content><author><name>Park Jong hun</name></author><category term="kotlin" /><category term="kotlin" /><category term="apply" /><category term="with" /><category term="let" /><category term="also" /><category term="run" /><summary type="html"><![CDATA[함수형 프로그래밍은 Kotlin의 구문과 Kotlin의 표준 라이브러리의 다양한 함수에서 강력히 지지되고 지원됩니다. 이 게시물에서는 apply, with, let, also, run과 같은 다섯 가지 고차 함수를 살펴보겠습니다.]]></summary></entry><entry><title type="html">venv 사용법 How to use venv in python environment</title><link href="https://linkeverything.github.io/python/python-venv/" rel="alternate" type="text/html" title="venv 사용법 How to use venv in python environment" /><published>2024-12-26T00:00:00+00:00</published><updated>2024-12-26T00:00:00+00:00</updated><id>https://linkeverything.github.io/python/python-venv</id><content type="html" xml:base="https://linkeverything.github.io/python/python-venv/"><![CDATA[<p>venv는 Python의 표준 라이브러리 중 하나로, 별도의 패키지나 모듈 환경을 제공하는 가상 
환경을 만들기 위해 사용할 수 있습니다. 즉, 프로젝트마다 다른 버전의 패키지를 사용하고 
싶을 때, 이를 격리된 환경에서 관리할 수 있게 도와준다.</p>

<blockquote>
  <p>실질적으로 거의 대부분의 python 프로젝트에서 활용한다고 볼 수 있습니다. 각 프로젝트에서
사용하는 라이브러리의 버전에 따라 충돌이 발생할 수 있기 때문에 호환 가능한 버전 관리
목적이라고 보면 될 것 같습니다. (spring 의 pom.xml, build.gradle등에서 버전
관리하는 것과 유사)</p>
</blockquote>

<h2 id="venv의-장점">venv의 장점</h2>
<ul>
  <li>격리된 환경: 다양한 프로젝트에서 서로 다른 패키지나 Python 버전의 충돌 없이 작업할 수 있습니다.</li>
  <li>버전 관리: 프로젝트별로 필요한 패키지와 그 버전을 쉽게 관리할 수 있습니다.</li>
  <li>의존성 문제 해결: 각 프로젝트의 의존성을 명확하게 알 수 있으므로 배포나 협업 시 문제를 
최소화 할 수 있습니다.</li>
</ul>

<h2 id="venv의-단점">venv의 단점</h2>
<ul>
  <li>추가적인 공간: 각 가상 환경마다 패키지를 복제하기 때문에 디스크 공간이 추가로 필요합니다.</li>
  <li>관리: 여러 가상 환경을 사용하면 관리가 복잡해질 수 있습니다.</li>
</ul>

<h2 id="venv-사용-방법">venv 사용 방법</h2>

<h3 id="가상-환경-생성">가상 환경 생성</h3>
<p>Windows:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> venv <span class="o">[</span>가상환경명]
</code></pre></div></div>

<p>Linux/Mac:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 <span class="nt">-m</span> venv <span class="o">[</span>가상환경명]
</code></pre></div></div>

<h3 id="가상-환경-활성화">가상 환경 활성화</h3>
<p>Windows:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>가상환경명]<span class="se">\S</span>cripts<span class="se">\a</span>ctivate
</code></pre></div></div>
<p>Linux/Mac:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source</span> <span class="o">[</span>가상환경명]/bin/activate
</code></pre></div></div>

<h3 id="가상-환경-비활성화">가상 환경 비활성화</h3>
<p>모든 운영체제에서:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>deactivate
</code></pre></div></div>

<h3 id="패키지-설치">패키지 설치</h3>
<p>활성화된 가상 환경에서,</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install</span> <span class="o">[</span>패키지명]
</code></pre></div></div>

<h3 id="requirementstxt를-활용한-패키지-설치">requirements.txt를 활용한 패키지 설치</h3>
<p>requirements.txt 파일 내의 패키지를 한번에 모두 설치할 때는,</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install</span> <span class="nt">-r</span> requirements.txt 
</code></pre></div></div>

<h3 id="가상-환경-내-패키지-리스트-확인-및-requirementstxt-만들기">가상 환경 내 패키지 리스트 확인 및 requirements.txt 만들기</h3>
<p>가상환경 내 패키지 리스트 확인:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip freeze
</code></pre></div></div>

<h3 id="가상환경-내-패키지를-requirementstxt-만들기">가상환경 내 패키지를 requirements.txt 만들기</h3>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip freeze <span class="o">&gt;</span> requirements.txt
</code></pre></div></div>

<h3 id="가상환경-삭제하기">가상환경 삭제하기</h3>
<p>가상 환경은 결국 폴더 형태로 저장되기 때문에, 해당 폴더를 삭제하면 가상 환경도 함께 삭제된다.</p>

<h2 id="활용-예시">활용 예시</h2>

<h3 id="가상-환경-생성-및-활성화">가상 환경 생성 및 활성화</h3>
<p>Windows:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> venv myenv
myenv<span class="se">\S</span>cripts<span class="se">\a</span>ctivate
</code></pre></div></div>

<p>Linux/Mac:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 <span class="nt">-m</span> venv myenv
<span class="nb">source </span>myenv/bin/activate
</code></pre></div></div>

<h3 id="패키지-설치-및-확인">패키지 설치 및 확인</h3>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>requests
pip freeze
</code></pre></div></div>

<h3 id="가상-환경-종료">가상 환경 종료</h3>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>deactivate
</code></pre></div></div>

<h2 id="추가-tip">추가 Tip</h2>

<h3 id="powershell에서-가상-환경-활성화-안될-때-활성화-시키기">PowerShell에서 가상 환경 활성화 안될 때 활성화 시키기</h3>

<p>Windows:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>myenv<span class="se">\S</span>cripts<span class="se">\a</span>ctivate.ps1
</code></pre></div></div>

<h3 id="보안-오류로-가상-환경-활성화가-안될-때">보안 오류로 가상 환경 활성화가 안될 때</h3>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+ .<span class="se">\S</span>cripts<span class="se">\A</span>ctivate.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : 보안 오류: <span class="o">(</span>:<span class="o">)</span> <span class="o">[]</span>, PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess
</code></pre></div></div>
<p>위와 같은 문구가 뜨면서 가상환경 진입이 안될 때는 윈도우 실행정책이 막혀 있는 경우입니다.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Set-ExecutionPolicy <span class="nt">-ExecutionPolicy</span> Unrestricted <span class="nt">-Scope</span> LocalMachine
</code></pre></div></div>
<p>cmd창에 위와 같이 입력하여 정책을 변경하면 가상환경이 활성화 되는 것을 확인 할 수 있습니다.</p>

<h2 id="참고자료-및-출처">참고자료 및 출처</h2>

<ul>
  <li><a href="https://blog.deeplink.kr/?p=942">https://blog.deeplink.kr/?p=942</a></li>
</ul>]]></content><author><name>Park Jong hun</name></author><category term="python" /><category term="python" /><category term="venv" /><summary type="html"><![CDATA[venv는 Python의 표준 라이브러리 중 하나로, 별도의 패키지나 모듈 환경을 제공하는 가상 환경을 만들기 위해 사용할 수 있습니다. 즉, 프로젝트마다 다른 버전의 패키지를 사용하고 싶을 때, 이를 격리된 환경에서 관리할 수 있게 도와준다.]]></summary></entry><entry><title type="html">Collection에서 원소를 안전하게 삭제하기 How to remove elements from collection safely.</title><link href="https://linkeverything.github.io/java/remove-element-in-collection/" rel="alternate" type="text/html" title="Collection에서 원소를 안전하게 삭제하기 How to remove elements from collection safely." /><published>2024-12-24T00:00:00+00:00</published><updated>2024-12-24T00:00:00+00:00</updated><id>https://linkeverything.github.io/java/remove-element-in-collection</id><content type="html" xml:base="https://linkeverything.github.io/java/remove-element-in-collection/"><![CDATA[<p>이 문서에서는 Java의 collection에서 원소를 삭제하는 것 관련내용을 정리합니다.</p>

<p>Java개발을 하다보면, 리스트를 순회하면서 특정 원소를 삭제하고 싶을 때가 있습니다.
예를 들어, 다음과 같이 알파벳과 숫자가 섞여있는 리스트가 있다고 가정해봅시다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o">&lt;</span><span class="nc">Character</span><span class="o">&gt;</span> <span class="n">letters</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&gt;();</span>
<span class="n">letters</span><span class="o">.</span><span class="na">addAll</span><span class="o">(</span><span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="sc">'A'</span><span class="o">,</span> <span class="sc">'B'</span><span class="o">,</span> <span class="sc">'1'</span><span class="o">,</span> <span class="sc">'2'</span><span class="o">,</span> <span class="sc">'C'</span><span class="o">,</span> <span class="sc">'D'</span><span class="o">,</span> <span class="sc">'3'</span><span class="o">,</span> <span class="sc">'E'</span><span class="o">,</span> <span class="sc">'4'</span><span class="o">,</span> <span class="sc">'5'</span><span class="o">));</span>
</code></pre></div></div>

<p>저는 이 리스트에서 숫자만 찾아서 모두 삭제하려고 한다는걸 가정합니다.</p>

<h2 id="concurrentmodificationexception-발생">ConcurrentModificationException 발생</h2>
<p>Collection에서 원소를 지우는 것은 <code class="language-plaintext highlighter-rouge">boolean remove(Object o)</code> 메소드를 사용하면 됩니다.
Collection 의 각 원소에 대해서 for 루프를 돌면서 해당 원소가 숫자인지 여부를 체크한 후에,
숫자이면 remove 함수를 호출합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="o">(</span><span class="nc">Character</span> <span class="n">letter</span> <span class="o">:</span> <span class="n">letters</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="nc">Character</span><span class="o">.</span><span class="na">isDigit</span><span class="o">(</span><span class="n">letter</span><span class="o">))</span> <span class="o">{</span>
        <span class="n">letters</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">letter</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>하지만 위 코드를 실행해 보면, ConcurrentModificationException을 발생시킵니다.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Exception <span class="k">in </span>thread <span class="s2">"main"</span> java.util.ConcurrentModificationException
  at java.util.ArrayList<span class="nv">$Itr</span>.checkForComodification<span class="o">(</span>ArrayList.java:901<span class="o">)</span>
  at java.util.ArrayList<span class="nv">$Itr</span>.next<span class="o">(</span>ArrayList.java:851<span class="o">)</span>
...
</code></pre></div></div>

<p>이해를 위해서 개념적으로만 접근하자면, 해당 element 기준으로 for 문을 돌고 있을 때,
해당 엘리먼트를 Collection에서 삭제해 버리면, <strong>아직 for문을 빠져나가기 전이고, 그래서
해당 element를 기준으로 돌고 있는데</strong>, 삭제되었다고 생각하여 문제를 일으키게 되는 것입니다.</p>

<h2 id="인덱스-기반-for-문으로-해결-시도">인덱스 기반 for 문으로 해결 시도</h2>
<p>그럼, 위 처럼 element기준으로 for 문을 순환하지 말고, 
인덱스를 사용하는 for 문으로 바꿔서 루프를 돌려보겠습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">letters</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
    <span class="nc">Character</span> <span class="n">letter</span> <span class="o">=</span> <span class="n">letters</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
    <span class="k">if</span> <span class="o">(</span><span class="nc">Character</span><span class="o">.</span><span class="na">isDigit</span><span class="o">(</span><span class="n">letter</span><span class="o">))</span> <span class="o">{</span>
        <span class="n">letters</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">i</span><span class="o">);</span> <span class="c1">// 또는 letters.remove(letter);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>이번에는 Exception은 발생하지 않는데, 리스트를 출력해보면 원하는대로 숫자 원소가 모두 삭제되지 않았음을 확인할 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">letters</span><span class="o">);</span>

<span class="o">[</span><span class="no">A</span><span class="o">,</span> <span class="no">B</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="no">C</span><span class="o">,</span> <span class="no">D</span><span class="o">,</span> <span class="no">E</span><span class="o">,</span> <span class="mi">5</span><span class="o">]</span>
</code></pre></div></div>

<p>왜 이런 일이 발생하는 걸까요? 원인은 리스트에서 i 번째 원소 삭제되면, i + 1 번째 원소가 그 자리에 오게되고, 
리스트의 길이가 1만큼 짧아지는데서 찾을 수 있습니다. 예를 들어, 1이 삭제되면 그 자리에 2가 오게 
되므로, for 문은 2를 건너뛰고 바로 C를 체크하게 됩니다.</p>

<p>이런 문제를 해결하기 위해서, 삭제하고 나면 내 자리에 다음 element가 오니까, 
아래와 같이 해결하기도 합니다. 하지만 온전히 동작하기엔 조금 이상한 부분은 있습니다.
개념적으로만 이해하세요.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">letters</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
    <span class="nc">Character</span> <span class="n">letter</span> <span class="o">=</span> <span class="n">letters</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
    <span class="k">if</span> <span class="o">(</span><span class="nc">Character</span><span class="o">.</span><span class="na">isDigit</span><span class="o">(</span><span class="n">letter</span><span class="o">))</span> <span class="o">{</span>
        <span class="n">letters</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">i</span><span class="o">);</span> 
        <span class="n">i</span><span class="o">--;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="이터레이터로-해결">이터레이터로 해결</h2>

<p>자바의 Iterator 인터페이스는 remove 메소드를 제공하고 있습니다.
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html#remove--">자바 공식 문서</a>를 보면 
이 메소드를 사용하는 것이 컬렉션을 순회하면서 원소를 삭제할 수 있는 유일하게 안전한 
방법이라고 가이드하고 있습니다.</p>

<blockquote>
  <p>Note that Iterator.remove is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.</p>
</blockquote>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="o">(</span><span class="nc">Iterator</span><span class="o">&lt;</span><span class="nc">Character</span><span class="o">&gt;</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">letters</span><span class="o">.</span><span class="na">iterator</span><span class="o">();</span> <span class="n">iter</span><span class="o">.</span><span class="na">hasNext</span><span class="o">();</span> <span class="o">)</span> <span class="o">{</span>
    <span class="nc">Character</span> <span class="n">letter</span> <span class="o">=</span> <span class="n">iter</span><span class="o">.</span><span class="na">next</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="nc">Character</span><span class="o">.</span><span class="na">isDigit</span><span class="o">(</span><span class="n">letter</span><span class="o">))</span> <span class="o">{</span>
        <span class="n">iter</span><span class="o">.</span><span class="na">remove</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>가이드 대로 위와 같이 이터레이터를 이용하여 코드를 작성하면 원하던 대로 리스트에서 숫자 원소들이 삭제되는 것을 확인할 수 있습니다.</p>

<h2 id="자바8-에서는">자바8 에서는…</h2>

<p>Collection 인터페이스에 <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-">removeIf 메소드가 추가</a>
되어 다음과 같이 한 줄의 코드면 충분합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">letters</span><span class="o">.</span><span class="na">removeIf</span><span class="o">(</span><span class="nl">Character:</span><span class="o">:</span><span class="n">isDigit</span><span class="o">);</span>
</code></pre></div></div>

<p>추가로 원본 리스트에 변경을 가하지 않고, 숫자 원소가 삭제된 새로운 리스트를 얻고 싶은 경우, 
다음과 같이 스트림 API를 사용할 수 있습니다. 하지만 이렇게 하면 하나의 Collection을 
재 생성하기 때문에 크게 권장하는 방법은 아닐 수 있습니다. (필요에 따라서 선택!)</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o">&lt;</span><span class="nc">Character</span><span class="o">&gt;</span> <span class="n">alphabets</span> <span class="o">=</span> <span class="n">letters</span><span class="o">.</span><span class="na">stream</span><span class="o">()</span>
    <span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="nl">Character:</span><span class="o">:</span><span class="n">isAlphabetic</span><span class="o">)</span>
    <span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toList</span><span class="o">());</span>
</code></pre></div></div>

<h2 id="참고자료-및-출처">참고자료 및 출처</h2>

<ul>
  <li><a href="https://www.daleseo.com/how-to-remove-from-list-in-java/">https://www.daleseo.com/how-to-remove-from-list-in-java/</a></li>
  <li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html#remove--">https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html#remove--</a></li>
  <li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-">https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-</a></li>
</ul>]]></content><author><name>Park Jong hun</name></author><category term="java" /><category term="java" /><category term="collection" /><category term="remove" /><summary type="html"><![CDATA[이 문서에서는 Java의 collection에서 원소를 삭제하는 것 관련내용을 정리합니다.]]></summary></entry><entry><title type="html">AWS Configure 여러 계정 스위칭하면서 사용 가능하게 하기</title><link href="https://linkeverything.github.io/aws/aws-account-switch/" rel="alternate" type="text/html" title="AWS Configure 여러 계정 스위칭하면서 사용 가능하게 하기" /><published>2024-12-19T00:00:00+00:00</published><updated>2024-12-19T00:00:00+00:00</updated><id>https://linkeverything.github.io/aws/aws-account-switch</id><content type="html" xml:base="https://linkeverything.github.io/aws/aws-account-switch/"><![CDATA[<p>AWS를 사용하게 되면, 특히 실제 업무에서 활용하게 되면, 여러 account를 전환하면서 
사용해야 하는 경우가 발생한다. 쉽게는 개발/검증 등의 계정을 각각 둘 수도 있고, 
개인적인 경우라면 회사용, 개인 개발용, 학습용 등등을 두고 사용하는 것이 가능하다.</p>

<p>여기서는 이러한 계정들을 다 하나의 PC에 설치해 두고, 서로 전환해 가면서 사용할 수 있는 방법을 살펴보고자 한다.
여기서 계정을 설치한다는 의미는 awscli 상에 로그인 정보를 넣어두는 것을 의미하며, 
웹 콘솔에 대한 부분은 아니다.</p>

<h2 id="aws-configure---profile">AWS Configure <code class="language-plaintext highlighter-rouge">--profile</code></h2>

<p>처음에 AWS Console 의 IAM에서 생성한 사용자에 대해, access key 를 생성해두면, 
로컬 환경에서 다음과 같이 설정하여 configure 할 수 있다.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws configure

AWS Access Key ID <span class="o">[</span><span class="k">****************</span>XXXX]:
AWS Secret Access Key <span class="o">[</span><span class="k">****************</span>XXXX]:
Default region name <span class="o">[</span>ap-northeast-2]:
Default output format <span class="o">[</span>json]:
</code></pre></div></div>

<p>사실 계정을 전환하려면 위 명령어 <code class="language-plaintext highlighter-rouge">aws configure</code>를 한 번 더 
입력하고 다시 계정 정보를 입력하면 되지만, 비밀번호도 아닌 access key 
를 가지고 하는 저 과정을 매번 전환하면서 하기란 귀찮은 일이다.</p>

<p>이럴때는 <code class="language-plaintext highlighter-rouge">--profile</code>옵션을 이용하여 각 configure정보를 profile에 저장한다.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws configure <span class="nt">--profile</span> testUser

AWS Access Key ID <span class="o">[</span><span class="k">****************</span>XXXX]:
AWS Secret Access Key <span class="o">[</span><span class="k">****************</span>XXXX]:
Default region name <span class="o">[</span>ap-northeast-2]:
Default output format <span class="o">[</span>json]:
</code></pre></div></div>

<p>저렇게 profile을 지정해 두면, awscli 명령어 뒤에 <code class="language-plaintext highlighter-rouge">--profile</code>과 함께 
사용하고자 하는 profile 이름을 입력하여 사용할 수 있다.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws s3 <span class="nb">ls</span> <span class="nt">--profile</span> testUser
</code></pre></div></div>

<h2 id="기본-프로파일-설정하기">기본 프로파일 설정하기</h2>

<p>위와 같이 설정하면, 프로파일로 동작하긴 하지만, 다른 툴이나 바이너리에서 
aws 인증정보를 활용하려고 할 때 여전히 사용하지 못한다. 따라서 
아래와 같이 <code class="language-plaintext highlighter-rouge">AWS_DEFAULT_PROFILE</code> 값을 지정해 주면 문제없이 사용이
가능하다.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">AWS_DEFAULT_PROFILE</span><span class="o">=</span>testUser
</code></pre></div></div>

<p>이제 <code class="language-plaintext highlighter-rouge">--profile</code> 옵션 없이 command 실행해도 정상적으로 동작한다.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws s3 <span class="nb">ls</span> 
</code></pre></div></div>

<h2 id="기본값-활성화하기">기본값 활성화하기</h2>

<p>만약 profile을 정말 자주 변경해야 한다면, 위와 같이 <code class="language-plaintext highlighter-rouge">set</code> 명령어를 이용해서 설정을 매번 바꿔줘야 한다. 
하지만 매번 로그인하던 것 보다는 훨씬 편하다.</p>

<p>자주 변경되지는 않으나 종종 변경해야 하고, 기본값이 되었으면 하는 profile이 
명확하다면 위 설정 부분을 <code class="language-plaintext highlighter-rouge">.zshrc</code> 혹은 <code class="language-plaintext highlighter-rouge">.bashrc</code>에 넣는다.</p>

<h2 id="출처-및-참고자료">출처 및 참고자료</h2>

<ul>
  <li><a href="https://novemberde.github.io/post/2018/06/20/AWS-config-switching/">https://novemberde.github.io/post/2018/06/20/AWS-config-switching/</a></li>
</ul>]]></content><author><name>Park Jong hun</name></author><category term="aws" /><category term="aws" /><category term="account" /><summary type="html"><![CDATA[AWS를 사용하게 되면, 특히 실제 업무에서 활용하게 되면, 여러 account를 전환하면서 사용해야 하는 경우가 발생한다. 쉽게는 개발/검증 등의 계정을 각각 둘 수도 있고, 개인적인 경우라면 회사용, 개인 개발용, 학습용 등등을 두고 사용하는 것이 가능하다.]]></summary></entry><entry><title type="html">sudo 권한으로 crontab 등록하기 How to add to crontab with sudo permission</title><link href="https://linkeverything.github.io/linux/sudo-crontab/" rel="alternate" type="text/html" title="sudo 권한으로 crontab 등록하기 How to add to crontab with sudo permission" /><published>2024-12-19T00:00:00+00:00</published><updated>2024-12-19T00:00:00+00:00</updated><id>https://linkeverything.github.io/linux/sudo-crontab</id><content type="html" xml:base="https://linkeverything.github.io/linux/sudo-crontab/"><![CDATA[<p>Linux 시스템에서 crontab으로 등록하면 반복적인(스케쥴링) 작업을 
할 수 있습니다. 하지만 crontab이 실행되는 계정이 sudo(root) 권한이
없기 때문에 sudo 권한이 필요한 작업은 추가로 몇 가지 작업이 필요합니다.</p>

<h2 id="별도의-shell-로-작성">별도의 shell 로 작성</h2>
<p>뒤쪽에서 설명할 것 처럼, 권한 처리를 shell 파일에 주는 것이 안전하기 때문에
별도의 shell 에서 하고자 하는 작업(sudo 권한 필요)을 하게끔 정의합니다.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#/bin/sh</span>
<span class="nv">LOGFILE</span><span class="o">=</span><span class="s2">"/Users/criss/dev/work/crontab-log/mtu-</span><span class="si">$(</span><span class="nb">date</span> +%F<span class="si">)</span><span class="s2">.crontab.log"</span>

/sbin/ifconfig utun4 mtu 1320

<span class="nv">CUR_DATE</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span><span class="si">)</span>
<span class="nv">UTUN4_VAL</span><span class="o">=</span><span class="si">$(</span>/sbin/ifconfig | <span class="nb">grep </span>utun4<span class="si">)</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$CUR_DATE</span><span class="s2"> &gt; </span><span class="nv">$UTUN4_VAL</span><span class="s2">"</span> <span class="o">&gt;&gt;</span> <span class="s2">"</span><span class="nv">$LOGFILE</span><span class="s2">"</span> 2&gt;&amp;1
</code></pre></div></div>
<p>위 내용에서 <code class="language-plaintext highlighter-rouge">ifconfig</code> 명령어가 sudo 권한을 요구한다.</p>

<h2 id="shell을-sudo권한으로-실행해도-password를-묻지-않도록-수정">shell을 sudo권한으로 실행해도 password를 묻지 않도록 수정</h2>
<p>위 shell 을 crontab 에 등록하기 이전에, sudo 권한으로 실행 시,
비밀번호 입력 없이도 실행이 가능하도록 변경해줘야 한다.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>visudo
</code></pre></div></div>
<p>위 명령어로 sudo 권한을 조정하는 화면을 불러오고,</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>criss <span class="nv">ALL</span><span class="o">=(</span>ALL<span class="o">)</span> NOPASSWD: /Users/criss/dev/work/mtu.sh
</code></pre></div></div>
<p>를 맨 마지막줄에 추가하여 해당 파일이 비밀번호 입력 없이
실행 가능하도록 한다.</p>

<h2 id="crontab에-등록">crontab에 등록</h2>
<p>이제 아래 명령어로 crontab 수정화면을 불러오고,</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>crontab <span class="nt">-u</span> criss <span class="nt">-e</span>
</code></pre></div></div>
<p>아래와 같이 crontab 을 등록하면 됩니다.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@reboot <span class="nb">sudo</span> /Users/pc00079589/dev/work/mtu.sh
<span class="k">*</span>/1 <span class="k">*</span> <span class="k">*</span> <span class="k">*</span> <span class="k">*</span> <span class="nb">sudo</span> /Users/pc00079589/dev/work/mtu.sh
</code></pre></div></div>

<h2 id="추가-로그-파일-주기적으로-삭제">(추가) 로그 파일 주기적으로 삭제</h2>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 0 <span class="k">*</span> <span class="k">*</span> <span class="k">*</span> find /Users/criss/dev/work/crontab-log <span class="nt">-name</span> <span class="s2">"*.crontab.log"</span> <span class="nt">-mtime</span> +7 <span class="nt">-exec</span> <span class="nb">rm</span> <span class="o">{}</span> <span class="se">\\</span><span class="p">;</span>
</code></pre></div></div>

<h2 id="출처-및-참고자료">출처 및 참고자료</h2>

<ul>
  <li>Chat GPT</li>
</ul>]]></content><author><name>Park Jong hun</name></author><category term="linux" /><category term="sudo" /><category term="crontab" /><summary type="html"><![CDATA[Linux 시스템에서 crontab으로 등록하면 반복적인(스케쥴링) 작업을 할 수 있습니다. 하지만 crontab이 실행되는 계정이 sudo(root) 권한이 없기 때문에 sudo 권한이 필요한 작업은 추가로 몇 가지 작업이 필요합니다.]]></summary></entry><entry><title type="html">[SpringBoot] JPA Specifications 를 이용한 Table Join</title><link href="https://linkeverything.github.io/study/springboot/join-table-specifications/" rel="alternate" type="text/html" title="[SpringBoot] JPA Specifications 를 이용한 Table Join" /><published>2024-01-03T00:00:00+00:00</published><updated>2024-01-03T00:00:00+00:00</updated><id>https://linkeverything.github.io/study/springboot/join-table-specifications</id><content type="html" xml:base="https://linkeverything.github.io/study/springboot/join-table-specifications/"><![CDATA[<p>Spring Data JPA 에는 Specifications 라는 것이 존재합니다. 이를 활요어하면 복잡하게 구성될 수 있는 <code class="language-plaintext highlighter-rouge">WHERE</code> 조건, <code class="language-plaintext highlighter-rouge">JOIN</code> TABLE 등의 행위를 편하게 진행할 수 있습니다.</p>

<p>여기서는 간단한 튜토리얼 형태로 어떻게 Join 할 수 있는 지 살펴봅니다.</p>

<h2 id="jpa-specification">JPA Specification</h2>

<p>Spring Data JPA 에서는 <mark style="background-color: #ffdce0">Specification</mark>이라는 interface 를 제공합니다.</p>

<p>이것을 활용하면, 재사용 가능한 component 들을 활용하여 dynamic query 를 생성할 수 있습니다.</p>

<p>여기서는 <em>Author</em>와 <em>Book</em> 이라는 class가 있다고 가졍합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Entity</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Author</span> <span class="o">{</span>

    <span class="nd">@Id</span>
    <span class="nd">@GeneratedValue</span><span class="o">(</span><span class="n">strategy</span> <span class="o">=</span> <span class="nc">GenerationType</span><span class="o">.</span><span class="na">IDENTITY</span><span class="o">)</span>
    <span class="kd">private</span> <span class="nc">Long</span> <span class="n">id</span><span class="o">;</span>

    <span class="kd">private</span> <span class="nc">String</span> <span class="n">firstName</span><span class="o">;</span>

    <span class="kd">private</span> <span class="nc">String</span> <span class="n">lastName</span><span class="o">;</span>

    <span class="nd">@OneToMany</span><span class="o">(</span><span class="n">cascade</span> <span class="o">=</span> <span class="nc">CascadeType</span><span class="o">.</span><span class="na">ALL</span><span class="o">)</span>
    <span class="kd">private</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Book</span><span class="o">&gt;</span> <span class="n">books</span><span class="o">;</span>

    <span class="c1">// getters and setters</span>
<span class="o">}</span>
</code></pre></div></div>

<p><em>Author</em> entity에 대해서 dynamic query 를 작성하기위해, Specification interface를 implements 합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">AuthorSpecifications</span> <span class="o">{</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">Specification</span><span class="o">&lt;</span><span class="nc">Author</span><span class="o">&gt;</span> <span class="nf">hasFirstNameLike</span><span class="o">(</span><span class="nc">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="o">(</span><span class="n">root</span><span class="o">,</span> <span class="n">query</span><span class="o">,</span> <span class="n">criteriaBuilder</span><span class="o">)</span> <span class="o">-&gt;</span>
          <span class="n">criteriaBuilder</span><span class="o">.</span><span class="na">like</span><span class="o">(</span><span class="n">root</span><span class="o">.&lt;</span><span class="nc">String</span><span class="o">&gt;</span><span class="n">get</span><span class="o">(</span><span class="s">"firstName"</span><span class="o">),</span> <span class="s">"%"</span> <span class="o">+</span> <span class="n">name</span> <span class="o">+</span> <span class="s">"%"</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">Specification</span><span class="o">&lt;</span><span class="nc">Author</span><span class="o">&gt;</span> <span class="nf">hasLastName</span><span class="o">(</span><span class="nc">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="o">(</span><span class="n">root</span><span class="o">,</span> <span class="n">query</span><span class="o">,</span> <span class="n">cb</span><span class="o">)</span> <span class="o">-&gt;</span>
          <span class="n">cb</span><span class="o">.</span><span class="na">equal</span><span class="o">(</span><span class="n">root</span><span class="o">.&lt;</span><span class="nc">String</span><span class="o">&gt;</span><span class="n">get</span><span class="o">(</span><span class="s">"lastName"</span><span class="o">),</span> <span class="n">name</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>마지막으로, <em>AuthorRepository</em>가 <mark style="background-color: #ffdce0">JpaSpecificationExecutor</mark> 를 상속받도록 구성합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Repository</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">AuthorsRepository</span> <span class="kd">extends</span> <span class="nc">JpaRepository</span><span class="o">&lt;</span><span class="nc">Author</span><span class="o">,</span> <span class="nc">Long</span><span class="o">&gt;,</span> <span class="nc">JpaSpecificationExecutor</span><span class="o">&lt;</span><span class="nc">Author</span><span class="o">&gt;</span> <span class="o">{</span>
<span class="o">}</span>
</code></pre></div></div>

<p>이러한 방식으로, 두 개의 specification을 chain해서 query 를 생성할 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">whenSearchingByLastNameAndFirstNameLike_thenOneAuthorIsReturned</span><span class="o">()</span> <span class="o">{</span>
    
    <span class="nc">Specification</span><span class="o">&lt;</span><span class="nc">Author</span><span class="o">&gt;</span> <span class="n">specification</span> <span class="o">=</span> <span class="n">hasLastName</span><span class="o">(</span><span class="s">"Martin"</span><span class="o">)</span>
      <span class="o">.</span><span class="na">and</span><span class="o">(</span><span class="n">hasFirstNameLike</span><span class="o">(</span><span class="s">"Robert"</span><span class="o">));</span>

    <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Author</span><span class="o">&gt;</span> <span class="n">authors</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">findAll</span><span class="o">(</span><span class="n">specification</span><span class="o">);</span>

    <span class="n">assertThat</span><span class="o">(</span><span class="n">authors</span><span class="o">).</span><span class="na">hasSize</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="jpa-specification을-이용한-table-join">JPA Specification을 이용한 Table Join</h2>

<p>앞서 살펴보았던 <em>Author</em> entity가 <em>Book</em> entity와 one-to-many (일대다) 관계를 가지고 있음을 알 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Entity</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Book</span> <span class="o">{</span>

    <span class="nd">@Id</span>
    <span class="nd">@GeneratedValue</span><span class="o">(</span><span class="n">strategy</span> <span class="o">=</span> <span class="nc">GenerationType</span><span class="o">.</span><span class="na">IDENTITY</span><span class="o">)</span>
    <span class="kd">private</span> <span class="nc">Long</span> <span class="n">id</span><span class="o">;</span>

    <span class="kd">private</span> <span class="nc">String</span> <span class="n">title</span><span class="o">;</span>

    <span class="c1">// getters and setters</span>
<span class="o">}</span>
</code></pre></div></div>

<p><mark style="background-color: #ffdce0">Criteria Query API</mark> 를 이용해서, Specification를 만들 때, 두 개의 Table을 Join 할 수 있습니다.
이러한 방식으로 <em>Book</em> entity의 특정 field를 우리의 쿼리에 포함할 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="nc">Specification</span><span class="o">&lt;</span><span class="nc">Author</span><span class="o">&gt;</span> <span class="nf">hasBookWithTitle</span><span class="o">(</span><span class="nc">String</span> <span class="n">bookTitle</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">return</span> <span class="o">(</span><span class="n">root</span><span class="o">,</span> <span class="n">query</span><span class="o">,</span> <span class="n">criteriaBuilder</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span>
        <span class="nc">Join</span><span class="o">&lt;</span><span class="nc">Book</span><span class="o">,</span> <span class="nc">Author</span><span class="o">&gt;</span> <span class="n">authorsBook</span> <span class="o">=</span> <span class="n">root</span><span class="o">.</span><span class="na">join</span><span class="o">(</span><span class="s">"books"</span><span class="o">);</span>
        <span class="k">return</span> <span class="n">criteriaBuilder</span><span class="o">.</span><span class="na">equal</span><span class="o">(</span><span class="n">authorsBook</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"title"</span><span class="o">),</span> <span class="n">bookTitle</span><span class="o">);</span>
    <span class="o">};</span>
<span class="o">}</span>
</code></pre></div></div>

<p>이제, 앞서 만들어두었던 Specification과 병합할 수 있습니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">whenSearchingByBookTitleAndAuthorName_thenOneAuthorIsReturned</span><span class="o">()</span> <span class="o">{</span>

    <span class="nc">Specification</span><span class="o">&lt;</span><span class="nc">Author</span><span class="o">&gt;</span> <span class="n">specification</span> <span class="o">=</span> <span class="n">hasLastName</span><span class="o">(</span><span class="s">"Martin"</span><span class="o">)</span>
      <span class="o">.</span><span class="na">and</span><span class="o">(</span><span class="n">hasBookWithTitle</span><span class="o">(</span><span class="s">"Clean Code"</span><span class="o">));</span>

    <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Author</span><span class="o">&gt;</span> <span class="n">authors</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">findAll</span><span class="o">(</span><span class="n">specification</span><span class="o">);</span>

    <span class="n">assertThat</span><span class="o">(</span><span class="n">authors</span><span class="o">).</span><span class="na">hasSize</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>생성된 쿼리를 확인하면 join이 추가되었음을 알 수 있습니다.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">select</span> 
  <span class="n">author0_</span><span class="p">.</span><span class="n">id</span> <span class="k">as</span> <span class="n">id1_1_</span><span class="p">,</span> 
  <span class="n">author0_</span><span class="p">.</span><span class="n">first_name</span> <span class="k">as</span> <span class="n">first_na2_1_</span><span class="p">,</span> 
  <span class="n">author0_</span><span class="p">.</span><span class="n">last_name</span> <span class="k">as</span> <span class="n">last_nam3_1_</span> 
<span class="k">from</span> 
  <span class="n">author</span> <span class="n">author0_</span> 
  <span class="k">inner</span> <span class="k">join</span> <span class="n">author_books</span> <span class="n">books1_</span> <span class="k">on</span> <span class="n">author0_</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">books1_</span><span class="p">.</span><span class="n">author_id</span> 
  <span class="k">inner</span> <span class="k">join</span> <span class="n">book</span> <span class="n">book2_</span> <span class="k">on</span> <span class="n">books1_</span><span class="p">.</span><span class="n">books_id</span> <span class="o">=</span> <span class="n">book2_</span><span class="p">.</span><span class="n">id</span> 
<span class="k">where</span> 
  <span class="n">author0_</span><span class="p">.</span><span class="n">last_name</span> <span class="o">=</span> <span class="o">?</span> 
  <span class="k">and</span> <span class="n">book2_</span><span class="p">.</span><span class="n">title</span> <span class="o">=</span> <span class="o">?</span>
</code></pre></div></div>

<h2 id="참고자료-및-출처">참고자료 및 출처</h2>

<ul>
  <li><a href="https://www.baeldung.com/spring-jpa-joining-tables">https://www.baeldung.com/spring-jpa-joining-tables</a></li>
</ul>]]></content><author><name>Park Jong hun</name></author><category term="study" /><category term="springboot" /><category term="join" /><category term="table" /><category term="jpa" /><category term="specification" /><summary type="html"><![CDATA[Spring Data JPA 에는 Specifications 라는 것이 존재합니다. 이를 활요어하면 복잡하게 구성될 수 있는 WHERE 조건, JOIN TABLE 등의 행위를 편하게 진행할 수 있습니다.]]></summary></entry><entry><title type="html">[SpringBoot] 이미지나 파일을 return 하기 How to return image or file</title><link href="https://linkeverything.github.io/study/springboot/return-image-or-file/" rel="alternate" type="text/html" title="[SpringBoot] 이미지나 파일을 return 하기 How to return image or file" /><published>2023-12-29T00:00:00+00:00</published><updated>2023-12-29T00:00:00+00:00</updated><id>https://linkeverything.github.io/study/springboot/return-image-or-file</id><content type="html" xml:base="https://linkeverything.github.io/study/springboot/return-image-or-file/"><![CDATA[<p>백엔드 개발을 진행하다 보면, 이미지나 파일 같은 raw data 를 직접 return 해야 하는 경우가 발생합니다. 물론 download 기능을 구현할 때에도 마찬가지겠지만, 그 이외에도 이러한 기능이 필요한 경우가 간혹 있습니다.</p>

<p class="notice--info">이 post에는 sample 이 존재하지 않습니다. 필요 시 나중에 추가하도록 하겠습니다.</p>

<h2 id="dependency-추가">dependency 추가</h2>

<p>API 를 작성할 것이므로 아래 dependency 를 추가합니다. 아마 대부분의 API서버에 이미 추가되어 있을 것입니다.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency&gt;</span>
    <span class="nt">&lt;groupId&gt;</span>org.springframework.boot<span class="nt">&lt;/groupId&gt;</span>
    <span class="nt">&lt;artifactId&gt;</span>spring-boot-starter-web<span class="nt">&lt;/artifactId&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
</code></pre></div></div>

<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">implementation</span><span class="o">(</span><span class="s2">"org.springframework.boot:spring-boot-starter-web"</span><span class="o">)</span>
</code></pre></div></div>

<h2 id="responsebody-사용하기"><code class="language-plaintext highlighter-rouge">@ResponseBody</code> 사용하기</h2>

<p>가장 간단히 사용할 수 있는 것은 <code class="language-plaintext highlighter-rouge">@ResponseBody</code> annotation 을 추가하는 것입니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@GetMapping</span><span class="o">(</span><span class="s">"/get-text"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nd">@ResponseBody</span> <span class="nc">String</span> <span class="nf">getText</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="s">"Hello world"</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>이렇게 하면, view 를 찾는 것 대신에 Hello World 라는 문자열 자체를 return 하게 됩니다.</p>

<p class="notice--info"><code class="language-plaintext highlighter-rouge">@Controller</code> 대신 <code class="language-plaintext highlighter-rouge">@RestController</code>를 사용하는 Controller 를 구현하게 되면, 위 <code class="language-plaintext highlighter-rouge">@ResponseBody</code> 가 없어도 view를 찾지 않고 그대로 문자열을 return 합니다.</p>

<h2 id="이미지-return-을-위한-produces-사용하기">이미지 return 을 위한 <code class="language-plaintext highlighter-rouge">produces</code> 사용하기</h2>

<p>이미지나 파일을 return 하는 데 있어서 byte array 를 return 하는 것이 쓰입니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@GetMapping</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"/image"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nd">@ResponseBody</span> <span class="kt">byte</span><span class="o">[]</span> <span class="nf">getImage</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="nc">InputStream</span> <span class="n">in</span> <span class="o">=</span> <span class="n">getClass</span><span class="o">()</span>
      <span class="o">.</span><span class="na">getResourceAsStream</span><span class="o">(</span><span class="s">"/com/baeldung/produceimage/image.jpg"</span><span class="o">);</span>
    <span class="k">return</span> <span class="nc">IOUtils</span><span class="o">.</span><span class="na">toByteArray</span><span class="o">(</span><span class="n">in</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>이렇게 하면 이미지에 대한 정보가 아무것도 없기 때문에 client 에서는 이미지임을 알 수 없습니다. 이러한 문제를 방지하기 위해서, <code class="language-plaintext highlighter-rouge">@GetMapping</code> annotation에 <mark style="background-color: #ffdce0">produces</mark> 값을 넣어줍니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@GetMapping</span><span class="o">(</span>
  <span class="n">value</span> <span class="o">=</span> <span class="s">"/get-image-with-media-type"</span><span class="o">,</span>
  <span class="n">produces</span> <span class="o">=</span> <span class="nc">MediaType</span><span class="o">.</span><span class="na">IMAGE_JPEG_VALUE</span>
<span class="o">)</span>
<span class="kd">public</span> <span class="nd">@ResponseBody</span> <span class="kt">byte</span><span class="o">[]</span> <span class="nf">getImageWithMediaType</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="nc">InputStream</span> <span class="n">in</span> <span class="o">=</span> <span class="n">getClass</span><span class="o">()</span>
      <span class="o">.</span><span class="na">getResourceAsStream</span><span class="o">(</span><span class="s">"/com/baeldung/produceimage/image.jpg"</span><span class="o">);</span>
    <span class="k">return</span> <span class="nc">IOUtils</span><span class="o">.</span><span class="na">toByteArray</span><span class="o">(</span><span class="n">in</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>이렇게 <mark style="background-color: #ffdce0">produces</mark> 값에 <code class="language-plaintext highlighter-rouge">MediaType.IMAGE_JPEG_VALUE</code>를 지정하면 return 되는 값을 이미지로 처리해야 함을 알려줄 수 있습니다.</p>

<p>이제, 브라우저는 이 return 값이 이미지임을 알고 이미지로 올바르게 처리해 줍니다.</p>

<h2 id="raw-file-을-return-하기-위한-produces">Raw file 을 return 하기 위한 <code class="language-plaintext highlighter-rouge">produces</code></h2>

<p><code class="language-plaintext highlighter-rouge">produces</code> 는 여러가지 값을 가질 수 있습니다. 전체 목록은 <a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/MediaType.html">여기</a>에 있습니다. 
return 하고자 하는 타입에 맞게 적절한 값을 넣어주면 됩니다.</p>

<p>그래서, 만약 우리가 파일 자체를 return 하고 싶다면 <code class="language-plaintext highlighter-rouge">APPLICATION_OCTET_STREAM_VALUE</code> 를 사용합면 됩니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@GetMapping</span><span class="o">(</span>
  <span class="n">value</span> <span class="o">=</span> <span class="s">"/get-file"</span><span class="o">,</span>
  <span class="n">produces</span> <span class="o">=</span> <span class="nc">MediaType</span><span class="o">.</span><span class="na">APPLICATION_OCTET_STREAM_VALUE</span>
<span class="o">)</span>
<span class="kd">public</span> <span class="nd">@ResponseBody</span> <span class="kt">byte</span><span class="o">[]</span> <span class="nf">getFile</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="nc">InputStream</span> <span class="n">in</span> <span class="o">=</span> <span class="n">getClass</span><span class="o">()</span>
      <span class="o">.</span><span class="na">getResourceAsStream</span><span class="o">(</span><span class="s">"/com/baeldung/produceimage/data.txt"</span><span class="o">);</span>
    <span class="k">return</span> <span class="nc">IOUtils</span><span class="o">.</span><span class="na">toByteArray</span><span class="o">(</span><span class="n">in</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>그렇지 않다면, Byte array 대신에 <code class="language-plaintext highlighter-rouge">ByteArrayResource</code> 를 사용해도 됩니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@GetMapping</span><span class="o">(</span>
  <span class="n">value</span> <span class="o">=</span> <span class="s">"/get-file-via-byte-array-resource"</span><span class="o">,</span> 
  <span class="n">produces</span> <span class="o">=</span> <span class="nc">MediaType</span><span class="o">.</span><span class="na">APPLICATION_OCTET_STREAM_VALUE</span>
<span class="o">)</span>
<span class="kd">public</span> <span class="nd">@ResponseBody</span> <span class="nc">Resource</span> <span class="nf">getFileViaByteArrayResource</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">URISyntaxException</span> <span class="o">{</span>
    <span class="nc">Path</span> <span class="n">path</span> <span class="o">=</span> <span class="nc">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">getClass</span><span class="o">().</span><span class="na">getResource</span><span class="o">(</span><span class="s">"/com/baeldung/produceimage/data.txt"</span><span class="o">).</span><span class="na">toURI</span><span class="o">());</span>
    <span class="nc">ByteArrayResource</span> <span class="n">resource</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ByteArrayResource</span><span class="o">(</span><span class="nc">Files</span><span class="o">.</span><span class="na">readAllBytes</span><span class="o">(</span><span class="n">path</span><span class="o">));</span>
    <span class="k">return</span> <span class="n">resource</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Resource</code> 클래스는 Spring이 제공하는 높은 수준의 abstract class입니다. 이 클래스는 다양한 소스로부터 리소스를 다루는 데 도움을 줍니다. 
InputStream 을 통한 Resource의 사용은 다음과 같은 이점이 있습니다.</p>

<ul>
  <li>리소스에 대한 추가적인 메타데이터를 제공한다.</li>
  <li>Spring 의 다른 abstract 들과 유연하게 연결지을 수 있다.</li>
  <li>mock 하기 쉽다.</li>
</ul>

<h2 id="참고자료-및-출처">참고자료 및 출처</h2>

<ul>
  <li><a href="https://www.baeldung.com/spring-controller-return-image-file">https://www.baeldung.com/spring-controller-return-image-file</a></li>
</ul>]]></content><author><name>Park Jong hun</name></author><category term="study" /><category term="springboot" /><category term="response" /><category term="image" /><category term="file" /><summary type="html"><![CDATA[백엔드 개발을 진행하다 보면, 이미지나 파일 같은 raw data 를 직접 return 해야 하는 경우가 발생합니다. 물론 download 기능을 구현할 때에도 마찬가지겠지만, 그 이외에도 이러한 기능이 필요한 경우가 간혹 있습니다.]]></summary></entry><entry><title type="html">[Mac] Dock에 빈 공간 추가하기 How to add a spacer to dock</title><link href="https://linkeverything.github.io/mac/mac-dock-spacer/" rel="alternate" type="text/html" title="[Mac] Dock에 빈 공간 추가하기 How to add a spacer to dock" /><published>2023-12-27T00:00:00+00:00</published><updated>2023-12-27T00:00:00+00:00</updated><id>https://linkeverything.github.io/mac/mac-dock-spacer</id><content type="html" xml:base="https://linkeverything.github.io/mac/mac-dock-spacer/"><![CDATA[<p>맥의 dock 은 프로그램 실행을 빠르게 할 수 있게 도와주는 유용한 환경입니다. 최근 목록을 보여주거나, 특정 폴더를 넣어서 그 내용을 빠르게 확인할 수 있습니다.</p>

<h2 id="많아질수록-가독성이-떨어진다">많아질수록 가독성이 떨어진다?</h2>

<p>하지만 아이콘이 많아져서 dock이 길어지게 되면, 아무리 아이콘의 가독성이 좋다고 하더라도 쉽게 눈에 들어오지 않는 것이 문제입니다.</p>

<p>빠르게 실행하려고 dock 에 넣어놓은 것인데, 이 dock을 한참이나 찾고 있어야되는 상황이 벌어진다는 것이죠.</p>

<h2 id="dock의-구분자">dock의 구분자?</h2>

<p>dock에도 아이콘 사이를 구분하는 일종의 구분자가 있습니다. dock 은 크게</p>
<ul>
  <li>(고정해 둔) 프로그램 영역</li>
  <li>최근 실행 항목 영역 (이건 꺼둔 사람은 보이지 않음)</li>
  <li>폴더와 휴지통 영역</li>
</ul>

<p>이 세 개의 영역을 spacer혹은 파이프(<code class="language-plaintext highlighter-rouge">|</code>) 라고 부를 법한 것이 구분하고 있습니다.</p>

<p>따라서 최근 실행 항목 중, 필요한 것은 고정해 둘 수 있고 (좌측으로 이동), 폴더의 내용을 탐색할 수 있게 되는 것입니다.</p>

<h2 id="구분자를-추가할-수-없다">구분자를 추가할 수 없다?</h2>

<p>문제는 여기서 발생합니다.</p>

<p>위에서 말한 파이프(<code class="language-plaintext highlighter-rouge">|</code>) 모양은 사용자가 임의로 추가할 수 없고, 위에서 말한 목적의 구분자로만 사용됩니다.</p>

<p>따라서 고정해둔 앱들 사이에 저 파이프를 넣을 수는 없습니다. 비슷하게 생긴 이미지 파일로 dummy app을 추가하는 등의 방법도 보았지만, 
필요 이상으로 번거로운 것이 문제입니다.</p>

<h2 id="spacer-의-추가">spacer 의 추가</h2>

<p>이런 문제를 해결하기 위한 방법으로, 정식 방법처럼 종작하는 것이 바로 spacer를 추가하는 것입니다.</p>

<p><img src="/assets/images/posts/etc/apple/2023-12-27-mac-dock-spacer/20211112-dock.webp" alt="" /></p>

<p>위 그림처럼 넓은 것과 좁근 것의 두 가지 종류를 추가할 수 있습니다.</p>

<h4 id="regular-spacer-script">Regular spacer script:</h4>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>defaults write com.apple.dock persistent-apps <span class="nt">-array-add</span> <span class="s1">'{"tile-type"="spacer-tile";}'</span><span class="p">;</span> killall Dock
</code></pre></div></div>

<h4 id="small-spacer-script">Small spacer script:</h4>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>defaults write com.apple.dock persistent-apps <span class="nt">-array-add</span> <span class="s1">'{"tile-type"="small-spacer-tile";}'</span><span class="p">;</span> killall Dock
</code></pre></div></div>

<h2 id="옮기기삭제는-다른-아이콘과-동일하게">옮기기/삭제는 다른 아이콘과 동일하게</h2>

<p>이렇게 추가한 spacer 를 옮기거나 추가/삭제 하는 것은 고정해 둔 다른 앱 아이콘과 동일하게 사용하면 된다.</p>

<h2 id="reference">Reference</h2>

<ul>
  <li><a href="https://chrispennington.blog/blog/add-spacer-in-macos-dock/">https://chrispennington.blog/blog/add-spacer-in-macos-dock/</a></li>
</ul>]]></content><author><name>Park Jong hun</name></author><category term="mac" /><category term="dock" /><category term="spacer" /><summary type="html"><![CDATA[맥의 dock 은 프로그램 실행을 빠르게 할 수 있게 도와주는 유용한 환경입니다. 최근 목록을 보여주거나, 특정 폴더를 넣어서 그 내용을 빠르게 확인할 수 있습니다.]]></summary></entry></feed>