<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hyun-jii.log</title>
        <link>https://velog.io/</link>
        <description>Backend Developer👩‍💻</description>
        <lastBuildDate>Thu, 18 Apr 2024 15:42:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hyun-jii.log</title>
            <url>https://images.velog.io/images/hyun-jii/profile/f25e7bde-130c-47e3-9d3c-95ec9f71be66/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hyun-jii.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hyun-jii" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Exception 에 대한 알아보자]]></title>
            <link>https://velog.io/@hyun-jii/Exception-%EC%97%90-%EB%8C%80%ED%95%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@hyun-jii/Exception-%EC%97%90-%EB%8C%80%ED%95%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Thu, 18 Apr 2024 15:42:23 GMT</pubDate>
            <description><![CDATA[<p>개발을 하다 보면 exception 일 때 <code>try~catch</code> 로 처리하는 경우가 있고, <code>Runtime Exception</code> 으로 처리되게 하는 경우가 있다. 처리하는 방식이 어떻게 다른지, 어떤 방법이 더 선호되는지 알아보자.</p>
<p>우선 Exception 의 개념에 대해 먼저 알아보자.</p>
<h2 id="exception-이란">Exception 이란?</h2>
<p><code>Exception</code> 은 한마디로 예외이다. 그렇다면 예외와 에러의 차이는 무엇일까?</p>
<ul>
<li><code>예외</code>란 개발자가 구현한 로직 내에서 발생하는 것으로, 예측하여 처리가 가능하다.</li>
<li><code>에러</code>란 시스템 내 비정상적인 상황이 생겼을 때 발생하며, 미리 처리 및 예측이 불가하다.</li>
</ul>
<p>이제 Exception 에 대해 간단히 알았으니, Exception 에 어떤 종류가 있는지 알아보자.  </p>
<h2 id="exception-의-종류">Exception 의 종류</h2>
<p><img src="https://velog.velcdn.com/images/hyun-jii/post/7c7774a6-3c42-451c-b0c2-cb3e3faccce4/image.png" alt="">  </p>
<p>다음 그림을 보면 Exception 이 <code>Checked Exception</code> 과 <code>Unchecked Exception</code> 두가지로 나누어져 있는 것을 볼 수 있다.<br>이제 이 두가지의 차이를 알아보자.</p>
<h3 id="checked-exception">Checked Exception</h3>
<p>compile 단계에서 발생하는 exception 으로 반드시 예외에 대한 처리를 선언해야 한다.
    ex) IOException, FileNotFoundException 등</p>
<ul>
<li><p><code>try ~ catch</code> 로 예외 처리</p>
<pre><code class="language-java">  public void test() {
      try {
          File file = new File(&quot;text.txt&quot;);
          FileReader fr = new FileReader(file);

      } catch (FileNotFoundException e) {
          e.printStackTrace();
      }

</code></pre>
</li>
</ul>
<ul>
<li><p><code>throws FileNotFoundException</code> 으로 예외를 전가, 상위 메서드의 선언부에도 시그니처 선언</p>
<pre><code class="language-java">  public void test() throws FileNotFoundException {
      File file = new File(&quot;text.txt&quot;);
      FileReader fr = new FileReader(file);
  }

</code></pre>
</li>
</ul>
<h3 id="unchecked-exception">Unchecked Exception</h3>
<p>실행 단계에서 발생하는 exception 으로 예외에 대한 처리를 미리 할 수 없다.
    ex) RuntimeException, NullPointerException, IllegalArgumentException 등</p>
<pre><code class="language-java">    public static void main(String[] args) {
        int[] array = new int[5];

        System.out.println(arr[6]);  // ArrayIndexOutOfBoundsException 발생
    }

</code></pre>
<p>checked exception 은 예외처리를 강제하여, try~catch 를 사용하여 예외를 잡거나, throws Exception 으로 예외를 전가하여 예외를 처리해야하고, 예외를 처리하 않으면 컴파일 오류가 발생하는 반면, RuntimeException 의 경우 예외 처리를 강제하지 않으며, 실행 시 예외가 발생하므로 컴파일 오류가 발생하지 않는다.</p>
<h3 id="checked-exception-에-대한-오해">Checked Exception 에 대한 오해</h3>
<p>일반적으로 스프링에서 Checked Exception 은 예외 발생 시 롤백이 되지않고, Unchecked Exception은 예외 발생 시 롤백이 되는 것으로 알고 있다.</p>
<p>하지만 이는 스프링의 기본 설정일 뿐이고, Checked Exception 또한 롤백되게 설정할 수 있다.</p>
<pre><code class="language-java">@Override
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}</code></pre>
<ul>
<li>위의 코드를 보면 rollabackOn의 경우 기본적으로 RuntimeException 과 Error 시에만 롤백을 한다.</li>
</ul>
<pre><code class="language-java">@Transactional(rollbackFor = {RuntimeException.class, Error.class})
public void test() {

}</code></pre>
<ul>
<li>@Transactional 의 경우 기본적으로 사용하면 위와 같은 설정으로 RuntimeException 과 Error 시에만 롤백한다.</li>
</ul>
<p>그렇다면 Checked Exception 도 롤백되게 설정하려면 어떻게 해야할까?</p>
<pre><code class="language-java">@Transactional(rollbackFor = Exception.class)
public void test() {

}</code></pre>
<ul>
<li>다음과 같이 rollbackFor 설정에 특정 Exception 이 발생하면 롤백하도록 설정할 수 있다. 위와 같이 <code>rollbackFor = Exception.class</code> 로 작성하면, 모든 Exception 에 대해 롤백된다.</li>
</ul>
<h3 id="runtimeexception-이-더-좋다">RuntimeException 이 더 좋다?</h3>
<p>실무에서 경험해보면 try ~ catch 를 사용하여 예외를 처리하는 경우를 본 경우가 드물다. 또한 RuntimeException 을 사용하는 것을 권장한다. 왜 그럴까?</p>
<p>Checked Exception 을 사용할 때의 몇 가지 단점 때문이다.</p>
<ul>
<li><p>Checked Exception 은 예외를 복구할 수 있을 때 사용한다. 
Checked Exception 을 사용하는 경우는 예외를 복구할 수 있는 경우이다. 하지만 일반적으로 예외를 복구할 수 있는 상황은 거의 없다. 예를 들어 사용자가 잘못된 값을 보낸 경우 exception 이 발생했을 때, exception 이 발생한 후 해당 프로세스가 종료되며, 재요청을 보내는 것이지 예외가 복구되는 것이다 아니다.</p>
</li>
<li><p>open / closed 원칙 위배
checked exception 에서 예외를 상위 메소드로 던질 경우, 상위 메소드 선언부에도 시그니처를 변경해야한다. 만약 하위 메소드에서 checked exception 을 발생시키면, 이를 호출하는 모든 상위 메서드의 선언부를 변경해야한다. 이는 기존 코드의 변경을 최소화하여 수정을 가능하게 해야한다는 open/closed 원칙에 위배된다.</p>
</li>
<li><p>다른 언어들은 checked exception 이 없다.
java 외에 다른 프로그래밍 언어는 checked exception 이라는 개념이 없다. 다른 언어로 개발한다고 exception 처리에 문제가 있는 것은 아니므로 checked exception 이라는 개념이 굳이 필요없다. 물론 장점도 있지만 종속성 비용이 더 크다.</p>
</li>
</ul>
<h2 id="마무리">마무리</h2>
<p>Checked Exception 에 대한 논란은 지속되어 왔다. Checked Exception 은 자바의 실수다 아니다로 많은 논란이 있었는데, 현재는 Checked Exception 을 사용하지 않는 방향으로 논란이 종결된 것 같다. Checked Exception 은 대규모의 프로그래밍을 개발할 때 오히려 생산성을 떨어트린다는 결과가 나왔고, 현재 Java 도 함수형 인터페이스에서 Checked Exception 을 허용하지 않고 있다.</p>
<p>이렇게 Exception 에 관련해서 개념과 논란에 대해 알아보며, 어떤 처리 방식이 선호되고, 단점이 무엇인지 알게된 시간이었다.</p>
<h2 id="references">References</h2>
<p><a href="https://www.nextree.co.kr/p3239/">https://www.nextree.co.kr/p3239/</a></p>
<p><a href="https://jangjjolkit.tistory.com/3">https://jangjjolkit.tistory.com/3</a></p>
<p><a href="https://velog.io/@sangmin7648/%EB%8B%B9%EC%8B%A0%EC%9D%98-Checked-Exception%EC%9D%80-%ED%95%84%EC%9A%94-%EC%97%86%EB%8B%A4">https://velog.io/@sangmin7648/당신의-Checked-Exception은-필요-없다</a></p>
<p><a href="https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%98%88%EC%99%B8-%EB%8D%98%EC%A7%80%EA%B8%B0throw-%EC%98%88%EC%99%B8-%EC%97%B0%EA%B2%B0Chained-Exception">https://inpa.tistory.com/entry/JAVA-☕-예외-던지기throw-예외-연결Chained-Exception</a>
<a href="https://velog.io/@eastperson/Java%EC%9D%98-Checked-Exception%EC%9D%80-%EC%8B%A4%EC%88%98%EB%8B%A4-83omm70j">https://velog.io/@eastperson/Java%EC%9D%98-Checked-Exception%EC%9D%80-%EC%8B%A4%EC%88%98%EB%8B%A4-83omm70j</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[@Transactional 동작 과정에 대해 알아보자]]></title>
            <link>https://velog.io/@hyun-jii/Transactional-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@hyun-jii/Transactional-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Tue, 25 Jul 2023 16:15:00 GMT</pubDate>
            <description><![CDATA[<h2 id="transactional-이란">@Transactional 이란?</h2>
<p>Spring 의 핵심 개념 중 하나인 <code>AOP</code> 기술을 활용한 예 중 하나로, Spring에선 <code>Spring AOP</code> 방식으로 동작한다. 
메서드나 클래스에 어노테이션을 붙이면 해당 메서드 또는 클래스의 작업들을 하나의 트랜잭션으로 관리하겠다는 의미이다.
그렇다면 AOP 가 무엇일까? AOP 에 대해 잠시 알아보자.</p>
<h3 id="aop란">AOP란?</h3>
<p><code>Aspect Object Programming</code> 으로 관점 지향 프로그래밍 방법이다. 관심사의 분리라고도 말하며, 기존 OOP의 단점을 보완한 방식이다. 여러 클래스 사이에 공통적으로 포함되어있는 로그, 트랜잭션, 예외 처리 코드등 부가 기능을 비지니스 로직과 분리하여 관리하는 방식이다. 그로 인해 비지니스 로직은 비지니스 로직에 초점을 맞출 수 있고, 코드의 중복을 줄일 수 있으며, 유지 보수를 원활하게 할 수 있다.</p>
<p>AOP 에 대해 간단히 알아보았으니 본격적으로 <code>@Transactional 의 동작 과정</code>에 대해 알아보자</p>
<h2 id="transactional-동작-과정">@Transactional 동작 과정</h2>
<p>@Transactional 은 <code>Spring AOP</code> 방식으로 동작한다. 그리고 Spring AOP 방식은 프록시 패턴을 활용한다. <code>프록시 패턴</code>을 활용하는 이유는 Target 에 코드를 추가하지 않고, 부가 기능을 추가할 수 있기 때문이다.  </p>
<ol>
<li>먼저 @Transactional 어노테이션이 적용된 메서드를 호출하면, Spring 은 해당 메서드를 감싸는 동적 프록시 객체를 생성한다. <blockquote>
<p>동적 프록시 생성 방법 : JDK Proxy 와 CGLIB (아래 참고)</p>
</blockquote>
</li>
</ol>
<ol start="2">
<li>생성된 동적 프록시는 메서드가 호출될 때 중간에 가로채서 트랜잭션을 시작하고, 트랜잭션 컨텍스트를 실행한다.(트랜잭션을 실행하는 코드는 JDBC 와 동일)</li>
<li>트랜잭션을 컨텍스트를 실행한 후 원본 메서드를 호출하여, 비지니스 로직을 실행한다.</li>
<li>메서드 내에서 데이터베이스 작업 등의 트랜잭션 처리가 발생할 때, 프록시는 이를 트랜잭션 컨텍스트에 위임해서 실행한다.</li>
</ol>
<p>위 설명을 그림으로 표현하면 아래 그림과 같다고 볼 수 있다.
<img src="https://velog.velcdn.com/images/hyun-jii/post/63e11f82-7201-4b51-b807-60eaab41918c/image.png" alt=""></p>
<h3 id="jdbc-트랜잭션-방법">JDBC 트랜잭션 방법</h3>
<p>JDBC 에서 트랜잭션을 실행하는 코드이다. @Transactional 도 내부적으론 아래와 같은 코드를 실행한다. </p>
<pre><code class="language-java">Connection connection = dataSource.getConnection();

try (connection) {
    connection.setAutoCommit(false);

    // DB 작업

    connection.commit();
} catch (SQLException e) {
    connection.rollback();
}</code></pre>
<h2 id="동적-프록시-객체-생성-방법">동적 프록시 객체 생성 방법</h2>
<p>동적 프록시 객체를 생성하는 방법은 2가지가 있다. Spring 은 JDK Proxy 를 기본으로 사용하고, Spring Boot 는 CGLib 방식을 기본으로 사용한다.
<img src="https://velog.velcdn.com/images/hyun-jii/post/3f979c27-7b1e-4c3d-9564-624308bc0ddc/image.png" alt=""></p>
<h3 id="jdk-proxy">JDK Proxy</h3>
<p>Target 의 상위 인터페이스를 상속 받아 프록시를 생성하며 다음과 같은 특징을 가진다.</p>
<ul>
<li>프록시를 생성할 클래스가 인터페이스를 구현하지 않고, 구체 클래스에 의존한다면 해당 클래스를 찾을 수 없어 런타임 에러가 발생</li>
<li>인터페이스를 반드시 구현해야하며, 리플렉션을 사용한 방식으로 비용이 더 비싸다</li>
<li>Spring default</li>
</ul>
<h3 id="cglibcode-generation-library-proxy">CGLIB(Code Generation Library) Proxy</h3>
<p>Target 을 상속 받아 프록시를 생성하며 다음과 같은 특징을 가진다.</p>
<ul>
<li>프록시를 생성할 클래스가 인터페이스를 구현하고 있지 않은 경우</li>
<li>클래스의 바이트 코드를 조작하여 서브클래스를 동적으로 생성하는 라이브러리</li>
<li>인터페이스를 구현할 필요가 없고, 구체 클래스에 의존하므로 런타임 에러가 발생할 확률이 적음</li>
<li>Spring Boot default</li>
</ul>
<h2 id="references">References</h2>
<p><a href="https://blogshine.tistory.com/291">https://blogshine.tistory.com/291</a>
<a href="https://jeong-pro.tistory.com/228">https://jeong-pro.tistory.com/228</a>
<a href="https://minkukjo.github.io/framework/2021/05/23/Spring">https://minkukjo.github.io/framework/2021/05/23/Spring</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA Naming Strategy, To upper snake case]]></title>
            <link>https://velog.io/@hyun-jii/JPA-Naming-Strategy-To-upper-snake-case</link>
            <guid>https://velog.io/@hyun-jii/JPA-Naming-Strategy-To-upper-snake-case</guid>
            <pubDate>Mon, 17 Apr 2023 16:16:53 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>JPA 프로젝트를 진행하던 와중, DB 연결 전 h2로 진행하다가 실제 DB와 연결을 했더니 갑자기 테이블이 존재하지 않는다는 에러가 발생했다.</p>
<ul>
<li>에러 문구<blockquote>
<p>Table &#39;test.notice_list&#39; doesn&#39;t exist</p>
</blockquote>
</li>
</ul>
<p>???? 우선 DB를 확인해봤는데 테이블은 정상적으로 존재했다.
구글링을 찾아보니 JPA 네이밍 전략때문이었다.
현 프로젝트의 DB는 모두 UPPER SNAKE CASE를 사용하는데, LOWER SNAKE CASE 전략이 적용되고 있던 것이다..!
전에는 우연찮게 DB와 네이밍 전략이 맞아서 해당 문제가 발생하지 않았었다. </p>
<p>그렇다면 네이밍 전략에는 어떤 것이 있는지 알아보자.</p>
<h2 id="jpa-naming-strategy">JPA Naming Strategy</h2>
<p>JPA 네이밍 전략은 두가지 방식이 있다.</p>
<ul>
<li>Implict Naming Strategy (암시적 네이밍 전략)</li>
<li>Physical Naming Strategy (물리적 네이밍 전략)</li>
</ul>
<h3 id="implict-naming-strategy">Implict Naming Strategy</h3>
<p>일명 암시적 네이밍 전략으로, 말 그대로 명시적으로 설정하지 않은 것을 암시적으로 설정해주는 것이다.
우리가 Entity를 구성할 때, 테이블명을 <code>@Table</code>, 컬럼명을 <code>@Column</code> 이라고 지정하는 경우가 있다. 이렇게 @Table, @Column으로 이름을 지정하면 명시적으로 지정하는 것이 된다. 암시적 네이밍은 이렇게 명시적으로 지정되지 않은 곳에 적용된다.</p>
<p>JPA에서 <code>default</code>로 설정된 암시적 네이밍 규칙은 <code>ImplicitNamingStrategyJpaCompliantImpl</code> 방식을 사용한다.
이는 이름을 Entity에 설정한 컬럼명 그대로 가는 것이다. 
일반적으로 camelCase로 작성하므로 <code>userId</code> 이런 방식일 것이다.</p>
<blockquote>
<p>그렇다면 아무 설정도 하지 않은 나는 lower snake 규칙이 적용된것일까??</p>
</blockquote>
<p>찾아보니 spring 에서 제공하는 네이밍 규칙이 있었다. <code>org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy</code> 이 방식은 <code>ImplicitNamingStrategyJpaCompliantImpl</code> 방식을 상속받아 구현되어 있었다.</p>
<pre><code class="language-java">package org.springframework.boot.orm.jpa.hibernate;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitJoinTableNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;

public class SpringImplicitNamingStrategy extends ImplicitNamingStrategyJpaCompliantImpl {
    public SpringImplicitNamingStrategy() {
    }

    public Identifier determineJoinTableName(ImplicitJoinTableNameSource source) {
        String name = source.getOwningPhysicalTableName() + &quot;_&quot; + source.getAssociationOwningAttributePath().getProperty();
        return this.toIdentifier(name, source.getBuildingContext());
    }
}</code></pre>
<h3 id="physical-naming-strategy">Physical Naming Strategy</h3>
<p>물리적 네이밍 전략으로, 명시적, 암시적 전략 보다 나중에 실행되는 전략이다.
즉, 암시적, 명시적 전략을 설정해도 마지막에 무조건 물리적 네이밍 전략의 설정값으로 적용된다는 것이다.
예를 들어 명시적으로 <code>@Table(name=&quot;notice_list&quot;)</code> 설정하고, 물리적 네이밍 전략으로 <code>camelCase</code> 를 설정하면 테이블명은 최종적으로 <code>noticeList</code> 가 된다.</p>
<p>DB의 경우 어떤 테이블은 소문자고, 어떤 테이블은 대문자를 쓰진 않을 것이다. 프로젝트에 따라 일괄적인 네이밍을 사용할텐데, 암시적 전략을 사용하면 명시적 전략과 별도로 구분되므로 관리가 용이하지 않다.</p>
<p>그러므로 필자도 관리의 용이성을 위해 물리적 네이밍 전략을 사용하기로 했다.</p>
<p>물리적 네이밍 전략을 보니 이미 구현되어 있는 것들이 있었다.</p>
<blockquote>
<ul>
<li>CamelCaseToUnderscoresNamingStrategy -&gt; lower snake </li>
</ul>
</blockquote>
<ul>
<li>PhysicalNamingStrategyStandardImpl -&gt; Entity 변수명 그대로</li>
</ul>
<p>위 두가지가 있었는데 대문자로 변환해주는 건 없다..그러므로 커스텀해야한다..
하지만 <code>CamelCaseToUnderscoresNamingStrategy</code> 이 전략에서 대문자로만 변경하면 되니 코드를 조금만고치면 된다. </p>
<h3 id="custom-physical-naming-strategy---upper-snake">custom physical naming strategy -&gt; upper snake</h3>
<pre><code class="language-java">public class UpperCaseNamingStrategy implements PhysicalNamingStrategy {

    public UpperCaseNamingStrategy() {
    }

    public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return this.apply(name, jdbcEnvironment);
    }

    public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return this.apply(name, jdbcEnvironment);
    }

    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return this.apply(name, jdbcEnvironment);
    }

    public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return this.apply(name, jdbcEnvironment);
    }

    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return this.apply(name, jdbcEnvironment);
    }

    private Identifier apply(Identifier name, JdbcEnvironment jdbcEnvironment) {
        if (name == null) {
            return null;
        } else {
            StringBuilder builder = new StringBuilder(name.getText().replace(&#39;.&#39;, &#39;_&#39;));

            for(int i = 1; i &lt; builder.length() - 1; ++i) {
                if (this.isUnderscoreRequired(builder.charAt(i - 1), builder.charAt(i), builder.charAt(i + 1))) {
                    builder.insert(i++, &#39;_&#39;);
                }
            }

            return this.getIdentifier(builder.toString(), name.isQuoted(), jdbcEnvironment);
        }
    }

    protected Identifier getIdentifier(String name, boolean quoted, JdbcEnvironment jdbcEnvironment) {
        if (this.isCaseInsensitive(jdbcEnvironment)) {
            name = name.toUpperCase(Locale.ROOT);
        }

        return new Identifier(name, quoted);
    }

    protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
        return true;
    }

    private boolean isUnderscoreRequired(char before, char current, char after) {
        return Character.isLowerCase(before) &amp;&amp; Character.isUpperCase(current) &amp;&amp; Character.isLowerCase(after);
    }
}
</code></pre>
<ul>
<li><code>physicalNamingStrategy</code> 를 implements 해서 구현하면 된다.</li>
<li>기존의 <code>CamelCaseToUnderscoresNamingStrategy</code> 를 참고했으므로 <code>getIdentifier</code> 메서드만 수정해서 넣어주었다. </li>
</ul>
<pre><code class="language-java">protected Identifier getIdentifier(String name, boolean quoted, JdbcEnvironment jdbcEnvironment) {
        if (this.isCaseInsensitive(jdbcEnvironment)) {
            name = name.toUpperCase(Locale.ROOT);
        }

        return new Identifier(name, quoted);
    }</code></pre>
<p>위와 같이 커스텀 물리적 규칙을 만들어주고 application.yml 에 적용해 주면 된다.</p>
<pre><code class="language-yml">spring:
    jpa:
      hibernate:
        naming:
          physical-strategy: {패키지 경로}.UpperCaseNamingStrategy</code></pre>
<p>이렇게 설정하면 원하던 <code>upper snake</code> 형식으로 이름이 정해진다.</p>
<h2 id="결론">결론</h2>
<p>최종적으로 위의 방식으로 lower snake -&gt; upper snake 형식을 적용할 수 있었다.
JPA가 Entity에 작성된 이름들을 변환하는 것은 알고 있었지만, default로 왜 이런 규칙이 적용되는지, 물리적 전략, 암시적 전략으로 나뉘어져 있는 건 몰랐다. 역시 에러와 삽질이 지식 쌓기에는 굿,,,</p>
<h2 id="references">References</h2>
<p><a href="https://www.baeldung.com/hibernate-naming-strategy">https://www.baeldung.com/hibernate-naming-strategy</a>
<a href="https://velog.io/@mumuni/Hibernate5-Naming-Strategy-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC">https://velog.io/@mumuni/Hibernate5-Naming-Strategy-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Cloud Config 개념 및 설정 방법]]></title>
            <link>https://velog.io/@hyun-jii/Spring-Cloud-Config-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@hyun-jii/Spring-Cloud-Config-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 14 Feb 2023 07:32:56 GMT</pubDate>
            <description><![CDATA[<h3 id="spring-cloud-config-란">Spring Cloud Config 란?</h3>
<p><img src="https://velog.velcdn.com/images/hyun-jii/post/8b2116f3-8105-4714-99d2-2343d24572d1/image.png" alt=""></p>
<p>위 그림으로 간단히 설명 가능하다. Spring Cloud Config 는 여러 서비스들의 설정 정보들을 중앙으로 관리하기 용이하도록 도와주는 것이다.</p>
<p>그렇다면 왜 설정 정보를 외부에 따로 중앙으로 관리할까?
-&gt; 예를 들어 A, B, C 서비스가 있다. 각각의 서비스들은 같은 설정 정보를 사용한다. 만약 각각의 서비스들에 설정 정보를 관리한다면, 설정 정보를 변경할 때 모든 서비스의 설정 정보를 수정해야한다. 그리고 설정 정보를 변경했으므로 모두 재배포를 해야한다. 여간 불편한 일이 아닐 수 없다. 그와중에 한군데의 설정 정보를 잘못 변경하면 장애로 이어질 수 있다.
그러므로 설정 정보의 변경시 관리 용이를 위해 외부에 중앙으로 관리하는 것이 관리적인 측면에서 편하다.</p>
<hr>
<p>이제 사용하는 이유도 알았으니 자세히 알아보자</p>
<ul>
<li>분산 시스템에서 외부화된 설정 정보를 서버 및 클리이언트에게 제공하는 시스템</li>
<li>Config Server 는 외부에서 모든 환경에 대한 정보들을 관리해주는 중앙 서버</li>
<li>설정 정보를 외부로 분리한 후, 설정 정보를 변경하였을 때 별도로 재배포가 필요없음</li>
<li>설정 정보를 분리함으로써 보안적인 측면 강화</li>
</ul>
<p>** 즉, Spring Cloud Config Server 는 Client 와 설정 정보 저장소 간의 중간 다리 역할 이라고 볼 수 있다.** </p>
<p>앞으로 client, sever, 저장소 라는 말을 사용하여 설명할텐데 그림과 비교해봤을 때 이렇게 생각하면 된다.</p>
<blockquote>
</blockquote>
<ul>
<li>Spring Boot MircroSevice -&gt; Spring Cloud Config Client</li>
<li>Spring Cloud Config Server</li>
<li>Git -&gt; 설정 정보 저장소</li>
</ul>
<hr>
<h3 id="설정-정보-저장-방법">설정 정보 저장 방법</h3>
<p>설정 정보를 저장하는 방법은 다양하게 존재한다. 위 그림은 <code>git</code>으로 나와있지만, git 이외에도 다음과 같은 저장 방식을 제공하므로 필요에 맞게 저장소를 선택하면 된다.</p>
<ol>
<li>Git repo</li>
<li>AWS S3 </li>
<li>Redis</li>
<li>JDBC</li>
<li>File</li>
<li>Vault</li>
</ol>
<hr>
<h3 id="spring-cloud-config-설정-정보-갱신">Spring Cloud Config 설정 정보 갱신</h3>
<p>Spring Cloud Config 의 장점 중 하나가 설정 정보를 변경하여도 재배포가 필요없다는 것이다.
저장소에 변경 사항이 발생하면 Config Sever 에서 이를 탐지할 수 있다. 그러나 Client에 변경 사항이 반영되려면 다음과 같은 별도의 조치가 필요하다.
예시에서는 1번 방법을 사용한다.</p>
<ol>
<li>actuator API 사용</li>
<li>Spring Cloud Bus 사용</li>
<li>Watcher 를 통해 변경 여부 확인</li>
</ol>
<hr>
<p>이제 본격적으로 구축 방법에 대해 다음과 같은 순서로 알아보자.</p>
<blockquote>
</blockquote>
<ol>
<li>설정 정보 저장소 구축</li>
<li>Spring Cloud Config Server 구축</li>
<li>기존 서비스(Client)를 Config Sever 와 연동</li>
</ol>
<h2 id="설정-정보-저장소-구축">설정 정보 저장소 구축</h2>
<ol>
<li><p>설정 파일을 저장할 저장소(git repo) 를 만들고 다음과 같이 파일을 생성 후, 중앙 관리할 설정 정보를 옮긴다.</p>
<ul>
<li><p>파일 이름 규칙 : {application name}-{profile}.yml</p>
<pre><code>application.yml // 공통
application-local.yml // 로컬 환경
application-dev.yml // 개발 환경
application-prod.yml // 운영 환경</code></pre></li>
<li><ul>
<li>Client 를 설정할 때 한번 더 설명하지만, 설정한 <code>application name</code> 을 client 쪽에서 맞춰줘야 한다.**</li>
</ul>
</li>
</ul>
</li>
</ol>
<h2 id="spring-cloud-config-server-구축">Spring Cloud Config Server 구축</h2>
<ol>
<li><p>프로젝트를 생성 후에 <code>spring cloud config server</code> 의존성을 주입한다.</p>
<pre><code class="language-xml">&lt;dependency&gt;
     &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
     &lt;artifactId&gt;spring-cloud-config-server&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
<p>** 현재 본인이 사용할 <code>spring boot version</code>에 따라 <code>spring cloud version</code>이 달라지므로 확인하고 버전을 설정해야 한다. **</p>
</li>
<li><p><code>actuator</code> 의존성을 주입한다. 
 Client에서 설정 정보를 갱신하는 방법중에 하나인 actuator 를 사용하려면 의존성을 주입해야한다. 갱신 방법 중 다른 것을 사용한다면 이 과정을 패스해도 된다.</p>
<pre><code class="language-xml">&lt;dependency&gt;
     &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
     &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;
</code></pre>
</li>
<li><p><code>application.yml</code> 에 다음의 설정을 추가한다.</p>
<pre><code class="language-sql"> server:
   port: 8888
 spring:
   application:
     name: config
   cloud:
     config:
       server:
         git:
           uri: https://github.com/~ 
           search-paths: config-file/**
           default-label: main
           username: hyunjee
           password: dafsfsfad</code></pre>
<ul>
<li><p>config 서버는 기본으로 8888 사용</p>
<ul>
<li>uri : 설정 파일이 있는 git repo 주소</li>
<li>default-label : git branch name, 설정 정보 저장소 repo 브랜치 </li>
<li>search-paths : 설정 파일들을 찾을 경로, 설정 정보 저장소에서 설정 정보 파일 위치</li>
<li>username, password : https 를 사용 시 인증 수단</li>
</ul>
<p><strong>저장소 주소를 ssh 나 https 로 설정하면 별도의 인증 값을 더 적어줘야 한다. (privateKey or password)</strong> </p>
</li>
</ul>
</li>
<li><p>메인 클래스에 메인 클래스에 <code>@EnableConfigServer</code> 붙이기</p>
<pre><code class="language-java">@SpringBootApplication
@EnableConfigServer
public class Application {

   public static void main(String[] args) {
       SpringApplication.run(Application.class, args);
   }
}</code></pre>
</li>
<li><p>설정 서버 실행 및 확인</p>
<ul>
<li><p>spring cloud config server 의 <code>endpoint</code> 로 config server가 정상 동작하는지 확인한다.
설정 정보 저장소에 존재하는 <code>application-dev.yml</code> 파일 정보를 읽고 싶다면 
<code>http://localhost:8888/application/dev</code> 라고 요청을 보내면 된다. 
응답값에 설정 정보들이 존재한다면 정상 동작하는 것이다.</p>
</li>
<li><p>Spring Cloud Config Sever Endpoint
<code>application</code>-&gt; application name
<code>label</code> -&gt; 설정 정보 저장소 브랜치</p>
<pre><code class="language-jsx">- /{application}/{profile}[/{label}]
- /{application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties</code></pre>
</li>
</ul>
</li>
</ol>
<h2 id="spring-cloud-config-client-구축">Spring Cloud Config Client 구축</h2>
<ol>
<li>yml 에서 설정 정보를 읽어 올 수 있는 class 를 생성한다.</li>
</ol>
<pre><code class="language-sql">@Getter
@ConfigurationProperties(&quot;com.hyunjee&quot;)
@RefreshScope
public class Config {

    private String minFileSize;
    private String maxFileSize;

}</code></pre>
<ul>
<li><p><code>@RefreshScope</code> 는 설정 정보가 변경되었을 때 설정 정보를 다시 불러오는 역할이며</p>
<p>  설정 정보가 저장되어 있는 git repo 에 수정이 발생했을 때 <code>/actuator/refresh</code> 호출하면 변경된 설정 정보가 반영됨 -&gt; actuator API 를 사용하지 않는다면 필요없다.</p>
</li>
</ul>
<ol start="2">
<li>Config Sever 와의 연동을 위해 Client yml 파일에 해당 설정을 추가한다.</li>
</ol>
<pre><code class="language-yaml">spring:
  application:
    name: application
  profiles:
    active: dev
  config:
    import: optional:configserver:http://localhost:8888</code></pre>
<ul>
<li><p><code>spring.aplication.name</code> - 설정 정보 저장소에서 지정한 application name
 설정 정보 저장소에서 <code>application-dev.yml</code> 이와 같은 형식으로 작성했다면 application name은 application 이다! client에서 설정하는 <code>application name</code>을 보고 설정 정보를 찾기 때문에 반드시 원하는 설정 정보의 <code>application name</code>을 적어줘야 한다.</p>
<ul>
<li><p><code>spring.profiles.active</code> - 구동 환경 설정, dev라고 할 경우 설정 정보 저장소에서 <code>application-dev</code> 파일을 읽게된다.</p>
</li>
<li><p><code>spring.config.import</code> 설정 </p>
<p>config server 주소를 입력, 여러개의 config server를 입력할 수도 있다. 여러개 입력했을 경우 첫번째 서버와 연결이 실패하면 두번째 서버와 연결한다. 
또한 config server와 통신에 실패했을 때 에러를 던지고 서버를 중지하려면 <code>optional</code> 부분을 제거하면 된다. optional을 붙였는데 통신에 실패한다면 외부의 설정 정보를 읽지 못하므로 내부의 설정 정보를 읽게 된다.</p>
</li>
</ul>
</li>
</ul>
<p>이렇게 Spring Cloud Config 의 개념과 구축 방법을 간단히 알아보았다. 사실 실무에서는 예시에 설명된 방식이 아닌 상황에 따른 다양한 방식을 고려하게 될 것이다. 그러므로 다음 편에는 실무에서 Spring Cloud Config 를 적용한 적용기를 올려보려고 한다. </p>
<h2 id="references">References</h2>
<p><a href="https://mangkyu.tistory.com/253">https://mangkyu.tistory.com/253</a>
<a href="https://mangkyu.tistory.com/254">https://mangkyu.tistory.com/254</a>
<a href="https://godekdls.github.io/Spring%20Cloud%20Config/contents/">https://godekdls.github.io/Spring Cloud Config/contents/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2022년 회고]]></title>
            <link>https://velog.io/@hyun-jii/2022%EB%85%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@hyun-jii/2022%EB%85%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 08 Jan 2023 13:34:27 GMT</pubDate>
            <description><![CDATA[<p>처음으로 한 해를 마무리하는 회고를 적어보려고 한다. 사실 여태까지 회고를 적지않은 이유는 어쩌다보니 연말에 작성을 하지 못한 이유도 있고.. 회고를 꼭 적어야하나? 라는 생각 때문이었다.
그러다 어느날 작년에 뭐했지..? 잘 기억이 나지 않는다.. 그래서 올해는 한 해를 되돌아보는 시간을 가져야겠다고 다짐했다. 그렇게 마음먹고 보니 회고라는 것을 써야겠다는 생각이 들었다. 생각해보니 한 해를 단순히 생각으로만 되돌아보는 것보다 글로 적으며 정리하는게 더욱 효과적이지 않는가..? 아 사람들이 이래서 회고를 쓰는구나.. 라는 생각이 들었다.. 아무튼 서론이 길었는데 그래서 올해는 회고를 써본다!</p>
<h2 id="이직">이직</h2>
<hr>
<p>올해 가장 큰 이슈다. 첫 직장을 그만두고 이직을 하게되었다. 약 2년 반을 다녔는데 길다면 길고 짧다면 짧은 시간이었다. 
이직을 한 이유는 처음부터 3년차쯤에 이직을 할 계획을 가지고 있기도 했고, 새로운 비지니스를 다뤄보고 싶었다. 오랜만에 이력서를 수정하고 이직 준비를 하려니 생각보다 쉽지는 않았다. 이래서 미리미리 이력 사항 정리해놓으라고 하는건데... 알면서도 안하다가 또 깨달았다..하하 코딩테스트도 준비하고 면접도 준비하고... 오랜만에 준비하니 긴장이 많이 됐다.
아무튼 결론은 현재 새로운 회사를 11월부터 다니는 중이다. 전 회사는 B2B 였는데 현 회사는 B2C이며 아예 비지니스 결이 다른 회사다. 아 그리고 스타트업이다. 그래서 모든게 새로웠다. 스타트업이라는 곳에 한번쯤 가보고 싶었는데 막상 다녀보니 이런게 스타트업인가..? 스타트업만의 문화도 있는 것 같고..느껴보지 않았던 조직의 문화를 느끼고 있다. 업무 또한 모든게 새로워서 적응하는데 시간과 노력이 들겠지만 나는 새로운 환경에 적응하는 것을 즐기는 편이므로 현재까지 재밌게 다니고 있는 것 같다.</p>
<h2 id="개발-공부">개발 공부</h2>
<hr>
<p>2022년 한 해에는 주로 전 회사에서 새로운 프로젝트 위주로 담당하게 되었다. 그러다보니 회사에서 사용하고 있지 않은 기술을 도입함으로써 평소 쓰지 않았던 기술들을 공부하고 사용해보는 경험이 많은 해였던 것 같다.
우선 API Gateway 구축 프로젝트를 진행했는데 사실 Spring Cloud Gateway 라는 것은 어렵지 않았지만 이를 구현하는 Webflux가 참 어려웠다.. Webflux를 공부하고 실무에 적용해볼 수 있는 기회가 있어서 너무 좋았지만 어려워서 참 삽질을 많이 한 프로젝트였다. 그리고 중간에 여러번 엎어진 프로젝트라 제일 기간도 길었고 기억에 남는 프로젝트이다.
그리고 신규 솔루션 프로젝트에서 그동안 사용하던 MyBatis를 제거하고 JPA를 도입했다. 그로인해 JPA 강의를 듣게되었는데, 드디어 그 유명한 김영한센세의 JPA강의를 듣게 되었다. 항상 듣고싶었지만 실무에서 JPA를 사용하지않아서 조금 미루고 있었는데..드디어 실무에서 사용해 볼 수 있어서 너무 좋았다. 영한센세의 JPA강의는 소문대로 명강이었다. JPA에 대해 정말 빠르고 정확하게 개념을 이해할 수 있었고 수강생들도 많다보니 수강생 큐엔에이에서 많은 에러에 대한 정보를 알 수 있는 점도 좋았다. 아무튼 JPA 배우실 분은 무조건 인프런에서 김영한님께로 가시면 된다..!
업무 외적으로는 리팩터링과 모던 자바에 대해 스터디를 통해 공부를 진행했었다. 하반기에도 스프링이나 자바 스터디를 계속 하고 싶었는데 이직 준비로 인해 잘 못했던 것 같아서 아쉽긴 하다.</p>
<h2 id="다양한-사건들">다양한 사건들</h2>
<hr>
<ul>
<li><p>2022년은 개인적으로 그렇게 잘 풀린 해는 아니었다.
일단 연초부터 코인으로 많이 날렸다...그래서 연초부터 점심 아껴쓰기, 돈 아껴쓰기 챌린지를 하며 별이별짓을 다했다 ㅎ 그리고 2022년내내 계속 경제 상황도 안좋다보니 주식도 잘 안되고..물론 이건 나에게만 국한된 일은 아니지만... 투자적인 측면으로 일단 잘 안됐다.ㅎ</p>
</li>
<li><p>그리고 마지막으로 쐐기를 박은 사건이 있었는데..
바로 인대가 파열되어 깁스를 하게 되었다... 살면서 한번도 인대를 다친 적도 없고 깁스를 해 본적도 없고.. 발을 다쳐 거동이 불편해져본 것도 처음이었다..
다친 이유도 어이가 없어서 난 아직도 이 상황이 어이가 없다. 매트리스를 바꿨는데 평소 쓰던것보다 조금 높았다. 그래서 아침에 일어나서 침대에 내려오다가 발을 헛디뎌 주저앉았는데 인대가 파열됐다... 근데 정말 아프더라.. 처음엔 다친지도 몰랐다가 밤에 정말 아파 죽는 줄 알았다. 정말 일어날 수도 없었고 발을 조금도 움직일 수 없었다. ㅜㅜ
아무튼 모두들 건강조심하고..발 다리는 절대 다치지말자..진짜 개고생이다. 생각보다 우리의 발은 매우 열일을 하고 있고 조그마한 일에도 발에 힘이 들어간다. 문을 열때도 힘이 들어간다..!! 그리고 고생은 고생대로 하고 병원비도 어마무시하고 집에만 있어야한다. 덕분에 연말에 집에서 칩거만하며 한 해를 마무리했다..</p>
</li>
</ul>
<h2 id="2023년">2023년</h2>
<hr>
<p>어느덧 2023년이 되었다. 이제 이직도 했으니 올 한해는 다시 개발 공부를 적극적으로 해보려고 한다. 스터디도 다시 꾸려보고 싶고 기회가 된다면 새로운 회사에서 사내 스터디도 만들어보고 싶다. 그리고 코로나 탓(?)도 있지만 외부 컨퍼런스를 참석한 경험이 거의 없다. 그래서 올해는 외부 컨퍼런스등도 적극적으로 참여해보려고 한다. 
마지막으로 발이 얼른 다 나았으면 좋겠고..!! 올해는 작년보다 여행을 좀 더 자주 다녀보고 싶다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flyway 정의, DB 마이그레이션 도구]]></title>
            <link>https://velog.io/@hyun-jii/Flyway-%EC%A0%95%EC%9D%98-DB-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EB%8F%84%EA%B5%AC</link>
            <guid>https://velog.io/@hyun-jii/Flyway-%EC%A0%95%EC%9D%98-DB-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EB%8F%84%EA%B5%AC</guid>
            <pubDate>Sat, 03 Sep 2022 12:32:58 GMT</pubDate>
            <description><![CDATA[<p>이번 신규 프로젝트에 Flyway 를 적용하게되어 Flyway 관련 세미나를 진행하게 되었다. 그래서 세미나를 준비하며 정리했던 Flyway 개념에 대해 간단히 소개해보고자 한다. 
그리고 프로젝트 적용기는 추후에 별도로 작성해보려한다.</p>
<h2 id="flyway-란">Flyway 란?</h2>
<ul>
<li>데이터베이스 마이그레이션 툴 오픈소스</li>
<li>Migrate, Clean, Info, Validate, Undo, Baseline, Repair  명령 기반으로 동작 가능</li>
<li>마이그레이션은 SQL 로 작성될수도 있고(PL/SQL, T-SQL도 가능) JAVA 로 작성도 가능</li>
<li>대부분의 데이터베이스 지원</li>
<li>마이그레이션을 위한 JAVA API, Maven, Gradle 플러그인, Spring Boot, Dropwizard 등 다양한 플러그인 제공</li>
</ul>
<p>한마디로 데이터베이스 마이그레이션을 용이하게 도와주는 도구이다!</p>
<h3 id="사용하는-이유">사용하는 이유</h3>
<ul>
<li><p>일반적인 배포 환경 : 로컬 → 통합환경 → 테스트 환경 → 운영 환경
  소스 코드는 형상관리 툴등을 이용해 변경 이력을 관리할 수 있지만, 디비는..? 이력 관리 방법이 없다. 그러므로 Flyway 와 같은 도구를 사용해서 관리할 수 있다.(Ruby 는 소스 코드로 디비 스키마를 관리한다고 한다.)</p>
<p>  <img src="https://velog.velcdn.com/images/hyun-jii/post/184b96c3-0b3b-4588-9094-c84c1f3d9b42/image.png" alt=""></p>
<ul>
<li>변경된 스키마를 일일히 바꿔줘야 한다면 너무 번거롭고, 누락될 가능성도 있다.</li>
</ul>
</li>
</ul>
<h2 id="동작-방식">동작 방식</h2>
<p>Flyway 가 어떤 방식으로 데이터베이스를 마이그레이션하는지 알아보자.</p>
<ol>
<li><p>먼저 flyway 가 database 가 비워져있다면, <code>schema history table</code> (flyway가 데이터베이스의 상태를 추적하는 테이블) 을 생성한다. 이미 존재한다면 생성 하지 않는다.
<img src="https://velog.velcdn.com/images/hyun-jii/post/50694f41-edf8-4436-a629-720d909e3338/image.png" alt=""></p>
</li>
<li><p>database 의 상태를 추적하는 테이블 생성
위 그림에서 데이터베이스가 비어있으므로, 테이블을 생성한다.
<img src="https://velog.velcdn.com/images/hyun-jii/post/b0523779-417c-4aa9-9549-695b7118745c/image.png" alt=""></p>
</li>
</ol>
<ol start="3">
<li>그 다음 파일 시스템 또는 클래스패스를 스캔하여, sql 또는 Java 로 작성된 마이그레이션 파일을 버전 번호 순서대로 마이그레이션 한다.
Vesion1 실행 후, Version2 실행
<img src="https://velog.velcdn.com/images/hyun-jii/post/db4f70f9-b3d7-4b3a-bdf6-460528059a32/image.png" alt=""></li>
</ol>
<ol start="4">
<li>마이그레이션이 적용된 후 schema history table 이 다음과 같이 업데이트 된다. 
테이블에서 버전정보, 체크섬, 성공여부등을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/hyun-jii/post/589a34c7-f05e-42a8-a7ab-05dc2425244e/image.png" alt=""></li>
</ol>
<ul>
<li>초기 상태 완료 후, 버전정보를 저장하고 있기때문에, 다음 마이그레이션시에 이전 버전 또는 현재 버전과 같은 마이그레이션 파일은 무시한다. ex) 현재 버전이 v3라면, 파일명이 v3 또는 v3 보다 작은 숫자는 무시한다.</li>
</ul>
<h2 id="마이그레이션-파일명-규칙">마이그레이션 파일명 규칙</h2>
<p>flyway 는 마이그레이션 대상 파일명과 경로로 파일을 구분하고 스캔한다.
기본 경로인 <code>/resources/db/migration</code> 에 존재하는 파일들을 스캔한다. (경로는 설정으로 변경 가능)
<img src="https://velog.velcdn.com/images/hyun-jii/post/70387f58-4911-4e01-bcf9-a8aa2dcb8071/image.png" alt=""></p>
<ul>
<li>prefix : V 는 버전 마이그레이션, R은 반복마이그레이션, U는 취소 마이그레이션(유료)</li>
<li>version : 버전 마이그레이션에만 사용되며 숫자와 점, 언더바 조합으로 구성 ex )V2, V2.1, V2_3</li>
<li>separator : 설명부분을 구분하기 위한 구분자로 반드시 underscore 2개를 사용해야함 (__)</li>
<li>description : schema_version 테이블에 저장시 설명 컬럼에 저장됨</li>
<li>suffix : 확장자 기본은 .sql</li>
</ul>
<h2 id="command">Command</h2>
<ul>
<li>flyway에서 제공하는 명령어를 살펴보자. 무료 버전 명령어만 살펴보자.(check, snapshot, undo 은 유료)</li>
</ul>
<ol>
<li>Migrate : 스키마를 최신 버전으로 마이그레이션
<img src="https://velog.velcdn.com/images/hyun-jii/post/027bc035-bd26-42db-9d55-bfbb71c7c1c4/image.png" alt=""></li>
</ol>
<ol start="2">
<li>Info : 모든 마이그레이션 상세정보 출력
마이그레이션의 성공여부와 실행 시간등 상세 정보를 알 수 있다.
<img src="https://velog.velcdn.com/images/hyun-jii/post/eba95673-8b08-4f63-8c53-0b4d2652d5ee/image.png" alt=""></li>
</ol>
<ol start="3">
<li>Clean : database 의 schema_version 테이블을 포함한 모든 테이블, 뷰, 프로시저등을 drop (운영환경에서 실행 금지)
<img src="https://velog.velcdn.com/images/hyun-jii/post/c355c32a-f9d7-4cb1-80a6-3a638c7e272d/image.png" alt=""></li>
</ol>
<ol start="4">
<li>Validate : database 에 적용된 마이그레이션 정보의 유효성 검증
데이터베이스에 적용된 마이그레이션과 로컬에서 사용하는 마이그레이션이 일치하는지 확인할 수 있으며, 저장된 체크섬을 확인하여 검증
<img src="https://velog.velcdn.com/images/hyun-jii/post/6ae316c5-ce12-436d-a8e5-b880d2cd9b6c/image.png" alt=""></li>
</ol>
<ol start="5">
<li><p>Baseline : 특정 버전을 base로 사용하고 싶을 떄 사용
flyway 로 관리하기 전의 database 를 baseline 으로 설정할 때 사용</p>
<p><img src="https://velog.velcdn.com/images/hyun-jii/post/96cd43f1-1847-4ce5-ab23-3513e159b49b/image.png" alt=""></p>
</li>
</ol>
<ol start="6">
<li>Repair : flyway_schema_table의 이슈를 수리하기 위해 사용<ul>
<li>실패한 마이그레이션 항목 제거(DDL 트랜잭션을 지원하지 않는 database만 해당)</li>
<li>적용된 마이그레이션의 체크섬, 설명, 타입들을 사용 가능한 마이그레이션의 체크섬으로 재정렬</li>
</ul>
</li>
</ol>
<pre><code>![](https://velog.velcdn.com/images/hyun-jii/post/a464f215-32e8-4c03-a83f-f6fd4ef2c70c/image.png)</code></pre><p>이와 같이 Flyway의 개념 및 동작과정과 기능들을 알아보았다. 확실히 간단하게 데이터베이스 마이그레이션을 할 수 있는 도구라고 생각된다.
하지만 생각보다 부분 유료 기능이 꽤나 많다... 유용하게 쓸 수 있는 Rollback 기능등은 유료다. 자세한 가격은 나와있지 않지만 유료버전은 꽤나 비싸다고 한다..
Liquibase는 롤백기능도 무료로 제공한다고 하니, flyway랑 비교해서 선택하면 될 것 같다..!</p>
<h2 id="references">References</h2>
<p><a href="https://flywaydb.org/documentation/">https://flywaydb.org/documentation/</a>
<a href="https://www.popit.kr/%EB%82%98%EB%A7%8C-%EB%AA%A8%EB%A5%B4%EA%B3%A0-%EC%9E%88%EB%8D%98-flyway-db-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-tool/">https://www.popit.kr/나만-모르고-있던-flyway-db-마이그레이션-tool/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MariaDB] r2dbc batch insert, webflux blocking call 처리]]></title>
            <link>https://velog.io/@hyun-jii/MariaDB-r2dbc-batch-insert-webflux-blocking-call-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@hyun-jii/MariaDB-r2dbc-batch-insert-webflux-blocking-call-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 29 Aug 2022 08:38:32 GMT</pubDate>
            <description><![CDATA[<p>현재 webflux 와 r2dbc 를 활용하여 다중 api를 개발하던 중, r2dbc 에서 batch insert 를 어떻게 사용하는지 찾아보게되었고, 이를 공유해 보고자 한다.</p>
<p>우선 <code>R2dbcCrudRepository</code> 를 사용하고 있었기 때문에, 단순히 batch insert를 위해서는 제공하는 <code>saveAll</code> 메서드를 사용해도된다. </p>
<p>궁금하니 다른 방법도 알아보자.
MariaDB의 공식문서에 r2dbc batch insert 의 예시가 잘 나와있다.</p>
<pre><code class="language-java">        try {
            conf = MariadbConnectionConfiguration.builder()
                .host(&quot;127.0.0.1&quot;).port(3306)
                .username(&quot;username&quot;).password(&quot;password&quot;)
                .database(&quot;db&quot;).build();

            connFactory = new MariadbConnectionFactory(conf);
        }
        catch (Exception e) {
            e.printStackTrace();
        }

        try {
            conn = connFactory.create().block();
            Batch batch = conn.createBatch();

            for(Rule rule : ruleList) {
                batch.add(&quot;INSERT INTO rule (route_path_id, user_id) VALUES (&quot; +
                    rule.getRoutePathId() + &quot;,&#39;&quot; + rule.getUserId() + &quot;&#39;)&quot;);
            }

            return Flux.from(batch.execute()).subscribe();
        }
        catch (Exception e) {

        }
        finally {
            conn.close();
        }</code></pre>
<p>공식 문서를 참고해서 batch insert 로직을 작성했다.  </p>
<pre><code class="language-java">  for(Rule rule : ruleList) {
                batch.add(&quot;INSERT INTO rule (route_path_id, user_id) VALUES (&quot; +
                    rule.getRoutePathId() + &quot;,&#39;&quot; + rule.getUserId() + &quot;&#39;)&quot;);
            }</code></pre>
<p>위와 같이 VALUES 에 문자열 변수를 넣을 땐 <code>&#39;&#39;</code> 작은 따옴표로 감싸는 것을 잊지말자.
그렇지 않으면 <code>unknown column 컬럼명 in field list</code> 라는 에러를 만날 수 있다...</p>
<p>이렇게 작성 후 실행을 해보면 아마 에러가 날 것이다.
왜냐하면 <code>block()</code> 메서드 때문이다.</p>
<p>논블로킹 처리 중 <code>block()</code> 메서드를 사용했기 때문이다.
비동기 작업을 하는 스레드에 block 을 걸어서 동기식으로 만든다면, 비동기 작업의 의미가 없어지게 된다.
커넥션은 동기화가 필요한 작업이므로 block이 필요하다. 그러면 어떻게 해아할까?</p>
<p>비동기 작업 중 blocking call을 하고싶다면 다음과 같은 방식을 사용하면 된다.</p>
<pre><code class="language-java">Mono blockingWrapper = Mono.fromCallable(() -&gt; { 
    return // blocking 코드 작성
});
blockingWrapper = blockingWrapper.subscribeOn(Schedulers.boundedElastic()); </code></pre>
<p>동기식 코드를 <code>Mono.fromCallable</code> 로 감싼 후, <code>Schedulers.boundedElastic()</code> 을 사용하면, subscribe 가 발생했을 때 동기식 코드를 별도의 스레드에서 동작하도록 하는 것이다.
그러므로 동기식 코드가 다른 스레드에서 동작하므로, 에러가 발생하지 않는다.</p>
<ul>
<li><code>Mono.fromCallable</code>
데이터의 방출을 구독 전까지 지연시킨다.
Cold Publisher로, subscribe가 일어나지 않으면 동작하지 않는다.</li>
</ul>
<ul>
<li><code>Schedulers.boundedElastic</code>
별도의 스레드를 생성하며, 생성할 수 있는 스레드 개수의 제한을 둔다.
그러므로 요청이 많을 경우 blocking 작업을 잠시 큐에 넣어놓을 수 있다.
기존에 <code>elastic</code> 은 생성할 수 있는 스레드 개수의 제한을 두지 않는다는 문제로 현재 deprecated 되었다.</li>
</ul>
<p>최종적으로 작성한 코드는 다음과 같다.</p>
<pre><code class="language-java">Mono blocking = Mono.fromCallable(() -&gt; {
            try {
                conf = MariadbConnectionConfiguration.builder()
                    .host(&quot;127.0.0.1&quot;).port(3306)
                    .username(&quot;username&quot;).password(&quot;password&quot;)
                    .database(&quot;db&quot;).build();

                connFactory = new MariadbConnectionFactory(conf);
            }
            catch (Exception e) {
                e.printStackTrace();
            }

            conn = connFactory.create().block();
            Batch batch = conn.createBatch();

            for(Rule rule : ruleList) {
                batch.add(&quot;INSERT INTO rule (route_path_id, user_id) VALUES (&quot; +
                    rule.getRoutePathId() + &quot;,&#39;&quot; + rule.getUserId() + &quot;&#39;)&quot;);
            }

            return Flux.from(batch.execute()).subscribe();
        });

        return blocking.subscribeOn(Schedulers.boundedElastic());</code></pre>
<h2 id="reference">Reference</h2>
<p><a href="https://mariadb.com/docs/connect/programming-languages/java-r2dbc/native/batch/#batch-operations">https://mariadb.com/docs/connect/programming-languages/java-r2dbc/native/batch/#batch-operations</a>
<a href="https://godekdls.github.io/Reactor%20Core/appendixbfaqbestpracticesandhowdoi/#b1-how-do-i-wrap-a-synchronous-blocking-call">https://godekdls.github.io/Reactor%20Core/appendixbfaqbestpracticesandhowdoi/#b1-how-do-i-wrap-a-synchronous-blocking-call</a>
<a href="https://binux.tistory.com/135?category=907689">https://binux.tistory.com/135?category=907689</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[@WebFluxTest 테스트 오류, Bean 을 찾지 못하는 오류]]></title>
            <link>https://velog.io/@hyun-jii/WebfluxTest-%EC%A3%BC%EC%9E%85-%EC%98%A4%EB%A5%98-MockBean-did-not-detect-default-resource-location-for-test-class</link>
            <guid>https://velog.io/@hyun-jii/WebfluxTest-%EC%A3%BC%EC%9E%85-%EC%98%A4%EB%A5%98-MockBean-did-not-detect-default-resource-location-for-test-class</guid>
            <pubDate>Fri, 17 Jun 2022 09:36:46 GMT</pubDate>
            <description><![CDATA[<h3 id="이슈-사항">이슈 사항</h3>
<p>현재 webflux 기반으로 개발을 진행중인데, webflux 기반에서 만든 api 를 테스트하기 위해 테스트 코드를 작성하던 중 만난 문제이다.</p>
<p>먼저 @WebfluxTest 를 사용하여 간단하게 controller 테스트를 진행했다.</p>
<pre><code class="language-java">@WebFluxTest
public class UserKeyControllerTest {

    @Autowired
    private WebTestClient webTestClient;

    @MockBean
    private UserKeyService userKeyservice;

    @Test
    public void getUserKeysTest() {
        this.webTestClient.get().uri(&quot;/api/user&quot;)
            .exchange()
            .expectStatus().isOk();
    }
}</code></pre>
<p>우선 다음과 같이 간단히 작성하고, 테스트를 돌려보는데 실패가 났다.
에러를 보니 주입이 되지 않았다는 에러...</p>
<ul>
<li>해당 빈을 찾을 수 없다는 에러 메시지
<img src="https://velog.velcdn.com/images/hyun-jii/post/2cc004fe-bb24-490c-bc8c-6725008baf89/image.jpeg" alt=""></li>
</ul>
<p>음? 하지만 나는 의존성이 있는 <code>UserKeyService</code>를 <code>@MockBean</code>을 통해 주입해줬는데..??
내가 잠시 놓치는 부분이 있나 싶어서 부랴부랴 UserKeyController 에 가서 다른 의존성이 있는지 확인해봤다. 
<strong>하지만 UserKeyService 외에는 의존성이 없었다.</strong>
에러 내용을 보면 UserKeyController와 전혀 관계가 없는 다른 서비스 파일의 의존성이 주입되지 않았다는 메시지가 나타난다.
왜 갑자기 RoutePathService...?</p>
<p>대체 뭐지...? 뭔가 싶어서 에러 메시지에 나타나는 서비스를 <code>@MockBean</code> 으로 등록했다.
그리고 실행한 뒤 이번엔 또 다른 서비스에서 의존성이 주입되지 않았다는 메시지가 뜨는게 아닌가..!</p>
<p>보아하니 @MockBean을 등록하지 않은 모든 서비스 파일에 대해 이 에러를 뱉고 있다는 생각이..! 그렇다면 대체 왜..? 어디선가 서로 얽혀있는 것인가...?
그러다 디버깅 메시지를 읽어보던 중 눈에 띄는 메시지가 있었다.</p>
<ul>
<li>Did not detect default resource location for test class ....
<img src="https://velog.velcdn.com/images/hyun-jii/post/61e4e4f3-979c-46cd-bc3a-3e196931d268/image.png" alt=""></li>
</ul>
<p>응? 위치를 탐지하지 못한다고...? 황급히 구글링을 해보았다.</p>
<h3 id="해결방법">해결방법</h3>
<p>해당 문제는 <code>@SpringBootApplication</code> 에 선언한 클래스명과 테스트 클래스명이 일치하지 않아서 발생하는 문제였다.
클래스 이름은 <code>UserKeyController</code> 인데 테스트 클래스명은 <code>UserKeyContorllerTest</code> 이기 때문에 위와 같은 메시지가 나타났다. 
이는 <code>@ContextConfiguration</code> 을 통해 클래스명을 지정해주면 해결할 수 있다.</p>
<pre><code class="language-java">@ContextConfiguration(classes = UserKeyController.class)
@WebfluxTest
public class UserKeyControllerTest {</code></pre>
<p>다음과 같이 선언해주니 정상 동작하는 것을 확인할 수 있었다.
아무래도 이름이 달라서 처음부터 @Controller 등 스캔 대상 범위의 모든 파일을 스캔하는 것이 아닌가 싶다.. 그래서 모든 컨트롤러에 해당하는 의존성 관계를 찾을 수 없다는 에러가 나타난 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리팩터링 스터디 회고]]></title>
            <link>https://velog.io/@hyun-jii/%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%EC%8A%A4%ED%84%B0%EB%94%94-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@hyun-jii/%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%EC%8A%A4%ED%84%B0%EB%94%94-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 13 Feb 2022 15:36:41 GMT</pubDate>
            <description><![CDATA[<h2 id=""><img src="https://images.velog.io/images/hyun-jii/post/888d9c9f-83f5-4222-b3c6-2ba79c6692cd/sdfsdfs.PNG" alt=""></h2>
<h3 id="개요">개요</h3>
<p>오랜만에 스터디를 새로 시작했다. 어떤 주제로 스터디를 할까 하다가 스터디원들의 기술 스택에도 영향을 받지않고, 모두에게 의미 있는 스터디를 생각하다 리팩터링 스터디를 해보기로 했다.</p>
<p>마틴 파울러의 <code>리팩터링 2판</code> 책을 가지고 스터디를 진행하였고,
챕터를 나누어, 매주 돌아가면서 발표를 진행하는 방식으로 스터디를 진행했다.
우선 이 책을 통해 얻은점 및 후기를 먼저 애기해보려한다.</p>
<h3 id="책-후기">책 후기</h3>
<p>리팩터링 스터디를 통해 리팩터링의 다양한 방법과 리팩터링이 필요한 순간을 알게 된 것 같다.
리팩터링 방법은 무궁무진하고 방법도 다양하지만, 필자의 경험과 예시를 통해서 리팩터링이라는 것에 대해 좀 더 제대로 알게된 것 같다. 특히 예시가 자세해서 이해하기 좋았다.
책을 읽다보면 우리가 흔히 사용하는 방법<strong>(변수, 함수 이름 짓기, 쪼개기, 옮기기등)</strong>도 있었고, 이렇게도 할 수 있구나 라는 생각도 들게 하는 방법<strong>(생성자 올리기, 서브클래스 위임, 레코드 캡슐화등)</strong>도 있었다.
나도 실무에서 리팩터링을 고민할 때 가장 궁금한 점이 이 코드를 리팩터링 해야하는 것인가 말아야 하는 것인가 언제 리팩터링을 해야하는가 가 가장 모호하면서도 궁금한 부분이었는데 이 책을 통해서 어느정도 해소된 것 같다. </p>
<ul>
<li>코드가 지저분한데 어디서부터 고쳐야 할지 모르겠다.</li>
<li>코드의 확장성을 위한 리팩터링</li>
</ul>
<p>위와 같은 부분들이 특히 많은 도움이 되었다.
그리고 내가 몰랐던 다양한 리팩터링 기법들을 알게되어, 앞으로 코드를 볼때 리팩터링 부분에 있어서 더 넓은 시야를 가지게 된 것 같다.</p>
<h3 id="스터디-후기">스터디 후기</h3>
<p>어쩌다보니 책 후기가 길어진 것 같다.. 스터디 회고인데...
아무튼 책 한권으로 챕터를 나누어 발표하다보니, 스터디가 빠르게 끝났다.
항상 느끼는 것이지만 스터디의 장점은 강제성과 적은 시간의 최대 효율이라고 생각한다.
돌아가면서 리팩터링 기법을 소개하다보니, 빠르게 기법들을 훑을 수 있었다.
그런 부분에서 스터디는 성공적이었지만 아쉬움이 남는 것이 있다.</p>
<p>우선 리팩터링 방법을 소개하면서 코드 예시가 굉장히 많은 책이다.
그래서 한명씩 정리하며 발표할 때, 코드 부분을 어떻게 설명해야 할지 난감했다. 코드의 길이도 굉장히 길었기 때문이다..
그래서 해당 리팩터링 방법의 기본적인 설명과 절차, 간단한 예시 위주로 정리하며 설명하였는데, 설명이나 절차만 보고 이해되지 않는 기법도 꽤나 있었다. (물론 이런 경우는 다같이 책을 보며 진행하기는 했다.)
아무튼 개인적인 생각으로 챕터를 나누어 설명하면서 스터디를 진행하기에 애매한 책이 아니었나 싶다.. 좀 더 좋은 방안을 찾아봐야 할 것 같다.</p>
<p>이상으로 스터디 회고를 마쳐본다..뭔가 주저리주저리 였던 것 같다.. 스터디 방법에 대해 아쉽다고는 했으나 리팩터링 책은 정말 좋다. 지인분께서도 꼭 읽어야하는 필독서라고 하셨는데, 왜 그런 말씀을 하셨는지 이해가 된다. 아직 안 읽어본 사람이 있다면 읽어보는 것을 추천한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프록시(Proxy) vs 게이트웨이(Gateway)]]></title>
            <link>https://velog.io/@hyun-jii/%ED%94%84%EB%A1%9D%EC%8B%9CProxy-vs-%EA%B2%8C%EC%9D%B4%ED%8A%B8%EC%9B%A8%EC%9D%B4Gateway</link>
            <guid>https://velog.io/@hyun-jii/%ED%94%84%EB%A1%9D%EC%8B%9CProxy-vs-%EA%B2%8C%EC%9D%B4%ED%8A%B8%EC%9B%A8%EC%9D%B4Gateway</guid>
            <pubDate>Sun, 23 Jan 2022 13:20:11 GMT</pubDate>
            <description><![CDATA[<h3 id="프록시란">프록시란?</h3>
<p>클라이언트와 서버 사이에 중개자 역할을 하는 서버이다.</p>
<p>같은 프로토콜을 사용하는 네트워크 간의 통신이다. </p>
<p>프록시를 사용하면 보안 측면에 이점이나, 캐시 기능으로 성능을 향상시킬 수 있다.</p>
<p>프록시의 종류에는 여러가지가 있다.</p>
<ul>
<li><p>캐시 프록시 서버  </p>
<ul>
<li>말그대로 캐시를 수행하는 프록시 서버이다. 클라이언트의 요청과 응답을 저장해두었다가 같은 요청이 오면 프록시는 서버로 요청을 보내지 않고, 캐시되어 있는 응답값을 클라이언트에게 보내준다.  </li>
</ul>
</li>
</ul>
<ul>
<li><p>포워드 프록시</p>
<ul>
<li><p>클라이언트의 요청을 프록시 서버에서 받고, 프록시 서버에서 서버에 접근한다.</p>
<p>서버의 응답값을 프록시 서버에서 받고, 이를 클라이언트에게 전송하기 때문에 클라이언트의 요청 ip 를 서버에서 알 수 없다. (서버는 프록시 서버와 통신하기 때문이다.) </p>
<p>ex) 내부망에서 인터넷상의 서버에 요청할 때
<img src="https://images.velog.io/images/hyun-jii/post/bf26425f-2e9e-423c-ba0f-639ab9bc14a9/1.PNG" alt=""></p>
</li>
</ul>
</li>
</ul>
<ul>
<li><p>리버스 프록시</p>
<ul>
<li><p>포워드 프록시의 반대로, 서버를 위한 프록시이다. </p>
<p>클라이언트의 요청을 리버스 프록시 서버에서 받으므로 클라이언트의 요청에 대한 애플리케이션 서버를 알 수 없다. </p>
<p>ex) 인터넷상의 클라이언트가 내부망의 서버를 호출할 때
<img src="https://images.velog.io/images/hyun-jii/post/1f8965fe-41ff-48f2-82f1-711aa29be5ea/2.PNG" alt="">  </p>
</li>
</ul>
</li>
</ul>
<h3 id="게이트웨이란">게이트웨이란?</h3>
<p><img src="https://images.velog.io/images/hyun-jii/post/23fb1f00-b4fc-45ea-bf56-c0af11f78c23/3.PNG" alt=""></p>
<ul>
<li>프록시와 같이 클라이언트와 서버 사이의 중개자 역할을 한다. 
그러나 게이트웨이는 서로 다른 프로토콜을 사용하는 네트워크 간의 통신을 가능하게 한다.
위의 그림에서 프록시는 프로토콜이 <code>HTTP</code> 로 같은 반면, 게이트웨이는 <code>HTTP</code> <code>POP</code>
프로토콜을 변환하는 것을 알 수 있다.</li>
</ul>
<ul>
<li><p>그러나 사실 프록시와 게이트웨이의 차이는 모호하다고 한다.</p>
<p>  프록시와 게이트웨이의 가장 큰 차이점은 프로토콜의 변환 여부인데,</p>
<p>  클라이언트의 http 와 서버의 http 가 버전이 다르기 때문에, 프록시도 프로토콜을 변환 할 수 있다.</p>
</li>
</ul>
<h2 id="references">References</h2>
<p><a href="https://coding-start.tistory.com/342">https://coding-start.tistory.com/342</a>
<a href="https://firework-ham.tistory.com/23">https://firework-ham.tistory.com/23</a>
<a href="https://sujinhope.github.io/2021/06/13/Network-%ED%94%84%EB%A1%9D%EC%8B%9C(Proxy)%EB%9E%80,-Forward-Proxy%EC%99%80-Reverse-Proxy.html">https://sujinhope.github.io/2021/06/13/Network-프록시(Proxy)란,-Forward-Proxy와-Reverse-Proxy.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Cloud Gateway 개념]]></title>
            <link>https://velog.io/@hyun-jii/Spring-Cloud-Gateway-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@hyun-jii/Spring-Cloud-Gateway-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Mon, 04 Oct 2021 15:00:01 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-cloud-gateway-란">Spring Cloud Gateway 란?</h2>
<p> Spring Reactive 생태계 위에 Spring Cloud 팀이 구현한 API Gateway 이다.
그렇다면 API Gateway 에 대해 잠시 알아보자.</p>
<h3 id="api-gateway">API Gateway</h3>
<ul>
<li>reverse proxy 를 향상 시킨 것이다.</li>
<li>Netflix zuul, Amazon API Gateway, Spirng Cloud Gateway 등이 있다.</li>
</ul>
<p>이제 Spring Cloud Gateway 특징을 알아보자</p>
<ul>
<li>Netty 서버를 활용하여, 비동기 요청 처리를 제공한다.</li>
<li>사용하기 쉬운 Pedicate 과 filters</li>
<li>Circuit Breaker 통합</li>
<li>Spring Cloud DiscoveryClient 통합</li>
<li>Rate Limiting</li>
<li>Path 재작성</li>
</ul>
<p>위와 같은 특징들이 있는데, 우선 기본적인 동작방식과 특징 먼저 알아보자.</p>
<h2 id="spring-cloud-gateway-동작-과정">Spring Cloud Gateway 동작 과정</h2>
<p><img src="https://images.velog.io/images/hyun-jii/post/5083b9e2-14bd-4bce-94d5-cc159fa7bfd3/gateway1.PNG" alt=""></p>
<ol>
<li>Gateway Client 에서 요청을 보낸다.</li>
<li>Gateway Handler Mapping 에서 요청이 해당 경로(route)와 일치하다고 생각되면, Gateway Web Handler 로 전송한다.</li>
<li>Gateway Web Handler 는 요청과 관련된 filter 로 요청을 전송한다.</li>
<li>필터는 pre filter 와 post filter 로 나누어지는데, 우선 pre filter 가 실행된다.</li>
<li>프록시 요청이 실행된다.</li>
<li>post filter 가 실행된 후, client 에게 응답된다.</li>
</ol>
<h3 id="주요-특징">주요 특징</h3>
<ul>
<li><p>Route: Gateway의 기본 블록이라고 보면 된다.
  목적지 URI 와  predicate, filter 를 정의하는 곳이다.</p>
<pre><code class="language-yaml">  spring:
    cloud:
      gateway:
        routes:
        - id: after_route
          uri: https://example.org
          predicates:
          - Cookie=mycookie,mycookievalue</code></pre>
</li>
<li><p>Predicate : 일종의 조건문이며, client의 요청이 설정한 조건과 일치하는지 확인하는 곳이다.
  무조건 1개 이상 설정해야 한다.
  예를 들어 <code>Path:/api/**</code> 이와 같이 설정했다면, 
  client 요청이 <code>api/route/1</code>  이런 형식으로 들어와야 해당 조건이 일치하게 된다.
  위 예시는 <code>Path</code> 라는 조건을 활용한 것이고, Path 외에도 다양한 조건이 있다.
  (Cookie, After, Before, Between, Header 등등)</p>
<p>  <a href="https://cloud.spring.io/spring-cloud-gateway/reference/html/#gateway-request-predicates-factories">https://cloud.spring.io/spring-cloud-gateway/reference/html/#gateway-request-predicates-factories</a></p>
<pre><code class="language-yaml">  spring:
    cloud:
      gateway:
        routes:
        - id: after_route
          uri: https://example.org
          predicates:
          - After=2017-01-20T17:42:47.789-07:00[America/Denver]</code></pre>
</li>
<li><p>Filter:  요청 또는 응답을 다음단계로 보내기 전에 수정할 수 있는 곳이다.
  Spring Cloud Gateway는 요청 또는 응답을 수정하기 위한 많은 내장 GatewayFilter Factory  포함되어 있다. 그리고 내장 외에 custom filter 를 정의하여 사용할 수도 있다.
  (AddReqeustHeader, CircuitBreaker, PrefixPath, redis-rate-limiter 등등)</p>
<p>  <a href="https://cloud.spring.io/spring-cloud-gateway/reference/html/#gatewayfilter-factories">https://cloud.spring.io/spring-cloud-gateway/reference/html/#gatewayfilter-factories</a></p>
<pre><code class="language-yaml">  spring:
    cloud:
      gateway:
        routes:
        - id: add_request_header_route
          uri: https://example.org
          filters:
          - AddRequestHeader=X-Request-red, blue </code></pre>
</li>
</ul>
<ul>
<li><p>route, predicate, filter 를 <code>yml</code> 파일로 위와 같이 작성할 수 있지만, <code>java dsl</code> 을 통해서 <code>java</code> 파일로 구성할 수도 있다.</p>
<pre><code class="language-java">  @Bean
  public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
      return builder.routes()
        .route(&quot;r1&quot;, r -&gt; r.host(&quot;**.baeldung.com&quot;)
          .and()
          .path(&quot;/baeldung&quot;)
          .uri(&quot;http://baeldung.com&quot;))
        .route(r -&gt; r.host(&quot;**.baeldung.com&quot;)
          .and()
          .path(&quot;/myOtherRouting&quot;)
          .filters(f -&gt; f.prefixPath(&quot;/myPrefix&quot;))
          .uri(&quot;http://othersite.com&quot;)
          .id(&quot;myOtherID&quot;))
      .build();
  }</code></pre>
</li>
</ul>
<h2 id="references">References</h2>
<p><a href="https://medium.com/@niral22/spring-cloud-gateway-tutorial-5311ddd59816">https://medium.com/@niral22/spring-cloud-gateway-tutorial-5311ddd59816</a>
<a href="https://cloud.spring.io/spring-cloud-gateway/reference/html/">https://cloud.spring.io/spring-cloud-gateway/reference/html/</a>
<a href="https://www.baeldung.com/spring-cloud-gateway">https://www.baeldung.com/spring-cloud-gateway</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SPA란? Single Page Application]]></title>
            <link>https://velog.io/@hyun-jii/SPA%EB%9E%80-Single-Page-Application</link>
            <guid>https://velog.io/@hyun-jii/SPA%EB%9E%80-Single-Page-Application</guid>
            <pubDate>Sun, 08 Aug 2021 15:25:44 GMT</pubDate>
            <description><![CDATA[<h2 id="spa-란">SPA 란?</h2>
<ul>
<li><strong>Single Page Application</strong> 의 약자이다.
단일 페이지로 구성된 웹애플리케이션을 말하며, 화면 이동 시 필요한 데이터를 서버에서 html로 전달받지 않고, 필요한 데이터만 json 형태로 전달 받아 동적으로 렌더링한다.
그러므로 네이티브 앱과 유사한 사용자 경험을 제공할 수 있다.</li>
</ul>
<h2 id="기존-어플리케이션과-spa-의-차이">기존 어플리케이션과 SPA 의 차이</h2>
<ul>
<li><p>기존 어플리케이션은 화면 이동 시에 서버에서 html 을 전달받아 다시 로딩하기 떄문에,
 렌더링 시간이 오래 걸린다. 
 ( 과거에는 웹페이지의 용량이 작아서 이런 방식이 괜찮았지만, 요즘 웹 페이지의 용량이 비대해짐에 따라 spa가 등장하게 되었다.)  </p>
<pre><code> ![](https://images.velog.io/images/hyun-jii/post/b517f390-3fc6-470c-9554-adb775f09cca/sdfsdfsdfsdf.PNG)</code></pre></li>
<li><p>SPA는 초기 렌더링시에 html 을 구성하고, 후에 필요한 데이터만 json 형태로 서버에서
 전달받기 떄문에 속도가 빠르다.  </p>
</li>
</ul>
<p>   <img src="https://images.velog.io/images/hyun-jii/post/b7c07d93-b21a-4e2d-9eb8-81d166597536/werwerwere.PNG" alt=""></p>
<ul>
<li>다음 사진에서 기존 애플리케이션과 SPA 의 차이를 한 눈에 확인할 수 있다.
기존방식은 페이지가 모두 새로 그려지기때문에 색깔이 모두 다른 반면에 SPA 는 필요한 데이터 영역만 변경되기 때문에, 변경된 부분만 색깔이 다른 것을 확인할 수 있다.</li>
</ul>
<p><img src="https://images.velog.io/images/hyun-jii/post/6c2c15f8-2e29-452a-be66-0eb608931333/hjjhjh.PNG" alt=""></p>
<h2 id="spa-의-장점">SPA 의 장점</h2>
<ul>
<li>페이지를 모두 새로 그리는 것이 아니기 때문에 로딩 시간이 빠르다.</li>
<li>좋은 캐싱 능력
SPA 는 요청을 한번 보내고, 서버로부터 받은 모든 데이터를 저장하고 있기 때문에
사용자가 offline 상태여도 동작한다.</li>
<li>사용자 경험 개선
사용자가 더이상 새로운 페이지 로딩을 기다릴 필요가없으므로 사용자 경험이 개선된다.</li>
<li>신속한 프론트엔드 개발
분리된 SPA의 아키텍쳐 때문이다. 
프론트엔드와 백엔드의 분리로 인해 프론트엔드 독립적으로 빌드, 테스트 및 배포를 할 수 있다.  </li>
</ul>
<h2 id="spa-의-단점">SPA 의 단점</h2>
<ul>
<li>검색 엔진 최적화가 어렵다
페이지마다 고유의 <code>url</code> 이 존재하지 않으므로 <code>history</code> 관리 및 <code>seo</code> 대응에 문제가 있다. 하지만 seo 대응 기술이 별도로 존재하기 때문에 대응가능하다.</li>
<li>복잡한 SPA 는 오히려 로딩시간을 증가시킬 수 있다. 
복잡한 페이지는 크기가 크기 때문에 빌드할 때 불편할 수 있다.</li>
</ul>
<h2 id="references">References</h2>
<p><a href="https://poiemaweb.com/js-spa">https://poiemaweb.com/js-spa</a>
<a href="https://www.excellentwebworld.com/what-is-a-single-page-application/">https://www.excellentwebworld.com/what-is-a-single-page-application/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[javascript 디자인 패턴]]></title>
            <link>https://velog.io/@hyun-jii/javascript-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@hyun-jii/javascript-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Sun, 04 Jul 2021 11:47:50 GMT</pubDate>
            <description><![CDATA[<p>이 글은 javascript 공부를 위해 <code>33-js-concepts</code> 을 참고하여 작성한 내용입니다.
<a href="https://github.com/hyun-jii/33-js-concepts">33-js-concepts </a></p>
<p>(javascript 의 디자인 패턴의 종류는 매우 많습니다.
그 중 주요 패턴들만 소개하고, 더 많은 패턴이 궁금하시면 맨아래 링크를 참조해주세요.)</p>
<h2 id="디자인패턴이란">디자인패턴이란?</h2>
<ul>
<li>소프트웨어 디자인에서 흔히 발생하는 문제들을 위한 재사용가능한 해결책이다.</li>
</ul>
<h2 id="module-pattern">Module Pattern</h2>
<ul>
<li><p>흔히들 생각하는 모듈화 패턴이라고 생각할 수 있다.</p>
</li>
<li><p>모듈화의 장점</p>
<ul>
<li><p>모듈의 변화가 다른 부분 코드의 영향을 미치지 않는다.</p>
</li>
<li><p>namespace의 오염을 피할 수 있다.</p>
</li>
<li><p>모듈을 재사용 할 수 있다.</p>
</li>
<li><p>캡슐화 개념을 모방하는데도 사용된다.</p>
<p>  ( javascript는 접근제어자가 없기 때문에, 모듈화를 통해 접근을 제어할 수 있다.)</p>
</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">const myModule = (function() {

  const privateVariable = &#39;Hello World&#39;;

  function privateMethod() {
    console.log(privateVariable);
  }
  return {
    publicMethod: function() {
      privateMethod();
    }
  }
})();
myModule.publicMethod();</code></pre>
<h2 id="revealing-module-pattern">Revealing Module Pattern</h2>
<ul>
<li><p>위의 모듈 패턴을 개선한 패턴이다</p>
<p>  (모듈패턴의 단점은 private 함수나 변수를 사용하기 위해 public 함수를 만들어야 한다.)</p>
</li>
<li><p>Revealing Module Pattern 은 반환되는 object 속성들에 private 함수를 매핑한다.</p>
</li>
</ul>
<pre><code class="language-jsx">const myRevealingModule = (function() {

  let privateVar = &#39;Peter&#39;;
  const publicVar  = &#39;Hello World&#39;;
  function privateFunction() {
    console.log(&#39;Name: &#39;+ privateVar);
  }

  function publicSetName(name) {
    privateVar = name;
  }
  function publicGetName() {
    privateFunction();
  }
  /** reveal methods and variables by assigning them to object     properties */
return {
    setName: publicSetName,
    greeting: publicVar,
    getName: publicGetName
  };
})();
myRevealingModule.setName(&#39;Mark&#39;);
// prints Name: Mark
myRevealingModule.getName();</code></pre>
<ul>
<li>Revealing Module Pattern의 장점<ul>
<li>return문 변경을 통해 함수나 변수들을 public 에서 private로 변경할 수 있다.</li>
<li>return문에 함수정의를 포함하지 않는다. 그러므로 코드가 깔끔하고 가독성이 좋다.</li>
</ul>
</li>
</ul>
<h2 id="singleton-pattern">Singleton Pattern</h2>
<ul>
<li><p>싱글톤 패턴은 한번만 인스턴스화 할 수 있는 object 이다.</p>
<p>  해당 클래스의 인스턴스가 이미 생성되어 있다면, 단순히 그 object를 참조한다.</p>
<p>  그러므로 생성자의 반복 호출은 항상 같은 object를 나타낸다.</p>
</li>
</ul>
<pre><code class="language-jsx">const user = {
  name: &#39;Peter&#39;,
  age: 25,
  job: &#39;Teacher&#39;,
  greet: function() {
    console.log(&#39;Hello!&#39;);
  }
};</code></pre>
<ul>
<li>javascript는 고유한 메모리를 차지하고, 우리가 user 라는 object를 호출했을 때, 이 object의 참조를 반환한다.</li>
</ul>
<pre><code class="language-jsx">const user1 = user;
user1.name = &#39;Mark&#39;;</code></pre>
<pre><code class="language-jsx">// prints &#39;Mark&#39;
console.log(user.name);
// prints &#39;Mark&#39;
console.log(user1.name);
// prints true
console.log(user === user1);</code></pre>
<ul>
<li><p>javscript의 값 대신 참조를 전달하기 때문에, 위와 같이 <code>const user1 = user</code> 는 user 객체를 참조하는 뜻이므로, user1의 name을 변경하면 user의 name도 같이 변경되는 것을 볼 수 있다.</p>
</li>
<li><p>Singleton 패턴은 생성자 함수의 사용에도 영향을 준다.</p>
</li>
</ul>
<pre><code class="language-jsx">let instance = null;
function User() {
  if(instance) {
    return instance;
  }
  instance = this;
  this.name = &#39;Peter&#39;;
  this.age = 25;

  return instance;
}
const user1 = new User();
const user2 = new User();
// prints true
console.log(user1 === user2);</code></pre>
<ul>
<li><p>생성자 함수를 호출할때, <code>instance</code> object의 생성 여부를 체크하여, 생성이 되어있지 않으면 instance를 생성하고, 생성되어있으면 생성되어있는 object를 retrun 한다.</p>
<p>  그래서 <code>user1 === user2</code> 는 true 이다.</p>
</li>
<li><p>Singleton 패턴은 모듈 패턴을 사용할 때도 영향을 미친다.</p>
</li>
</ul>
<pre><code class="language-jsx">const singleton = (function() {
  let instance;

  function init() {
    return {
      name: &#39;Peter&#39;,
      age: 24,
    };
  }
  return {
    getInstance: function() {
      if(!instance) {
        instance = init();
      }

      return instance;
    }
  }
})();
const instanceA = singleton.getInstance();
const instanceB = singleton.getInstance();
// prints true
console.log(instanceA === instanceB);</code></pre>
<h2 id="references">References</h2>
<p><a href="https://blog.bitsrc.io/understanding-design-patterns-in-javascript-13345223f2dd">https://blog.bitsrc.io/understanding-design-patterns-in-javascript-13345223f2dd</a></p>
<p><a href="https://www.digitalocean.com/community/tutorial_series/javascript-design-patterns">https://www.digitalocean.com/community/tutorial_series/javascript-design-patterns</a></p>
<p><a href="https://github.com/fbeline/design-patterns-JS/blob/master/docs.md">https://github.com/fbeline/design-patterns-JS/blob/master/docs.md</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[3sum, Leetcode medium - java]]></title>
            <link>https://velog.io/@hyun-jii/3sum-Leetcode-medium-java</link>
            <guid>https://velog.io/@hyun-jii/3sum-Leetcode-medium-java</guid>
            <pubDate>Sun, 14 Mar 2021 09:00:01 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<blockquote>
<p>Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.<br>Notice that the solution set must not contain duplicate triplets.  </p>
</blockquote>
<p><strong>Example 1:</strong>  </p>
<pre><code class="language-html">Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]</code></pre>
<p><strong>Example 2:</strong>  </p>
<pre><code class="language-html">Input: nums = []
Output: []</code></pre>
<p><strong>Example 3:</strong>  </p>
<pre><code class="language-html">Input: nums = [0]
Output: []</code></pre>
<h3 id="나의-풀이">나의 풀이</h3>
<pre><code class="language-java">class Solution {
    public List&lt;List&lt;Integer&gt;&gt; threeSum(int[] nums) {
        Set&lt;List&lt;Integer&gt;&gt; set = new HashSet&lt;&gt;();
        Set&lt;Integer&gt; threeSum = new HashSet&lt;&gt;();

        Arrays.sort(nums);

        if(nums.length &lt; 3) {
            return new ArrayList&lt;&gt;();
        }

        for(int i=0; i&lt;nums.length; i++) {
            int result = -nums[i];
            threeSum.clear();
            for(int j=i+1; j&lt;nums.length; j++) {
                if(threeSum.contains(result - nums[j])) {
                    set.add(Arrays.asList(nums[i], nums[j], (result - nums[j])));
                }
                threeSum.add(nums[j]);
            }
        }
        return new ArrayList&lt;&gt;(set);
    } 
}</code></pre>
<ul>
<li><code>a+b+c = 0</code> 이 되는 숫자를 찾는 것이므로, <code>a+b = -c</code> 라고 생각하여 풀이하였다.<br>이 과정에 <code>HashSet</code>을 두번 사용하였다. 첫번째 <code>threeSum</code> HashSet은 값을 넣기 위한 용도라, 굳히 HashSet을 사용할 필요는 없었다...<br>두번째는 <code>[a,b,c]</code> 배열이 중복되면 안되므로, 중복을 허용하지 않는 HashSet을 활용하였다.<br>이와 같이 풀으니<code>1083 ms</code> 라는 매우 느린 속도가 나왔다...<br>그래서 다른 사람들의 풀이를 참고하여 다시 풀어보았다.  </li>
</ul>
<h3 id="다른-풀이">다른 풀이</h3>
<pre><code class="language-java">class Solution {
    public List&lt;List&lt;Integer&gt;&gt; threeSum(int[] nums) {
        // 오름차순 정렬
        Arrays.sort(nums);
        List&lt;List&lt;Integer&gt;&gt; result = new ArrayList&lt;&gt;();

        for(int i=0; i&lt; nums.length; i++) {
            // 첫번째 원소가 양수이면, nums 배열이 양수만 존재하므로 return
            if(nums[i] &gt; 0) {
                return result;
            }      
            // 중복 제거
            if(i&gt;0 &amp;&amp; nums[i] == nums[i-1]) {
                continue;    
            }
            int left = i+1;
            int right = nums.length-1;

            while(left &lt; right) {
                int sum = nums[i] + nums[left] + nums[right];

                if(sum &gt; 0) {
                    right--;
                }
                else if(sum &lt; 0) {
                    left++;
                }
                else {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    left++;
                    right--;
                    // 중복 제거
                    while(left&lt;right &amp;&amp; nums[left] == nums[left-1]) {
                        left++;
                    }
                    while(left&lt;right &amp;&amp; nums[right] == nums[right+1]) {
                        right--;
                    }   
                }   
            }  
        }
        return result;
    }
}</code></pre>
<ul>
<li>위 풀이는 중복 제거를 위해 <code>HashSet</code>을 사용하지 않고, 조건문에 의해 중복을 걸러내는 방식이다.    </li>
</ul>
<p><img src="https://user-images.githubusercontent.com/54610824/98461064-8efee500-21ec-11eb-811b-d2d61e0e2d45.PNG" alt="다른풀이">  </p>
<ul>
<li>위 그림과 같이 <code>nums[i]</code>와 <code>nums[left]</code>, <code>nums[right]</code>의 합이 0 보다 크면 <code>right</code>를 감소하고,<br>0보다 작으면 <code>left</code>를 증가시킨다.<br>그리고 합이 0 이 되면, 리스트에 해당 배열을 넣고, <code>left</code>와 <code>right</code>을 둘 다 각각 뒤와 앞 인덱스로 이동시킨다.<br>하지만 이동 시킨 <code>left</code>와 <code>right</code>이 전 인덱스 값과 같다면 <code>left</code>의 경우는 한칸 더 뒤로, <code>right</code>의 경우는 한칸 더 앞으로 이동한다.<br>왜냐하면 값이 같다면, 세개의 수가 합쳐서 0의 되는 숫자들이 같을 수 밖에 없기 때문이다.<br>위 과정을 주어진 nums 배열의 길이만큼 반복한다.   </li>
</ul>
<ul>
<li>위와 같은 방법으로 해결하니 실행속도가 <code>17 ms</code> 가 나왔다.<br>아무래도 HashSet을 사용하는 방식이 반복문의 로직을 더 많이 실행하여 이와 같은 속도차이가 나게된 것 같다.  </li>
</ul>
<h3 id="link">Link</h3>
<p><a href="https://leetcode.com/problems/3sum">https://leetcode.com/problems/3sum</a>  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[함수선언문, 함수표현식과 호이스팅]]></title>
            <link>https://velog.io/@hyun-jii/%ED%95%A8%EC%88%98%EC%84%A0%EC%96%B8%EB%AC%B8-%ED%95%A8%EC%88%98%ED%91%9C%ED%98%84%EC%8B%9D%EA%B3%BC-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85</link>
            <guid>https://velog.io/@hyun-jii/%ED%95%A8%EC%88%98%EC%84%A0%EC%96%B8%EB%AC%B8-%ED%95%A8%EC%88%98%ED%91%9C%ED%98%84%EC%8B%9D%EA%B3%BC-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85</guid>
            <pubDate>Sun, 14 Mar 2021 08:59:15 GMT</pubDate>
            <description><![CDATA[<p>javascript 코드를 보면 다음과 같은 경우를 보았을 것이다.  </p>
<pre><code class="language-javascript">function getName() {
    console.log(&#39;name&#39;);
}

var name = function() {
   console.log(&#39;name&#39;);
};</code></pre>
<ul>
<li>javascript 에서 함수를 변수에 담을 수 있다.<br>이렇게 사용하는 것을 <code>함수 표현식</code> 이라고 한다.<br>그리고 function getName() 과 같이 함수를 선언하는 것을 <code>함수 선언문</code>이라고 한다.</li>
</ul>
<p>그렇다면 함수 표현식과 함수 선언문 둘다 함수인데 어떻게 다르며, 어떻게 사용되는지 알아보자.<br>우선 둘의 차이는 호이스팅을 빼놓고 설명할 수 없다.  </p>
<h2 id="호이스팅">호이스팅</h2>
<ul>
<li><p>호이스팅(Hoisting)의 사전적 의미는 <code>끌어 올리다</code> 라는 뜻을 가지고 있다.<br>여기서도 같은 의미로 쓰인다. 함수 안에 있는 변수나 함수 맨위로 끌어올린다는 것이다.  </p>
</li>
<li><p>실제로 코드가 끌어올려지는 것은 아니며, 자바스크립트 Parser가 내부적으로 끌어올려서 처리한다.  </p>
</li>
</ul>
<h3 id="호이스팅-대상">호이스팅 대상</h3>
<ul>
<li><code>var</code> 와 <code>함수 선언문</code> 이 호이스팅 대상이다.<br><code>let</code> 또는 <code>const</code> 그리고 <code>함수 표현식</code>은 해당되지 않는다.  </li>
</ul>
<h3 id="호이스팅-규칙">호이스팅 규칙</h3>
<p><em>부등호가 큰 쪽이 먼저 인식된다</em>  </p>
<ul>
<li>변수 선언 &gt; 함수 선언  </li>
<li>할당되어있는 변수 &gt; 할당되지 않은 변수    </li>
</ul>
<h3 id="함수표현식의-호이스팅">함수표현식의 호이스팅</h3>
<ul>
<li>다음 예제들의 결과를 생각해보자.  </li>
</ul>
<pre><code class="language-javascript">count();

var count = function() {
    console.log(&#39;count는 1이다.&#39;);
}</code></pre>
<pre><code class="language-javascript">var count = function() {
    console.log(&#39;count는 1이다.&#39;);
}

count();</code></pre>
<pre><code class="language-javascript">count();

let count = function() {
    console.log(&#39;count는 1이다.&#39;);
}</code></pre>
<hr>
<ul>
<li><strong>첫번째 결과는 TypeError 이다.</strong> <code>error : count is not function</code>  </li>
</ul>
<p>count() 호출 후, var count를 선언하며 함수를 담았다.<br><code>var</code> 는 호이스팅의 영향을 받으므로 위로 끌어올려진다.<br>그러므로 아래와 같이 <code>var count;</code> 가 가장 먼저 실행된다. 변수에 아무 값도 담지 않았으므로 <code>undefined</code> 상태이다.<br>그 후로 count()가 호출되면 위에 선언한 <code>count</code>가 호출되므로 변수를 호출하는 격이된다.<br>그러므로 <code>not function</code> 이라는 에러 메시지가 나타난다.  </p>
<pre><code class="language-javascript">var count;    // undefined

count();      // count는 함수가 아닌데 왜 함수를 호출하니?

var count = function() {
    console.log(&#39;count는 1이다.&#39;);
}</code></pre>
<ul>
<li><strong>두번째 결과는 정상적으로 console.log가 동작한다.</strong>  </li>
</ul>
<p><code>var count</code>가 호이스팅으로 인해 위로 끌어올려지지만, count() 호출 전 count에 함수를 담으므로<br>count() 를 호출하였을 때 정상 작동한다.      </p>
<pre><code class="language-javascript">var count;    //undefined

var count = function() {
    console.log(&#39;count는 1이다.&#39;);
}

count();</code></pre>
<ul>
<li><strong>세번째 결과는 ReferenceError 이다.</strong>  </li>
</ul>
<p>세번째 예시는 첫번째 예시에서 <code>var</code> 를 <code>let</code>으로 바꾼 것이다.<br>세번째도 역시 에러를 발생하지만, 첫번째와 다른 <code>Referecne Error</code>가 발생한다.<br><code>let</code> 은 호이스팅의 영향을 받지 않기 때문에, 예시 작성한 코드 순서대로 실행된다.<br>그러므로 count()라는 함수가 정의되지 않았는데 호출하였기 떄문이다.      </p>
<pre><code class="language-javascript">count();

let count = function() {
    console.log(&#39;count는 1이다.&#39;);
}</code></pre>
<h3 id="함수선언문의-호이스팅">함수선언문의 호이스팅</h3>
<pre><code class="language-javascript">count();

function count() {
    console.log(&#39;count는 2이다.&#39;);
}</code></pre>
<pre><code class="language-javascript">function count() {
    console.log(&#39;count는 2이다.&#39;);
}

count();</code></pre>
<hr>
<ul>
<li><strong>첫번째, 두번째 모두 정상 작동한다.</strong><br>호출이 함수선언문의 위에 있든 아래쪽에 있든 함수 선언문은 호이스팅 영향으로 끌어올려지기 때문이다.  </li>
</ul>
<h3 id="결론">결론</h3>
<p>함수선언문을 사용하는게 좋을까? 함수표현식을 사용하는게 좋을까?<br>찾아보니 어떤 사람은 함수 선언문을 권장하고, 또 어떤사람은 함수 표현식을 권장한다.<br>또한 두 개의 차이는 없고 한가지만 사용하는 것이 좋다는 사람도 있어 뭐라 결론을 내리지 못하겠다.<br>개인적으로 const와 let을 사용하며, 코드 순대로 동작하게 작성하는 것이 호이스팅의 영향을 최소화 할 수 있고, 호이스팅의 영향을 최소화하는 것이 가독성이나 유지보수측면에서도 좋지 않을까 생각한다.      </p>
<h3 id="reference">Reference</h3>
<p><a href="https://gmlwjd9405.github.io/2019/04/22/javascript-hoisting.html">https://gmlwjd9405.github.io/2019/04/22/javascript-hoisting.html</a><br><a href="https://yuddomack.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85Hoisting">https://yuddomack.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85Hoisting</a><br><a href="https://ko.javascript.info/function-expressions">https://ko.javascript.info/function-expressions</a>  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Github API v3 사용방법, 깃허브 API 적용]]></title>
            <link>https://velog.io/@hyun-jii/Github-API-v3-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95-%EA%B9%83%ED%97%88%EB%B8%8C-API-%EC%A0%81%EC%9A%A9</link>
            <guid>https://velog.io/@hyun-jii/Github-API-v3-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95-%EA%B9%83%ED%97%88%EB%B8%8C-API-%EC%A0%81%EC%9A%A9</guid>
            <pubDate>Sun, 14 Mar 2021 08:58:38 GMT</pubDate>
            <description><![CDATA[<ul>
<li>최근 깃허브 API v3을 활용하여, 이슈 리스트를 만들어보았다.<br>이를 계기로 API v3의 간단한 사용법을 소개하려고 한다.  </li>
</ul>
<h3 id="토큰-발급">토큰 발급</h3>
<p><img src="https://user-images.githubusercontent.com/54610824/82111722-17428700-9782-11ea-8f05-d3961276b336.PNG" alt="1">  </p>
<ul>
<li>우선 깃허브 API 인증을 위해 토큰을 발급하자.<br>깃허브의 <code>Settings -&gt; Developer settings -&gt; Personal access tokens</code> 로 이동하여,<br><code>Generate new token</code> 을 클릭하면, 위와 같은 화면이 나타난다.<br><code>Note</code> 에 원하는 토큰 이름을 적고, <code>scope</code>를 선택해주자.<br>필자는 <code>repo</code>만 선택하고 생성하였다.<br>생성이 완료되면 <code>토큰 값</code>이 나타난다.<br>토큰은 만든 순간에만 보이므로 다른 곳에 복사하여 저장해놓아야 한다.<br>만약 까먹었다면, 재발급 해야하니 주의하자.      </li>
</ul>
<blockquote>
<p>Github API 는 인증을 하지 않아도 사용할 수 있다.<br>그러나 인증을 하지 않으면, 요청이 1시간에 60번으로 제한된다.<br>인증을 할 경우 1시간에 5000번까지 요청할 수 있다.  </p>
</blockquote>
<h3 id="github-api-v3">Github API v3</h3>
<ul>
<li><a href="https://developer.github.com/v3/">https://developer.github.com/v3/</a><br>위의 링크는 github API 문서이다.<br>사용방법 설명이 잘 되어있고, 다양한 API 를 사용할 수 있다.<br>필자는 여기서 <code>ISSUE</code> 를 활용해보겠다.  </li>
</ul>
<p><img src="https://user-images.githubusercontent.com/54610824/82111771-5244ba80-9782-11ea-8fd4-97d4dfba080d.PNG" alt="3">  </p>
<ul>
<li>우측 메뉴에서 <code>ISSUE</code> 를 클릭하면, issue 를 활용한 api들을 확인할 수 있다.<br><code>repository의 이슈 리스트</code> 를 만들 것이므로, <code>List repository issues</code> 를 클릭한다.    </li>
</ul>
<p><img src="https://user-images.githubusercontent.com/54610824/82111845-bff0e680-9782-11ea-9c89-7a2306791a4c.PNG" alt="4">  </p>
<ul>
<li>클릭하면, <code>List repository issues</code> 에 대한 사용방법이나 주의사항이 처음에 나온다.<br><code>GET /repos/:owner/:repo/issues</code> 이와 같은 방식으로 API 를 요청하라는 의미이다.<br>만약 repository의 주인이 <code>hyun-jii</code> 이고, repositoy의 이름이 <code>test</code> 라면<br><code>https://api.github.com/repos/hyun-jii/test/issues</code><br>위의 URL을 <code>GET</code> 방식으로 요청하는 것이다.  </li>
</ul>
<p><img src="https://user-images.githubusercontent.com/54610824/82111890-1100da80-9783-11ea-8d17-c7d6e15f6f3b.PNG" alt="5">  </p>
<ul>
<li>밑으로 내리면, 몇개의 parameters 의 <code>type</code> 과 설명이 나와있다.<br>해당 parameter를 사용할 때 참고하면 된다.  </li>
</ul>
<p><img src="https://user-images.githubusercontent.com/54610824/82111894-1b22d900-9783-11ea-8c30-ed7cf978aa61.PNG" alt="6">  </p>
<ul>
<li>이제 API 요청 시 응답되는 형식이다.<br>위를 통해 응답 객체의 구조를 알 수 있고, 하나의 이슈당 얻을 수 있는 정보들을 알 수 있다.<br>해당 이슈의 title, state, user, label, number 등등 스크롤을 내리다보면 정말 다양한 정보를 활용할 수 있다.<br>필자는 간단하게 테스트로 각 이슈의 title 만 추출해 보겠다.  </li>
</ul>
<pre><code class="language-html">&lt;h1&gt;Issue List&lt;/h1&gt;
&lt;div class=&quot;container&quot;&gt;&lt;/div&gt;</code></pre>
<ul>
<li>우선 간단하게 테스트용으로 화면을 구성했다.  </li>
</ul>
<pre><code class="language-javascript">$(document).ready(function () {
  var auth = window.btoa(&quot;hyun-jii: 토큰 값 &quot;);

    $.ajax({
      type: &quot;GET&quot;,
      headers: {
        Authorization: &quot;Basic &quot; + auth,
      },
      url: &quot;https://api.github.com/repos/hyun-jii/CRESCENDO/issues&quot;,
      dataType: &quot;json&quot;,
      success: function (response) {
        var array = response;
        for (var i = 0; i &lt; array.length; i++) {
          $(&quot;.container&quot;).append(&quot;&lt;p&gt;&quot; + array[i].title + &quot;&lt;/p&gt;&quot;);
        }
      },
    });
});</code></pre>
<ul>
<li>jquery ajax 를 활용하여 구현해보았다.<br><code>GET</code> 방식으로 지정된 형식의 <code>URL</code>을 요청하고, <code>headers</code> 에서 토큰을 인증한다.  </li>
</ul>
<blockquote>
<p><code>window.btoa()</code> 는 Base64 인코딩 메소드이다.  </p>
</blockquote>
<ul>
<li>성공 시 response를 응답받아 <code>title</code>을 추출하였다.<br>API 문서에서 title 이 겉에 있는 첫번째 배열에 쌓여있으므로, <strong>array[i].title</strong> 로 추출하였다.<br>만약 <code>user 의 id</code> 를 구하고 싶다면, <strong>array[i].user.id</strong><br><code>label name</code>을 구하려면 이중 반복문을 활용하여 <strong>array[i].label[j].name</strong> 으로 추출한다.    </li>
</ul>
<p><img src="https://user-images.githubusercontent.com/54610824/82111899-24ac4100-9783-11ea-929c-2178eaa53955.PNG" alt="7">  </p>
<ul>
<li>이제 이를 실행하면, 다음과 같이 이슈의 제목들만 나타나게 된다.<br>현재 오픈된 이슈들만 나오게 되며, 전체 이슈나 닫힌 이슈만 보고 싶으면 url 에 추가하면 된다.<br><code>issues?stae=all</code> 이 외에도 page 설정이나 라벨등 다양한 설정 방법이 있으니 문서를 참고해보자.  </li>
</ul>
<h3 id="token-인증-방법">token 인증 방법</h3>
<p><a href="https://developer.github.com/v3/auth/">https://developer.github.com/v3/auth/</a>  </p>
<ul>
<li>위 링크에 들어가면 token 인증 방법이 설명되어 있다.<br>필자는 <code>Basic Authentication</code> 을 이용하여 인증하였다.  </li>
</ul>
<pre><code class="language-javascript">var auth = window.btoa(&quot;hyun-jii: 토큰 값 &quot;);

headers: {
        Authorization: &quot;Basic &quot; + auth
      }</code></pre>
<ul>
<li>기본 인증방식에서 <code>Base64</code> 인코딩을 통해 인증하는 방식은 위와 같은 방식으로 사용한다.<br>그러나 <code>Base64</code> 인코딩은 복호화가 가능하여 보안이 취약하다.<br>그러므로 HTTPS / TLS 사용이 권장된다.  </li>
</ul>
<p><img src="https://user-images.githubusercontent.com/54610824/82142578-4affd880-9878-11ea-867d-4b3f3cf2005b.PNG" alt="8">  </p>
<ul>
<li>마지막으로 이전에 만든 이슈 리스트를 소개하며 이번 포스팅을 마친다.<br>Github API 를 처음 활용해보았는데, 활용해보니 어렵지 않고, 생각보다 많은 API를 제공했다.<br>다음에 다른 API를 활용하여, 재밌는 것을 만들어 보고 싶다.  </li>
</ul>
<h3 id="reference">Reference</h3>
<p><a href="https://developer.mozilla.org/ko/docs/Web/HTTP/Authentication">https://developer.mozilla.org/ko/docs/Web/HTTP/Authentication</a>  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS 탄력 IP (Elastic IP) 삭제]]></title>
            <link>https://velog.io/@hyun-jii/AWS-%ED%83%84%EB%A0%A5-IP-Elastic-IP-%EC%82%AD%EC%A0%9C</link>
            <guid>https://velog.io/@hyun-jii/AWS-%ED%83%84%EB%A0%A5-IP-Elastic-IP-%EC%82%AD%EC%A0%9C</guid>
            <pubDate>Sun, 14 Mar 2021 08:58:12 GMT</pubDate>
            <description><![CDATA[<ul>
<li>AWS EC2에서 탄력 IP를 삭제해보자<br>우선 탄력 IP를 생성한 후, 인스턴스와 연결하지 않으면 돈이 부과된다!  </li>
<li><em>그리고 <code>탄력 IP</code>가 연결된 인스턴스를 삭제하면, 탄력IP도 따로 삭제하거나<br>다른 인스턴스에 연결해야한다.*</em><br>필자는 인스턴스 지우면 같이 지워지는 줄 알고 방치하다.....돈이 부과됐다....<br>EC2에 들어가자마자, 대시보드에 뻔히 탄력 IP 의 여부가 나와있는데도.....지나치기 쉽상이다..하하..  </li>
</ul>
<hr>
<p><img src="https://user-images.githubusercontent.com/54610824/76762552-d73c4380-67d4-11ea-926c-f3d3549ccdec.PNG" alt="2">  </p>
<ul>
<li>우선 EC2 인스턴스 애기가 나왔으므로, 인스턴스를 삭제하는 방법도 같이 알아보겠다.<br><code>EC2 인스턴스</code>에 들어가서 작업을 클릭한 후 종료를 클릭하면 된다.<br>그러면 상태가 <code>terminated</code>로 변하며, 몇시간 뒤에 인스턴스 내역에서 사라진다.    </li>
</ul>
<p><strong>그리고 만약 인스턴스 중지를 클릭했다면, 탄력 IP를 삭제하거나 다른 인스턴스와 연결해야한다.<br>왜냐하면 <code>탄력IP</code>는 실행중인 인스턴스와 연결해야 돈이 부과되지 않는다.</strong>    </p>
<p><img src="https://user-images.githubusercontent.com/54610824/76762533-cf7c9f00-67d4-11ea-9f6c-ef380332e953.jpg" alt="1">    </p>
<ul>
<li>다음으로 탄력 IP 삭제 방법은 해당 아이피를 선택한 후<br><code>Actions</code>에서 <code>탄력적IP주소 릴리스</code>를 클릭하면 끝이다.<br>매우 간단한데, 용어가 릴리스라고 되어있어서 필자도 처음에 헷갈렸다.<br>아마 내가 쓰는 IP를 이제 세상에 놓아주는 것이라 릴리스라고 쓰는 것 같다.  </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 component-scan 개념 및 동작 과정]]></title>
            <link>https://velog.io/@hyun-jii/%EC%8A%A4%ED%94%84%EB%A7%81-component-scan-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@hyun-jii/%EC%8A%A4%ED%94%84%EB%A7%81-component-scan-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Sun, 14 Mar 2021 08:57:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>스프링 component-scan의 개념과 동작 과정에 대해 알아보려고 한다!  
얼마 전 면접에서 component-scan의 동작 과정에 대해 설명해달라는 질문을 받았다...<br>질문을 받았을 때, component-scan...? 이것은..스캔하는건데...뭐라 설명을 못했다..<br>집에 돌아오는 길에 생각해보니 부끄럽지만 내 자신이 정확한 개념도 모르고 있는 것 같았다.<br>이를 통해 component-scan에 대해 공부하고 정리해보았다.  </p>
</blockquote>
<h2 id="component-scan-이란">component-scan 이란?</h2>
<ul>
<li><strong>빈으로 등록 될 준비를 마친 클래스들을 스캔하여, 빈으로 등록해주는 것이다.</strong><br>빈으로 등록 될 준비를 하는 것이 무엇일까?
우리가 <code>@Controller</code>, <code>@Service</code>, <code>@Component</code>, <code>@Repository</code> 어노테이션을 붙인
클래스들이 빈으로 등록 될 준비를 한 것이다.  </li>
</ul>
<blockquote>
<p>component-scan은 기본적으로 @Component 어노테이션을<br>빈 등록 대상으로 포함한다.<br>그렇다면 @Controller 나 @Service는 어떻게 인식하는 걸까?<br>그 이유는 @Controller나 @Service가 @Component를 포함하고 있기 때문이다.  </p>
</blockquote>
<h2 id="component-scan-사용방법">component-scan 사용방법</h2>
<p>component-scan 을 사용하는 방법은<br><code>xml 파일에 설정하는 방법</code>, 과 <code>자바파일안에서 설정하는 방법</code>이 있다.  </p>
<p><strong>1. xml 파일에 설정</strong>  </p>
<pre><code class="language-xml">&lt;context:component-scan base-package=&quot;com.rcod.lifelog&quot;/&gt; </code></pre>
<p>다음과 같이 xml 파일에 설정하고, base package를 적어주면<br>base package 기준으로 클래스들을 스캔하여 빈으로 등록한다.<br>base package에 여러개의 패키지를 쓸 수 있다.  </p>
<pre><code class="language-xml">&lt;context:component-scan base-package=&quot;com.rcod.lifelog, com.rcod.example&quot;/&gt; </code></pre>
<p>위와 같이 설정하면, base pacakage 하위의 @Controller, @Service<br>@Repository, @Component 클래스가 모두 빈으로 등록되므로,<br>특정한 객체만 빈으로 등록하여 사용하고 싶다면<br><code>include-filter</code>나 <code>exclude-filter</code>를 통해 설정할 수 있다. </p>
<ul>
<li><p>exclude-filter  </p>
<pre><code class="language-xml">&lt;context:component-scan base-package=&quot;com.rcod.lifelog&quot;&gt;
  &lt;context:exclude-filter type=&quot;annotation&quot; 
      expression=&quot;org.springframework.stereotype.Controller&quot;/&gt;
&lt;/context:component-scan&gt;</code></pre>
<p>@Controller 를 제외하고 싶다면 위와 같이 <code>exclude-filter</code>를 사용하여<br><code>org.springframework.stereotype.Controller</code>를 명시해준다.  </p>
</li>
<li><p>include-filter  </p>
<pre><code class="language-xml">&lt;context:component-scan base-package=&quot;com.rcod.lifelog&quot; use-default=&quot;false&quot;&gt;
  &lt;context:include-filter type=&quot;annotation&quot; 
      expression=&quot;org.springframework.stereotype.Controller&quot;/&gt;
&lt;/context:component-scan&gt;</code></pre>
<p><code>use-default=&quot;false&quot;</code>는 기본 어노테이션 @Controller, @Component등을<br>스캔하지 않는다는 것이다.<br>기본 어노테이션을 스캔하지 않는다고 설정하고, <code>include-filter</code>를 통해서<br>위와 같이 특정 어노테이션만 스캔할 수 있다.    </p>
</li>
</ul>
<p><strong>2. 자바 파일안에서 설정</strong>  </p>
<pre><code class="language-java">@Configuration
@ComponentScan(basePackages = &quot;com.rcod.lifelog&quot;)
public class ApplicationConfig {
}</code></pre>
<p><code>@Configuration</code> 은 이 클래스가 xml을 대체하는 설정 파일임을 알려준다.<br>해당 클래스를 설정 파일로 설정하고 <code>@ComponentScan</code>을 통하여<br><code>basePackages</code>를 설정해준다.    </p>
<ul>
<li><p>위와 같이 <code>component-scan</code>을 사용하는 두 가지 방법이 있다.<br>만약 component-scan을 사용하지 않으면, 빈으로 설정할 클래스들을<br>우리가 직접 xml 파일에 일일이 등록해 주어야 한다.  </p>
<pre><code class="language-xml">      &lt;bean id=&quot;mssqlDAO&quot; class=&quot;com.test.spr.MssqlDAO&quot;&gt;&lt;/bean&gt;

      &lt;!-- MemberList 객체에 대한 정보 전달 및 의존성 주입 --&gt;
      &lt;bean id=&quot;member&quot; class=&quot;com.test.spr.MemberList&quot;&gt;

          &lt;!-- 속성의 이름을 지정하여 주입 --&gt;
          &lt;property name=&quot;dao&quot;&gt;
              &lt;ref bean=&quot;mssqlDAO&quot;/&gt;
          &lt;/property&gt;

      &lt;/bean&gt;</code></pre>
<p><code>MssqlDAO</code> 와 <code>MemberList</code>를 빈으로 등록하고,<br>MemberList에 Mssql을 주입한 것이다.<br>위와 같이 코드가 매우 길어지고, 일일이 추가하기에 복잡해진다.  </p>
</li>
</ul>
<h2 id="component-scan-동작-과정">component-scan 동작 과정</h2>
<p>ConfigurationClassParser 가 Configuration 클래스를 파싱한다.<br>@Configuration 어노테이션 클래스를 파싱하는 것이다.<br> &nbsp;&nbsp;&nbsp; &nbsp;  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;⬇<br>ComponentScan 설정을 파싱한다.<br>base-package 에 설정한 패키지를 기준으로<br>ComponentScanAnnotationParser가 스캔하기 위한 설정을 파싱한다.<br> &nbsp;&nbsp;&nbsp; &nbsp;  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;⬇<br>base-package 설정을 바탕으로 모든 클래스를 로딩한다.<br> &nbsp;&nbsp;&nbsp; &nbsp;  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;⬇<br>ClassLoader가 로딩한 클래스들을 BeanDefinition으로 정의한다.<br>생성할 빈의 대한 정의를 하는 것이다.<br> &nbsp;&nbsp;&nbsp; &nbsp;  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;⬇<br>생성할 빈에 대한 정의를 토대로 빈을 생성한다.    </p>
<hr>
<ul>
<li>component-scan에 대해 열심히 정리해본다고 하였는데, 잘 되었는지 모르겠다.<br>이를 통해 component-scan의 개념과 어노테이션에 대해 한 층 더 알게 된 것 같다.      </li>
</ul>
<blockquote>
<p>동작 과정은 References 의 마지막 두 개의 블로그를 참고했습니다.<br>잘못된 부분이 있다면 언제든지 댓글로 알려주세요😄</p>
</blockquote>
<h3 id="references">References</h3>
<p><a href="https://yookeun.github.io/java/2014/07/04/spring-component-scan/">https://yookeun.github.io/java/2014/07/04/spring-component-scan/</a><br><a href="https://zorba91.tistory.com/249">https://zorba91.tistory.com/249</a><br><a href="https://galid1.tistory.com/494">https://galid1.tistory.com/494</a><br><a href="https://jess-m.tistory.com/14">https://jess-m.tistory.com/14</a><br><a href="https://yonggar-ri.tistory.com/entry/Spring-ComponentScan-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EB%B6%84%EC%84%9D">https://yonggar-ri.tistory.com/entry/Spring-ComponentScan-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EB%B6%84%EC%84%9D</a>  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[개념박살내기 스터디 회고]]></title>
            <link>https://velog.io/@hyun-jii/%EA%B0%9C%EB%85%90%EB%B0%95%EC%82%B4%EB%82%B4%EA%B8%B0-%EC%8A%A4%ED%84%B0%EB%94%94-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@hyun-jii/%EA%B0%9C%EB%85%90%EB%B0%95%EC%82%B4%EB%82%B4%EA%B8%B0-%EC%8A%A4%ED%84%B0%EB%94%94-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 14 Mar 2021 08:56:54 GMT</pubDate>
            <description><![CDATA[<p>짧지만 친구들과 진행했던 스터디 회고를 써보려고 한다.<br>지난 1일 1커밋 이후로 컴퓨터 구조에 대해 스터디를 진행했다.    </p>
<p><a href="https://hyun-jii.github.io/2019-11-01-first/">1일 1커밋 한달 회고</a>  </p>
<p>지난 1일 1커밋이 매우 성공적이었다고 생각하여, 이번 스터디도 잘 되리라 생각했지만<br>결론부터 말하자면 반반 이었던 것 같다...    </p>
<p><strong>우선 우리가 스터디를 진행한 방식에 대해 소개해보려고 한다.</strong>    </p>
<h2 id="스터디-규칙">스터디 규칙</h2>
<ul>
<li>1일 1커밋 때와 같이 노션을 통해 규칙과 일정을 관리했다.    </li>
</ul>
<blockquote>
<p><code>컴퓨터 개념 박살내기 목표</code> 가 스터디 규칙이고,<br><code>HTTP 완벽 가이드</code>는 앞으로 진행할 스터디이다.      </p>
</blockquote>
<p><img src="https://user-images.githubusercontent.com/54610824/75523560-9bbd1d80-5a4f-11ea-81bb-3ba595053c82.PNG" alt="1">  </p>
<ul>
<li><strong>발표는 2주에 한 번, 모두가 발표를 진행했고, 발표는 디스코드를 통해 진행했다.</strong><br>아무래도 만나서 하면 시간맞추기도 어렵고, 장소 정하는 것도 일이기 때문이었다.      </li>
</ul>
<ul>
<li><strong>하지만 몇 번의 스터디 진행 후, 만나서 발표하기로 결정했다.</strong><br>퇴근 후, 모두의 시간을 맞춰야 하니 주로 밤 10시쯤 진행되었는데,
피곤할 시간이기도 하고, 디스코드로 진행하다보니 집중이 잘 안되었기 때문이다.<br>현재 만나서 진행하니 훨씬 집중도 잘되고 효울이 올라간 것 같다.<br>다행히 시간과 장소에 관한 문제는 아직 없었고, 현재까지 잘 진행하고 있다.    </li>
</ul>
<h2 id="스터디-일정">스터디 일정</h2>
<p><img src="https://user-images.githubusercontent.com/54610824/75523654-c3ac8100-5a4f-11ea-8c46-82dd6c1f71ec.PNG" alt="3">  </p>
<ul>
<li><code>노션</code>에 발표할 내용과 그 발표를 할 사람, 그리고 발표여부를 체크하여 <code>일정관리</code>를 했다.<br>언제 누가 무엇을 발표했는지 한눈에 알아볼 수 있어서 좋았던 것 같다.  </li>
</ul>
<h2 id="깃허브-관리">깃허브 관리</h2>
<p><img src="https://user-images.githubusercontent.com/54610824/75523618-b2fc0b00-5a4f-11ea-835c-96073f6cb008.PNG" alt="2">  </p>
<ul>
<li>그리고 <code>깃허브 팀</code>을 만들어서, 각자 발표할 내용을 <code>깃허브에 정리</code>했다.<br>발표 후에도 언제나 발표했던 내용을 확인할 수 있고, 발표 후에 더 추가사항이나 질문을 댓글을 통해 남길 수 있었다.  </li>
</ul>
<h2 id="결과">결과</h2>
<ul>
<li>결과는 위에서 말했다싶이 반반..(?) 이었던 것 같다.<br>강의식 스터디를 함으로써 모두에게 공부가 되고, 도움이 되었다.<br>하지만 <strong>첫번째 문제는 발표 날짜가 미뤄지는 날이 꽤나 있었다.</strong><br>정해진 날짜에 일이 생기곤 하면 미뤄지곤 해서 일정이 많이 밀렸다...ㅠㅠ<br>앞으로는 어떤 사유든 벌금을 내고, 모두가 반드시 만날 수 있는 날에 발표를 하기로 정했다.    </li>
</ul>
<ul>
<li><strong>두번째 문제는 복습을 통한 질문이나 피드백이 부족했다.</strong><br>아무래도 블로그를 찾아가며 자료 정리를 하게 되는데, 이 개념이 맞는지 검증할 수 있는 사람이 없었다...<br>그리고 발표 후에 복습을 통해 댓글을 남기기로 하였는데...잘 지켜지지 않았다...    </li>
</ul>
<h2 id="앞으로">앞으로...</h2>
<ul>
<li>컴퓨터 구조를 끝내고 이제 웹 스터디를 진행하기로 했다.<br>하지만 방식을 바꾸어 책을 교재로 두고 진행하기로 하였다.  </li>
<li><em>아무래도 블로그를 찾아가며 자료를 정리하기엔, 자료에 대한 정확도가 한계가 있다고 판단했다.*</em><br>그리하여 <code>HTTP 완벽 가이드</code> 라는 책을 구입하여 챕터별로 돌아가며 발표하기로 했다.<br>책이 워낙 두꺼워 2주에 한번 씩 진행하면 연말쯤에 끝이 날 것 같지만,<br>이번에는 복습과 일정관리를 확실히 해서 잘 진행되었으면 좋겠다!  </li>
</ul>
<h3 id="함께한-사람들">함께한 사람들</h3>
<p><a href="https://ugaemi.github.io/">유경님블로그</a><br><a href="https://saem-ee.tistory.com/">새미님블로그</a><br><a href="https://tudiiii.github.io/">수진님블로그</a>  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[REST API 란? REST API 와 Restful]]></title>
            <link>https://velog.io/@hyun-jii/REST-API-%EB%9E%80-REST-API-%EC%99%80-Restful</link>
            <guid>https://velog.io/@hyun-jii/REST-API-%EB%9E%80-REST-API-%EC%99%80-Restful</guid>
            <pubDate>Sun, 14 Mar 2021 08:55:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>REST API? Restful? API는 아는데 REST API는 뭐지..?
내가 처음 REST API를 접했을 때 든 생각이었다.
그 때 이해가 잘 안되서 나중에 꼭 정리해봐야겠다고 생각해서 포스팅을 하게 됐다.</p>
</blockquote>
<h2 id="rest-api-란">REST API 란?</h2>
<p>쉽게 말해 REST 아키텍쳐를 따르는 API 라고 할 수 있다.<br>그렇다면 REST 아키텍쳐는 무엇일까?  </p>
<h2 id="restrepresentational-state-transfer-란">REST(Representational State Transfer) 란?</h2>
<ul>
<li>HTTP를 이용해 통신하는 네트워크 상에서 정한 약속이며,
인터넷과 웹과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍쳐의 한 형식이다.  </li>
<li><em>웹이 깨지는 현상 없이 어떻게 HTTP를 개선할지에 대한 것이 만든 계기이다.*</em>    </li>
</ul>
<p><strong>Representational</strong> : 자원을 대표하는 식별자<br><strong>State Transfer</strong> : 자원의 상태를 전송하는 방법</p>
<ul>
<li>즉, 자원을 대표하는 식별자와 자원의 상태를 전송하는 방법을 포함하고 있어야 한다.<br>자원은 URI를 나타내고, 전송하는 방법은 HTTP Method를 나타낸다.  </li>
</ul>
<h2 id="대표적인-http-method">대표적인 HTTP Method</h2>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="center"></th>
</tr>
</thead>
<tbody><tr>
<td align="center">Create</td>
<td align="center">POST</td>
</tr>
<tr>
<td align="center">Read</td>
<td align="center">GET</td>
</tr>
<tr>
<td align="center">Update</td>
<td align="center">PUT</td>
</tr>
<tr>
<td align="center">Delete</td>
<td align="center">DELETE</td>
</tr>
</tbody></table>
<h2 id="restful-이란">Restful 이란?</h2>
<p>REST 제약조건의 집합을 모두 지키는 것을 <code>Restful</code> 하다고 한다.</p>
<h2 id="rest-제약조건의-집합">REST 제약조건의 집합</h2>
<h3 id="1-client--server">1. Client / Server</h3>
<p>REST API에서 자원을 가지고 있는 쪽은 서버, 자원을 요청하는 쪽은 클라이언트이다.<br>서버는 API를 제공하고, 클라이언트는 사용자 인증, Context(세션,쿠키)등을 직접 관리하여,
서버와 클라이언트의 역할을 확실히 구분시킴으로써 서로 간의 의존성을 약하게 한다.    </p>
<h3 id="2-stateless">2. Stateless</h3>
<p>클라이언트의 Context가 서버에 저장되면 안된다.<br>서버는 각각의 요청을 별개의 것으로 인식해야하기 때문이다.<br>REST API는 세션정보나 쿠키정보를 활용하여, 상태정보를 저장 및 관리하지 않는다.<br>이전 요청이 다음 요청과 연관되어서는 안된다.<br>Stateless(무상태성)은 서버의 처리방식에 일관성을 부여하고, 서버의 부담을 줄여준다.    </p>
<h3 id="3-cacheable">3. Cacheable</h3>
<p>REST API도 HTTP 프로토콜에서 사용하는 Last-Modifid Tag 또는 E-Tag를 이용하여
캐싱을 구현할 수 있고, 캐싱을 활용하여 대량의 요청을 효율적으로 처리할 수 있다.  </p>
<h3 id="4-layered-system">4. Layered System</h3>
<p>REST API 서버는 다중 계층으로 구성될 수 있다.<br>보안, 로드밸런싱, 암호화등을 위해 계층을 추가 하여 구조를 변경할 수 있다.<br>또한 Proxy나 Gateway와 같은 네트워크 기반의 중간매체를 사용할 수 있게 해준다.<br>그래서 클라이언트는 서버와 직접 통신하는지, 중간서버와 통신하는지 알 수 없다.   </p>
<h3 id="5-code-on-demandoptional">5. Code on demand(optional)</h3>
<p>코드를 클라이언트한테 보내서 실행할 수 있어야 한다.<br>ex) javascript   </p>
<h3 id="6-uniform-interface">6. Uniform Interface</h3>
<p>자원에 대한 요청을 통일되고, 한정적으로 수행하는 아키텍쳐 스타일<br>REST API는 HTTP를 사용하는 모든 플랫폼에서 요청 가능하다.    </p>
<p><strong>Uniform Inteface 에는 4가지 제약 조건이 있다.</strong>  </p>
<ol>
<li><code>identification of resources</code> 
자원은 유일하게 식별할 수 있어야한다.  </li>
<li><code>manipulation of resources through representations</code><br>HTTP Method로 표현해야한다.<br>예를 들어 수정한다고 하면 <code>PUT/members/1</code>   </li>
<li><code>self-descriptive messages</code><br>스스로 설명할 수 있는 메시지<br>메시지를 통해 어떤 언어로 무엇을 요청하는지 알 수 있어야 한다.  </li>
</ol>
<p><strong>self-descriptive 하지 않은 경우</strong>  </p>
<pre><code class="language-json">GET/members/1</code></pre>
<p><strong>self-descriptive 한 경우</strong>
이 URI가 어디로 향하는지 목적지를 적어주는 것이다.    </p>
<pre><code class="language-json">GET/members/1
Host: hyun-jii.github.io  </code></pre>
<ol start="4">
<li><code>hypermedia as the engine of application state (HATEOAS)</code><br>하이퍼링크를 통해서 상태가 전이(HATEOAS) 되어야 한다.<br>예를 들어 <code>HTML</code>의 경우 <code>a태그</code>를 통해서 상태가 전이 된다. 이는 HATEOAS를 지킨 것이라고 할 수 있다.<br>그러나 <code>JSON</code>의 경우는 a태그와 같은 것이 없으므로 <strong>Media type을 정의하거나,<br>명세를 작성하여 Link 헤더에 링크하는 법, data로 하이퍼링크를 표현하는 법이 있다.</strong>      </li>
</ol>
<blockquote>
<p>REST를 알아보았으니 REST의 자원을 나타내는 URI에 대해서도 알아보자      </p>
</blockquote>
<h2 id="uri와-url">URI와 URL</h2>
<ul>
<li><p><code>URL</code>은 Uniform Resource Locator로 인터넷 상 자원의 위치를 나타내고,<br><code>URI</code>는 Uniform Resource Identifier로 인터넷 상의 자원을 식별하기 위한 문자열의 구성이다.    </p>
</li>
<li><p>그렇다면 우리가 알고있는 URL? URI? 정확히 무엇이 다른걸까??<br>우선 URI는 URL을 포괄하는 개념이다. URI가 더 상위개념이다.<br>예를 들어 <code>https://hyun-jii.github.io</code> 는 자원의 위치와 자원을 식별할 수 있으므로<br>URL 이면서 URI 이다.    </p>
</li>
</ul>
<ul>
<li>그리고 <code>https://hyun-jii.github.io/posts/first.html</code> 는<br><code>hyun-jii.github.io</code> 하위에 <code>posts</code> 디렉토리가 있고, 디렉토리 아래<br><code>first.html</code> 이라는 자원의 위치를 나타내고, 식별하므로 URL 이면서 URI 이다.    </li>
</ul>
<ul>
<li>마지막으로 <code>https://hyun-jii.github.io/157</code> 은 뒤에 <code>157</code>은 자원의 위치를 나타내지 않고,<br>자원을 식별만 하므로 URI이다.    </li>
</ul>
<h2 id="uri-설계-시-주의할-점">URI 설계 시 주의할 점</h2>
<ol>
<li>슬래시 구분자(/)는 계층 관계를 나타낼 때 사용한다.<br>ex) <code>https://hyun-jii.github.io/posts/12</code>  </li>
<li>URI 마지막은 슬래시(/)를 포함하지 않는다.  </li>
<li>하이픈은 URI 가독성을 높일 떄 사용한다.  </li>
<li>밑줄(<em>)는 URI에 사용하지 않는다. 밑줄(</em>)은 가독성이 떨어지고,<br>밑줄에 의해 문자가 가려진다.  </li>
<li>URI 경로에는 소문자를 사용한다.  </li>
<li>파일 확장자를 URI에 포함시키지 않는다.<br>HTTP Accept Header를 사용한다.<br>ex) <code>Accept : text/html</code>  </li>
</ol>
<blockquote>
<p>현재 대부분이 REST API라고 불리는 것이 완벽한 REST API가 아니라고 한다.<br>다른 제약조건들은 잘 지켜지고 있으나, Uniform Interface의<br>self-descriptive와 HATEOAS가 잘 지켜지고 있지 않다.<br>아무래도 시간과 구현의 어려움때문인 것 같다.  </p>
</blockquote>
<blockquote>
<p>본인도 아직 제대로 된 REST API를 설계해 본적이 없다...<br>이 글을 정리하며 REST 규칙에 대해 제대로 알게된 것 같다.  </p>
</blockquote>
<h2 id="references">References</h2>
<p> <a href="https://jeong-pro.tistory.com/180?category=793347">https://jeong-pro.tistory.com/180?category=793347</a><br> <a href="https://mangkyu.tistory.com/46">https://mangkyu.tistory.com/46</a><br> <a href="https://meetup.toast.com/posts/92">https://meetup.toast.com/posts/92</a><br> <a href="https://slides.com/eungjun/rest#/6">https://slides.com/eungjun/rest#/6</a><br> <a href="http://sunychoi.github.io/java/2015/04/27/uri-url.html">http://sunychoi.github.io/java/2015/04/27/uri-url.html</a>    </p>
]]></description>
        </item>
    </channel>
</rss>