<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>B.log</title>
        <link>https://velog.io/</link>
        <description>Active 🙌 Curious 🤔 Energetic 💪</description>
        <lastBuildDate>Wed, 09 Oct 2024 04:03:06 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>B.log</title>
            <url>https://velog.velcdn.com/images/cher_blair/profile/ee69a8e8-ef15-4c5e-b0cb-2d032bc5e6dd/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. B.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/cher_blair" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Java] Lombok의 @Builder를 코드에 적용해보았습니다. 🐯]]></title>
            <link>https://velog.io/@cher_blair/Java-Lombok%EC%9D%98-Builder%EB%A5%BC-%EC%BD%94%EB%93%9C%EC%97%90-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%95%98%EC%8A%B5%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@cher_blair/Java-Lombok%EC%9D%98-Builder%EB%A5%BC-%EC%BD%94%EB%93%9C%EC%97%90-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%95%98%EC%8A%B5%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Wed, 09 Oct 2024 04:03:06 GMT</pubDate>
            <description><![CDATA[<p> 데이터를 저장하기 위해 VO를 받아 Bean을 생성할 때!
 하나씩 하나씩 set 하기 너무너무 귀찮지 않나요?!
 좋은 방법이 있어 코드에 적용해보았습니다.
 항상 적용 가능한 코드라기 보다는 매개 변수가 많을 때 더 유용할 것 같음.
 시작합니다!!</p>
<hr>
<p>Lombok의 <code>@Builder</code>는 객체를 <strong>빌더 패턴(Builder Pattern)</strong>으로 생성할 수 있도록 도와주는 애노테이션이다. 빌더 패턴은 특히 매개변수가 많거나 선택적으로 제공되는 경우에 유용한 객체 생성 방식으로, 코드의 가독성을 높이고 유지보수를 쉽게 만들어준다. Lombok은 이 패턴을 매우 간단하게 구현할 수 있게 해준다.</p>
<h3 id="주요-특징">주요 특징</h3>
<ol>
<li><strong>유연한 객체 생성</strong>: 모든 필드를 한 번에 설정할 필요 없이, 필요한 필드만 선택적으로 설정할 수 있다.</li>
<li><strong>불변성(Immutable)</strong>: 빌더 패턴을 사용하면 객체를 생성한 후 수정할 수 없도록 만들기 쉽다.</li>
<li><strong>가독성 향상</strong>: 매개변수가 많을 때, 코드의 가독성을 높여주고 매개변수의 순서를 기억할 필요가 없어진다.</li>
</ol>
<h3 id="기본-사용법">기본 사용법</h3>
<p>Lombok의 <code>@Builder</code> 애노테이션을 클래스 또는 생성자에 붙여 사용한다.</p>
<pre><code class="language-java">import lombok.Builder;

@Builder
public class User {
    private String name;
    private int age;
    private String email;
}
</code></pre>
<h3 id="빌더-사용-예시">빌더 사용 예시</h3>
<p>위의 클래스에 대해 빌더 패턴을 사용하여 객체를 생성할 수 있다.</p>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        User user = User.builder()
                        .name(&quot;John Doe&quot;)
                        .age(30)
                        .email(&quot;john.doe@example.com&quot;)
                        .build();

        System.out.println(user);
    }
}
</code></pre>
<h3 id="builder의-장점">@Builder의 장점</h3>
<ul>
<li><strong>매개변수의 순서가 중요하지 않음</strong>: 빌더를 사용하면 생성자에 넣는 매개변수의 순서를 기억할 필요 없이, 각 필드를 명시적으로 설정할 수 있다.</li>
<li><strong>유연한 객체 생성</strong>: 모든 필드를 다 채울 필요 없이, 필요한 필드만 선택적으로 설정할 수 있다.</li>
<li><strong>읽기 쉬운 코드</strong>: 많은 매개변수를 가진 객체를 생성할 때 가독성이 높아진다.</li>
</ul>
<h3 id="builder와-allargsconstructor-noargsconstructor와의-조합">@Builder와 @AllArgsConstructor, @NoArgsConstructor와의 조합</h3>
<p>Lombok의 <code>@AllArgsConstructor</code>와 <code>@NoArgsConstructor</code>와 함께 사용하면 더 유연하게 객체 생성 방식을 조합할 수 있다.</p>
<pre><code class="language-java">import lombok.Builder;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Product {
    private String name;
    private double price;
    private int stock;
}
</code></pre>
<p>이와 같이 Lombok의 <code>@Builder</code>는 복잡한 객체 생성 과정을 단순화해주고, 객체 생성 시 유연성을 높이는 데 매우 유용하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Spring BeanUtils .copyProperties을 사용해서 VO를 Bean으로, Bean을 VO로 간편복사 해보았습니다. 🧐]]></title>
            <link>https://velog.io/@cher_blair/Spring-Spring-BeanUtils-.copyProperties%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-VO%EB%A5%BC-Bean%EC%9C%BC%EB%A1%9C-Bean%EC%9D%84-VO%EB%A1%9C-%EA%B0%84%ED%8E%B8%EB%B3%B5%EC%82%AC-%ED%95%B4%EB%B3%B4%EC%95%98%EC%8A%B5%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@cher_blair/Spring-Spring-BeanUtils-.copyProperties%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-VO%EB%A5%BC-Bean%EC%9C%BC%EB%A1%9C-Bean%EC%9D%84-VO%EB%A1%9C-%EA%B0%84%ED%8E%B8%EB%B3%B5%EC%82%AC-%ED%95%B4%EB%B3%B4%EC%95%98%EC%8A%B5%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Wed, 09 Oct 2024 03:44:56 GMT</pubDate>
            <description><![CDATA[<p>객체를 값마다 돌면서 set하는 게 아니라, 간편하게 처리할 수 있는 방법을 알아내서 코드에 바로 적용해보았다. 
코드가 한결 깔끔하고 보기 좋아졌음 🙌🙌
.copyProperties에 대한 설명 시작합니다!</p>
<hr>
<p><code>copyProperties()</code>는 Spring Framework의 <code>BeanUtils</code> 클래스에 있는 메서드로, <strong>하나의 객체에서 다른 객체로 필드 값을 복사</strong>할 때 사용된다. 주로 두 객체 간에 같은 필드 이름과 타입을 가진 경우에 유용하게 사용할 수 있다. 이 메서드를 사용하면 객체의 특정 필드들을 일일이 복사하지 않고 한 번에 처리할 수 있어 코드를 간결하게 유지할 수 있다.</p>
<h3 id="기본-사용법">기본 사용법</h3>
<pre><code class="language-java">import org.springframework.beans.BeanUtils;

public class Main {
    public static void main(String[] args) {
        SourceObject source = new SourceObject();
        source.setName(&quot;John&quot;);
        source.setAge(30);

        TargetObject target = new TargetObject();
        BeanUtils.copyProperties(source, target);

        System.out.println(target.getName());  // &quot;John&quot;
        System.out.println(target.getAge());   // 30
    }
}
</code></pre>
<p>위 코드에서 <code>BeanUtils.copyProperties(source, target)</code>를 사용하면 <code>source</code> 객체의 <code>name</code>과 <code>age</code> 값이 <code>target</code> 객체로 복사된다.</p>
<h3 id="주요-특징">주요 특징</h3>
<ol>
<li><strong>같은 이름과 타입을 가진 필드만 복사</strong>: 소스와 타깃 객체의 필드 이름과 타입이 일치해야 복사된다.</li>
<li><strong>null 값도 복사</strong>: 소스 객체의 필드 값이 <code>null</code>일 경우, 타깃 객체의 필드 값도 <code>null</code>로 덮어씌워진다.</li>
<li><strong>부분 복사 가능</strong>: 소스 객체에 존재하지만 타깃 객체에 없는 필드는 무시된다. 즉, 타깃 객체에 정의되지 않은 필드는 복사되지 않는다.</li>
</ol>
<h3 id="특정-필드를-제외하고-복사">특정 필드를 제외하고 복사</h3>
<p><code>copyProperties()</code>는 특정 필드를 제외하고 복사할 수도 있다. 예를 들어, 특정 필드는 복사하고 싶지 않을 때 <code>ignoreProperties</code> 매개변수를 사용할 수 있다.</p>
<pre><code class="language-java">BeanUtils.copyProperties(source, target, &quot;age&quot;);
</code></pre>
<p>위 코드는 <code>age</code> 필드를 제외하고 나머지 필드를 복사한다.</p>
<h3 id="사용-시-주의사항">사용 시 주의사항</h3>
<ol>
<li><strong>필드 타입 일치</strong>: 필드의 이름과 타입이 일치하지 않으면 해당 필드는 복사되지 않는다.</li>
<li><strong>getter, setter 필요</strong>: <code>copyProperties()</code>는 필드에 직접 접근하는 것이 아니라 <strong>getter와 setter 메서드</strong>를 통해 값을 복사한다. 따라서 필드에 대한 getter/setter가 정의되어 있어야 한다.</li>
<li><strong>복잡한 객체</strong>: 깊은 복사(deep copy)가 아니라 <strong>얕은 복사(shallow copy)</strong>를 수행한다. 즉, 객체 안에 참조된 객체의 필드는 복사되지 않고 동일한 객체 참조를 가리키게 된다.</li>
</ol>
<h3 id="실사용-예시">실사용 예시</h3>
<p><code>copyProperties()</code>는 주로 다음과 같은 상황에서 유용하다.</p>
<ul>
<li><strong>DTO(Data Transfer Object)와 Entity 간 데이터 복사</strong>: 서비스 레이어에서 DTO와 Entity 간 데이터를 주고받을 때, 두 객체 간에 필드가 유사하다면 일일이 값을 할당하지 않고 <code>copyProperties()</code>로 쉽게 처리할 수 있다.</li>
</ul>
<pre><code class="language-java">UserDTO userDTO = new UserDTO();
userDTO.setName(&quot;John&quot;);
userDTO.setAge(30);

UserEntity userEntity = new UserEntity();
BeanUtils.copyProperties(userDTO, userEntity);
</code></pre>
<ul>
<li><strong>기존 객체의 값 업데이트</strong>: 기존에 생성된 객체의 일부 필드 값을 다른 객체로부터 업데이트해야 할 때 유용하다.</li>
</ul>
<p>Spring의 <code>copyProperties()</code>를 사용하면 객체 간의 데이터 전송이 훨씬 간결해지고, 반복되는 코드를 줄일 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Stream.of().anyMatch(Objects::isNull) 을 사용해서 null 체크 코드를 만들어보았습니다. 🐯]]></title>
            <link>https://velog.io/@cher_blair/Java-Stream.of.anyMatchObjectsisNull-%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-null-%EC%B2%B4%ED%81%AC-%EC%BD%94%EB%93%9C%EB%A5%BC-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%95%98%EC%8A%B5%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@cher_blair/Java-Stream.of.anyMatchObjectsisNull-%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-null-%EC%B2%B4%ED%81%AC-%EC%BD%94%EB%93%9C%EB%A5%BC-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%95%98%EC%8A%B5%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Wed, 09 Oct 2024 03:33:34 GMT</pubDate>
            <description><![CDATA[<p>Java Stream을 사용해서 좀 더 깔끔하게 null 체크하기! 
시작합니다! 🐯</p>
<hr>
<p><code>Stream.of().anyMatch(Objects::isNull)</code>는 Java의 Stream API와 <code>Objects</code> 클래스의 유틸리티 메서드를 조합해 <strong>스트림 내에 <code>null</code> 값이 있는지 확인</strong>할 때 사용하는 코드이다. 이 구문을 자세히 분석하면 아래와 같은 요소들로 구성된다.</p>
<h3 id="1-streamof">1. <code>Stream.of()</code></h3>
<p><code>Stream.of()</code>는 Java 8에서 추가된 <strong>Stream API</strong>의 메서드로, <strong>지정된 요소들로 스트림(Stream)을 생성</strong>한다. 스트림은 데이터를 처리하는데 있어 선언적이고 함수형 스타일을 사용할 수 있도록 도와주는 클래스이다. <code>Stream.of()</code>는 가변인자(varargs)를 받기 때문에 여러 개의 요소를 전달할 수 있다.</p>
<p>예시:</p>
<pre><code class="language-java">Stream&lt;String&gt; stream = Stream.of(&quot;A&quot;, &quot;B&quot;, &quot;C&quot;);
</code></pre>
<p>여기서 <code>&quot;A&quot;</code>, <code>&quot;B&quot;</code>, <code>&quot;C&quot;</code>는 스트림에 포함된 요소들이다.</p>
<h3 id="2-objectsisnull">2. <code>Objects::isNull</code></h3>
<p><code>Objects::isNull</code>은 Java의 <code>Objects</code> 유틸리티 클래스에 있는 정적 메서드로, <strong>입력된 객체가 <code>null</code>인지 확인</strong>하는 메서드이다. 이는 <code>object == null</code>과 동일한 역할을 한다.</p>
<pre><code class="language-java">boolean result = Objects.isNull(someObject);  // true if someObject is null
</code></pre>
<h3 id="3-anymatch">3. <code>anyMatch()</code></h3>
<p><code>anyMatch()</code>는 <strong>Stream API</strong>에서 제공하는 메서드로, 스트림의 요소 중 <strong>하나라도 주어진 조건(predicate)을 만족하면 <code>true</code>를 반환</strong>한다. 모든 요소를 확인하지 않고, 조건에 맞는 첫 번째 요소를 찾으면 탐색을 중단하고 <code>true</code>를 반환한다.</p>
<p>예시:</p>
<pre><code class="language-java">boolean hasEven = Stream.of(1, 2, 3).anyMatch(n -&gt; n % 2 == 0);  // true
</code></pre>
<p>위 코드에서 스트림에 있는 숫자 중 하나라도 짝수면 <code>true</code>를 반환한다.</p>
<h3 id="4-결합-streamofanymatchobjectsisnull">4. 결합: <code>Stream.of().anyMatch(Objects::isNull)</code></h3>
<p>이제 이 코드의 전체 의미를 보면, <code>Stream.of()</code>로 생성된 스트림 내에 <strong><code>null</code>이 있는지 확인</strong>하는 과정이다. <code>Objects::isNull</code>은 요소가 <code>null</code>인지 판단하는 기준이 되고, <code>anyMatch()</code>는 해당 조건을 만족하는 요소가 하나라도 있으면 <code>true</code>를 반환한다.</p>
<h3 id="예시">예시:</h3>
<pre><code class="language-java">boolean hasNull = Stream.of(&quot;A&quot;, null, &quot;C&quot;).anyMatch(Objects::isNull);
System.out.println(hasNull);  // true
</code></pre>
<p>이 코드는 <code>&quot;A&quot;</code>, <code>null</code>, <code>&quot;C&quot;</code>로 구성된 스트림에서 <strong>하나라도 <code>null</code>인 값이 있는지</strong>를 확인하는 것이다. <code>null</code> 값이 존재하기 때문에 <code>hasNull</code>은 <code>true</code>가 된다.</p>
<h3 id="동작-과정-요약">동작 과정 요약:</h3>
<ol>
<li><code>Stream.of(&quot;A&quot;, null, &quot;C&quot;)</code>: 스트림에 <code>&quot;A&quot;</code>, <code>null</code>, <code>&quot;C&quot;</code> 세 가지 값을 넣는다.</li>
<li><code>anyMatch(Objects::isNull)</code>: 각 요소가 <code>null</code>인지 검사하는데, <code>null</code>이 발견되면 <code>true</code>를 반환하고 스트림 탐색을 종료한다.</li>
</ol>
<h3 id="5-실제-사용-예시">5. 실제 사용 예시</h3>
<p>이 코드는 다양한 곳에서 유용하게 사용할 수 있다. 예를 들어, 메서드에 전달된 여러 매개변수나 리스트에 <code>null</code> 값이 있는지 빠르게 확인할 수 있다.</p>
<h3 id="예시-1-메서드-매개변수-확인">예시 1: 메서드 매개변수 확인</h3>
<pre><code class="language-java">public void processData(String a, String b, String c) {
    if (Stream.of(a, b, c).anyMatch(Objects::isNull)) {
        throw new IllegalArgumentException(&quot;One or more arguments are null&quot;);
    }
    // 처리 로직
}
</code></pre>
<p>여기서는 메서드로 전달된 <code>a</code>, <code>b</code>, <code>c</code> 중 하나라도 <code>null</code>이면 예외를 던진다.</p>
<h3 id="예시-2-리스트에-null-값이-있는지-확인">예시 2: 리스트에 null 값이 있는지 확인</h3>
<pre><code class="language-java">List&lt;String&gt; list = Arrays.asList(&quot;apple&quot;, null, &quot;banana&quot;);
boolean containsNull = list.stream().anyMatch(Objects::isNull);

System.out.println(containsNull);  // true
</code></pre>
<p>리스트에서 <code>null</code> 값을 찾을 때도 <code>anyMatch</code>와 <code>Objects::isNull</code>을 사용할 수 있다.</p>
<h3 id="결론">결론</h3>
<p><code>Stream.of().anyMatch(Objects::isNull)</code>는 스트림에서 <code>null</code> 값을 빠르게 찾는 데 유용한 구문이다. <code>Stream.of()</code>로 스트림을 만들고, <code>anyMatch()</code>를 사용해 <code>Objects::isNull</code>을 기준으로 <code>null</code> 여부를 검사한다. 이 방식은 코드의 간결성과 가독성을 높이며, 특히 데이터 유효성 검사나 필드 검증에서 자주 사용될 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[yml로 다국어 처리하기. DB방식과 Spring properties로 관리하는 방법과의 차이점은?!]]></title>
            <link>https://velog.io/@cher_blair/yml%EB%A1%9C-%EB%8B%A4%EA%B5%AD%EC%96%B4-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0.-DB%EB%B0%A9%EC%8B%9D%EA%B3%BC-Spring-properties%EB%A1%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EA%B3%BC%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80</link>
            <guid>https://velog.io/@cher_blair/yml%EB%A1%9C-%EB%8B%A4%EA%B5%AD%EC%96%B4-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0.-DB%EB%B0%A9%EC%8B%9D%EA%B3%BC-Spring-properties%EB%A1%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EA%B3%BC%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80</guid>
            <pubDate>Wed, 09 Oct 2024 03:27:15 GMT</pubDate>
            <description><![CDATA[<p>다국어를 일부 yml 파일로 처리하자는 의견이 나와서 작업을 진행했다.
기존에 다국어를 이미 DB 또는 Spring properties로 관리하고 있었기 때문에 왜 일부는 yml로 빼는지 의문이 생겨 찾아보고 정리한 글!
시작합니다.</p>
<hr>
<h2 id="yml-파일에-다국어-값을-저장하는-이유">YML 파일에 다국어 값을 저장하는 이유?!</h2>
<h3 id="1-구조화된-설정-관리"><strong>1. 구조화된 설정 관리</strong>:</h3>
<ul>
<li><strong>YML 파일</strong>은 기본적으로 <strong>설정 파일</strong>로 사용되기 때문에, 다국어와 관련된 설정 값을 하나의 파일에서 구조화된 형태로 관리할 수 있다.</li>
<li>다국어뿐만 아니라 <strong>서버 설정</strong>, <strong>외부 API 설정</strong> 등을 함께 관리할 수 있기 때문에 <strong>설정 통합 관리</strong>가 가능하다. 예를 들어, <code>batchmonitor.server-type</code>처럼 특정 모듈의 서버 유형을 YML 파일에 저장함으로써 코드 변경 없이 설정만 업데이트할 수 있다.</li>
</ul>
<h3 id="2-배포-시-수정이-쉬움"><strong>2. 배포 시 수정이 쉬움</strong>:</h3>
<ul>
<li>YML 파일은 <strong>애플리케이션의 설정 파일</strong>이기 때문에, <strong>코드를 수정하지 않고</strong> 설정을 변경하거나 추가할 수 있다.</li>
<li>예를 들어, 새로운 언어가 추가되거나 기존 서버 유형의 명칭이 바뀌는 경우에도 <strong>애플리케이션 재시작</strong> 또는 <strong>핫스왑</strong>만으로 쉽게 변경사항을 적용할 수 있다. DB나 message code 방식보다 <strong>빠르게 대응</strong>할 수 있음.</li>
</ul>
<h3 id="3-경량화-및-속도"><strong>3. 경량화 및 속도</strong>:</h3>
<ul>
<li><strong>YML 파일</strong>은 애플리케이션 구동 시 메모리에 로드 되어 빠르게 참조할 수 있다. DB에서 값을 불러오는 것보다 <strong>조회 속도</strong>가 빠르고, 코드 내에서 간단히 접근할 수 있어. 메시지 코드 방식보다는 비교적 가볍고 단순하다.</li>
</ul>
<h3 id="4-환경별-설정-가능"><strong>4. 환경별 설정 가능</strong>:</h3>
<ul>
<li>Spring에서 <strong>프로파일</strong> 기능을 이용해 YML 파일을 환경별로 다르게 설정할 수 있다. 즉, 개발 환경, 테스트 환경, 운영 환경에서 각기 다른 다국어 설정을 사용할 수 있음.</li>
<li>DB 방식으로 다국어를 관리할 경우 환경마다 데이터베이스에 추가 설정을 해야 하거나 복잡한 동기화가 필요할 수 있지만, YML 파일은 <strong>환경에 맞게 쉽게 분리</strong>할 수 있음.</li>
</ul>
<h3 id="5-단순성과-유지보수-용이성"><strong>5. 단순성과 유지보수 용이성</strong>:</h3>
<ul>
<li>YML 파일에 다국어 설정을 관리하면 <strong>복잡한 로직 없이</strong> 간단히 값을 가져와 사용할 수 있다. message code 방식은 <code>messages.properties</code>에 다국어 값을 정의하고 코드에서 <code>MessageSource</code>를 통해 불러오는 방식인데, 이는 좀 더 <strong>복잡한 구조</strong>를 요구한다.</li>
<li>반면 YML 파일을 사용하는 방식은 <strong>직관적</strong>이고 추가/수정이 용이해. 예를 들어, 새로운 서버 유형이 추가되거나 언어가 추가될 때 YML 파일에 <strong>직접 추가</strong>하면 되니까 유지보수가 간편함.</li>
</ul>
<h2 id="다른-방식과-비교">다른 방식과 비교</h2>
<h3 id="1-db에-저장">1. <strong>DB에 저장</strong>:</h3>
<ul>
<li><strong>장점</strong>: 다국어 데이터를 <strong>동적으로 관리</strong>할 수 있고, 데이터베이스 쿼리를 통해 검색 및 통계를 낼 수 있다. 다국어 데이터를 중앙에서 통합 관리할 수 있고, 다국어 텍스트가 많을 때 유리함.</li>
<li><strong>단점</strong>: 조회 속도가 느릴 수 있고, 변경 사항을 반영하려면 <strong>DB 업데이트 및 쿼리 수정</strong>이 필요하다. 애플리케이션 레이어에서 DB 연결 없이 간단한 다국어 처리를 할 수 없기 때문에, YML 파일처럼 가볍지 않음.</li>
</ul>
<h3 id="2-spring-message-code-messagesproperties">2. <strong>Spring Message Code (<code>messages.properties</code>)</strong>:</h3>
<ul>
<li><strong>장점</strong>: 메시지 코드 방식은 Spring이 기본 제공하는 기능이므로 통합성이 높고, 다국어 메시지를 일관되게 관리할 수 있다. 기본적인 메시지 포맷팅, 다국어 지원, 그리고 재사용성이 높음.</li>
<li><strong>단점</strong>: <strong>추가/수정 시 properties 파일</strong>을 수정해야 하고, 코드 내에서 <code>MessageSource</code>와의 연결이 필요해 복잡도가 약간 올라감. 특히 다국어와 상관없는 설정 값을 관리할 때는 적합하지 않다.</li>
</ul>
<hr>
<aside>
💡

<p>YML 파일에 다국어 설정을 저장하는 이유는 <strong>구조화된 설정 관리</strong>, <strong>간편한 유지보수</strong>, <strong>속도</strong>, 그리고 <strong>환경별로 쉽게 적용 가능</strong>하기 때문이다. 다국어 값의 변화나 추가가 자주 일어나는 경우, 코드 수정 없이 YML 파일만 수정해서 적용할 수 있다는 점에서 매우 유용함. 특히, <strong>설정 값</strong>으로 처리하는 경우 YML 파일을 사용하는 것이 적합하다.</p>
</aside>

<hr>
<h2 id="yml에-다국어-설정을-저장하는-단점">YML에 다국어 설정을 저장하는 <strong>단점</strong>:</h2>
<h3 id="1-대규모-데이터-관리에-불리함"><strong>1. 대규모 데이터 관리에 불리함</strong>:</h3>
<ul>
<li>YML 파일은 설정 파일로써 <strong>소규모의 간단한 데이터</strong>를 관리하기에 적합해. 하지만 <strong>다국어 항목이 많아지고 데이터가 복잡해지면</strong> 파일 자체의 크기가 커지고, 관리가 어려워질 수 있다.</li>
<li>특히 수백, 수천 개의 다국어 메시지를 처리해야 하는 경우, YML 파일로 관리하는 것은 비효율적이다. 이런 경우 <strong>DB</strong>처럼 대량의 데이터를 효율적으로 저장하고 조회할 수 있는 방식이 필요함.</li>
</ul>
<h3 id="2-동적-변경의-어려움"><strong>2. 동적 변경의 어려움</strong>:</h3>
<ul>
<li>YML 파일은 <strong>정적 설정 파일</strong>이기 때문에, 애플리케이션이 실행된 후에는 내용을 변경하기 어렵다. 변경 사항이 있을 경우 애플리케이션을 <strong>재시작</strong>해야 적용되기 때문에, 실시간으로 다국어 설정을 업데이트하거나 동적으로 적용할 수 없다는 단점이 있다.</li>
<li>반면 <strong>DB</strong>나 <strong>properties 파일</strong>을 사용하면, 다국어 메시지를 <strong>실시간으로 변경</strong>하고, 즉시 반영할 수 있다. 예를 들어, 관리자가 다국어 텍스트를 변경할 수 있는 <strong>관리 화면</strong>을 제공하려면 DB 방식이 유리하다.</li>
</ul>
<h3 id="3-버전-관리의-복잡성"><strong>3. 버전 관리의 복잡성</strong>:</h3>
<ul>
<li>YML 파일은 코드와 함께 <strong>버전 관리</strong>되기 때문에, 다국어 텍스트의 <strong>변경 이력</strong>을 따로 관리하려면 번거로울 수 있다. 특히 여러 개발자가 동시에 작업할 경우, <strong>충돌</strong>이 발생할 수 있고 이를 해결하기 위해 불필요한 작업이 추가될 수 있다.</li>
<li>DB를 사용하면 다국어 메시지에 대한 변경 이력을 <strong>자동으로 추적</strong>할 수 있고, 메시지의 추가/수정/삭제도 <strong>별도 관리</strong>가 가능하다.</li>
</ul>
<h3 id="4-파일-크기-증가"><strong>4. 파일 크기 증가</strong>:</h3>
<ul>
<li>YML 파일의 크기가 커지면 <strong>애플리케이션 구동 속도</strong>에 영향을 줄 수 있다. 모든 다국어 데이터를 로드해야 하므로 메모리 사용량도 증가할 수 있다. 반면, DB는 필요한 데이터만 <strong>필터링해서 조회</strong>하기 때문에 메모리 부담이 적고, <strong>확장성</strong>도 높다.</li>
</ul>
<h3 id="5-환경에-따른-설정-분리의-어려움"><strong>5. 환경에 따른 설정 분리의 어려움</strong>:</h3>
<ul>
<li>YML 파일을 환경별로 나누기는 쉬우나, 다국어 설정이 각 환경마다 크게 달라지면 파일 관리가 복잡해질 수 있다. DB는 환경에 상관없이 동일한 데이터베이스를 사용할 수 있으므로, <strong>환경에 독립적</strong>인 다국어 데이터 관리에 적합하다.</li>
</ul>
<h2 id="왜-db나-properties-파일과-섞어서-사용하는가">왜 <strong>DB</strong>나 <strong>properties 파일</strong>과 <strong>섞어서 사용하는가</strong>?</h2>
<p><strong>YML 파일</strong>과 <strong>DB</strong> 또는 <strong>properties 파일</strong>을 <strong>섞어서 사용하는 이유</strong>는, 각각의 <strong>장점과 단점</strong>을 잘 조합하여 <strong>효율적인 다국어 관리</strong>를 하기 위함이다. </p>
<p><strong>적용 방법:</strong> </p>
<h3 id="1-yml은-기본-설정"><strong>1. YML은 기본 설정</strong>:</h3>
<ul>
<li><strong>고정된 소규모의 다국어 값</strong>(예: 서버 유형, 고정된 시스템 메시지)은 YML 파일에 저장해서 사용한다. 이런 값들은 자주 변경되지 않으므로 YML 파일로 관리하면 편리함.</li>
</ul>
<h3 id="2-db는-대규모-데이터-및-동적-변경"><strong>2. DB는 대규모 데이터 및 동적 변경</strong>:</h3>
<ul>
<li><strong>동적으로 추가되거나 자주 수정되는 다국어 메시지</strong>는 DB에서 관리한다. 예를 들어, 사용자 정의 메시지나 관리자가 자주 수정하는 내용을 실시간으로 반영하려면 DB에 저장하는 게 훨씬 유리하다.</li>
<li>DB 방식은 다국어 메시지를 동적으로 불러올 수 있고, 애플리케이션을 재시작하지 않아도 되기 때문에 <strong>실시간 반영</strong>이 가능하다.</li>
</ul>
<h3 id="3-properties는-메시지-코드-관리"><strong>3. properties는 메시지 코드 관리</strong>:</h3>
<ul>
<li><strong>Spring의 message code</strong>(<code>.properties 파일</code>)는 주로 <strong>에러 메시지</strong>나 <strong>폼 필드 검증 메시지</strong>처럼 특정 로직에서 <strong>반복적으로 사용되는 메시지</strong>를 관리할 때 유리하다. properties 파일은 다국어 메시지를 중앙에서 관리하고, 필요할 때마다 손쉽게 가져올 수 있음.</li>
<li>properties 방식은 <strong>다양한 언어의 에러 메시지</strong>를 코드로 정의해두고, 쉽게 유지보수할 수 있는 장점이 있다.</li>
</ul>
<hr>
<aside>
💡

<p>YML 파일은 다국어 설정을 관리하는 데 <strong>간단하고 빠르지만</strong>, <strong>대규모 데이터</strong>나 <strong>실시간 변경</strong>이 필요한 경우에는 한계가 있다. 그래서 <strong>DB</strong>나 <strong>properties 파일</strong>을 사용하는 방식과 <strong>섞어서 사용</strong>하는 것이 일반적임.</p>
<p>각각의 방법을 <strong>상황에 맞게 조합</strong>해서 사용하면 <strong>효율적</strong>이고, 필요에 따라 다국어 데이터를 <strong>확장</strong>하거나 <strong>동적으로 관리</strong>할 수 있는 유연성을 갖출 수 있다.</p>
</aside>]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] properties 파일이 깨져 보일 때?!]]></title>
            <link>https://velog.io/@cher_blair/IntelliJ-properties-%ED%8C%8C%EC%9D%BC%EC%9D%B4-%EA%B9%A8%EC%A0%B8-%EB%B3%B4%EC%9D%BC-%EB%95%8C</link>
            <guid>https://velog.io/@cher_blair/IntelliJ-properties-%ED%8C%8C%EC%9D%BC%EC%9D%B4-%EA%B9%A8%EC%A0%B8-%EB%B3%B4%EC%9D%BC-%EB%95%8C</guid>
            <pubDate>Mon, 29 Apr 2024 13:01:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>.properties에 기록된 데이터들이 외계어처럼 깨져 보일 때?! charset을 설정해서 해결하자! 😎</p>
</blockquote>
<p>업무 중, 다국어를 설정해 둔 .properties 파일의 내용이 깨져 보이는 이슈가 있었다. 구글링을 통해 해결 방법을 찾았고, 답은 간단했다. 아래와 같이 file Encodings 설정을 해주면 해결 완료! </p>
<blockquote>
<p>Default encoding for properties files: UTF-8</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/8b1675d4-2eb0-4199-818b-1b6d5625c399/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] SQL warning 제거하기!]]></title>
            <link>https://velog.io/@cher_blair/intelliJ-SQL-warning-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cher_blair/intelliJ-SQL-warning-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 02 Apr 2024 12:51:21 GMT</pubDate>
            <description><![CDATA[<p>xml 파일로 작성된 sql에 온통 노랗게 밑줄이 그어져 있어 거슬려서 찾아 본 sql warning 제거 방법! 빠르게 적용해보자 😎✨</p>
<blockquote>
<p><strong>File &gt; Setting &gt; Editor &gt;  Inspections</strong> </p>
</blockquote>
<p>경고 표시를 제거하기 위해서는 SQL 메뉴에서 아래 3개 항목을 체크 해제 해야한다.   </p>
<blockquote>
</blockquote>
<ol>
<li>No data sources configured   </li>
<li>SQL dialect detection   </li>
<li>Unresolved reference**</li>
</ol>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/99c84416-4b34-4c8a-a1b3-3e388c7608d2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[연결이 비공개로 설정되어 있지 않습니다. ERR 해결하기!]]></title>
            <link>https://velog.io/@cher_blair/%EC%97%B0%EA%B2%B0%EC%9D%B4-%EB%B9%84%EA%B3%B5%EA%B0%9C%EB%A1%9C-%EC%84%A4%EC%A0%95%EB%90%98%EC%96%B4-%EC%9E%88%EC%A7%80-%EC%95%8A%EC%8A%B5%EB%8B%88%EB%8B%A4.-ERR-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cher_blair/%EC%97%B0%EA%B2%B0%EC%9D%B4-%EB%B9%84%EA%B3%B5%EA%B0%9C%EB%A1%9C-%EC%84%A4%EC%A0%95%EB%90%98%EC%96%B4-%EC%9E%88%EC%A7%80-%EC%95%8A%EC%8A%B5%EB%8B%88%EB%8B%A4.-ERR-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 01 Apr 2024 13:34:03 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/cher_blair/post/94d22377-9587-47b7-a68c-831b2d0827ff/image.png" alt=""></p>
<p>회사에서 노트북으로 모바일 웹을 띄우려고 시도했다가, 위와 같은 화면을 만났다!
당황하지 말고 아래 방법으로 해결하자.
방법은 간단하다!
화면 아무 곳이나 클릭한 뒤 thisisunsafe 를 타이핑 하기만 하면 놀랍게도 화면이 정상적으로 표시된다.
이유가 뭘까 ㅎㅎ 궁금궁금 🤷‍♀️</p>
<blockquote>
<p>해결방법 👉🏻 브라우저 내 아무곳이나 클릭 → <code>thisisunsafe</code> 입력</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] logitech 마우스 설정으로 더 빠르고 편리한 개발하기!]]></title>
            <link>https://velog.io/@cher_blair/intelliJ-logitech-%EB%A7%88%EC%9A%B0%EC%8A%A4-%EC%84%A4%EC%A0%95%EC%9C%BC%EB%A1%9C-%EB%8D%94-%EB%B9%A0%EB%A5%B4%EA%B3%A0-%ED%8E%B8%EB%A6%AC%ED%95%9C-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cher_blair/intelliJ-logitech-%EB%A7%88%EC%9A%B0%EC%8A%A4-%EC%84%A4%EC%A0%95%EC%9C%BC%EB%A1%9C-%EB%8D%94-%EB%B9%A0%EB%A5%B4%EA%B3%A0-%ED%8E%B8%EB%A6%AC%ED%95%9C-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 29 Mar 2024 07:46:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>비싼 마우스 제대로 활용해보자!! 😎✨</p>
</blockquote>
<p>방법은 간단하다. 로지텍 옵션 프로그램을 실행 후, 자주쓰는 인텔리제이 단축키를 이 버튼 저 버튼에 등록해주면 끝!
나는 브레이크 포인트를 보여주는 디버깅 창과 콘솔을 깔끔하게 지워주는 단축키, 그리고 북마크를 스크롤키 옵션에 넣어 바로 띄울 수 있게 설정해뒀다.
각자 자주쓰는 키를 단축키로 설정해두고 인텔리제이를 좀 더 편하게 사용하자 :)</p>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/d274c81e-d9bb-4280-8f8b-7f344dbb34bc/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/10fb4427-d6c2-465d-be76-a9ce66be48fd/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Review] 2023 프로그래머스 컨퍼런스 회고 - 개발자 커리어 성장: 끊임 없는 성장과 성숙 (by. 한기용)]]></title>
            <link>https://velog.io/@cher_blair/Review-2023-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%BB%A8%ED%8D%BC%EB%9F%B0%EC%8A%A4-%ED%9A%8C%EA%B3%A0-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%BB%A4%EB%A6%AC%EC%96%B4-%EC%84%B1%EC%9E%A5-%EB%81%8A%EC%9E%84-%EC%97%86%EB%8A%94-%EC%84%B1%EC%9E%A5%EA%B3%BC-%EC%84%B1%EC%88%99-by.-%ED%95%9C%EA%B8%B0%EC%9A%A9</link>
            <guid>https://velog.io/@cher_blair/Review-2023-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%BB%A8%ED%8D%BC%EB%9F%B0%EC%8A%A4-%ED%9A%8C%EA%B3%A0-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%BB%A4%EB%A6%AC%EC%96%B4-%EC%84%B1%EC%9E%A5-%EB%81%8A%EC%9E%84-%EC%97%86%EB%8A%94-%EC%84%B1%EC%9E%A5%EA%B3%BC-%EC%84%B1%EC%88%99-by.-%ED%95%9C%EA%B8%B0%EC%9A%A9</guid>
            <pubDate>Mon, 29 Jan 2024 12:56:33 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>2023년 혹은 2022년의 어느 날, 프로그래머스 컨퍼런스를 참여해서 강연을 듣고 정리한 내용들의 회고록</p>
</blockquote>
<h1 id="1-연사-소개">1. 연사 소개</h1>
<h1 id="2-일반적인-커리어-성장">2. 일반적인 커리어 성장</h1>
<h2 id="2-1-커리어는-정글짐">2-1. 커리어는 정글짐</h2>
<ul>
<li>vs 사다리</li>
<li>꼭 위로 가지 않아도 된다</li>
<li>여러 가지 역할을 시도해 보는 것이 커리어에 좋다.</li>
<li>안 해 봤던 일을 해 보는 것도 좋다.</li>
<li>어떤 역할이 나한테 맞는지 체험 해보자.</li>
<li>요즘 전문성 &amp; 안정성이란?<ul>
<li>변화를 두려워하지 않고, 나에게 필요한 지식이나 기술을 필요한 만큼 배울 수 있는 학습력 → 전문성을 쌓을 수 있다.</li>
</ul>
</li>
</ul>
<h2 id="2-2-결과를-내는-데-집중하기">2-2. 결과를 내는 데 집중하기</h2>
<ul>
<li>성취하는 경험하기 → 자신감</li>
<li>working backwards<ul>
<li>문제 정의의 중요성: 소통을 통해 맞는 결과부터 정의<ul>
<li>성공 실패는 어떻게 정의되나? 우선 순위는?</li>
<li>의사 소통 능력의 중요성</li>
</ul>
</li>
<li>질문 많이 하고 잘 하기 (자기 검열 안 하기)</li>
</ul>
</li>
<li>Done is better than perfect<ul>
<li>완벽주의 보다는 완료주의</li>
<li>가능하면 literation 기반으로 일 하기</li>
</ul>
</li>
</ul>
<h2 id="2-3-나의-성장을-저해하는-요소">2-3. 나의 성장을 저해하는 요소</h2>
<ul>
<li>나이 혹은 남과의 비교</li>
<li>나에 대한 고정 관념 (vs. growth mindset)</li>
<li>과거의 상처<ul>
<li>나이와 현명함의 관계</li>
<li>나를 돌아보고 상처가 있다면 치유하자.</li>
</ul>
</li>
</ul>
<h2 id="2-4-인격적인-성숙">2-4. 인격적인 성숙</h2>
<ul>
<li>Hire Character, Tran Skill - Peter Schutz<ul>
<li>기술은 가르칠 수 있으나, 성품은 가르칠 수 없다.</li>
</ul>
</li>
<li>긍정적인 태도<ul>
<li>팀웍 vs Brilliant Jerk</li>
</ul>
</li>
<li>주기적으로 회고하는 자세</li>
<li>실수를 인정하고, 모르는 것을 모른다고 하는 여유</li>
</ul>
<h2 id="2-5-시작과-꾸준함의-중요성---복리가-있는-일-하기">2-5. 시작과 꾸준함의 중요성 - 복리가 있는 일 하기</h2>
<ul>
<li>아무 일도 안 하면 아무런 일도 발생하지 않음<ul>
<li>시작이 반</li>
</ul>
</li>
<li>Practice Makes Perfact<ul>
<li>꾸준히 매일 하다 보면 발전하는 게 조금 더 오랜 시간을 두고 보임</li>
</ul>
</li>
<li>복리가 있는 일들<ul>
<li>운동</li>
<li>배움/학습 (호기심)</li>
<li>네트워킹 (사람 만나기)</li>
<li>책 읽기/글쓰기</li>
</ul>
</li>
</ul>
<h2 id="2-6-네트워크의-중요성">2-6. 네트워크의 중요성</h2>
<ul>
<li>사람들을 만날 때 호기심 갖기<ul>
<li>첫 인상에 사로잡히지 않기</li>
<li>질문의 중요성</li>
</ul>
</li>
<li>좋은 사람들과 일하는 환경의 중요성</li>
<li>좋은 평판 유지하기<ul>
<li>팀플레이어, 공정한 사람, 긍정적인 사람 등</li>
</ul>
</li>
<li>도움 요청하고 도움 받기</li>
</ul>
<h2 id="2-7-커뮤니티의-힘">2-7. 커뮤니티의 힘</h2>
<ul>
<li><p>심플스텝스 (WE GROW TOGETHER)</p>
<ul>
<li><p>2017년 설립된 미국 이주 한인 여성 재취업 목적 비영리 단체</p>
</li>
<li><p>다수의 재취업 성공 케이스들을 만들어 냄</p>
<ul>
<li><p>서포트 네트워크와 롤 모델의 중요성</p>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/c8351bc4-9bfc-4b0c-81fe-0cbaf53edafc/image.png" alt=""></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>- 실리콘 밸리에서 날아온 데이터 엔지니어링
    - 총 10기 370명</code></pre><h2 id="2-8-자신의-강점과-약점을-이해하기">2-8. 자신의 강점과 약점을 이해하기</h2>
<ul>
<li>내 강점을 최대화하는 일을 찾기</li>
<li>단 내 강점이 약점이 되는 순간이 있는데 그걸 잘 인지하기<ul>
<li>Peter’s principle: 사람들은 일반적으로 무능할 때까지 승진함 (혹은 성장함)</li>
<li>Comfort zone에서 나오기<ul>
<li>완벽한 사람</li>
<li>실행을 아주 잘 하는 사람?</li>
</ul>
</li>
</ul>
</li>
<li>인지는 꼭 해야하지만 꼭 변화할 필요가 있을까?</li>
</ul>
<h2 id="2-9-사기-증후군-극복하기">2-9. 사기 증후군 극복하기</h2>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/db7e3536-542e-4e04-86bd-1cb106b797e0/image.png" alt=""></p>
<h1 id="3-개발자의-커리어-성장">3. 개발자의 커리어 성장</h1>
<h2 id="31-기본기-확실히-하기">3.1 기본기 확실히 하기</h2>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/040974d3-bf88-4d2a-97af-f56a36419787/image.png" alt="">
<img src="https://velog.velcdn.com/images/cher_blair/post/6d553c62-cb2d-4527-af54-1b3b3dfb34ee/image.png" alt="">
<img src="https://velog.velcdn.com/images/cher_blair/post/0d4398f0-4af4-4656-b253-52cab4afe48b/image.png" alt="">
<img src="https://velog.velcdn.com/images/cher_blair/post/01a059cd-f5c1-4e9f-8cd1-0e1a04b393ec/image.png" alt="">
<img src="https://velog.velcdn.com/images/cher_blair/post/0ede05cd-0266-492a-a484-32e73e138b47/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ngnix ?!]]></title>
            <link>https://velog.io/@cher_blair/Ngnix</link>
            <guid>https://velog.io/@cher_blair/Ngnix</guid>
            <pubDate>Sun, 28 Jan 2024 10:11:27 GMT</pubDate>
            <description><![CDATA[<h2 id="️-ngnix-">⁉️ Ngnix ?!</h2>
<blockquote>
</blockquote>
<p>높은 성능과 안정성을 갖춘 현재 가장 많이 사용되고 있는 웹서버.
Apache 같은 웹 서버와 비교하면 빠르고, 대규모 어플리케이션 처리에 적합하다는 장점이 있음.</p>
<br>

<h2 id="ngnix를-이해하기-위해-필요한-용어-4가지">Ngnix를 이해하기 위해 필요한 용어 4가지</h2>
<ol>
<li><p>클라이언트</p>
<ul>
<li>클라이언트란 서비스를 이용하기 위해 네트워크를 통해 요청을 보내는 주체를 말함.</li>
<li>예를 들어, 인터넷에서 웹페이지를 보기 위해 웹 브라우저를 실행하면 웹 브라우저가 클라이언트가 된다.</li>
<li>웹 개발 영역에서 보통 클라이언트라 하면 크롬, 사파리, 익스플로러 등 웹 브라우저를 의미한다고 보면 됨.<br>
</li>
</ul>
</li>
<li><p>웹 서버</p>
<ul>
<li>웹 서버는 클라이언트의 요청에 따라 HTML, CSS, JS, 이미지 파일과 같은 정적 파일을 응답하여 제공하는 소프트웨어를 말함.</li>
<li>웹 서버는 HTTP 프로토콜을 사용하여 클라이언트와 통신한다.</li>
<li>대표적인 웹서버로는 <code>Ngnix</code>, <code>Apache</code> 등이 있음.</li>
<li>이 외에도 Microsoft IIS, LIGHTTPD 등 다양한 웹서버가 존재함.<br>
</li>
</ul>
</li>
<li><p>WAS (Web Application Server)</p>
<ul>
<li>WAS는 클라이언트의 요청에 대해 동적인 처리를 담당하는 영역.</li>
<li>웹 서버와 달리 어플리케이션 로직을 실행할 수 있도록 구성되어 있다.</li>
<li>예를 들어 회원가입이나 로그인 등의 로직을 처리하는 영역이 WAS.</li>
<li>또한 데이터베이스 연동, 트랜젝션 관리, 보안, 로깅 등의 기능도 제공한다.</li>
<li>이를 통해 웹 어플리케이션의 안정성과 성능을 향상시키며, 개발자들은 어플리케이션 개발에 집중할 수 있다.</li>
<li>대표적인 WAS로는 <code>Tomcat</code>, <code>JBoss</code>, <code>WebLogic</code> 등이 있음.</li>
<li>웹 서버와 WAS는 역할의 차이가 있지만, 현업에서는 웹 서버로 퉁(?)쳐서 얘기하곤 한다.</li>
<li>Node.js는 웹 서버 또는 WAS로도 사용할 수 있다.<br>
</li>
</ul>
</li>
<li><p>DB</p>
<ul>
<li>DB는 조직이나 개인이 필요한 정보를 체계적으로 저장, 관리하고 검색할 수 있는 시스템이다.</li>
<li>DB는 일반적으로 다수의 사용자가 공유할 수 있으므로, 대규모 데이터의 저장과 검색을 처리할 수 있다.</li>
<li>가장 많이 사용되는 DB 유형으로는 관계형(RDBMS), NoSQL 등이 있다.<br>


</li>
</ul>
</li>
</ol>
<h2 id="일반적인-요청과-응답의-순서">일반적인 요청과 응답의 순서</h2>
<p>-&gt; 보통의 웹 서비스는 클라이언트 &gt; 웹서버 &gt; WAS &gt; DB 순으로 <code>요청</code> 되고, DB &gt; WAS &gt; 웹 서버 &gt; 클라이언트 순으로 <code>응답</code>이 된다.
<br></p>
<h2 id="웹-서버를-사용해야-하는-이유-️">웹 서버를 사용해야 하는 이유 ⁉️</h2>
<ol>
<li><p>WAS의 부담을 줄여주기 위함</p>
<ul>
<li>WAS는 로그인, 회원가입, 개인정보 수정 등 동적 작업을 처리하는 것만으로도 작업량이 많음. 이에 HTML, CSS, JS, 이미지 등 정적인 파일을 클라이언트에게 전달하는 역할을 웹 서버에게 위임함으로써 WAS의 작업 부담을 줄일 수 있다.<br><br></li>
</ul>
</li>
<li><p>보안 기능 제공</p>
<ul>
<li>웹 서버는 보안 기능을 제공하여 웹 페이지에 대한 접근을 제어할 수 있다.</li>
<li>예를 들어, 웹 서버는 SSL/TLS 프로토콜을 사용하여 데이터를 암호화하고, 엑세스 제어, 웹 방화벽 보안 기능을 제공하여 웹 사이트를 보호할 수 있음 <br><br></li>
</ul>
</li>
<li><p>높은 성능 제공</p>
<ul>
<li>웹 서버는 대부분 비동기 처리 방식을 사용하여 높은 성능을 제공함.</li>
<li>예를 들어 Ngnix, Apache 등의 웹 서버는 이벤트 기반, 멀티 프로세싱, 스레드 풀 등의 기술을 사용하여 수천대의 클라이언트 요청을 동시에 처리할 수 있다. <br><br></li>
</ul>
</li>
</ol>
<h2 id="ngnix를-사용하는-이유-️">Ngnix를 사용하는 이유 ‼️</h2>
<ol>
<li><p>높은 성능과 적은 메모리 사용</p>
<ul>
<li>Ngnix는 비동기 I/O 처리 방식을 사용하여 높은 성능을 제공한다. 이를 통해 대규모 웹 사이트에서도 빠른 응답 시간을 보장할 수 있다.</li>
<li>또한 Ngnix는 적은 메모리 사용량으로도 높은 성능을 제공한다. 이를 통해 운용 비용을 절감할 수 있다. <br><br></li>
</ul>
</li>
<li><p>리버스 프록시(Reverse Proxy) 사용이 가능함</p>
<ul>
<li><p>프록시(Proxy)의 사전 정의는 &quot;대리&quot;이다. 인터넷 접속을 할 때 보안상의 문제로 직접 통신을 주고 받을 수 없을 때, 그 사이의 중계기로서 대리로 통신을 수행하는 기능을 프록시라고 말한다. 이렇게 중계를 기능하게 하는 것을 <code>프록시 서버</code> 라고 부른다.</p>
</li>
<li><p>프록시는 크게 <code>포워드 프록시</code>와 <code>리버스 프록시</code>로 구분한다.<br><br></p>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/9868b781-d693-4357-b649-fcfe212c0099/image.png" alt=""></p>
</li>
<li><p>포워드 프록시</p>
<ul>
<li>클라이언트와 인터넷 그 사이에 있는 영역을 말한다.</li>
<li>클라이언트가 어떠한 정보를 요청하면 포워드 프록시가 이를 대신 받아서 서버에게 전달한다. 이후 서버의 응답 또한 포워드 프록시가 대신 받아서 클라이언트에게 전달한다.</li>
<li>포워드 프록시를 사용하면 클라이언트의 IP 주소가 웹 서버에 노출되지 않는다. 따라서, 클라이언트의 위치나 신원을 식별하는 것이 어려워지므로 보안이 강화된다.</li>
<li>또한 접근 제어를 수행할 수 있다. 포워드 프록시를 사용하여 특정 IP 주소, 도메인 또는 URL에 대한 접근을 제한할 수 있다.</li>
<li>미디어 스트리밍을 지원할 수 있다. 미디어 파일은 일반적으로 큰 용량을 가지고 있기 때문에, 웹 서버에서 직접 전송하는 것은 효율적이지 않다. 포워드 프록시를 사용하여 미디어 파일을 캐시하고, 클라이언트에게 빠르게 제공할 수 있다. <br><br></li>
</ul>
</li>
</ul>
</li>
</ol>
<pre><code>![](https://velog.velcdn.com/images/cher_blair/post/a940b0b2-ef68-4e4a-8a55-982b904275a3/image.png)

- 리버스 프록시
    - 인터넷과 백엔드 그 사이에 있는 서버 영역을 말한다.
    - 로드 밸런싱이 가능하다.
    - 캐싱 서버로도 이용이 가능하다. 예를 들어 클라이언트가 이미지를 요청했을 때 처음에는 서버에서 가져오지만 이후에는 동일한 요청이 있을 때 캐시 서버에서 가져와 클라이언트에게 전달해 준다. 이를 통해 사이트 접속 속도를 빠르게 유지할 수 있다.
    - 보안 효과가 있다. WAS가 데이터를 응답할 때는 기기의 명칭이나 주소 등 실제로는 민감한 정보들이 담겨져 있다. 이러한 정보들을 중간에서 숨겨 줌으로써 보안을 높일 수 있게 된다. &lt;br&gt;&lt;br&gt;</code></pre><ol start="3">
<li>SSL 지원<ul>
<li>SSL(Secure Sockets Layer)은 웹 사이트와 사용자 간의 통신을 암호화하고 보안을 유지하는 데 사용되는 프로토콜이다. SSL은 HTTPS (HTTP Secure)로 알려진 보안 HTTP 프로토콜의 기반 기술이다. </li>
<li>HTTPS는 HTTP 프로토콜의 암호화 된 버전이다. SSL 프로토콜을 사용하여 웹 서버와 클라이언트 간 보안 연결을 설정하고, SSL  인증서를 사용하여 서버의 신원을 인증한다. 이를 통해 중간자 공격과 같은 보안 위협을 방지하고, 사용자의 개인 정보와 웹 사이트의 기밀 정보를 보호할 수 있다.</li>
<li>Ngnix는 HTTPS 인증서를 제공해 줄 수 있다. <br><br></li>
</ul>
</li>
</ol>
<ol start="4">
<li><p>데이터 압축</p>
<ul>
<li>클라이언트가 보내는 요청이 Text일 경우 gzip을 사용하여 해당 데이터를 압축 시킬 수 있다.</li>
</ul>
</li>
<li><p>비동기 처리</p>
<ul>
<li><p>Ngnix는 이벤트 루프 방식을 사용하여 높은 성능을 제공한다. 이를 통해 동시에 여러 요청이 들어왔을 때도 많은 트래픽을 동시에 처리할 수 있어 빠른 응답 시간을 보장한다.<br><br><br><br></p>
<p>(이미지 출처: <a href="https://www.cloudflare.com/ko-kr/learning/cdn/glossary/reverse-proxy/">https://www.cloudflare.com/ko-kr/learning/cdn/glossary/reverse-proxy/</a>)</p>
</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 명령어 BASIC!]]></title>
            <link>https://velog.io/@cher_blair/%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%AA%85%EB%A0%B9%EC%96%B4-BASIC</link>
            <guid>https://velog.io/@cher_blair/%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%AA%85%EB%A0%B9%EC%96%B4-BASIC</guid>
            <pubDate>Sun, 28 Jan 2024 08:59:33 GMT</pubDate>
            <description><![CDATA[<p><code>ls</code>    현재 위치의 파일 목록 조회
<code>cd</code>    디렉토리 이동
<code>touch</code>    0바이트 파일 생성, 파일의 날짜와 시간을 수정
<code>mkdir</code>    디렉토리 생성
<code>cp</code>    파일 복사
<code>mv</code>    파일 이동
<code>rm</code>    파일 삭제
<code>cat</code>    파일의 내용을 화면에 출력, 리다이렉션 기호(&#39;&gt;&#39;)를 사용하여 새로운 파일 생성
<code>redirection</code>    화면의 출력 결과를 파일로 저장
<code>alias</code>    자주 사용하는 명령어들을 별명으로 정의하여 쉽게 사용할 수 있도록 설정
<code>df</code>    디스크 여유 공간 확인
<code>du</code>    디렉토리 디스크 사용량 확인
<code>ps</code>    프로세스 확인
<code>less</code>    파일의 내용을 한 화면에 보여주는 기능
<code>history</code>    실행한 명령어들의 이력을 조회
<code>su</code>    사용자 전환</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dynatree?]]></title>
            <link>https://velog.io/@cher_blair/Dynatree</link>
            <guid>https://velog.io/@cher_blair/Dynatree</guid>
            <pubDate>Sun, 28 Jan 2024 08:48:34 GMT</pubDate>
            <description><![CDATA[<h2 id="dynatree-">Dynatree ?</h2>
<aside>
💡 트리 구조를 구현해주는 jQuery Plugin

</aside>

<h2 id="option-">Option ?</h2>
<pre><code class="language-javascript">$(&quot;#tree&quot;).dynatree({
    title: &quot;Dynatree&quot;, // Tree&#39;s name (only used for debug outpu)
    minExpandLevel: 1, // 1: root node is not collapsible
    imagePath: null, // Path to a folder containing icons. Defaults to &#39;skin/&#39; subdirectory.
    children: null, // Init tree structure from this object array.
    initId: null, // Init tree structure from a &lt;ul&gt; element with this ID.
    initAjax: null, // Ajax options used to initialize the tree strucuture.
    autoFocus: true, // Set focus to first child, when expanding or lazy-loading.
    keyboard: true, // Support keyboard navigation.
    persist: false, // Persist expand-status to a cookie
    autoCollapse: false, // Automatically collapse all siblings, when a node is expanded.
    clickFolderMode: 3, // 1:activate, 2:expand, 3:activate and expand
    activeVisible: true, // Make sure, active nodes are visible (expanded).
    checkbox: false, // Show checkboxes.
    selectMode: 2, // 1:single, 2:multi, 3:multi-hier
    fx: null, // Animations, e.g. null or { height: &quot;toggle&quot;, duration: 200 }
    noLink: false, // Use &lt;span&gt; instead of &lt;a&gt; tags for all nodes
    // Low level event handlers: onEvent(dtnode, event): return false, to stop default processing
    onClick: null, // null: generate focus, expand, activate, select events.
    onDblClick: null, // (No default actions.)
    onKeydown: null, // null: generate keyboard navigation (focus, expand, activate).
    onKeypress: null, // (No default actions.)
    onFocus: null, // null: set focus to node.
    onBlur: null, // null: remove focus from node.

    // Pre-event handlers onQueryEvent(flag, dtnode): return false, to stop processing
    onQueryActivate: null, // Callback(flag, dtnode) before a node is (de)activated.
    onQuerySelect: null, // Callback(flag, dtnode) before a node is (de)selected.
    onQueryExpand: null, // Callback(flag, dtnode) before a node is expanded/collpsed.

    // High level event handlers
    onPostInit: null, // Callback(isReloading, isError) when tree was (re)loaded.
    onActivate: null, // Callback(dtnode) when a node is activated.
    onDeactivate: null, // Callback(dtnode) when a node is deactivated.
    onSelect: null, // Callback(flag, dtnode) when a node is (de)selected.
    onExpand: null, // Callback(flag, dtnode) when a node is expanded/collapsed.
    onLazyRead: null, // Callback(dtnode) when a lazy node is expanded for the first time.
    onCustomRender: null, // Callback(dtnode) before a node is rendered. Return a HTML string to override.
    onCreate: null, // Callback(dtnode, nodeSpan) after a node was rendered for the first time.
    onRender: null, // Callback(dtnode, nodeSpan) after a node was rendered.
    postProcess: null, // Callback(data, dataType) before an Ajax result is passed to dynatree.

    // Drag&#39;n&#39;drop support
    dnd: {
        // Make tree nodes draggable:
        onDragStart: null, // Callback(sourceNode), return true, to enable dnd
        onDragStop: null, // Callback(sourceNode)
        // Make tree nodes accept draggables
        autoExpandMS: 1000, // Expand nodes after n milliseconds of hovering.
        preventVoidMoves: true, // Prevent dropping nodes &#39;before self&#39;, etc.
        revert: false, // true: slide helper back to source if drop is rejected
        onDragEnter: null, // Callback(targetNode, sourceNode, ui, draggable)
        onDragOver: null, // Callback(targetNode, sourceNode, hitMode)
        onDrop: null, // Callback(targetNode, sourceNode, hitMode, ui, draggable)
        onDragLeave: null // Callback(targetNode, sourceNode)
    },
    ajaxDefaults: { // Used by initAjax option
        cache: false, // false: Append random &#39;_&#39; argument to the request url to prevent caching.
        timeout: 0, // &gt;0: Make sure we get an ajax error for invalid URLs
        dataType: &quot;json&quot; // Expect json format and pass json object to callbacks.
    },
    strings: {
        loading: &quot;Loading…&quot;,
        loadError: &quot;Load error!&quot;
    },
    generateIds: false, // Generate id attributes like &lt;span id=&#39;dynatree-id-KEY&#39;&gt;
    idPrefix: &quot;dynatree-id-&quot;, // Used to generate node id&#39;s like &lt;span id=&quot;dynatree-id-&lt;key&gt;&quot;&gt;.
    keyPathSeparator: &quot;/&quot;, // Used by node.getKeyPath() and tree.loadKeyPath().
    cookieId: &quot;dynatree&quot;, // Choose a more unique name, to allow multiple trees.
    cookie: {
        expires: null // Days or Date; null: session cookie
    },
    // Class names used, when rendering the HTML markup.
    // Note:
    // These settings only apply on initialisation.
    // If only single entries are passed for options.classNames, all other
    // values are still set to default.
    classNames: {
        container: &quot;dynatree-container&quot;,
        node: &quot;dynatree-node&quot;,
        folder: &quot;dynatree-folder&quot;,

        empty: &quot;dynatree-empty&quot;,
        vline: &quot;dynatree-vline&quot;,
        expander: &quot;dynatree-expander&quot;,
        connector: &quot;dynatree-connector&quot;,
        checkbox: &quot;dynatree-checkbox&quot;,
        nodeIcon: &quot;dynatree-icon&quot;,
        title: &quot;dynatree-title&quot;,
        noConnector: &quot;dynatree-no-connector&quot;,

        nodeError: &quot;dynatree-statusnode-error&quot;,
        nodeWait: &quot;dynatree-statusnode-wait&quot;,
        hidden: &quot;dynatree-hidden&quot;,
        combinedExpanderPrefix: &quot;dynatree-exp-&quot;,
        combinedIconPrefix: &quot;dynatree-ico-&quot;,
        hasChildren: &quot;dynatree-has-children&quot;,
        active: &quot;dynatree-active&quot;,
        selected: &quot;dynatree-selected&quot;,
        expanded: &quot;dynatree-expanded&quot;,
        lazy: &quot;dynatree-lazy&quot;,
        focused: &quot;dynatree-focused&quot;,
        partsel: &quot;dynatree-partsel&quot;,
        lastsib: &quot;dynatree-lastsib&quot;
    },
    debugLevel: 1 // 0:quiet, 1:normal, 2:debug
});</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[SiteMesh ?!]]></title>
            <link>https://velog.io/@cher_blair/SiteMesh</link>
            <guid>https://velog.io/@cher_blair/SiteMesh</guid>
            <pubDate>Sun, 28 Jan 2024 08:28:04 GMT</pubDate>
            <description><![CDATA[<h2 id="sitemesh-">SiteMesh ?</h2>
<table>
<thead>
<tr>
<th align="center"></th>
<th>contents</th>
</tr>
</thead>
<tbody><tr>
<td align="center">정의</td>
<td>웹 페이지를 구성하는 레이아웃을 효율적으로 만들 수 있게 도와주는 프레임워크</td>
</tr>
<tr>
<td align="center">용도</td>
<td>웹 페이지에서 상단, 하단, 메뉴같이 공통된 부분들을 한 곳에 집중하여 처리하고, 변경되는 중간의 콘텐츠만 변경될 수 있도록 해 준다.</td>
</tr>
<tr>
<td align="center">동작 방식</td>
<td>공통으로 적용될 레이아웃과 구성 요소를 정의하고 있는 데코레이터를 응답 결과에 적용하는 방식으로 동작함.</td>
</tr>
<tr>
<td align="center">구성</td>
<td>레이아웃 코드 및 헤더와 몸체가 삽입될 위치 정보로 구성됨.어떤 생성 화면이든지 데코레이터를 적용하기만 하면 데코레이터에 정의된 레이아웃과 공통 영역이 적용된 응답 결과를 볼 수 있다.</td>
</tr>
</tbody></table>
<br>

<h2 id="sitemesh-vs-jsp-include-">SiteMesh vs jsp include ?</h2>
<table>
<thead>
<tr>
<th align="center"></th>
<th>SiteMesh</th>
<th>include</th>
</tr>
</thead>
<tbody><tr>
<td align="center">중복 코드 발생 가능성</td>
<td>낮음</td>
<td>높음</td>
</tr>
<tr>
<td align="center">사용 방식</td>
<td>완전한 HTML 페이지를 생성한 뒤 Decorator Pattern을 사용하여 HTML 페이지에 레이아웃을 입히는 방식</td>
<td>전체 페이지 중 내용 부분에 해당하는 코드만을 생성하는 방식 (Tiles, Velocity)</td>
</tr>
<tr>
<td align="center">특징</td>
<td>레이아웃 템플릿을 만들어주는 기술 중의 하나.</td>
<td>레이아웃 자체를 변경하거나 새로운 영역을 추가해야 할 경우 전체 jsp 페이지를 변경해주어야 하는 부담이 있음</td>
</tr>
</tbody></table>
<br>

<h2 id="sitemesh-동작-방식">SiteMesh 동작 방식</h2>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/96460406-80e3-4573-83c3-c5d727362d6d/image.png" alt=""></p>
<ol>
<li>위 그림에서 데코레이터에 전달되는 HTML 페이지는 <code>&lt;html&gt;</code>, <code>&lt;head&gt;</code>, <code>&lt;body&gt;</code> 등을 포함한 완전한 HTML 페이지이다. 이때 데코레이터에 전달되는 HTML 페이지는 레이아웃과 관련된 내용은 포함되지 않는다.</li>
<li>데코레이터는 레이아웃 정보를 담고 있는 JSP 페이지로서, 앞서 생성한 HTML 페이지에 저장된 (<code>&lt;title&gt;</code> 등의) 메타 정보와 <code>&lt;body&gt;</code> 태그에 포함된 내용을 추출한 뒤, 레이아웃의 알맞은 위치에 추출한 내용을 삽입하여 최종 결과를 생성하게 된다.</li>
</ol>
<br>

<h2 id="pagefilterservelet-filter-">PageFilter(servelet filter) ?</h2>
<aside>
💡 SiteMesh를 설정하기 위해 설정해야 하는 것

</aside>

<pre><code> &lt;filter-mapping&gt;
         &lt;filter-name&gt;sitemesh&lt;/filter-name&gt;
     &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
 &lt;/filter-mapping&gt;</code></pre><ul>
<li>위 예시는 / 로 들어오는 모든 요청에 대해서 PageFilter를 적용한다고 설정 한 것</li>
<li>PageFilter는 요청 URL과 매칭되는 데코레이터를 검색한 뒤, 데코레이터가 발견될 경우 결과 HTML에 매칭되는 데코레이터를 적용한다.</li>
<li>따라서, 데코레이터가 적용되어야 하는 URL의 경우 반드시 PageFilter에 매핑시켜주어야 함.</li>
</ul>
<br>

<h2 id="decoratror-">Decoratror ?</h2>
<aside>
💡 jsp 페이지로서, SiteMesh가 제공하는 커스텀 태그를 사용하여 결과 HTML 페이지를 꾸밈.

</aside>
<br>

<table>
<thead>
<tr>
<th align="center"></th>
<th>contents</th>
</tr>
</thead>
<tbody><tr>
<td align="center">&lt;decorator:head /&gt;</td>
<td>원본 소스에서 head 요소 부분에 있는 내용(<head>..</head>)을 &lt;decorator:head /&gt; 부분에 삽입</td>
</tr>
<tr>
<td align="center">&lt;decorator:body /&gt;</td>
<td>원본 소스에서 body 요소 부분에 있는 내용(<body>..</body>)을 &lt;decorator:head /&gt; 부분에 삽입</td>
</tr>
<tr>
<td align="center">&lt;decorator:title [default=&quot;..&quot;] /&gt;</td>
<td>원본 소스에서 title 요소 안에 내용을 &lt;decorator:title /&gt; 부분에 삽입</td>
</tr>
<tr>
<td align="center">&lt;decorator:getProperty property=&quot;...&quot; [default=&quot;...&quot;] [writeEntireProperty=&quot;..&quot;] /&gt;</td>
<td>원본 페이지의 프로퍼티를 가져옴</td>
</tr>
<tr>
<td align="center">&lt;decorator:usePage id=&quot;...&quot; /&gt;</td>
<td>원본 페이지의 객체에 접근할 수 있</td>
</tr>
<tr>
<td align="center">&lt;page:applyDecorator name=&quot;..&quot; /&gt;</td>
<td>decorator.xml에 decorator 등록된 name으로 해당 jsp를 적용</td>
</tr>
<tr>
<td align="center">&lt;page:param name=&quot;..&quot; /&gt;</td>
<td>decorator할 페이지에서 &lt;decorator:getProperty /&gt;로 값을 가져올 수 있음</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] log charset 설정하기]]></title>
            <link>https://velog.io/@cher_blair/intelliJ-log-charset-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cher_blair/intelliJ-log-charset-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 21 Jan 2024 10:20:16 GMT</pubDate>
            <description><![CDATA[<p>로그에 charset이 깨진다?! 
intelliJ log charset 설정하기! 🙌</p>
<p>[run] -&gt; [edit configurations...]</p>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/de32f8b6-6d1a-41a4-87e3-2c505ed0a513/image.png" alt=""></p>
<p><code>Dfile.encoding=UTF-8</code> 추가</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] compact middle packages ]]></title>
            <link>https://velog.io/@cher_blair/intelliJ-compact-middle-packages</link>
            <guid>https://velog.io/@cher_blair/intelliJ-compact-middle-packages</guid>
            <pubDate>Sun, 21 Jan 2024 10:14:58 GMT</pubDate>
            <description><![CDATA[<p>점(.)으로 구분되어 지는 프로젝트 구조를 하위 단계로 설정하는 방법! 🍒</p>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/a426ef79-df64-4ec3-ab4a-94cfc516ef95/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/87cde411-e1fd-48d8-9882-56f43bc042d7/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] 프로젝트(모듈) Tomcat 서버 context.xml 생성 방법]]></title>
            <link>https://velog.io/@cher_blair/intelliJ-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%AA%A8%EB%93%88-Tomcat-%EC%84%9C%EB%B2%84-context.xml-%EC%83%9D%EC%84%B1-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@cher_blair/intelliJ-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%AA%A8%EB%93%88-Tomcat-%EC%84%9C%EB%B2%84-context.xml-%EC%83%9D%EC%84%B1-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sun, 21 Jan 2024 10:05:56 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/cher_blair/post/f6126765-1734-435b-8843-3b7aef002285/image.png" alt=""></p>
<p>Project Structure 창에서 해당하는 웹 프로젝트(모듈)의 Web framework를 선택하고 [Add Application Server specific descriptor...]를 클릭</p>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/9d63e03a-5be0-40e5-b503-677c4221b2f7/image.png" alt=""></p>
<p>위와 같이 지정해준다.
Version은 5.0만 있지만 상관 없다.
[OK]를 클릭한다.</p>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/9090c87c-926f-449d-84e3-71d835237b74/image.png" alt=""></p>
<p>위와 같이 Tomcat Context Descriptor가 추가된 것을 볼 수 있다.
[OK]를 클릭한다.</p>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/5610ee1c-81e4-4832-bb41-c45faf8fadbc/image.png" alt=""></p>
<p>META-INF/context.xml 파일이 생성되었다.
여기에 DataSource 등의 필요한 설정을 추가해주면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] tomcat setting]]></title>
            <link>https://velog.io/@cher_blair/IntelliJ-tomcat-setting</link>
            <guid>https://velog.io/@cher_blair/IntelliJ-tomcat-setting</guid>
            <pubDate>Sun, 21 Jan 2024 10:00:10 GMT</pubDate>
            <description><![CDATA[<p>[run] -&gt; [edit configurations...]</p>
<p><img src="https://velog.velcdn.com/images/cher_blair/post/1c0ea418-2704-44d9-b0db-a3c8860a48e8/image.png" alt="">
<img src="https://velog.velcdn.com/images/cher_blair/post/3db0b0d3-da92-40a4-9621-b73c5f579d9c/image.png" alt="">
<img src="https://velog.velcdn.com/images/cher_blair/post/7dc20cb3-508b-4c99-8fee-fd52e512828a/image.png" alt="">
<img src="https://velog.velcdn.com/images/cher_blair/post/23dcdea5-69b1-4729-af75-abb1afc3c992/image.png" alt="">
<img src="https://velog.velcdn.com/images/cher_blair/post/0353b1b3-f852-4e9e-97ce-64de7f21f356/image.png" alt="">
<img src="https://velog.velcdn.com/images/cher_blair/post/5a4f694e-076d-49eb-a60e-0b5e32595a2e/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Mac] MacBook 자주 사용하는 단축키 모음 😎]]></title>
            <link>https://velog.io/@cher_blair/Mac-MacBook-%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%8B%A8%EC%B6%95%ED%82%A4-%EB%AA%A8%EC%9D%8C</link>
            <guid>https://velog.io/@cher_blair/Mac-MacBook-%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%8B%A8%EC%B6%95%ED%82%A4-%EB%AA%A8%EC%9D%8C</guid>
            <pubDate>Sun, 21 Jan 2024 09:44:12 GMT</pubDate>
            <description><![CDATA[<table>
<thead>
<tr>
<th>탭 닫기</th>
<th>command + w</th>
</tr>
</thead>
<tbody><tr>
<td>창 닫기</td>
<td>command + q</td>
</tr>
<tr>
<td>지우기</td>
<td>command + del</td>
</tr>
<tr>
<td>잘라내기 (finder)</td>
<td>command + c</td>
</tr>
<tr>
<td>잘라내기 (일반)</td>
<td>command + x</td>
</tr>
<tr>
<td>붙여넣기</td>
<td>command + option + v</td>
</tr>
<tr>
<td>스크린샷 (전체)</td>
<td>command + shift + 3</td>
</tr>
<tr>
<td>스크린샷 (일부)</td>
<td>command + shift + 4</td>
</tr>
<tr>
<td>스크린샷(+녹음)</td>
<td>command + shift + 5</td>
</tr>
<tr>
<td>강제종료</td>
<td>command + option + esc</td>
</tr>
<tr>
<td>전체화면</td>
<td>control + command + f</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 2751 - K번째 수 (2)]]></title>
            <link>https://velog.io/@cher_blair/%EB%B0%B1%EC%A4%80-2751-K%EB%B2%88%EC%A7%B8-%EC%88%98</link>
            <guid>https://velog.io/@cher_blair/%EB%B0%B1%EC%A4%80-2751-K%EB%B2%88%EC%A7%B8-%EC%88%98</guid>
            <pubDate>Sun, 21 Jan 2024 09:12:37 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/2751">👉🏻 백준 2751 문제 바로가기</a></p>
</blockquote>
<h2 id="❓-병합-정렬">❓ 병합 정렬?!</h2>
<blockquote>
<p><code>병합 정렬</code>: 분할 정복 방식을 사용해 데이터를 분할하고, 분할한 집합을 정렬하며 합치는 알고리즘 <br> 평균적인 시간 복잡도는 O(nlogn).</p>
</blockquote>
<br>


<h2 id="❗️pseudo-code">❗️pseudo code</h2>
<pre><code class="language-java">
❗️pesudo code

count (정렬할 수 개수)
sortedArray (정렬할 배열)
tempArray (정렬 시 잠시 사용 할 임시 배열)

for (count 만큼 반복) {
    sortedArray 선언
}
병합 정렬 함수 수행
결괏값 출력

// 병합 정렬 수행
병합 정렬 (start, end) {
    start(시작점), end(종료점), middle(중간점)

    // 재귀 함수 형태로 구현
    병합 정렬 (start, middle)
    병합 정렬 (middle + 1, end)
    for (start ~ end) {
        tempArray 저장
    }

    // 두 그룹 병합 로직
    index1 (앞쪽 그룹 시작점)
    index2 (뒤쪽 그룹 시작점)
    while (index1 &lt;= 중간점 &amp;&amp; index2 &lt;= 종료점) {
        양쪽 그룹 index가 가르키는 값을 비교한 후 더 작은 수를 선택해 배열에 저장하고,
        선택된 데이터의 index 값을 오른쪽으로 한 칸 이동하기
        반복문이 끝난 후 남아 있는 데이터 정리
    }
}
</code></pre>
<br>

<h2 id="✨-solve">✨ solve</h2>
<pre><code class="language-java">import java.io.*;

public class Main {
    public static int[] sortedArray, tempArray;
    public static long result;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int count = Integer.parseInt(br.readLine());
        sortedArray = new int[count + 1];
        tempArray = new int[count + 1];
        for (int i = 1; i &lt;= count; i++) {
            sortedArray[i] = Integer.parseInt(br.readLine());
        }

        // 병합 정렬 수행
        mergeSort(1, count);
        for (int i = 1; i &lt;= count; i++) {
            bw.write(sortedArray[i] + &quot;\n&quot;);
        }
        bw.flush();
        bw.close();
    }

    private static void mergeSort(int start, int end) {
        if (end - start &lt; 1) {
            return;
        }
        int middle = start + (end - start) / 2;

        // 재귀 함수 형태로 구현
        mergeSort(start, middle);
        mergeSort(middle + 1, end);
        for (int i = start; i &lt;= end; i++) {
            tempArray[i] = sortedArray[i];
        }
        int k = start;
        int index1 = start;
        int index2 = middle + 1;

        // 두 그룹 병합
        while (index1 &lt;= middle &amp;&amp; index2 &lt;= end) {
            if (tempArray[index1] &gt; tempArray[index2]) {
                sortedArray[k] = tempArray[index2];
                k++;
                index2++;
            } else {
                sortedArray[k] = tempArray[index1];
                k++;
                index1++;
            }
        }
        while (index1 &lt;= middle) {
            sortedArray[k] = tempArray[index1];
            k++;
            index1++;
        }
        while (index2 &lt;= end) {
            sortedArray[k] = tempArray[index2];
            k++;
            index2++;
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 11004 - K번째 수]]></title>
            <link>https://velog.io/@cher_blair/%EB%B0%B1%EC%A4%80-11004-K%EB%B2%88%EC%A7%B8-%EC%88%98</link>
            <guid>https://velog.io/@cher_blair/%EB%B0%B1%EC%A4%80-11004-K%EB%B2%88%EC%A7%B8-%EC%88%98</guid>
            <pubDate>Sat, 13 Jan 2024 07:06:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/11004">👉🏻 백준 11004 문제 바로가기</a></p>
</blockquote>
<h2 id="❓-퀵-정렬">❓ 퀵 정렬?!</h2>
<blockquote>
<p><code>퀵 정렬</code>: 기준값(pivot)을 선정해 해당 값보다 작은 데이터와 큰 데이터로 분류하는 것을 반복해 정렬하는 알고리즘. <br> 기준값이 어떻게 선정되는지가 시간 복잡도에 많은 영향을 미치고, 평균적인 시간 복잡도는 O(nlogn).</p>
</blockquote>
<ul>
<li>퀵 정렬의 핵심 이론</li>
</ul>
<pre><code>    pivot을 중심으로 계속 데이터를 2개의 집합으로 나누면서 정렬하는 것이 퀵 정렬의 핵심이다.</code></pre><br>

<ul>
<li>퀵 정렬의 과정<pre><code>1. 데이터를 분할하는 pivot을 설정한다.
2. pivot을 기준으로 아래의 과정을 거쳐 데이터를 2개의 집합으로 분리한다.
    1) start가 가리키는 데이터가 pivot이 가리키는 데이터보다 작으면 start를 오른쪽으로 1칸 이동한다.
    2) end가 가리키는 데이터가 pivot이 가리키는 데이터보다 크면 end를 왼쪽으로 1칸 이동한다.
    3) start가 가리키는 데이터가 pivot이 가리키는 데이터보다 크고, end가 가리키는 데이터가 pivot이 가리키는 데이터보다 작으면,
       start, end가 가리키는 데이터는 swap하고 start는 오른쪽, end는 왼쪽으로 1칸씩 이동한다.
    4) start와 end가 만날때까지 1) ~ 3)을 반복한다.
    5) start와 end가 만나면 만난 지점에서 가리키는 데이터와 pivot이 가리키는 데이터를 비교하여 pivot이 가리키는 데이터가 크면
       만난 지점의 오른쪽에, 작으면 만난 지점의 왼쪽에 pivot이 가리키는 데이터를 삽입한다.
3. 분리 집합에서 각각 다시 pivot을 선정한다.
4. 분리 집합이 1개 이하가 될 때까지 과정 1 ~ 3을 반복한다.</code></pre></li>
</ul>
<h2 id="❗️문제-분석하기">❗️문제 분석하기</h2>
<pre><code>  - 퀵 정렬을 구현해 주어진 수를 오름차순으로 정렬한 뒤, k번째 수 출력
  - 퀵 정렬 알고리즘에서 K번쨰 수를 좀 더 빨리 구하기 위한 아이디어 고민
  - 퀵 정렬 알고리즘을 구현하려면 먼저 pivot을 지정해야 함
  - 어떤 값을 pivot으로 정하면 k번째 수를 더 빨리 구할 수 있을까?! 고민
  - pivot 정하는 방법
      ㄴ pivot == K: K번째 수를 찾은 것이므로 알고리즘을 종료한다.
      ㄴ pivot &gt; K: pivot의 왼쪽 부분에 K가 있으므로 왼쪽(S ~ pivot -1)만 정렬을 수행한다.
      ㄴ pivot &lt; K: pivot의 오른쪽 부분에 K가 있으므로 오른쪽(pivot + 1 ~ E)만 정렬을 수행한다.
  - 데이터가 대부분 정렬되어 있는 경우 앞쪽에 있는 수를 pivot으로 선택하면 불필요한 연산이 많아짐
  - 이 문제의 경우 배열의 중간 위치를 pivot으로 설정하고 접근</code></pre><br>

<h2 id="❗️pseudo-code">❗️pseudo code</h2>
<pre><code class="language-java">
  count (숫자의 개수)
  numK (K번째의 수)
  savedArray (숫자 데이터 저장 배열)
  for (count 만큼 반복( {
      savedArray 배열 저장하기
  }
  퀵 소트 실행
  K번째 데이터 출력

  * 별도 함수 구현
  퀵 소트 함수 (시작, 종료, K) {
      피벗 구하기 함수 (시작, 종료)
      if (피벗 == K) {
          종료
      } else if (K &lt; 피벗) {
          퀵 소트 수행하기 (시작, 피벗 -1, K)
      } else {
          퀵 소트 수행하기 (피벗 + 1, 종료, K)
      }
  }
  피벗 구하기 함수 (시작, 종료) {
      데이터가 2개인 경우는 바로 비교하여 정렬
      middle (중앙 값)
      중앙 값을 시작 위치와 swap
      피벗을 시작 위치 값 savedArray[S]로 저장
      i (시작점), j (종료점)
      while (i &lt;= j) {
          피벗보다 큰 수가 나올 때까지 i++
          피벗보다 작은 수가 나올 때까지 j--
          찾은 i와 j 데이터를 swap
      }
      피벗 데이터를 나눠진 두 그룹의 경계 index에 저장하기
      경계 index 리턴
  }
</code></pre>
<br>

<h2 id="✨-solve">✨ solve</h2>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(in.readLine());
        int count = Integer.parseInt(st.nextToken());
        int numK = Integer.parseInt(st.nextToken());
        st = new StringTokenizer(in.readLine());

        int[] savedArray = new int[count];
        for (int i = 0; i &lt; count; i++) {
            savedArray[i] = Integer.parseInt(st.nextToken());
        }
        quickSort(savedArray, 0, count - 1, numK - 1);
        System.out.println(savedArray[numK - 1]);
    }

    private static void quickSort(int[] A, int S, int E, int K) {
        if (S &lt; E) {
            int pivot = partition(A, S, E);
            if (pivot == K) {
                return;
            } else if (K &lt; pivot) {
                quickSort(A, S, pivot - 1, K);
            } else {
                quickSort(A, pivot + 1, E, K);
            }
        }
    }

    private static int partition(int[] A, int S, int E) {
        if (S + 1 == E) {
            if (A[S] &gt; A[E]) {
                swap(A, S, E);
                return E;
            }
        }
        int M = (S + E) / 2;
        swap(A, S, M); // 중앙 값을 1번째 요소로 이동
        int pivot = A[S];
        int i = S + 1, j = E;
        while (i &lt;= j) {
            while (j &gt; S + 1 &amp;&amp; pivot &lt; A[j]) {
                j--;
            }
            while (i &lt;= E &amp;&amp; pivot &gt; A[i]) {
                i++;
            }
            if (i &lt;= j) {
                swap(A, i++, j--);
            }
        }
        // 피벗 데이터를 나눠진 두 그룹의 경계 index에 저장하기
        A[S] = A[j];
        A[j] = pivot;
        return j;
    }

    private static void swap(int[] A, int i, int j) {
        int temp = A[i];
        A[i] = A[j];
        A[j] = temp;
    }
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>