<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>fucct</title>
        <link>https://velog.io/</link>
        <description>식빵 고양이 디디</description>
        <lastBuildDate>Fri, 18 Dec 2020 07:13:04 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>fucct</title>
            <url>https://images.velog.io/images/bread_dd/profile/721f35be-a351-4050-9c70-22a5ec1ae562/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. fucct. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bread_dd" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[짤막한 회고]]></title>
            <link>https://velog.io/@bread_dd/%EC%A7%A4%EB%A7%89%ED%95%9C-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@bread_dd/%EC%A7%A4%EB%A7%89%ED%95%9C-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Fri, 18 Dec 2020 07:13:04 GMT</pubDate>
            <description><![CDATA[<p>OS 시험이 2시간 남았는데 도저히 머리에 안들어오기에 쓰는 회고..
운이 좋게도 우아한형제들과 네이버 인턴에 합격했다.
개인적으로 면접에 대한 압박감이 심해서 살면서 가장 많은 스트레스를 받았던 시기가 아닐까 싶다.
하지만 결국 죽지 못할 만큼의 고통은 날 또 성장시켰다. 
그렇게 고생을 했더니 면접에도 익숙해져 가는 것 같다.
요즘 시험공부를 핑계삼아 개발공부를 거의 하지 않았다. 집에서 공부하는게 도저히 적응이 안된다. 시험 전에 공부는 안하고 블로그 글을 쓰고 있는 것만 봐도 ..
어쨌든 시험이 끝나면, 이 회고 글도 고치고, 인턴쉽 서류 제출도 마무리하고, 그동안 미뤄왔던 개발 공부를 다시 해야겠다.
당장 인턴쉽에서 사용할 WebFlux와 ElasticSearch를 학습하는게 1순위 목표가 될 듯 하다. (물론 출근 1주일남았지만..)
OS 시험 박살내고 오겠습니다 😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[우아한 테크코스 WAS 미션 설계]]></title>
            <link>https://velog.io/@bread_dd/%EC%9A%B0%EC%95%84%ED%95%9C-%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-WAS-%EB%AF%B8%EC%85%98-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@bread_dd/%EC%9A%B0%EC%95%84%ED%95%9C-%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-WAS-%EB%AF%B8%EC%85%98-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Fri, 18 Sep 2020 08:19:36 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/bread_dd/post/8ca556e1-3c93-40de-8752-fde643c1d6c5/image.png" alt=""></p>
<p>우테코에서 주어진 첫번째 Level4 미션. WAS 구현하기.</p>
<p>고민했던 내용들을 정리해보도록 하겠습니다.</p>
<h1 id="책임의-분리">책임의 분리</h1>
<p>가장 먼저 생각했던 건 WAS가 정확하게 무슨 일을 해주느냐? 였습니다.</p>
<ol>
<li>Socket으로 부터 들어온 요청을 읽어 <strong>요청 객체</strong>를 생성한다.</li>
<li>요청 객체와 사용자 코드를 바탕으로 적절한 <strong>Handler</strong>를 찾는다.</li>
<li>요청 객체와 Handler를 바탕으로 적절하게 <strong>Parameter</strong>를 Binding 한다.</li>
<li>Handler와 Parameter를 바탕으로 Handler를 <strong>실행</strong>한다.</li>
<li>Handler 실행 결과를 통해 <strong>응답 객체</strong>를 생성한다.</li>
<li>응답 객체를 바탕으로 적절한 <strong>View</strong>를 찾는다.</li>
<li>응답 객체와 View를 바탕으로 Socket을 통해 응답을 반환한다.</li>
</ol>
<p>가장 먼저 7가지 책임이 떠올랐고, 해당 책임을 RequestHandler에서 생성 및 수행하도록 리팩토링 해봤습니다.</p>
<ol>
<li>Socket의 Reader로 읽은 정보를 통해 <strong>ServletRequest</strong> 생성
<img src="https://images.velog.io/images/bread_dd/post/f1687a1d-f43f-4488-8c06-66e2e98cffb7/image.png" alt=""></li>
<li>해당 Request의 Path를 통해 View가 존재하면 Mapping 과정 생략 후 즉시 <strong>View Resolving</strong>
<img src="https://images.velog.io/images/bread_dd/post/d96f0cc8-7fa7-441c-acdc-d9f0f83c215b/image.png" alt=""></li>
<li>사용자 Controller를 바탕으로 Handler Mapping
<img src="https://images.velog.io/images/bread_dd/post/f41557da-f423-4ca9-beb4-556b2ffb79a9/image.png" alt=""></li>
<li>Handler와 MessageConverter를 통해 Handler 실행
<img src="https://images.velog.io/images/bread_dd/post/ce2d6571-a53e-45e3-a52b-cac09972a0d5/image.png" alt=""></li>
<li>ServletResponse를 Socket Output으로 전송
<img src="https://images.velog.io/images/bread_dd/post/bffe4b12-5582-4f02-aaec-7bd078c58ec0/image.png" alt=""></li>
</ol>
<p><img src="https://images.velog.io/images/bread_dd/post/606ddf22-2709-4cb7-8fc7-557018da9f8b/image.png" alt="">
잘뜹니다.</p>
<p>하지만 객체지향의 설계원칙에 따라 부족한 부분에 대해서 생각해봅시다.</p>
<ol>
<li>객체 생성과 연관관계를 설정하는 책임과, 각 객체들을 실행하는 컨텍스트 책임, 둘 다 RequestHandler가 해주고 있습니다. 이 책임은 분리되어야 합니다.</li>
<li>각 객체들이 클래스 의존관계를 가지고 있습니다. 상황에 따라 모두 변할 수 있으므로 해당 클래스들을 모두 인터페이스 의존관계를 통해 변경에 유연한 구조를 가져야 합니다.</li>
<li>view가 있는 요청과 없는 요청을 판단하고, 그에 따라 결과를 반환하는 로직이 RequestHandler에 있습니다. RequestHandler는 각 객체들을 실행하는 컨텍스트 역할을 해야합니다.</li>
</ol>
<p><img src="https://images.velog.io/images/bread_dd/post/a4530143-18b1-4736-bc27-7a5e25554cea/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[오브젝트와 의존관계]]></title>
            <link>https://velog.io/@bread_dd/%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8%EC%99%80-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84</link>
            <guid>https://velog.io/@bread_dd/%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8%EC%99%80-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84</guid>
            <pubDate>Tue, 15 Sep 2020 12:51:54 GMT</pubDate>
            <description><![CDATA[<h1 id="12-dao의-분리">1.2 Dao의 분리</h1>
<h2 id="관심사-분리">관심사 분리</h2>
<p>시간이 지남에 따라 모든건 변한다. 개발자는 항상 미래에 대비해야 한다. </p>
<p>객체지향 기술이 변화에 효과적으로 대처할 수 있는 기술이다.</p>
<p>적은 변화와 변화에 대한 검증을 빠르게 하는 개발자.</p>
<p><strong>분리와 확장을 고려한 설계가 필요하다</strong></p>
<p>DB가 바뀌었을때, 웹 화면의 레이아웃이 바뀌었을때, 등등..</p>
<p>변화는 대체로 한 가지 관심에 대해 일어나지만 그에 따른 작업은 집중되지 않는 경우가 많다. → 변화에 대한 변경이 한 가지에 집중해서 일어나야 한다. 즉 관심이 같은 것 끼리는 모으고 관심이 다른 것은 분리해낸다.</p>
<p><strong>관심사 분리</strong></p>
<p>커넥션 만들기 추출</p>
<ul>
<li>DB Connection 가져오기 관심</li>
<li>사용자 등록 SQL 실행 관심</li>
<li>Resource Close 하는 관심</li>
</ul>
<p><strong>변경사항에 대한 검증: 리팩토링과 테스트</strong></p>
<p>두번째 실행부터 예외가 터진다. PK이기 때문에. → DB를 삭제해주고 다시실행하면 동작한다.</p>
<p>이런 과정을 <strong>리팩토링</strong>이라고 하고, <strong>메소드 추출 기법</strong>이라고 한다.</p>
<p>N사와 D사가 서로 다른 DB를 사용한다면, DB커넥션을 가져오는 방법 또한 변경될 가능성이 있다. UserDao 소스코드를 제공하지 않는다면 어떻게 해야할까.</p>
<p><strong>상속을 통한 확장</strong></p>
<p>어떻게 데이터를 등록하고 가져올지에 대한 관심 → UserDao</p>
<p>어떻게 Connection을 가져올지에 대한 관심 → NUserDao, DUserDao</p>
<p>로 분리한다. 이는 변경이 용이하다 뿐만 아니라 손쉽게 확장된다라고 볼 수 있다.</p>
<p>이를 <strong>템플릿 메소드 패턴</strong>이라고 한다.</p>
<p>서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는것을 <strong>팩토리 메소드 패턴</strong>이라고 부르기도 한다.</p>
<p>상속을 사용했다는 단점. <strong>이미 UserDao가 상속중</strong>이라면? 상속은 상하위 클래스의 관계가 너무 <strong>밀접</strong>하다.</p>
<p>슈퍼클래스의 변화가 서브클래스의 변화를 초래하게 됨. DB 커넥션 코드를 다른 곳에서 못쓰게됨 (서브클래스를 다른데서 못씀)</p>
<h1 id="13-dao의-확장">1.3 DAO의 확장</h1>
<p>데이터 엑세스 로직 → DB 커넥션 분리</p>
<p>변화의 성격이 다르다면. 변경되면 안된다.</p>
<ol>
<li><strong>메소드 분리</strong>를 통해 관심사를 분리했다</li>
<li><strong>상속</strong>을 통해 상하위 클래스로 분리했다</li>
<li>아예 독립된 클래스로 만들어보자.</li>
</ol>
<p>SimpleConnectionMaker방식의 문제점</p>
<p>직접 UserDao에서 생성해서 사용하므로 N사 D사에 따라 다른 클래스의 다른 메서드의 다른 타입을 사용할 수 없게 된다. </p>
<ol>
<li>D사 ConnectionMaker에서는 메서드명이 openConnection()이라면?</li>
<li>UserDao가 SimpleConnectionMaker에 대한 정보를 알아야 한다는 점 → N사에서 다른 클래스를 쓰면 다시 고쳐야함</li>
</ol>
<p>이런 문제는 Connection을 가져올때 너무 많이 알고있기 때문</p>
<p><strong>인터페이스 도입</strong></p>
<p>ConnectionMaker → NConnectionMaker, DConnectionMaker </p>
<p>UserDao에서 NConnectionMaker()를 생성해서 사용하는 방식 → 아직도 Connection에 대한 지식이 남아있다.</p>
<p><strong>관계설정 책임의 분리</strong></p>
<p>왜 UserDao가 인터페이스가 아닌 구현체를 알고있어야 하는가? </p>
<p>→ 아직도 어떤 ConnectionMaker를 사용할지를 결정하는 관계 설정의 대한 관심을 가지고 있다.</p>
<p>즉 UserDao와 ConnectionMaker 특정 구현체와의 관계를 설정해주는 것에 관한 관심이 남아있다.</p>
<p><strong>클라이언트</strong>는 사용하는 오브젝트. 사용되는 오브젝트는 <strong>서비스. 클라이언트 오브젝트는 관계를 결정해주는 기능(책임)을 맡기 적절한 곳이다.</strong></p>
<p>오브젝트 사이 관계는 런타임에 한 오브젝트가 다른 오브젝트의 레퍼런스를 갖는 방식으로 만들어진다.</p>
<p>외부에서 만들어 준 것을 가져오는 방식. 파라미터로 제공받은 오브젝트는 이용만 하면된다. 다른건 신경 x</p>
<p>클래스간 관계는 코드로 드러난다. 오브젝트간 관계는 런타임에 드러난다. 코드에서는 특정 클래스를 모르더라도 사런타임에 들어오므로 사용가능하다 → 다형성을 통해</p>
<p>UserDao 클라이언트는 ConnectionMaker 구현 클래스를 선택하고, 생성해서, 연결해준다. → <strong>Main 메소드</strong></p>
<p><strong>원칙과 패턴</strong></p>
<p><strong>SOLID</strong></p>
<ul>
<li><strong>단일 책임 원칙 :</strong> 하나의 클래스는 단 한가지의 변경이유만을 가져야한다. → <strong>응집도 (책임은 변경의 이유)</strong></li>
<li><strong>개방 폐쇄 원칙 :</strong> 클래스나 모듈은 확장에 열려있고 변경에는 닫혀 있어야 한다. → 변경을 예측하라. 첫번째 총알은 그냥맞고 그다음부터 적용한다.</li>
<li><strong>리스코프 치환 원칙 :</strong> A를 B로 모두 치환 가능하면 A는 B의 하위타입이다. → 인터페이스의 관점으로 확대</li>
<li><strong>인터페이스 분리 원칙:</strong> 클라이언트가 자신이 이용하지 않는 메서드에 대해 의존하지 않아야 한다는 원칙. 즉 메소드의 변경이 이 메소드를 사용하지 않는 클라이언트에 영향을 줄 때 인터페이스로 분리해야한다.</li>
<li><strong>의존관계 역전 원칙 :</strong> 상위 수준 모듈은 하위수준 모듈에 의존하면 안된다. 둘 모두 추상화에 의존해야한다.</li>
</ul>
<p><strong>높은 응집도와 낮은 결합도</strong></p>
<ul>
<li>응집도가 높다는 것. 변경에 대해 모듈에서 변화는 부분이 크다는 것. 여러 관심사와 책임이 얽혀있는 코드는 응집도가 낮다.</li>
<li>결합도가 낮다는 것. 책임과 관심사가 다른 오브젝트와는 느슨한 연결 형태를 유지한다는 것. 관계를 유지할땐 최소한의 방법으로 간접적으로. 결합도란 하나의 오브젝트의 변화가 일어날 때 관계를 맺고있는 오브젝트에게 변화를 요구하는 정도 라고 보면 된다.</li>
</ul>
<p><strong>전략 패턴</strong></p>
<p>전략 패턴은 자신의 기능 맥락에서 필요에ㄷ 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고 구현체를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴.</p>
<pre><code class="language-java">public class UserDao {
    private ConnectionMaker connectionMaker;
    public UserDao(ConnectionMaker connectionMaker){
        this.connectionMaker = connectionMaker;
    }

    public User get(String id){
        Connection connection = connectionmaker.getConnection();
        ...
    }
}
</code></pre>
<p>DB연결 방식이라는 알고리즘을 ConnectionMaker 인터페이스로 정의하고 구현체를 바꿔가면서 사용할 수 있게 분리했다.</p>
<p>또한 메인메서드와 같은 클라이언트의 필요성 또한 설명해준다. 전략 패턴을 적용 시 클라이언트는 컨텍스트가 사용할 전략을 컨텍스트의 생성자 등을 통해 제공해준다.</p>
<h1 id="14-제어의-역전ioc">1.4 제어의 역전(IoC)</h1>
<p>Main 메서드는 어떤 ConnectionMaker 구현체를 사용할지를 결정하는 기능을 떠맡았다.</p>
<p><strong>팩토리</strong></p>
<p>객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 역할을 하는 클래스를 만들어보자. 이런 오브젝트를 흔히 팩토리라고 부른다.</p>
<p>컴포넌트의 구조와 관계를 정의한 설계도 같은 역할을 한다.</p>
<p>제어의 역전이라는 건 프로그램의 제어 흐름 구조가 뒤바뀌는 것. </p>
<p>일반적으로 메인 → 오브젝트 생성 → 메소드 호출 → 메소드 안에서 오브젝트 생성 → 메소드 안 메소드 호출 </p>
<p>이런 흐름에서 각 오브젝트는 프로그램 흐름을 결정하거나 사용할 오브젝트를 구성하는 작업에 능동적으로 참여한다. 즉 모든 종류의 작업을 사용하는 쪽에서 제어하는 구조. <strong>생성해서 사용</strong></p>
<p>제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다. 당연히 생성하지도 않는다. 모든 제어 권한을 다른 대상에게 위임한다. 모든 오브젝트는 위임받은 오브젝트에 의해 결정되고 만들어진다.</p>
<p>프레임워크는 라이브러리가 아니다. 프레임워크는 흐름을 주도하고 개발자가 만든 코드를 사용하는 방식으로 동작한다. </p>
<p>기존 ConnectionMaker 구현 클래스를 결정하고 만드는 제어권은 UserDao에 있었으나 DaoFactory로 옮겨갔다. 자신이 어떤 ConnectionMaker 구현 클래스를 사용할 지에 대한 권한을 DaoFactory에게 넘김으로써 UserDao는 수동적인 존재가 되었다.</p>
<p>제어의 역전에서는 컴포넌트의 생성과 관계설정, 사용, 생명주기 관리 등을 관장하는 존재가 필요하다. DaoFactory는 가장 단순한 IoC 컨테이너로 불릴 수 있다.</p>
<h1 id="15-스프링의-ioc">1.5 스프링의 IoC</h1>
<p>스프링에서 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트를 빈 팩토리라고 부른다. (어플리케이션 컨텍스트라고도 한다.)</p>
<p>우리가 작성한 DaoFactory가 빈 팩토리의 설정 정보가 된다. </p>
<p>@Bean 메소드 이름은 빈 이름이 된다. 이름이 필요한 이유는 생성 방식이 다르게 될 수 있기 때문.</p>
<p><strong>애플리케이션 컨텍스트 동작방식</strong></p>
<p>DaoFactory가 Dao 오브젝트를 생성하고 관계를 맺어주는 녀석이였다면, 애플리케이션 컨텍스트는 IoC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당한다.</p>
<p>애플리케이션 컨텍스트는 @Configuration을 통해 설정정보를 등록해놓고 @Bean을 통해 메소드 이름을 가져와 빈 목록을 만들어 둔다.</p>
<p><strong>애플리케이션 컨텍스트를 사용하는 이유</strong></p>
<ul>
<li>클라이언트가 구체적인 팩토리 클래스를 알 필요가 없다 → 좀 더 일관된 방법으로 빈을 가져올 수 있다.</li>
<li>종합 IoC 서비스를 제공해준다 → 오브젝트가 만들어지는 방식을 다양하게. 효과적으로 오브젝트를 활용</li>
<li>빈을 검색하는 기능 제공</li>
</ul>
<p>스프링 <strong>용어 정리</strong></p>
<ul>
<li>빈: IoC방식으로 관리하는 오브젝트</li>
<li>빈 팩토리 : IoC 핵심 컨테이너. 빈 등록, 생성, 조회, 관리</li>
<li>애플리케이션 컨텍스트 : 빈 팩토리를 확장한 IoC 컨테이너. 스프링의 각종 부가 서비스를 제공한다</li>
<li>설정정보 설정 메타정보 : 애플리케이션 컨텍스트가 IoC를 적용하기 위해 사용하는 메타정보.</li>
<li>컨테이너 : 다 비슷</li>
<li>스프링</li>
</ul>
<h1 id="싱글톤-레지스트리와-오브젝트-스코프">싱글톤 레지스트리와 오브젝트 스코프</h1>
<p>애플리케이션 컨텍스트는 싱글톤을 저장하고 관리하는 싱글톤 레지스트리. 정상적인 자바 객체를 싱글톤으로 활용하게 해준다. </p>
<p><strong>싱글톤과 오브젝트의 상태</strong></p>
<p>멀티스레드 환경에선 상태관리에 주의를 기울여야 한다. 서비스 형태의 오브젝트로 사용되는 경우 무상태 방식으로 만들어져야 한다. 필드가 아닌 파라미터, 로컬변수, 리턴값 등을 활용해야한다.</p>
<p>물론 필드가 싱글톤이거나 읽기전용이라면 가능하다. 한번 초기화해주고 나면 수정되지 않기 때문.</p>
<p><strong>빈의 스코프</strong></p>
<p>스프링 빈의 기본 스코프는 싱글톤이다. 강제로 제거하지 않는 한 컨테이너가 존재하는 동안 계속 유지된다.</p>
<p>(프로토타입 스코프는 매번 새로 만들어준다) (Request Scope가 있다) (Session 스코프도 있다)</p>
<h1 id="의존관계-주입di">의존관계 주입(DI)</h1>
<p>IoC는 일반적인 개념. 객체를 생성하고 관계를 맺어주는 등의 작업을 담당하는 기능을 일반화한것이 IoC 컨테이너</p>
<p>IoC는 너무 포괄적인 개념이라, <strong>스프링은 IoC 방식의 핵심인 의존관계 주입</strong> 이라는 의도가 명확히 드러나는 이름을 사용하기 시작했다.</p>
<p><strong>DI는 오브젝트를 외부로부터 주입받아 다이나믹한 의존 관계를 만드는게 핵심</strong></p>
<p><strong>의존관계</strong></p>
<p>A가 B에 의존한다면, B가 변하면 A에 영향을 미친다. A가 변해도 B는 영향을 받지 않는다.</p>
<p>UserDao는 ConnectionMaker에 의존한다. ConnectionMaker 인터페이스가 변경된다면 UserDao는 영향을 받을것이다. 그러나 ConnectionMaker는 인터페이스임. 추상클래스 NConnectionMaker가 변경되더라도 UserDao엔 직접적인 영향을 미치지 않는다. → <strong>인터페이스에 대해서만 의존관계</strong>를 만든다면, 실제 구현클래스와의 관계는 느슨해지고, 변화에영향을 덜받게 된다. <strong>결합도가 낮아진다.</strong></p>
<p><strong>런타임</strong> 시에 의존관계를 맺는 대상을 <strong>의존 오브젝트</strong>라고 말한다. </p>
<p><strong>의존관계 주입이란</strong> </p>
<ul>
<li>클래스나 코드에서 런타임 시 의존관계가 드러나지 않는다. 인터페이스에만 의존하고 있는다.</li>
<li>런타임 시 의존관계는 컨테이너나 팩토리같은 제 3자가 결정한다.</li>
<li>외부로부터 의존 오브젝트를 주입받음으로써 의존관계가 만들어진다.</li>
</ul>
<p>DI의 핵심은 관계를 맺도록 도와주는 제 3자가 있다는점. → 관계설정 책임을 분리해낸 오브젝트. DaoFactory나 IoC 컨테이너등. </p>
<p>DaFactory는 런타임 의존관계 설정해주는 역할, IoC 방식의 오브젝트 생성 초기화 제공. </p>
<p>→ DI를 담당하는 컨테이너. DaoFactory는 일종의 IoC/DI 컨테이너</p>
<p>DI는 자신이 사용할 오브젝트에 대한 선택, 생성제어를 외부로 넘기고 자신은 수동적으로 주입받은 오브젝트를 사용한다. IoC의 개념과 거의 동일하다.</p>
<p><strong>의존관계 검색</strong></p>
<p>자신이 필요한 의존 오브젝트를 능동적으로 찾는다. 어떤 클래스가 아닌. </p>
<p>getBean을 통해 의존관계를 검색하여 가져온다.</p>
<p>의존관계 검색은 차이점이 주입받는 녀석이 빈일 필요는 없다. </p>
<p><strong>부가기능 추가</strong></p>
<p>Proxy처럼 사용</p>
<p>메소드를 이용한 DI</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Rest Docs와 Open Api (Swagger)]]></title>
            <link>https://velog.io/@bread_dd/Spring-Rest-Docs%EC%99%80-Open-Api-Swagger</link>
            <guid>https://velog.io/@bread_dd/Spring-Rest-Docs%EC%99%80-Open-Api-Swagger</guid>
            <pubDate>Mon, 14 Sep 2020 05:52:02 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 오늘은 Spring 진영의 문서화 프레임워크인 Spring Rest Docs와 Swagger를 비교하는 글을 작성해 볼까 합니다. Peloton 프로젝트 진행 중에 문서화와 관련해서 고민했던 부분들을 정리 해보았어요. 함께 공유해보면 좋을 것 같아요.</p>
<h1 id="spring-rest-docs의-특징">Spring Rest Docs의 특징</h1>
<p><img src="https://images.velog.io/images/bread_dd/post/0b6abd8f-051c-4bd6-be40-b905eab616b8/image.png" alt=""></p>
<p><small><em>Spring Rest Docs로 작성된 문서</em></small></p>
<p>Spring Rest docs는 Test 기반으로 동작합니다. 테스트 코드에 추가함으로써 문서가 작성돼요. 따라서 Production 코드에 영향을 미치지 않는다는게 큰 장점입니다. 테스트 기반이기 때문에 테스트가 수행되지 않으면 문서 또한 작성되지 않아요. 따라서 문제가 있는 경우엔 문서또한 작성되지 않는다는 장점(?)이 있죠. 
(물론 테스트가 수행되지 않는 서비스에 대해서도 문서를 보고싶은 사람도 있겠지만요.) 또한 문서 본연의 기능에 충실합니다. </p>
<p>하지만 테스트 기반으로 설계된 서비스가 아니라면, 사용이 불가능합니다. 또한 추가적인 설정들을 적용하기가 쉽지 않다는 단점을 가지고 있어요. 또한 사진에서 보시는 것 처럼 문서가 그렇게 이쁘지 않아요😅</p>
<p><img src="https://images.velog.io/images/bread_dd/post/57539a6f-9aff-4ec0-a82f-46f3aa1e5537/image.png" alt="">
<img src="https://images.velog.io/images/bread_dd/post/c03c51f1-4a8d-4e74-9d54-bede3783a1f9/image.png" alt=""></p>
<p>마지막으로 엔드 포인트마다 수많은 코드를 추가해줘야 한다는 단점이 있습니다. 반복성 작업을 해야해서 굉장히 힘들었습니다😭 중복을 제거하려면 좀 더 고민을 해봐야겠어요.</p>
<h1 id="swagger의-특징">Swagger의 특징</h1>
<p><img src="https://images.velog.io/images/bread_dd/post/aab71147-484a-4a6f-9505-5618f77bb5c2/image.png" alt=""></p>
<p><small><em>Swagger로 작성된 페이지</em></small></p>
<p>Spring Rest Docs와 반대로 Open Api로 작성된 문서는 Swagger UI를 통해  알록달록 굉장히 이쁩니다. 또한 의존성만 추가해도 이런 인터페이스를 한번에 제공하죠. 그리고 실제 API를 테스트하는 기능 또한 제공해요. 만약 Admin Page가 없다면, 이런 기능을 유용하게 사용할 수 있을 것 같아요!</p>
<p><img src="https://images.velog.io/images/bread_dd/post/ad9b8e35-71de-4512-a2b6-5a9912ae0f6e/image.png" alt=""></p>
<p><small><em>API 테스트 해보기</em></small></p>
<p><img src="https://images.velog.io/images/bread_dd/post/cd95ab8c-dfe6-461f-af10-124d89ae52ac/image.png" alt=""></p>
<p><small><em>심지어 작성된 객체의 필드도 보여준다..!</em></small></p>
<p>하지만 지금까지의 Swagger는 문서화 도구보단 API 테스터에 가까워요. 원하는 엔드포인트나 필요한 리소스들에 대한 설명은 어떻게 추가해줘야 할까요?</p>
<pre><code class="language-java">@Operation(summary = &quot;Get a book by its id&quot;)
@ApiResponses(value = { 
  @ApiResponse(responseCode = &quot;200&quot;, description = &quot;Found the book&quot;, 
    content = { @Content(mediaType = &quot;application/json&quot;, 
      schema = @Schema(implementation = Book.class)) }),
  @ApiResponse(responseCode = &quot;400&quot;, description = &quot;Invalid id supplied&quot;, 
    content = @Content), 
  @ApiResponse(responseCode = &quot;404&quot;, description = &quot;Book not found&quot;, 
    content = @Content) })
@GetMapping(&quot;/{id}&quot;)
public Book findById(@Parameter(description = &quot;id of book to be searched&quot;) 
  @PathVariable long id) {
    return repository.findById(id).orElseThrow(() -&gt; new BookNotFoundException());
}</code></pre>
<p>프로덕션 코드에 직접 어노테이션을 추가하는 방식으로 문서화를 구현할 수 있어요. 결과는 다음과 같죠.</p>
<p><img src="https://images.velog.io/images/bread_dd/post/e6e3c5ff-237e-4775-af9c-3b53b19abf66/image.png" alt=""></p>
<p>보시다시피 프로덕션 코드에 문서화와 관련된 코드가 추가된다는 점이 OpenApi의 단점이 아닐까 싶어요. 예제 코드만 보더라도 가독성이 굉장이 떨어지고, 실제 컨트롤러는 어디서부터 시작되는지 조차 파악하기 어렵죠. 또한 객체 지향의 관점에서도 컨트롤러에 문서화라는 역할이 추가되는 느낌이에요. </p>
<p>또한 테스트가 정상적으로 동작하지 않아도 문서가 작성된다는 문제점등이 있을 것 같아요. </p>
<h1 id="정리">정리</h1>
<p>정리해보자면 각 장단점은 이렇게 될 것 같아요. 본인의 서비스에 맞게 선택하셔서 사용하는게 가장 좋을 것 같습니다!</p>
<h3 id="spring-rest-docs">Spring Rest Docs</h3>
<table>
<thead>
<tr>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td>테스트 기반으로 수행됨</td>
<td>세부 설정하기 어려움</td>
</tr>
<tr>
<td>프로덕션 코드에 영향을 주지 않음</td>
<td>엔드포인트에 따른 코드가 많음</td>
</tr>
<tr>
<td>문서에 대한 신뢰성이 높음</td>
<td>추가적인 기능을 제공하지 않음</td>
</tr>
<tr>
<td>문서 본연의 기능에 충실함</td>
<td>문서가 예쁘지 않음..😭</td>
</tr>
</tbody></table>
<h3 id="swagger">Swagger</h3>
<table>
<thead>
<tr>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td>문서가 예쁘다</td>
<td>프로덕션 코드의 가독성이 떨어짐</td>
</tr>
<tr>
<td>API 테스트 기능을 제공</td>
<td>문서의 신뢰도가 떨어짐(테스트 기반이 아님)</td>
</tr>
<tr>
<td>객체들에 대한 정보를 제공</td>
<td>라이브러리의 무게가 무거움</td>
</tr>
<tr>
<td>의존성 추가만 해도 기본 UI를 제공</td>
<td>많은 어노테이션들을 학습해야함</td>
</tr>
<tr>
<td>TDD 서비스가 아니어도 사용가능</td>
<td></td>
</tr>
</tbody></table>
<h2 id="출처">출처</h2>
<p><a href="https://woowabros.github.io/experience/2018/12/28/spring-rest-docs.html">우아한 형제들 기술블로그</a>
<a href="https://www.baeldung.com/spring-rest-openapi-documentation">Baeldung</a>
<a href="https://swagger.io/docs/specification/about/">Swagger</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Data JDBC vs Spring Data JPA]]></title>
            <link>https://velog.io/@bread_dd/Spring-Data-JDBC-vs-Spring-Data-JPA</link>
            <guid>https://velog.io/@bread_dd/Spring-Data-JDBC-vs-Spring-Data-JPA</guid>
            <pubDate>Fri, 11 Sep 2020 07:52:55 GMT</pubDate>
            <description><![CDATA[<h1 id="spring-data-jdbc의-탄생-배경">Spring Data JDBC의 탄생 배경</h1>
<p>Spring Data JPA는 Java ORM 표준 JPA와 Hibernate로 만들어진 프레임워크입니다. 대부분의 Java 개발자들은 JPA를 공부하고 사용합니다.</p>
<p><img src="https://images.velog.io/images/bread_dd/post/1586b222-1a47-4ac7-9e1d-5b88821c5b2d/image.png" alt=""><small><em>구글 트렌드에서 본 대한민국의 Data JPA와 Data JDBC 검색량</em></small></p>
<p>하지만 Spring에는 새로운 ORM인 Spring Data JDBC 또한 존재합니다. 이미 광범위하게 사용하는 JPA와 어떤 차이점이 있을까요?</p>
<h2 id="jpa는-복잡하다">JPA는 복잡하다</h2>
<p>보통 JPA를 학습하기 전에 러닝 커브(learning curve)가 높다고들 이야기 합니다. 다음에 나와 있는 JPA의 특징들이 복잡하기 때문이죠.</p>
<ul>
<li>Lazy Loading</li>
<li>Dirty Checking</li>
<li>Session / 1st Level Cache</li>
<li>Proxy</li>
<li>M to x Mapping</li>
</ul>
<h3 id="lazy-loading-exception">Lazy Loading (Exception)</h3>
<p>코드만 보고 파악하기 힘든 Exception입니다. 코드가 복잡해질수록 지연 로딩으로 가져오는 객체가 어느 시점에 초기화되는지 알기 힘듭니다. 또한 코드만으로 다음에 어떤 SQL 문이 발생할지 예상하기 힘들다는 문제도 있죠. </p>
<h3 id="dirty-checking">Dirty Checking</h3>
<p>Entity Manager에 save를 하는데, 원치 않은 save가 발생할 수 있습니다. 그냥 테스트를 위해 객체를 조작하고 싶어도, 저장까지 되는 문제가 발생합니다. 특정 객체만을 제외하고 저장하고 싶은 경우에도 복잡한 원리를 이해해야만 합니다.</p>
<h3 id="session--1st-level-cache">Session / 1st Level Cache</h3>
<p>영속성 컨텍스트로 인해 항상 같은 객체만을 사용합니다. DB로부터 불러온 데이터와 조작한 객체를 가지고 비교하는 연산 등을 힘들게 합니다.</p>
<h3 id="proxy">Proxy</h3>
<p>엔티티 간 비교 시 프록시인지 아닌지를 확인해야만 하는 경우가 생깁니다. 이 경우 어떤 객체가 프록시이고 아닌지 판단하기 어렵죠.</p>
<h3 id="m-to-x-mapping">M to x Mapping</h3>
<p>M 대 N 연관관계는 다양한 문제를 야기하고 예측할 수 없습니다.</p>
<h1 id="simple-is-king">Simple is King</h1>
<p>Complexity의 반대는 뭘까요? <strong>Simple</strong>입니다. Spring Data JDBC의 특징은 단순함입니다. </p>
<ul>
<li>No lazy loading</li>
<li>No Session(Caching)</li>
<li>No Proxies</li>
<li>No Flushing : save와 동시에 Query (쓰기 지연 저장소가 없음)</li>
<li>No Many to x Relation</li>
</ul>
<p>Spring Data JDBC는 Domain Driven Design을 기반으로 합니다. 따라서 모든 Repository는 Aggregate Root 기준으로 존재합니다. 라이프사이클 또한 Aggregate Root와 하위 속성들이 동일합니다. 서로 다른 Aggregate 간 참조는 Id를 통해 수행합니다. 이러한 개념이 코드를 통해 알기 쉽게 설계되어있습니다.</p>
<p>Spring Data JDBC는 지연 로딩이 없고 항상 즉시 로딩 전략을 사용합니다. 
Aggregate는 항상 완전한 상태라는 걸 보장받을 수 있죠. Lazy Loading Exception 또한 고민할 필요가 없습니다. 하지만 불필요한 데이터까지 함께 가져오는 단점이 있습니다. 이 부분에 대한 자세한 설명이 없지만, 발표에서는 JPA에서도 fetch type을 Eager로 설정하면 발생하는 버그니까 대수롭지 않다는 듯이 넘어가더군요..😂</p>
<p>JPA에서 굉장히 효율적이라고 생각했던 기능인 Flushing도 없습니다. 소스 코드만으로 Flushing에서 생성될 Query문은 예측하기 매우 까다롭습니다. 이런 부분에서 Spring Data JDBC는 직관적으로 다가옵니다.</p>
<p>마지막으로 대망의 연관관계입니다. Spring Data JDBC는 일대다 혹은 일대일 단방향 관계만을 추구합니다. 
사실, 테이블 설계 과정에서 M to X 연관 관계는 피하려 해도 피할 수 없는 녀석이죠. Spring Data JDBC는 이러한 부분을 ID 참조로 피해갑니다. </p>
<p><img src="https://images.velog.io/images/bread_dd/post/7c5872c6-85d5-41bc-8a6a-1d2b167b9472/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/bread_dd/post/f1492150-3631-40a7-b854-4dfccabd4c30/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/bread_dd/post/cd5b1e25-8d63-41de-878f-9fff5f81540b/image.png" alt=""></p>
<p>게시판의 회원, 게시글, 댓글을 기준으로 고려해보면, Article과 Account는 M to 1 연관 관계입니다. 보시는 것처럼 accountId로 단순한 Long 값만을 가지고 있는 걸 보실 수 있습니다.</p>
<p>반대로 Article과 Comment는 1 to M 관계입니다. 기본적으로 Set을 사용하여 연관 관계를 표현하죠. 하지만 서로 다른 Aggregate Root인 경우 ID 참조를 통해 연관 관계를 표현해야 하므로 Ref 테이블이 생기죠. DDD의 개념을 코드에서 그대로 드러나게 됩니다.</p>
<h1 id="결론">결론</h1>
<p>Spring Data JDBC는 아직 완성되지 않은, 지속적으로 개선되어가는 프레임워크입니다. Derived Query나 Soring &amp; Paging도 얼마전 2.0.0버전부터 지원하게 되었죠. 성능적으로 JPA에 비해 비효율적인 부분이 많지만, 직관적인 Spring Data JDBC가 굉장히 매력적인 ORM이라고 생각합니다. 
Naver의 Spring Data JDBC Plus 프레임워크에 대해서도 설명해 드리고자 했으나 내용이 너무 길어질 것 같아서 다음 글에서 다뤄보도록 하겠습니다.</p>
<p>해당 내용은 Pivotal 사의 <a href="https://www.youtube.com/watch?v=AnIouYdwxo0">발표1</a>, <a href="https://www.youtube.com/watch?v=GOSW911Ox6s">발표2</a>를 바탕으로 작성했습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[회고 - 어떤 개발자가 되고 싶은가]]></title>
            <link>https://velog.io/@bread_dd/%ED%9A%8C%EA%B3%A0-%EC%96%B4%EB%96%A4-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%90%98%EA%B3%A0-%EC%8B%B6%EC%9D%80%EA%B0%80</link>
            <guid>https://velog.io/@bread_dd/%ED%9A%8C%EA%B3%A0-%EC%96%B4%EB%96%A4-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%90%98%EA%B3%A0-%EC%8B%B6%EC%9D%80%EA%B0%80</guid>
            <pubDate>Wed, 02 Sep 2020 15:37:20 GMT</pubDate>
            <description><![CDATA[<p>어려운 질문이다.
어쩌다가 개발자라는 꿈을 갖게 되었을까?</p>
<h1 id="시작">시작</h1>
<p>시작은 중학교 2학년 때.. 아버지께서 가져오신 삼성 노트북이 내 꿈을 결정했다.
당시 웹하드(온디스크, 파일노리..)가 유행이었는데 &#39;메이플 프리서버 만드는 방법&#39;이라는 파일을 보고 바로 다운로드했다. 밤을 새워 메이플 스토리란 게임의 프리(가짜) 서버를 만들었다. 같은 반 친구들이 하나둘씩 접속하기 시작한다. 그 당시 유행하던 네이트온, 웹 디스크, 네이버 카페 등등.. 친구들이 친구들을 불러모으고 그 친구들이 형, 동생들을 불러모은다. 어느새 수백, 수천 명의 사람들이 내가 만든 게임을 이용한다. 피드백이 들어오면 밤새도록 알지도 못하는 js파일을 메모장으로 수정해 나간다. 카페에 회원들이 고맙다고 글을 올려준다.</p>
<p>이 순간 난생처음 <strong>성취감</strong>이란걸  느꼈다.</p>
<h1 id="방황-그리고-깨달음">방황.. 그리고 깨달음</h1>
<p>초등학생 때 공부를 꽤 잘하는 편에 속했다. 중학교 1학년 때도 마찬가지였고. 그러나 부모님은 그보다 더 잘하길 원했고, 1등이 아니면 인정해주지 않으셨다. 중학교 1학년 때 꽤 좋은 성적이 나와서 부모님께 자랑했다가 오히려 혼난 적이 있었다. 그 사건 이후 공부를 해야 할 동기를 얻지 못했다. 학교도 안 가며 방황하기 시작했다. 그러던 시점에 만나게 된 게 프로그래밍이었다.</p>
<p>프리서버를 만들고 나서 내 삶의 목표 의식이 생겼다. <strong>성취감</strong>을 추구하며 살자. 사람은 성취감을 얻기 위해 살아가는구나. 이 생각은 27살인 지금도 마찬가지이다. 프로그래밍을 공부하기 위해선 대학을 가야 한다는 생각에 학교 공부도 다시 시작했다. 재수까지 해서 컴퓨터 공학과에 진학했다.</p>
<h1 id="또-다른-방황-똑같은-깨달음">또 다른 방황.. 똑같은 깨달음</h1>
<p>사실 컴퓨터공학과는 내가 상상한 곳은 아니었다. 개발보단 공학을 배우는 곳이었다. 만들고 싶은 걸 만드는 곳이 아니라, 이미 만들어진 걸 똑같이 만들 수 있는지 테스트하는 곳이었다. 다시 절망을 느끼고 방황하기 시작했다.
우아한 테크코스를 만나고, 개발에 대한 열정을 다시 확인할 수 있었다.</p>
<p>백엔드라는 단어가 무슨 뜻인지도 모른 채로 우아한 테크코스에 들어갔다. 사실 자바도 들어가기전 학교에서 수업으로 들었던 것뿐, 아는 게 없었다. 하지만 우테코에서 진정한 개발에 대해 배웠다. 원하는 걸 만들기 위해 배워야 하는 것들을 배웠다. 필요한 걸 얻기위해 공부하는 법을 배웠다. 매 순간의 배움의 연속이었다.</p>
<p><strong><a href="https://github.com/woowacourse-teams/2020-14f-guys">Peloton</a></strong> 이라는 서비스를 결과물로 얻었다. 사실 아쉬운 점도 많고 부족한 점도 많다. 하지만 대학 생활 전체를 다 합쳐도 얻지 못할 <strong>성취감</strong>을 얻게 되었다. 개발이 더 좋아졌다. 중학교 2학년의 얻은 깨달음이 틀리지 않았다는 걸 증명해냈다. 성취감이 나를 성장시킨다는 사실을.</p>
<h1 id="나는">나는</h1>
<p>성취감을 위해 발버둥 치는 개발자, 성취감을 위해 어떤 공부도 가리지 않는 개발자
그리고 
<strong>항상 성취해내는 개발자</strong>가 되고 싶다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Data Jdbc의 save 방식과 Auditing]]></title>
            <link>https://velog.io/@bread_dd/Spring-Data-Jdbc%EC%9D%98-save-%EB%B0%A9%EC%8B%9D%EA%B3%BC-Auditing</link>
            <guid>https://velog.io/@bread_dd/Spring-Data-Jdbc%EC%9D%98-save-%EB%B0%A9%EC%8B%9D%EA%B3%BC-Auditing</guid>
            <pubDate>Sun, 16 Aug 2020 08:54:58 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/bread_dd/post/540f01d5-4ac4-4193-b283-c6a12d2a0f18/spring-data-jdbc.jpg" alt=""></p>
<h2 id="spring-data-jdbc의-save-방식">Spring Data Jdbc의 Save 방식</h2>
<p>Spring Data Jdbc의 Save는 기본적으로 ID가 있는 Entity 객체에 대해선 Update를 수행하고 ID가 없는 Entity에 대해선 INSERT를 수행합니다. 이게 어떻게 동작하는 걸까요? </p>
<p><img src="https://images.velog.io/images/bread_dd/post/99495a6b-1ea0-473a-a406-f1a56852dab5/image.png" alt=""></p>
<p>위 메서드는 CrudRepository가 사용하는 JdbcAggregateTemplate 입니다. 
MappingContext에서 Save요청으로 들어온 인스턴스를 꺼내, isNew항목에 따라 체크하여 create또는 update를 수행합니다.</p>
<h2 id="id가-있는-entity-저장">Id가 있는 Entity 저장</h2>
<p>저는 ID가 있는 경우에도 Save를 하기 위해 Insert 인터페이스를 만들어서 사용하기로 했습니다. </p>
<p><img src="https://images.velog.io/images/bread_dd/post/088c6acf-60b7-4821-b77b-25375dbd1b28/image.png" alt=""></p>
<p>제가 Insert하고 싶었던 엔티티는 대략 다음과 같습니다.</p>
<p><img src="https://images.velog.io/images/bread_dd/post/59dfc605-5311-43dc-a16c-fae8da071f22/image.png" alt=""></p>
<p>해당 엔티티 객체에 아이디를 넣고 생성하려던 찰나, 다음과 같은 에러가 터지더군요.
<img src="https://images.velog.io/images/bread_dd/post/18c435ed-b1e4-4fef-8a28-05d424d0c25c/image.png" alt=""></p>
<p>CREATED_AT 칼럼은 분명히 Auditing을 통해 자동 주입 될텐데 어째서 null이 나오는 걸까요? 해당 문제의 범인을 찾기위해 열심히 Call Stack을 뒤져보던 찰나 이런 녀석을 발견했습니다. </p>
<p><img src="https://images.velog.io/images/bread_dd/post/ae90c8c4-8318-4f08-9a48-ab06a3c582af/image.png" alt=""></p>
<p>entity의 isNew 값이 true가 아니면 Created라고 판단하지 않아 createdAt 값이 Auditing되지 않은 것이 였습니다. 그렇다면 이 isNew 값은 어떻게 설정되는 걸까요?</p>
<p><img src="https://images.velog.io/images/bread_dd/post/33bc2fdd-303c-4c03-b9a6-52366d7b22a6/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/bread_dd/post/6ebeb6d1-61bd-45ec-a337-3cac259a7d5a/image.png" alt="">
PersistentEntityIsNewStrategy 클래스에서 이 값을 설정하는 전략을 선택하는데, 간단히 요약하자면, 엔티티 필드에 @Version 값 또는 @Id 값을 확인하여, 그 값이 0 또는 null이면 isNew가 true로 나오는 방식입니다.</p>
<p>따라서 id가 null이 아니라면, isNew는 항상 false가 나와, save를 해도 update가 수행되고, @createdDate auditing도 정상적으로 이루어지지 않는 것입니다. </p>
<p>해당 문제를 해결하기 위해, WithInsert가 아닌, Persistable을 implement하는 방식으로 다시 구현을 해보았습니다.</p>
<p><img src="https://images.velog.io/images/bread_dd/post/73ee3a92-ae4a-4ab3-adfe-cd933a73812a/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/bread_dd/post/b05096a4-0c5b-455c-bd2e-9f0cd97d5e3f/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/bread_dd/post/8c4d5a98-123a-4b7a-8fa1-a22f76715ba0/image.png" alt=""></p>
<p>Persistable을 통해 isNew를 직접 Setting 해준다면, 원하는 아이디로 저장이 가능합니다. Auditing 또한 정상적으로 동작하죠. 하지만, 저장 이후에 isNew를 false로 초기화 해주기 위해선 라이프사이클 이벤트나 콜백을 통해 제어해주어야 되는 번거로움이 있겠네요. </p>
<p>이 방법 이외에 라이프사이클 이벤트를 통해 원하는 아이디를 주입해주는 방법과 @Version을 사용하는 방법도 있습니다. 다음 글에선 라이프사이클 이벤트를 다뤄보고 싶네요</p>
<h2 id="마치며">마치며</h2>
<p>Spring Data Jdbc는 공부할수록 매력적인 프레임 워크인 것 같아요. 단순하고 직관적이죠. 하지만 항상 이렇게 생각하고 쉽게 접근하려다가 딜레마에 빠진 경우가 많은 것 같아요. 아직도 Spring Data Jdbc에 적응하진 못했나 봅니다 😭 공부를 충분히 하고나서 훗날 많은 성장을 이룬다면, Data JPA와 Data JDBC를 비교해보는 글을 써보고 싶네요.<br>긴 글 읽어주셔서 감사합니다😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Log4j 2 제대로 사용하기 - 개념]]></title>
            <link>https://velog.io/@bread_dd/Log4j-2-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@bread_dd/Log4j-2-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Tue, 21 Jul 2020 01:34:02 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;Configuration status=&quot;INFO&quot;&gt;
 &lt;!--    해당 설정파일에서 사용하는 프로퍼티--&gt;
    &lt;Properties&gt;
        &lt;Property name=&quot;logNm&quot;&gt;Spring Log4j2 Test&lt;/Property&gt;
        &lt;Property name=&quot;layoutPattern&quot;&gt;%style{%d{yyyy/MM/dd HH:mm:ss,SSS}}{cyan} %highlight{[%-5p]}{FATAL=bg_red, ERROR=red,
            INFO=green, DEBUG=blue}  [%C] %style{[%t]}{yellow}- %m%n -&lt;/Property&gt;
    &lt;/Properties&gt;
  &lt;!--    LogEvent를 전달해주는 Appender--&gt;
    &lt;Appenders&gt;
        &lt;Console name=&quot;Console_Appender&quot; target=&quot;SYSTEM_OUT&quot;&gt;
            &lt;PatternLayout pattern=&quot;${layoutPattern}&quot;/&gt;
        &lt;/Console&gt;
        &lt;RollingFile name=&quot;File_Appender&quot; fileName=&quot;logs/${logNm}.log&quot; filePattern=&quot;logs/${logNm}_%d{yyyy-MM-dd}_%i.log.gz&quot;&gt;
            &lt;PatternLayout pattern=&quot;${layoutPattern}&quot;/&gt;
            &lt;Policies&gt;
                &lt;SizeBasedTriggeringPolicy size=&quot;200KB&quot;/&gt;
                &lt;TimeBasedTriggeringPolicy interval=&quot;1&quot;/&gt;
            &lt;/Policies&gt;
            &lt;DefaultRolloverStrategy max=&quot;10&quot; fileIndex=&quot;min&quot;/&gt;
        &lt;/RollingFile&gt;
    &lt;/Appenders&gt;
  &lt;!--    실제 Logger--&gt;
    &lt;Loggers&gt;
        &lt;Root level=&quot;INFO&quot; additivity=&quot;false&quot;&gt;
            &lt;AppenderRef ref=&quot;Console_Appender&quot;/&gt;
            &lt;AppenderRef ref=&quot;File_Appender&quot;/&gt;
        &lt;/Root&gt;
        &lt;Logger name=&quot;org.springframework&quot; level=&quot;DEBUG&quot;
                additivity=&quot;false&quot;&gt;
            &lt;AppenderRef ref=&quot;Console_Appender&quot; /&gt;
            &lt;AppenderRef ref=&quot;File_Appender&quot;/&gt;
        &lt;/Logger&gt;
        &lt;Logger name=&quot;com.fucct&quot; level=&quot;INFO&quot; additivity=&quot;false&quot;&gt;
            &lt;AppenderRef ref=&quot;Console_Appender&quot; /&gt;
            &lt;AppenderRef ref=&quot;File_Appender&quot;/&gt;
        &lt;/Logger&gt;
        &lt;Logger name=&quot;com.fucct.springlog4j2.loggertest&quot; level=&quot;TRACE&quot; additivity=&quot;false&quot;&gt;
            &lt;AppenderRef ref=&quot;Console_Appender&quot; /&gt;
        &lt;/Logger&gt;
    &lt;/Loggers&gt;
&lt;/Configuration&gt;</code></pre>
<p><strong><em>log4j2.xml</em></strong></p>
<p>이전 글에서 log4j 2 설정파일을 보여드렸는데요, 각각의 요소가 뭘 의미하는지 알아보도록 할게요</p>
<h1 id="configuration">Configuration</h1>
<pre><code>&lt;Configuration status=&quot;INFO&quot;&gt;</code></pre><p>Configuration은 로그 설정의 최상위 요소입니다. 일반적으로 Configuration은 Properties, Appenders, Loggers요소를 자식으로 가집니다. 
status 속성은 log4j의 내부 이벤트에 대한 로그 레벨을 의미합니다. 또한 Configuration의 추가 속성은 <a href="https://logging.apache.org/log4j/log4j-2.2/manual/configuration.html">링크</a>에서 확인하실 수 있습니다.!</p>
<h2 id="properties">Properties</h2>
<pre><code>&lt;Properties&gt;
    &lt;Property name=&quot;logNm&quot;&gt;Spring Log4j2 Test&lt;/Property&gt;
    &lt;Property name=&quot;layoutPattern&quot;&gt;%style{%d{yyyy/MM/dd HH:mm:ss,SSS}}{cyan} %highlight{[%-5p]}{FATAL=bg_red, ERROR=red,
            INFO=green, DEBUG=blue}  [%C] %style{[%t]}{yellow}- %m%n -&lt;/Property&gt;
&lt;/Properties&gt;</code></pre><p>Properties는 해당 Configuration 파일에서 사용할 프로퍼티를 의미합니다. 변수라고 생각하시면 편해요😊. 저는 logNm으로 로그 이름을 설정했고, layoutPattern으로 로그의 패턴을 설정해 놓았습니다. name 속성의 값이 key, 태그 내의 값이 value가 됩니다.</p>
<h2 id="layout">Layout</h2>
<p>Appender는 로그 이벤트를 Layout을 통해 포매팅합니다. Appender가 받은 로그 이벤트를 Layout에게 전달하면, byte 배열을 반환합니다. Charset을 지정해주면 확실한 값을 얻을 수 있습니다.</p>
<p><strong>PatternLayout</strong></p>
<pre><code>%style{%d{yyyy/MM/dd HH:mm:ss,SSS}}{cyan} %highlight{[%-5p]}{FATAL=bg_red, ERROR=red,
            INFO=green, DEBUG=blue}  [%C] %style{[%t]}{yellow}- %m%n -</code></pre><p><img src="https://images.velog.io/images/bread_dd/post/a9188847-9adb-4f08-b4ef-41e524f10cab/image.png" alt="">
설정파일에 주어진 패턴대로 로그를 출력한 결과입니다. 날짜 포맷, 색깔, 출력 요소 등을 결정할 수 있죠. C언어의 printf 문 처럼 PatternLayout은 Format문자를 사용합니다. 각 의미는 다음과 같습니다.</p>
<ul>
<li><strong>%c, %logger</strong> : 해당 로그를 쓰는 로거의 이름. </li>
<li><strong>%C, %class</strong> : 해당 로그를 요청한 클래스 이름</li>
<li><strong>%d, %date</strong> : 해당 로그가 발생한 시간</li>
<li><strong>%enc, %encode</strong> : 특정 언어에서의 출력을 위한 문자 인코딩</li>
<li><strong>%ex, %exception, %throwable</strong> : 예외 로그. 길이를 설정할 수 있음.</li>
<li><strong>%F, %file</strong> : 해당 로그가 발생한 클래스 파일명</li>
<li><strong>%l, %location</strong> : 해당 로그가 발생한 클래스명.메소드명(파일:라인)</li>
<li><strong>%L, %line</strong> : 해당 로그가 발생한 라인 번호</li>
<li><strong>%m, %msg, %message</strong> : 로그문에 전달된 메시지</li>
<li><strong>%n</strong> : 줄바꿈</li>
<li><strong>%p, %level</strong> : 로그 레벨 </li>
<li><strong>%r, %relative</strong> : 로그 처리시간</li>
<li><strong>%t, %thread</strong> : 해당 로그가 발생한 스레드명</li>
<li><strong>%style{pattern}{ANSI style}</strong> : ANSI를 사용해 특정 패턴을 스타일링함</li>
<li><strong>%highlight{pattern}{style}</strong> : 로그 레벨명을 ANSI 색깔로 하이라이트</li>
</ul>
<p>정말 많지만..😭 한 번쯤 공부해봐야 깔끔한 로그를 작성하실 수 있을거에요. 필요한게 생길때마다 <a href="https://logging.apache.org/log4j/2.x/manual/layouts.html">링크</a>에 들려서 익숙해지시면 될 거에요!
그 밖에 HTML Layout과 JSON Layout 등 여러 레이아웃이 있지만 우선 PatternLayout만 알고 넘어가도 됩니다! 궁금하시다면 위 링크에서 찾아보실 수 있을거에요!</p>
<h2 id="appenders">Appenders</h2>
<p>Appender는 log 메세지를 특정 위치에 전달해주는 역할을 하는 녀석입니다. Appender는 layout을 통해 로그를 formatting 하고, 어떤 방식으로 로그를 제공할지 결정합니다.
Appender에는 여러 종류가 있습니다.</p>
<ul>
<li>ConsoleAppender</li>
<li>RollingFileAppender</li>
<li>AsyncAppender</li>
<li>FileAppender</li>
<li>JDBCAppender</li>
<li>SMTP</li>
<li>...</li>
</ul>
<p>저는 여기서 ConsoleAppender와 RollingFileAppender에 대해 설명드리겠습니다.</p>
<h3 id="consoleappender">ConsoleAppender</h3>
<pre><code>&lt;Console name=&quot;Console_Appender&quot; target=&quot;SYSTEM_OUT&quot;&gt;
    &lt;PatternLayout pattern=&quot;${layoutPattern}&quot;/&gt;
&lt;/Console&gt;</code></pre><p>Console Appender는 말 그대로 Console에 system.out 으로 로그를 출력하는 Appender입니다. 로그 출력 포매팅은 PatternLayout 방식으로 위에서 제가 설정한 Pattern 대로 로그를 출력합니다.
ConsoleAppender가 갖는 속성은 다음과 같습니다.</p>
<ul>
<li>layout : 출력 레이아웃 양식</li>
<li>name : Appender 이름</li>
<li>target : 출력 방식. 기본값은 SYSTEM_OUT, SYSTEM_ERR로 출력가능</li>
</ul>
<h3 id="rollingfileappender">RollingFileAppender</h3>
<pre><code>&lt;RollingFile name=&quot;File_Appender&quot; fileName=&quot;logs/${logNm}.log&quot; filePattern=&quot;logs/${logNm}_%d{yyyy-MM-dd}_%i.log.gz&quot;&gt;
    &lt;PatternLayout pattern=&quot;${layoutPattern}&quot;/&gt;
    &lt;Policies&gt;
         &lt;SizeBasedTriggeringPolicy size=&quot;200KB&quot;/&gt;
         &lt;TimeBasedTriggeringPolicy interval=&quot;1&quot;/&gt;
    &lt;/Policies&gt;
    &lt;DefaultRolloverStrategy max=&quot;10&quot; fileIndex=&quot;min&quot;/&gt;
&lt;/RollingFile&gt;</code></pre><p>로그가 한 파일에 계속 저장된다면, 파일이 너무 커져서 실행이 불가능해질 수도 있고, 파일에 문제가 생기면 로그 전부를 날려버릴 수도 있습니다. RollingFileAppender는 이런 문제를 해결해 줍니다. 파일에 로그를 기록하고, 특정 기준에 따라 압축하여 저장하는 방식의 Appender입니다. Console Appender와 대표적으로 다른 부분은 File과 관련된 속성, Policy와 관련된 속성, Strategy와 관련된 속성 입니다.</p>
<p><strong>1. Policy</strong></p>
<p>policy는 File Rolling Up 기준입니다. 하나의 RollingFileAppender에는 여러 Policy를 적용할 수 있습니다.</p>
<ul>
<li>OnStartupTriggeringPolicy : jvm start시 trigger</li>
<li>TimeBasedTriggeringPolicy : time에 따른 trigger</li>
<li>SizeBasedTriggeringPolicy : file size에 따른 trigger </li>
<li>CronTriggeringPolicy : Cron Expression(시간에 관한 표현)에 따른 trigger </li>
</ul>
<p>이름처럼 각각의 policy들은 특정 조건에 따라 trigger됩니다. 주어진 예제는 로그파일이 200KB가 되는 시점과 하루 마다 logs/${logNm}<em>%d{yyyy-MM-dd}</em>%i.log.gz 패턴으로 압축되어 저장됩니다.
여기서 재밌는 점은 TimeBasedTriggeringPolicy의 interval은 파일 패턴의 날짜 단위의 최소값으로 계산된다는 점입니다. 만약 파일패턴의 날짜 포맷이 초단위까지 있다면, interval을 1로하면 1초에 한번씩 압축이 되는 셈입니다. </p>
<p><strong>2. DefaultRolloverStrategy</strong></p>
<p>DefaultRolloverStrategy는 datetime 패턴과 파일패턴의 int값을 받아서 결정됩니다. datetime은 현재 시간으로 대체되고, 파일 패턴 숫자는 db의 autoincrement처럼 rollover 마다 1씩 증가합니다. 예제의 filePattern을 보시면 %d{yyyy-MM-dd} 가 날짜 패턴, %i가 파일패턴의 int입니다. 각각 rollover 이후에는 다음과 같이 저장됩니다.<img src="https://images.velog.io/images/bread_dd/post/2ef3f5a3-6ff6-4f3d-9fda-b721219fee96/image.png" alt=""></p>
<p>해당 Strategy에는 다음 속성을 부여할 수 있습니다.</p>
<ul>
<li><p>fileIndex : max로 설정 시 높은 index가 더 최신 파일이 됩니다. min으로 설정 시 작은 index가 최신 파일이 됩니다. 기존의 파일들을 rename하는 방식으로 동작합니다.</p>
</li>
<li><p>min : counter 최소값. 기본값은 1입니다.</p>
</li>
<li><p>max : counter 최대값. 만약 최대값에 도달하면 오래된 파일을 삭제합니다. 기본값은 7입니다.</p>
</li>
<li><p>compressionLevel : 0~9까지 정수값. 0은 압축하지 않고, 1은 최고 속도, 9는 최고 압축입니다. ZIP파일만 가능합니다. </p>
</li>
<li><p>tempCompressedFilePattern : 압축하는 동안의 파일 이름 패턴.</p>
<p>사실 다른 전략도 있으니, 궁금하시다면 한 번 찾아보세요!</p>
</li>
</ul>
<h2 id="logger">Logger</h2>
<pre><code>&lt;Logger name=&quot;com.fucct.springlog4j2.loggertest&quot; level=&quot;TRACE&quot; additivity=&quot;false&quot;&gt;
    &lt;AppenderRef ref=&quot;Console_Appender&quot; /&gt;
&lt;/Logger&gt;</code></pre><p>로거는 로깅을 직접 하는 요소입니다. 로거는 패키지 별로 설정할 수 있습니다.
Root패키지의 로거는 필수적이고, 추가적인 로거는 Logger로 설정할 수 있습니다. 해당 로거는 loggertest 패키지에 해당하는 파일들에 대해서 TRACE이상의 로그를 Console에 남기는 로거입니다. 만약 RollingFileAppender를 추가하고 싶다면 다음과 같이 설정해주면 됩니다.</p>
<pre><code>&lt;Logger name=&quot;com.fucct.springlog4j2.loggertest&quot; level=&quot;TRACE&quot; additivity=&quot;false&quot;&gt;
    &lt;AppenderRef ref=&quot;Console_Appender&quot; /&gt;
    &lt;AppenderRef ref=&quot;File_Appneder&quot; /&gt;
&lt;/Logger&gt;</code></pre><p>Additivity값은 중복된 로그를 남길 지에 대한 속성입니다. 
로거 계층 구조를 아신다면 logger가 어떻게 할당하는 지 알 수 있습니다. <a href="https://jononeworld.tistory.com/16">링크</a>를 참고해주시면 로거에 대한 좀 더 깊은 이해를 하실 수 있습니다. </p>
<h1 id="마치며">마치며</h1>
<p>전달할 내용은 많은데, 재미가 없는 부분이 많아서 어떻게 전달할 지 고민이 되는 글이었네요. 어느 정도 내용까지 전달할 지 고민하다가, 우선 전 글의 예제에 대한 설명이 가능할 정도의 내용을 전달해 드렸습니다. 부족하다 싶은 부분은 공식 문서를 통해 보충하신다면, 로깅에 대해 완벽히 학습하실 수 있을 것 같아요! 글에 오류가 있다면 지속적으로 수정하겠습니다. 긴 글 읽어주셔서 감사합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Log4j 2  제대로 사용하기 - 설정]]></title>
            <link>https://velog.io/@bread_dd/Log4j-2-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bread_dd/Log4j-2-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 18 Jul 2020 11:19:47 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/bread_dd/post/5d8c073f-04cb-4898-920e-aba6e1b31360/Apache_Log4j_Logo.png" alt=""></p>
<p>보통 개발 과정에서 실행 시 에러가 나면, 콘솔의 에러 로그를 찾아서 디버깅하고 다시 실행하기 마련이죠. 하지만 배포 환경이라면 어떨까요? 만약 배포 환경에서 에러가 난다면 콘솔의 로그를 찾는 것 만으로는 한계가 있을 거에요. 이처럼, 배포 환경에서의 로그는 개발 환경과는 다른 방식으로 처리되어야 하겠죠. 이번 글에서는 이러한 로깅을 도와주는 Log4j2를 다뤄보려고 합니다.</p>
<h1 id="사용법">사용법</h1>
<h2 id="의존성">의존성</h2>
<pre><code class="language-java">plugins {
    id &#39;org.springframework.boot&#39; version &#39;2.3.1.RELEASE&#39;
    id &#39;io.spring.dependency-management&#39; version &#39;1.0.9.RELEASE&#39;
    id &#39;java&#39;
}

group = &#39;com.fucct&#39;
version = &#39;0.0.1-SNAPSHOT&#39;
sourceCompatibility = &#39;1.8&#39;

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-mail&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-log4j2&#39;
    testImplementation &#39;org.springframework.boot:spring-boot-starter-log4j2&#39;
    implementation &#39;org.apache.logging.log4j:log4j-web:2.12.1&#39;
    implementation &#39;com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.1&#39;
    implementation &#39;com.lmax:disruptor:3.4.2&#39;
    compileOnly &#39;org.projectlombok:lombok&#39;
    runtimeOnly &#39;com.h2database:h2&#39;
    developmentOnly &#39;org.springframework.boot:spring-boot-devtools&#39;
    annotationProcessor &#39;org.projectlombok:lombok&#39;
    testImplementation(&#39;org.springframework.boot:spring-boot-starter-test&#39;) {
        exclude group: &#39;org.junit.vintage&#39;, module: &#39;junit-vintage-engine&#39;
    }
}

configurations {
    all {
        exclude group: &#39;org.springframework.boot&#39;, module: &#39;spring-boot-starter-logging&#39;
    }
}

test {
    useJUnitPlatform()
}
</code></pre>
<p><strong><em>build.gradle</em></strong></p>
<p>기본적으로 Spring은 Slf4j라는 로깅 프레임워크를 사용합니다. 구현체를 손쉽게 교체할 수 있도록 도와주는 프레임 워크입니다. Slf4j는 인터페이스고 내부 구현체로 logback을 가지고 있는데, 저는 Log4j 2를 사용하기 위해 exclude 했습니다.</p>
<h2 id="설정-파일">설정 파일</h2>
<pre><code class="language-XML">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;Configuration status=&quot;INFO&quot;&gt;
 &lt;!--    해당 설정파일에서 사용하는 프로퍼티--&gt;
    &lt;Properties&gt;
        &lt;Property name=&quot;logNm&quot;&gt;Spring Log4j2 Test&lt;/Property&gt;
        &lt;Property name=&quot;layoutPattern&quot;&gt;%style{%d{yyyy/MM/dd HH:mm:ss,SSS}}{cyan} %highlight{[%-5p]}{FATAL=bg_red, ERROR=red,
            INFO=green, DEBUG=blue}  [%C] %style{[%t]}{yellow}- %m%n -&lt;/Property&gt;
    &lt;/Properties&gt;
  &lt;!--    LogEvent를 전달해주는 Appender--&gt;
    &lt;Appenders&gt;
        &lt;Console name=&quot;Console_Appender&quot; target=&quot;SYSTEM_OUT&quot;&gt;
            &lt;PatternLayout pattern=&quot;${layoutPattern}&quot;/&gt;
        &lt;/Console&gt;
        &lt;RollingFile name=&quot;File_Appender&quot; fileName=&quot;logs/${logNm}.log&quot; filePattern=&quot;logs/${logNm}_%d{yyyy-MM-dd}_%i.log.gz&quot;&gt;
            &lt;PatternLayout pattern=&quot;${layoutPattern}&quot;/&gt;
            &lt;Policies&gt;
                &lt;SizeBasedTriggeringPolicy size=&quot;200KB&quot;/&gt;
                &lt;TimeBasedTriggeringPolicy interval=&quot;1&quot;/&gt;
            &lt;/Policies&gt;
            &lt;DefaultRolloverStrategy max=&quot;10&quot; fileIndex=&quot;min&quot;/&gt;
        &lt;/RollingFile&gt;
    &lt;/Appenders&gt;
  &lt;!--    실제 Logger--&gt;
    &lt;Loggers&gt;
        &lt;Root level=&quot;INFO&quot; additivity=&quot;false&quot;&gt;
            &lt;AppenderRef ref=&quot;Console_Appender&quot;/&gt;
            &lt;AppenderRef ref=&quot;File_Appender&quot;/&gt;
        &lt;/Root&gt;
        &lt;Logger name=&quot;org.springframework&quot; level=&quot;DEBUG&quot;
                additivity=&quot;false&quot;&gt;
            &lt;AppenderRef ref=&quot;Console_Appender&quot; /&gt;
            &lt;AppenderRef ref=&quot;File_Appender&quot;/&gt;
        &lt;/Logger&gt;
        &lt;Logger name=&quot;com.fucct&quot; level=&quot;INFO&quot; additivity=&quot;false&quot;&gt;
            &lt;AppenderRef ref=&quot;Console_Appender&quot; /&gt;
            &lt;AppenderRef ref=&quot;File_Appender&quot;/&gt;
        &lt;/Logger&gt;
        &lt;Logger name=&quot;com.fucct.springlog4j2.loggertest&quot; level=&quot;TRACE&quot; additivity=&quot;false&quot;&gt;
            &lt;AppenderRef ref=&quot;Console_Appender&quot; /&gt;
        &lt;/Logger&gt;
    &lt;/Loggers&gt;
&lt;/Configuration&gt;</code></pre>
<p><strong><em>log4j2.xml</em></strong></p>
<p>Log4j 2에선 설정파일을 Json, xml, yml등 다양한 방식으로 사용할 수 있습니다. 저는 jackson 의존성을 추가하고 싶지 않아서 xml을 사용했습니다. 해당 설정파일에 대해 알지 못해도 괜찮습니다. 다음 글에서 충분히 설명해드릴게요!😎
해당 설정을 log4j2.xml 로 지정하고, resources 패키지에 저장하시고, application.yml 파일에 해당 프로퍼티를 추가하시면 됩니다.</p>
<pre><code class="language-yml">logging:
  config: classpath:log4j2.xml
</code></pre>
<p><strong><em>application.yml</em></strong></p>
<p>해당 설정을 완료하시면 로거를 자유롭게 사용하실 수 있습니다. 다만 설정을 어떻게 하는지는 다음 글에서 다뤄보도록 하겠습니다. 너무 많은 내용을 한 글에 담으면 지루하더라구요 😭 </p>
<h2 id="실제-사용">실제 사용</h2>
<pre><code class="language-java">@RestController
@RequestMapping(&quot;/api/samples&quot;)
@RequiredArgsConstructor
@Slf4j
public class SampleController {
    private static final Logger logger = LogManager.getLogger(SampleController.class);

    @GetMapping
    public ResponseEntity&lt;String&gt; create(@RequestParam(&quot;name&quot;) String request) {
        logger.info(&quot;Hello. This is LogManager&#39;s logger&quot;);
        log.info(&quot;Hello. This is Lombok&#39;s logger&quot;);
        System.out.println(logger.equals(log));
        return ResponseEntity.ok(request);
    }
}
</code></pre>
<p><strong><em>SampleController.java</em></strong>
<em>(과연 출력문의 결과는 어떻게 될까요?)</em></p>
<p>해당 로깅의 결과는 다음과 같습니다.
<img src="https://images.velog.io/images/bread_dd/post/9fa00fb0-aedb-4e99-bf6d-12452aa8f54a/image.png" alt=""></p>
<p>컨트롤러의 첫 로거(logger)는 필드에 존재하는 로거입니다. 두 번째 로거(log)는 Lombok의 @Slf4j를 통해 받아온 로거입니다. 사실 같은 구현체를 쓰기 때문에 차이는 없습니다. 실제 Lombok이 컴파일된 코드는 다음과 같습니다. 
<img src="https://images.velog.io/images/bread_dd/post/7386f0e9-e061-47e5-ace6-dc08451d49e7/image.png" alt=""></p>
<h2 id="테스트">테스트</h2>
<p>참고로 테스트 코드에선 Lombok의존성과 Log4j2의존성을 테스트 스코프로 설정하지 않으면 사용할 수 없습니다. 물론 의존성을 추가하는 것도 좋지만 해당 방식으로 로거를 구해올 수 있습니다.</p>
<pre><code class="language-java">@SpringBootTest
public class LoggerTest {
    private static Logger logger = null;

    @BeforeEach
    public void setLogger() {
        System.setProperty(&quot;log4j.configurationFile&quot;, &quot;log4j2.yml&quot;);
        logger = LogManager.getLogger();
    }

    @RepeatedTest(2)
    void givenLoggerWithDefaultConfig_whenLogToConsole_thanOK() {
        Exception e = new RuntimeException(&quot;Hello World!&quot;);

        logger.info(&quot;THIS IS INFO LEVEL LOG&quot;);
        logger.debug(&quot;THIS IS DEBUG LEVEL LOG&quot;);
        logger.error(&quot;THIS IS ERROR LEVEL LOG.&quot;, e);
        logger.fatal(&quot;THIS IS FATAL LEVEL LOG.&quot;);
    }
}
</code></pre>
<p>물론 단순한 테스트에도 SpringBootTest를 해야한다는 단점이 있어, 더 좋은 방법이 있다면 공유해드리도록 하겠습니다. </p>
<h1 id="마치며">마치며</h1>
<p>이렇게 간단한 Log4j2 설정방법과 사용법에 대해 공유해봤습니다. 물론 구체적인 내용을 말씀드리진 않았지만, 설정이 안되면 코딩을 하면서 공부하기가 어렵더라구요 🤔 코딩 하면서 공부하는 걸 좋아하는 저로썬, 설정 먼저 해서 직접 쳐보고, 그 내용을 바탕으로 확장시켜 나가는게 좋다고 생각합니다. 여기서 글을 마무리 하고, 다음 글에선 설정 파일의 의미를 설명드리고, 커스텀하는 방법에 대해 이야기 해볼게요. 긴 글 읽어주셔서 감사합니다😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot Devtools  알아보기]]></title>
            <link>https://velog.io/@bread_dd/Spring-Boot-Devtools</link>
            <guid>https://velog.io/@bread_dd/Spring-Boot-Devtools</guid>
            <pubDate>Wed, 15 Jul 2020 12:58:43 GMT</pubDate>
            <description><![CDATA[<img src="https://images.velog.io/images/bread_dd/post/f1f23833-a09f-404f-b4be-38aa309e6ece/image.png" width=700/>

<p>Intellij의 Spring Initializer를 사용 시 처음 마주치는 화면입니다. Developer Tools. 참으로 멋있는 이름입니다.. 무언가 생산성을 극대화해줄 것만 같은 그 이름. 매 프로젝트를 생성할 때마다 해당 패키지를 포함하지만, 정작 제대로 알고 써본 적은 없는 Spring Boot DevTools에 관해 알아볼게요.</p>
<h2 id="제공하는-기능">제공하는 기능</h2>
<p>Spring Boot DevTools가 제공하는 기능은 크게 5가지입니다.</p>
<ul>
<li>Property Defaults</li>
<li>Automatic Restart</li>
<li>Live Reload</li>
<li>Global Settings</li>
<li>Remote Applications</li>
</ul>
<p>이번 글에선 가장 주로 쓰이는 세 가지 기능에 대해 알아보고 설정 방법에 대해 알려드릴게요.</p>
<h3 id="property-defaults">Property Defaults</h3>
<p><img src="https://images.velog.io/images/bread_dd/post/34aeaf11-f09f-43bc-a6ce-de6290c33fe3/image.png" alt="">
Spring에서 제공하는 thymeleaf는 기본적으로 캐싱 기능을 사용합니다. 하지만 개발과정에서 캐싱이 되어있다면, 우리가 thymeleaf 파일을 수정하더라도, 반영되지 않을 거에요. Application cache 설정값을 false로 수정해야 하죠. 
이러한 작업을 Spring Boot DevTools에서 제공합니다. 개발 시점과 배포 시점에 다른 설정을 기본적으로 개발 단계에 맞춰 설정해줍니다. 예를 들어 템플릿 엔진의 캐싱 기능을 개발 단계에서 자동으로 꺼주는 역할을 하죠. 따라서 개발자는 설정 파일을 건드리지 않아도 된답니다.</p>
<h3 id="automatic-restart">Automatic Restart</h3>
<p><img src="https://images.velog.io/images/bread_dd/post/b2d7b7a0-8fc9-47dc-9bbf-8bbcbac3225a/image.png" alt=""></p>
<p>Spring Boot Application을 개발하다 보면 애플리케이션을 재시작해야 하는 경우가 많더라구요. 심지어 클래스의 필드 하나를 고쳐도 Ctrl + R 단축키를 누르는 수고스러움을 감수해야 하죠. Spring Boot DevTools는 이 기능을 자동으로 제공합니다. 파일 수정 후 저장을 하면, Classpath에 존재하는 파일의 변경을 감지하고, 자동으로 서버를 restart 해줍니다. 설정을 통해 원하는 디렉터리만을 트리거로 설정할 수도 있어요!</p>
<h3 id="live-reload">Live Reload</h3>
<p><img src="https://images.velog.io/images/bread_dd/post/137ba6e0-027c-4c00-b2a9-6af5bc4bd09f/image.png" alt=""></p>
<p>React에 대한 경험이 있으시다면, Hot reload를 아실 겁니다. JS 파일을 수정하기만 해도 자동으로 브라우저가 새로 고침 되는 기능이죠. DevTools 또한 이 기능을 제공합니다(물론 리액트만큼 훌륭하진 않더라고요 😭). 내부적으로 live reload 서버를 두고, 브라우저 확장프로그램과 통신하는 방식으로 동작하죠. 
<a href="https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=ko">LiveReload</a> 링크를 통해 크롬 확장프로그램을 받고, enable을 하면 됩니다. </p>
<p><img src="https://images.velog.io/images/bread_dd/post/d7dbd9f7-fed8-4410-b068-e5430c1c8d46/image.png" alt="">
해당 버튼으로 활성화를 하는데, 이게 켜졌는지 안 켜졌는지 제대로 보이지 않아서, 마우스를 올려놓은 채 위 그림처럼 메시지가 뜨면 실행이 된 상태라고 보시면 됩니다. 뷰 페이지를 업데이트하면, 브라우저가 자동으로 업데이트되는 모습을 볼 수 있습니다!</p>
<h2 id="설정방법">설정방법</h2>
<ol>
<li><p>우선 Gradle에 의존성 추가합니다.</p>
<pre><code class="language-java">// build.gradle
configurations {
 developmentOnly
 runtimeClasspath {
     extendsFrom developmentOnly
 }
}
dependencies {
 developmentOnly(&quot;org.springframework.boot:spring-boot-devtools&quot;)
}</code></pre>
</li>
<li><p>Intellij 화면에서 Cmd + Shift + A 를 입력하여 Registry... 으로 들어가 compiler.automake.allow.when.app.running 을 체크하고 close 합니다.</p>
</li>
</ol>
<p><img src="https://images.velog.io/images/bread_dd/post/d86784d1-367e-4622-9617-3b22ad3bc2f1/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/bread_dd/post/ed4518ff-35d0-4e03-80ed-bcd146b814eb/image.png" alt=""></p>
<ol start="3">
<li><p>Preference 의 Build, Execution, Deployment -&gt; Compiler -&gt; Build project automatically 를 체크하고 apply 합니다.
<img src="https://images.velog.io/images/bread_dd/post/f75217d3-38cd-46cf-be16-d406db69803b/image.png" alt=""></p>
</li>
<li><p>만약 Live Reload가 정상적으로 수행이 안된다면, Intellij 우측 상단에 있는 APPLICATION 버튼을 누르면 Edit Configuration 버튼이 나옵니다. 클릭하고, Spring Boot의 application 에서 On &#39;Update&#39; action 과 On frame deactivation 값을 모두 Update resources 로 설정합니다. (저는 이걸 몰라서 엄청 헤맸네요 😭)
<img src="https://images.velog.io/images/bread_dd/post/8e67ee51-eef5-4c58-a588-efffbafca46b/image.png" alt="">
<img src="https://images.velog.io/images/bread_dd/post/169a8f59-30e4-4c35-b079-6806c7221c6b/image.png" alt=""></p>
</li>
</ol>
<p>이렇게 하시면 Spring Devtools 설정이 끝납니다!</p>
<h2 id="결론">결론</h2>
<p>Spring Devtools에서 제공하는 기능이 완벽하다고 보긴 어렵지만, 개발 과정에서 번거로움을 어느 정도 덜어주는 것 같아요! 또한 live reload 확장프로그램을 받지 않아도, 수정한 resource file을 recompile 하면 서버 재시작 없이 변화를 반영할 수 있답니다. 적절하게 활용한다면 개발 시간을 조금이나마 단축할 수 있을 것 같네요! 긴 글 읽어주셔서 감사합니다! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA는 왜 지연 로딩을 사용할까?]]></title>
            <link>https://velog.io/@bread_dd/JPA%EB%8A%94-%EC%99%9C-%EC%A7%80%EC%97%B0-%EB%A1%9C%EB%94%A9%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@bread_dd/JPA%EB%8A%94-%EC%99%9C-%EC%A7%80%EC%97%B0-%EB%A1%9C%EB%94%A9%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Sun, 14 Jun 2020 13:36:13 GMT</pubDate>
            <description><![CDATA[<p><img src="https://miro.medium.com/max/600/1*qRmUe3eC9rhx8WtqNATlag.png" alt="JPA"></p>
<p>JPA에서 테이블 간 연관 관계는 객체의 참조를 통해 이뤄집니다. 서비스가 커질수록, 참조하는 객체가 많아지고, 객체가 가지는 데이터의 양이 많아집니다. 이렇게 객체가 커질수록, DB로부터 참조하는 객체들의 데이터까지 한꺼번에 가져오는 행동은 부담이 커집니다. 따라서 JPA는 참조하는 객체들의 데이터를 가져오는 시점을 정할 수 있는데, 이것을 Fetch Type이라고 합니다.</p>
<h2 id="fetch-type">Fetch Type</h2>
<p><img src="https://images.velog.io/images/bread_dd/post/9a868e9f-2a40-420d-82ef-ada5404f6287/image.png" alt="">
<img src="https://images.velog.io/images/bread_dd/post/8ff8c976-fc7a-48bf-9a89-fb37d2735e18/image.png" alt="">
Fetch Type에는 두 가지가 있습니다.</p>
<ul>
<li>EAGER</li>
<li>LAZY</li>
</ul>
<p>EAGER는 성실한, 열심인 이라는 뜻을 가지고 있습니다. 말 그대로 데이터를 가져오는데 성실하죠. 하나의 객체를 DB로부터 읽어올 때 참조 객체들의 데이터까지 전부 읽어오는 방식을 뜻합니다. 반대로 LAZY 타입은 게을러서, 참조 객체들의 데이터들은 무시하고 해당 엔티티의 데이터만을 가져옵니다. 그렇다면 언제 어떤 방식을 써야 할까요?</p>
<h3 id="eager의-장점">EAGER의 장점?</h3>
<p>EAGER는 언제나 한 번의 쿼리로 모든 정보를 가져옵니다. 반대로 LAZY는 참조객체의 데이터를 사용하기 위해서 여러 번의 쿼리를 사용할 수 있습니다. 따라서 참조 객체와 항상 함께 로드되어야 하는 조건을 가진 엔티티에 대해선 LAZY 방식보단 EAGER 방식이 더 좋습니다.</p>
<p>예를 들어 게시판의 글과 저자를 생각해봅시다.</p>
<p>Article.java</p>
<pre><code class="language-java">@NoArgsConstructor
@Getter @Setter
@Entity
public class Article {

    @Id @GeneratedValue
    @Column(name = &quot;ARTICLE_ID&quot;)
    private Long id;

    private String content;

    @ManyToOne
    @JoinColumn(name = &quot;AUTHOR_ID&quot;)
    private Author author;
}</code></pre>
<p>Author.java</p>
<pre><code class="language-java">@NoArgsConstructor
@Getter @Setter
@Entity
public class Author {

    @Id @GeneratedValue
    @Column(name = &quot;AUTHOR_ID&quot;)
    private Long id;

    private String name;

    @OneToMany(mappedBy = &quot;author&quot;, cascade = ALL)
    private List&lt;Article&gt; articles = new ArrayList&lt;&gt;();

    public void addArticle(Article article) {
        articles.add(article);
        article.setAuthor(this);
    }
}</code></pre>
<p>(예제를 위한 Setter이므로 양해 부탁드립니다 😭)</p>
<p>게시글과 저자는 ManyToOne 양방향 연관 관계를 가지고 있습니다. 게시글을 불러올 때, 항상 저자의 이름과 함께 로드되어야 한다면, 서비스는 다음과 같은 순서로 수행될 것입니다.</p>
<pre><code class="language-java">Article findArticle = em.find(Article.class, article1.getId());
System.out.println(&quot;Author Name : &quot; + findArticle.getAuthor().getName());
System.out.println(&quot;Article Content : &quot; + findArticle.getContent());</code></pre>
<img src="https://images.velog.io/images/bread_dd/post/3b7ce454-b210-4d0b-8ae5-d460d9d38ec9/image.png" width=400/>

<p>쿼리문 결과는 다음과 같습니다.</p>
<ul>
<li>EAGER의 경우<img src="https://images.velog.io/images/bread_dd/post/6474fb62-0270-43cb-8755-706a170948f1/image.png" width=400/>


</li>
</ul>
<ul>
<li>LAZY의 경우<img src="https://images.velog.io/images/bread_dd/post/22e9fa62-ff93-40a0-a232-3fabae812db5/image.png" width=400/>
<img src="https://images.velog.io/images/bread_dd/post/ee8b8975-cd2b-4f19-adbe-364d96f8b6f2/image.png" width=400/>

</li>
</ul>
<p>EAGER의 경우 쿼리문이 한 번만 실행되지만, LAZY의 경우 2번의 쿼리문이 실행되므로 성능이 떨어진다고 볼 수 있습니다. </p>
<h2 id="이래도-lazy입니까">이래도 LAZY입니까?</h2>
<p>사실 이러한 장점에도 불구하고 Fetch type은 항상 LAZY를 써야 하는 게 맞습니다. 다음 두 경우를 살펴봅시다.</p>
<h3 id="참조가-많아지는-경우">참조가 많아지는 경우</h3>
<p>Article의 참조가 많아진다면 어떻게 될까요?</p>
<figure>
    <img src="https://images.velog.io/images/bread_dd/post/ed3fc46f-43f0-4966-8e31-5335aeb9b62a/image.png" width=400 title="참조추가"/>
  <figcaption>Article에 Favorite과 Comment 참조가 추가된 경우</figcaption>
</figure>
Article을 가져올 때 알 수 없는 테이블들이 전부 join이 되어 등장합니다. 테이블 설계가 복잡해질수록, 하나의 엔티티가 참조하는 테이블들은 늘어날 테고, 그에 따른 쿼리문도 굉장히 길어지겠죠. 이런 복잡한 쿼리문을 본 개발자는 해당 도메인이 어떻게 설계되었는지 확인해보아야 하고, 논리적인 Layer 분리가 이뤄지지 않은 셈이 되죠. 이러한 부분은 유지 보수를 힘들게 만들 수 있습니다. 

<h3 id="jpql의-사용">JPQL의 사용</h3>
<p>만약 JPQL을 통해 Article을 전부 가져온다면 어떻게 될까요?</p>
<pre><code class="language-java">Article article1 = new Article();
article1.setContent(&quot;집사! 빵이 먹고싶어요.&quot;);

Article article2 = new Article();
article2.setContent(&quot;집산가요? 냠냠이는 어디있죠?&quot;);

Article article3 = new Article();
article3.setContent(&quot;이보게. 엉덩이좀 두들겨주게나&quot;);

Author author1 = new Author();
author1.setName(&quot;디디&quot;);

Author author2 = new Author();
author2.setName(&quot;루루&quot;);

Author author3 = new Author();
author3.setName(&quot;모모&quot;);</code></pre>
<p>위 처럼 3개의 저자가 각자 한 개의 글을 썼다고 가정해봅시다. </p>
<pre><code class="language-java">List findArticles = em.createQuery(&quot;select a from Article a&quot;)
                .getResultList();</code></pre>
<p>위 JPQL 실행 시 발생하는 쿼리문은 다음과 같습니다.</p>
<img src="https://images.velog.io/images/bread_dd/post/071f65d3-e297-4b95-9707-e8de9c671ca4/image.png" width=400/>
<img src="https://images.velog.io/images/bread_dd/post/2d8b3b8d-c06d-4a1d-8043-4be432bd15c7/image.png" width=400/>
<img src="https://images.velog.io/images/bread_dd/post/92d379b3-7846-46ba-819e-c1bf3bbab118/image.png" width=400/>
<img src="https://images.velog.io/images/bread_dd/post/4987e664-88c6-4d78-864b-d9c48ab92a2f/image.png" width=400/>

<p>하나의 JPQL 문에 대해 총 쿼리문이 4개나 실행됐습니다. 이를 N+1 Select Problem이라고 하죠. 하나의 Select 요청에 대한 Select문이 n개나 추가되는 현상을 의미합니다. 왜 이런 일이 발생할까요? 이는 Fetch Type이 EAGER인 경우, Article 내의 Author 참조에 대한 데이터를 모두 가져와야 하기 때문입니다. 그 결과 단순한 조회 쿼리 하나가 지나치게 많은 쿼리문을 수행하게 되어 성능 저하를 유발할 수 있습니다. </p>
<h2 id="결론">결론</h2>
<p>아무리 단순한 구조의 설계라도, 시간이 지나면 변화되고 추가되기 마련입니다. 개발 과정에서 임시로 사용하는 게 아니라면, 항상 지연 로딩을 사용함으로써 성능 이슈가 발생할 가능성을 줄여주는 것이 좋습니다.</p>
]]></description>
        </item>
    </channel>
</rss>