<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>lily_younk.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 06 Jan 2026 06:53:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>lily_younk.log</title>
            <url>https://velog.velcdn.com/images/lily_younk/profile/6618892f-2ae2-4832-8798-ff6deff5ec78/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. lily_younk.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/lily_younk" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[리액트 컴포넌트]]></title>
            <link>https://velog.io/@lily_younk/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@lily_younk/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Tue, 06 Jan 2026 06:53:44 GMT</pubDate>
            <description><![CDATA[<h3 id="컴포넌트">컴포넌트</h3>
<p>재사용 가능한 UI 형태</p>
<h3 id="컴포넌트-만드는-두-가지-방법">컴포넌트 만드는 두 가지 방법</h3>
<h4 id="방법-1-함수로-만들기">방법 1: 함수로 만들기</h4>
<pre><code>export function MyComponent({ title }: Props) {
  return &lt;div&gt;{title}&lt;/div&gt;
}</code></pre><h4 id="방법-2-변수로-만들기-현재-코드-방식">방법 2: 변수로 만들기 (현재 코드 방식)</h4>
<pre><code>// React.FC = React.FunctionComponent의 줄임말
const MyComponent: React.FC&lt;Props&gt; = ({ title }) =&gt; {
  return &lt;div&gt;{title}&lt;/div&gt;
}</code></pre><h3 id="차이점">차이점</h3>
<p>언제 사용할 수 있나?
// 방법 1: 어디서든 사용 가능 (위에 써도 됨)</p>
<pre><code>&lt;MyComponent /&gt;
export function MyComponent() { ... }</code></pre><p>// 방법 2: 선언 후에만 사용 가능</p>
<pre><code>const MyComponent = () =&gt; { ... }
&lt;MyComponent /&gt; // 이제 사용 가능</code></pre><p>타입 지정
// 방법 1: 직접 타입 써야 함</p>
<pre><code>export function MyComponent({ title }: { title: string }) { ... }</code></pre><p>// 방법 2: React.FC가 자동으로 도움</p>
<pre><code>const MyComponent: React.FC&lt;{ title: string }&gt; = ({ title }) =&gt; { ... }
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[자바스크립트] 이벤트루프]]></title>
            <link>https://velog.io/@lily_younk/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EB%A3%A8%ED%94%84</link>
            <guid>https://velog.io/@lily_younk/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EB%A3%A8%ED%94%84</guid>
            <pubDate>Tue, 01 Apr 2025 08:15:45 GMT</pubDate>
            <description><![CDATA[<h3 id="싱글스레드-자바스크립트">싱글스레드 자바스크립트</h3>
<p>자바스크립트는 하나의 메인스레드를 가지는 싱글스레드 구조다. 한번에 한가지 일만 수행할 수 있지만, web APIs를 통해 비동기작업을 수행할 수 있다. 이벤트루프는 자바스크립트 엔진이 적절하게 동기작업과 비동기작업을 수행할 수 있도록 관리해준다.</p>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/a460ecfa-b54d-4815-ae9c-ebd74ca5f9ff/image.png" alt=""></p>
<h3 id="콜스택">콜스택</h3>
<p>자바스크립트 엔진이 코드 실행을 위해 사용하는 메모리 구조다. 동기 코드가 실행될때 함수가 콜스택에 쌓이고, 실행이 완료되면 제거된다. 후입선출 구조.</p>
<h3 id="콜백큐">콜백큐</h3>
<p>비동기적 작업이 완료되면 실행되는 콜백 함수들이 대기하는 공간. 함수의 우선순위에 따라 microtask queue와 task queue로 나뉘어 추가되고, 우선순위가 높은 microtask queue 부터 실행된 후 해당 큐가 비워지면 task queue가 실행된다.</p>
<h3 id="web-apis">web APIs</h3>
<p>브라우저에서 제공하는 API 모음. 멀티스레드 구조라서 비동기 작업을 동시에 처리할 수 있다. 비동기작업은 콜스택에서 실행되지 않고 여기서 실행된다. setTimeout(),  addEventListener()와 같은 이벤트핸들링이 여기서 수행된다.</p>
<h3 id="이벤트루프">이벤트루프</h3>
<p>이벤트 루프는 브라우저 내부의 콜스택, 콜백 큐, web APIs의 요소를 모니터링하면서 작업을 배분한다. 콜스택이 비어있으면 콜백큐에서 함수를 가져와 실행하는데, microtask queue에 있는 작업부터 먼저 꺼내서 콜스택에서 실행한 후, 비워지면 task queue에서 꺼내 실행한다.
이때 동기코드에서 무거운 연산이 실행되면서 콜스택을 계속 사용하고 있다면, 콜백큐에 있는 작업들의 실행이 지연되며 렌더링이 지연될 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 리액트 렌더링 과정]]></title>
            <link>https://velog.io/@lily_younk/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@lily_younk/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Thu, 20 Mar 2025 02:18:19 GMT</pubDate>
            <description><![CDATA[<h4 id="리액트-렌더링">리액트 렌더링</h4>
<p>리액트에서의 렌더링 개념은 컴포넌트가 ui를 생성하는 과정을 의미한다. jsx를 기반으로 가상의 dom을 만들고 실제 dom에 반영하는 방식으로 동작한다.
<img src="https://velog.velcdn.com/images/lily_younk/post/afd8f12a-36da-4160-8e5a-39410559571c/image.png" alt=""></p>
<h4 id="리액트-렌더링-종류">리액트 렌더링 종류</h4>
<ul>
<li>초기 렌더링 : 리액트 컴포넌트가 처음 실행되고 가상 dom을 생성한다. 가상 dom을 실제 dom에 반영하여 화면에 UI가 나타탄다.</li>
<li>업데이트 렌더링(리렌더링) :
컴포넌트의 props나 state가 변경될 때 다시 렌더링된다.</li>
</ul>
<h4 id="렌더링-과정">렌더링 과정</h4>
<ul>
<li>Render 단계 : 컴포넌트를 가상 dom으로 변환하는 단계. 리액트가 jsx를 가상 dom으로 변환한다.
기존 dom과 비교하여 변경된 부분을 찾는다. 실제 dom에 적용하기 전에 변경 사항을 미리 계산해둔다. 이때는 실제 dom이 변경되지 않고, 컴포넌트를 순수 함수처럼 실행한다.</li>
<li>Commit 단계 : 변경된 가상 dom을 실제 dom에 반영하는 단계. 실제 화면에 변화가 생기는 시점이다. dom을 업데이트하고, 애니메이션이나 네트워크 요청을 실행할 수 있다.</li>
</ul>
<h4 id="렌더링-시점">렌더링 시점</h4>
<p>리액트는 최초 렌더링 - 상태의 변화 - 리렌더링(렌더링)의 단계를 거치며 화면을 그린다. 
state의 변화를 일으키는 행위를 트리거(Trigger)라고 하는데, 트리거가 작동되면 컴포넌트는 변화된 내용을 감지하여 리렌더링을 거친다.</p>
<h4 id="useeffect">useEffect()</h4>
<p>useEffect()는 컴포넌트가 리렌더링이 될때마다 실행되는 hook이다.
기본적인 형태는
useEffect(function, deps)
function : 수행 함수, deps : 배열형태의 특정값 로 이루어진다.</p>
<pre><code>import React, { useEffect } from &#39;react&#39;;

//마운트 될때만 실행(최초 렌더링)
useEffect(() =&gt; {
    console.log();
}, [])

// 리렌더링 될때마다 실행
useEffect(() =&gt; {
    console.log();
})

//특정 props, state가 바뀔때 실행
useEffect(() =&gt; {
    console.log(name);
}, [name])</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[로드밸런서의 개념]]></title>
            <link>https://velog.io/@lily_younk/%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C%EC%9D%98-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@lily_younk/%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C%EC%9D%98-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Tue, 09 Jan 2024 02:18:48 GMT</pubDate>
            <description><![CDATA[<h2 id="로드밸런싱">로드밸런싱</h2>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/e35142d3-9b8b-4fde-b3eb-1813d27b241a/image.png" alt="">
로드 밸런서는 다중서버 환경에서 서버에 가해지는 부하를 분산시켜주는 장치 혹은 기술을 통칭한다. 클라이언트와 서버풀 사이에 위치하며, 한대의 서버로 부하가 집중되지 않도록 트래픽을 관리해 각각의 서버가 최적의 퍼포먼스를 보일 수 있도록 한다.</p>
<h2 id="로드밸런스-방법론알고리즘">로드밸런스 방법론(알고리즘)</h2>
<p>로드밸런싱을 하는 방법은 여러가지가 있다.</p>
<h3 id="라운드로빈">라운드로빈</h3>
<p>요청 순서에 따라 배정되는 방식이다. 순서대로 로드밸런싱되기 때문에, 같은 스펙의 서버를 구축했을 때 사용하기 좋다. 단 서버와의 연결이 오래 지속되는 경우에는 적합하지 않다. 서버와의 연결이 계속 이어지고 있는데 라운드로빈방식으로 트래픽을 나눠준다면 비대칭화과 될것이다.</p>
<h3 id="가중-라운드로빈">가중 라운드로빈</h3>
<p>서버의 스펙이 모두 다를때는 가중치를 설정하여 라운드로빈을 활용할 수 있다.
만약 A서버와 B서버 2대를 가지고 로드밸런싱을 할때, A서버가 B서버보다 스펙이 2배 좋다면, 가중치를 A서버 2, B서버 1로 설정하여 A서버에게 2배로 요청을 하게한다.</p>
<h3 id="ip해시">IP해시</h3>
<p>클라이언트의 IP주소를 특정 서버로 요청하는 방식이다. 예를들어 해외 IP는 A서버로, 국내 IP는 B 서버로 연결을 요청한다. 때문에 같은 IP로 들어온다면 매번 같은 서버로 연결되는 것이 보장된다.</p>
<h3 id="최소연결">최소연결</h3>
<p>요청이 들어온 시점에 가장 적은 연결상태를 보이는 서버에 우선적으로 요청하는 방식이다. 자주 세션처리가 늦어지거나, 서버의 연결시간을 예측하기 어려워 트래픽 분배가 어려운 경우에 적합하다. 예를들어 채팅 서비스는 서버와의 연결을 끊을지 지속할지 결정하는 주체가 클라이언트이다. 이럴땐 최소연결 방식을 고려하는 것이 좋다.</p>
<h3 id="최소-리스폰-타임">최소 리스폰 타임</h3>
<p>서버의 현재 응답시간을 고려하여 트래픽을 분배하는 방식이다. 최소 연결방식과 응답속도를 함께 고려하기 때문에, 가장 적은연결 + 빠른 응답속도의 서버에 우선적으로 연결시킨다.</p>
<h2 id="l4-로드밸런싱과-l7-로드밸런싱">L4 로드밸런싱과 L7 로드밸런싱</h2>
<p>로드밸런스 역할을 할 수 있는 네트워크 계층은 OSI 4,7 계층이다.</p>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/6b181226-e5ce-4543-af09-b52e84c9d754/image.png" alt=""></p>
<p>L4 로드밸런서는 4계층의 정보(TCP/UDP 포트정보)를 바탕으로 로드를 분산한다.
데이터 안을 들여보지 않고 패킷레벨에서만 로드를 분산하기 때문에 속도가 빠르고 효율이 높다.
데이터의 내용을 복호화할 필요가 없기에 안전함.
L7보다 가격이 저렴하다.</p>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/e3d655a1-a07a-41e0-93cd-a3e3227b38a5/image.png" alt=""></p>
<p>L7 로드밸런서는 7계층 위에서 동작하기 때문에, IP, Port 외에도 Url, payload, http header, cookie 등의 내용을 기준으로 부하를 분산한다. 그래서 콘텐츠기반 스위칭이라고도 한다. 상위 계층에서 로드를 분산하기 때문에 훨씬 더 섬세한 라우팅이 가능하다. 캐싱기능이 제공된다. 특정한 패턴을 지닌 바이러스를 감지하거나, Dos/DDos와 같은 비정상적인 트래픽을 사전에 필터링할 수 있어 서비스 안정성이 높다.
단 패킷의 내용을 복호화 해야하기 때문에 비용이 높다. 클라이언트와 로드밸런서가 인증서를 공유해야하기 때문에, 공격자가 로드밸런서를 통해 클라이언트 데이터에 접근할 위험성이 존재한다.</p>
<p>참고
<a href="https://post.naver.com/viewer/postView.nhn?volumeNo=27046347&amp;memberNo=2521903">로드밸런서의 개념과 특징</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[생성자에 매개변수가 많다면 빌더를 고려하라.]]></title>
            <link>https://velog.io/@lily_younk/%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EA%B0%80-%EB%A7%8E%EB%8B%A4%EB%A9%B4-%EB%B9%8C%EB%8D%94%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@lily_younk/%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EA%B0%80-%EB%A7%8E%EB%8B%A4%EB%A9%B4-%EB%B9%8C%EB%8D%94%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</guid>
            <pubDate>Sat, 18 Nov 2023 02:06:06 GMT</pubDate>
            <description><![CDATA[<h3 id="builder">Builder</h3>
<p>빌더패턴은 객체의 생성과정과 표현방법을 분리하여, 다양한 구성의 인스턴스를 만드는 생성 패턴이다.
생성자에 들어갈 변수를 각각 메소드로 받고, 마지막에 통합 빌드하여 객체를 생성한다.</p>
<h3 id="빌더패턴의-탄생-배경">빌더패턴의 탄생 배경</h3>
<h4 id="1-점층적-생성자-패턴">1) 점층적 생성자 패턴</h4>
<p>점층적 생성자 패턴은 필수 매개변수와 선택 매개변수를 점층적으로 늘려가며 받는 형태이다. 이 방식은 생성자를 여러개 오버로딩하여 만들어두고, 객체 생성시 필요한 변수가 포함된 생성자를 통해서 객체를 생성한다.</p>
<pre><code>public class Student {

    private int id;               //필수
    private String name;          //필수
    private String grade;         //필수

    private String phoneNumber;   //선택
    private String email;          //선택

    //필수멤버 생성자
    public Student(int id, String name, String grade) {
        this.id = builder.id;
        this.name = builder.name;
        this.grade = builder.grade;
    }

    //필수멤버 + 선택1 생성자
    public Student(int id, String name, String grade, String phnNum) {
        this.id = builder.id;
        this.name = builder.name;
        this.grade = builder.grade;
        this.phoneNumber = builder.phnNum;
    }

    //필수멤버 + 선택2 생성자
    public Student(int id, String name, String grade, String phnNm, String email){
        this.id = builder.id;
        this.name = builder.name;
        this.grade = builder.grade;
        this.phoneNumber = builder.phnNm;
        this.email = email;
    }

}</code></pre><pre><code>public static void main(String[] args) {
    Student stdnt1 = new Student(1, &quot;kim&quot;, 3);

    Student stdnt2 = new Student(2, &quot;Han&quot;, 2, &quot;010-0000-0000&quot;);

    Student stdnt3 = new Student(3, &quot;Park&quot;, 1, &quot;010-1111-1111&quot;, &quot;park@gmail.com&quot;);
}</code></pre><p>이 방식의 문제점은 매개변수가 많아질수록 커진다. 외부에선 몇번째 파라미터가 어떤 변수를 뜻하는지 전혀 구분할 수 없기 때문이다. 물론 IDE가 친절하게 설명을 해주긴 하지만, 코드 자체로 봤을때 가독성 있는 코드라고 할 수는 없다. 게다가 멤버변수가 많아질수록 만들어야 하는 생성자 수가 증가하기 때문에 유지보수 측면에서도 좋지 않다.</p>
<h4 id="2-자바-빈-패턴">2) 자바 빈 패턴</h4>
<p>점층적 생성자 패턴의 단점을 보완하기 위해, setter 메소드를 사용한 자바빈 패턴이 고려되었다.
매개변수가 없는 기본 생성자로 객체를 생성한 후, setter 메소드를 이용해 변수를 세팅해주는 방법이다.</p>
<pre><code>public class Student {

    private int id;               //필수
    private String name;          //필수
    private String grade;         //필수

    private String phoneNumber;   //선택
    private String email;          //선택

    public void setId(int id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    } 
    public void setGrade(String grade) {
        this.grade = grade;
    }
    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }
    public void setEmail(String email) {
        this.email = email;
    }


}</code></pre><pre><code>public static void main(String[] args) {
    Student stdnt1 = new Student();
    stdnt1.setId(1);
    stdnt1.setName(&quot;kim&quot;);
    stdnt1.setGrade(3);

    Student stdnt2 = new Student();
    stdnt2.setId(2);
    stdnt2.setName(&quot;Han&quot;);
    stdnt2.setGrade(2);
    stdnt2.setPhoneNumber(&quot;010-0000-0000&quot;);

    Student stdnt3 = new Student();
    stdnt3.setId(3);
    stdnt3.setName(&quot;Park&quot;);
    stdnt3.setGrade(1);
    stdnt3.setPhoneNumber(&quot;010-1111-1111&quot;);
    stdnt3.setEmail(&quot;email@gmail.com&quot;);

}</code></pre><p>이 방식은 생성자 오버로딩시 나타났던 가독성의 문제점이 사라졌다. 어떤 변수에 어떤 값이 들어가는지 잘 드러나고, setter 메소드로 유연한 객체 생성이 가능해졌다. 다만 객체 생성 시점에 필수 멤버가 세팅된다는 보장이 없다. 객체가 불안정하게 일관성이 깨진 상태로 사용이 가능해진다는 점이 문제점이 된다. 또한 setter메소드로 인해 객체의 불변성을 보장할 수 없어진다. 물론 setter를 사용한 후 freezing을 사용하여 불변으로 만들 수 있지만, freezing은 권장되지 않는 방식이다.</p>
<h4 id="3-빌더-패턴">3) 빌더 패턴</h4>
<p>빌더 패턴은 객체 생성시 Builder 클래스를 통해 변수를 하나씩 입력 받은후, build() 메소드로 인스턴스를 최종 생성한다.
Builder 클래스는 생성자가 많은 경우, 혹은 변경 불가능한 불변객체가 필요한 경우에 주로 사용되며, 코드의 가독성과 일관성, 불변성을 유지하는 것에 집중한다.
이펙티브자바에서는 빌더클래스를 static inner class로 구현하도록 한다.
그 이유로는</p>
<ul>
<li>하나의 빌더 클래스는 하나의 대상 객체만을 위해 사용됨</li>
<li>대상 객체는 오로지 빌더 객체에 의해 초기화됨</li>
<li>static을 통해 객체 생성 전 빌더를 사용할 수 있도록 함</li>
</ul>
<p>등이 있다.</p>
<p>아래 코드를 통해 빌더 패턴을 구현해 보았다.</p>
<pre><code>public class Student {
    //setter 메소드를 사용하지 않기때문에 객체를 freezing 시킬 수 있다. --&gt; final로 선언하여 값을 불변으로 사용가능
    private final int id;               //필수
    private final String name;          //필수
    private final String grade;         //필수
    private final String phoneNumber;   //선택
    private final String email;         //선택

    // inner Builder 클래스 생성, static으로 선언해야 객체 생성전에 메소드를 사용할 수 있다.
    public static class Builder {
        private final int id;
        private final String name;
        private final String grade ;
        private String phoneNumber = &quot;&quot;;
        private String email = &quot;&quot;;

        //필수 멤버를 사용한 빌더
        public Builder(int id, String name, String grade) {
            this.id = id;
            this.name = name;
            this.grade = grade;
        }

        // 선택멤버 set (setter는 void를 리턴하지만, 빌더는 자신을 리턴한다 --&gt; 메소드 체이닝이 가능해짐)
        Builder phoneNumber(String phoneNumber) {
            this.phoneNumber = phoneNumber;
            return this;
        }

        // 선택멤버 set
        Builder email(String email) {
            this.email = email;
            return this;
        }
    }

    //생성자는 외부에 노출되지 않고, 빌더 객체에 의해서만 초기화된다
    private Student(Builder builder) {
        this.name = builder.name;
        this.id = builder.id;
        this.grade = builder.grade;
        this.phoneNumber = builder.phoneNumber;
        this.email = builder.email;
    }

}</code></pre><pre><code>public static void main(String[] args) {
    Student stdnt = new Student.Builder(1, &quot;Kim&quot;, 3)
                               .phoneNumber(&quot;010-0000-0000&quot;)
                               .email(&quot;email@gmail.com&quot;)
                               .build();
}</code></pre><p>빌더패턴의 단점은 코드가 많아지고, 멤버가 중복된다는 점이다.
그래서 실무에서 빌더패턴을 사용할때는 주로 롬복의 @Builder 어노테이션을 사용해서 코드를 간결하게 줄일 수 있다.</p>
<pre><code>@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Student {
    //멤버변수
    private final int id;               //필수
    private final String name;          //필수
    private final String grade;         //필수
    private final String phoneNumber;   //선택
    private final String email;         //선택
}</code></pre><pre><code>public static void main(String[] args) {
    Student stdnt = new StudentBuilder()
                    .id(1)
                    .name(&quot;Kim&quot;)
                    .grade(3)
                    .phoneNumber(&quot;010-0000-0000&quot;)
                    .email(&quot;email@gmail.com&quot;)
                    .build();
}</code></pre><p>다만 롬복을 사용해서 빌더를 만들면, 객체생성시 어디까지가 필수멤버이고 어디까지가 선택멤버인지 가늠하기 어렵다는 문제점이 있다.
또한 롬복의 빌더는 모든 멤버가 파라미터인 생성자가 자동으로 생성되는데, 이렇게되면 외부에서 빌더를 통하지 않고 객체를 생성할수 있게 된다.
그래서 빌더로만 인스턴스 생성을 허용하고 싶으면 
@AllArgsConstructor(access = AccessLevel.PRIVATE)
를 통해 모든 파라미터를 가진 생성자의 access레벨을 private로 바꿔주면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 콜라 문제]]></title>
            <link>https://velog.io/@lily_younk/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%BD%9C%EB%9D%BC-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@lily_younk/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%BD%9C%EB%9D%BC-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Mon, 06 Nov 2023 12:14:33 GMT</pubDate>
            <description><![CDATA[<h3 id="콜라문제">콜라문제</h3>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/011427cd-bb54-4eb6-aad6-f9577295fbd6/image.png" alt=""></p>
<p>콜라문제는 빈병을 주고 새병을 받아 또 빈병을 만들고, 그 빈병으로 새병을 받고 또 빈병을 만들고 ... 같은 매커니즘을 반복하는 알고리즘이었다. 그래서 재귀함수를 사용해 문제를 풀어보았다.</p>
<pre><code>class Solution {
    int a;    // a, b, total은 전역변수 선언
    int b;
    int total;

    public int solution(int a, int b, int n) {
        this.a = a;
        this.b = b;

        getBottle(n);

        return total;
    }
    // 새병받기 메소드
    void getBottle(int n) {
        // a보다 가진 병이 적다면 재귀 탈출
        if(n &lt; a) {
            return;
        } 
        int mot = n/a * b;
        int namuji = n%a;
        total += mot;
        getBottle(mot + namuji); 
    }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[탐색 알고리즘 BFS]]></title>
            <link>https://velog.io/@lily_younk/%ED%83%90%EC%83%89-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-BFS</link>
            <guid>https://velog.io/@lily_younk/%ED%83%90%EC%83%89-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-BFS</guid>
            <pubDate>Sun, 05 Nov 2023 14:58:03 GMT</pubDate>
            <description><![CDATA[<h3 id="bfsbreadth-frist-search">BFS(Breadth Frist Search)</h3>
<p>너비우선탐색이라고도 불린다. 그래프를 깊게보다는 가까운 노드부터 넓게 탐색하는 알고리즘이다.
BFS의 탐색 순서는 다음과 같다. 
<img src="https://velog.velcdn.com/images/lily_younk/post/d99ebb58-cbaa-45f3-b496-c9eb5083d214/image.png" alt=""></p>
<ul>
<li>탐색 시작 노드를 큐에 삽입하고 방문처리를 한다.</li>
<li>큐에서 노드를 꺼내 해당 노드의 인접노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문처리를 한다.</li>
<li>위 과정을 더이상 수행할 수 없을 때까지 반복한다.</li>
</ul>
<p>BFS는 큐 자료구조를 이용해 데이터에 접근하며, 시간복잡도는 O(N)이 소요된다. BFS가 DFS보다 구현이 좀더 빠르게 동작한다.
주로 두 노드 사이의 최단경로, 혹은 임의의 경로를 찾고 싶을때 사용되는 알고리즘이다. DFS와 다르게 재귀적으로 동작하지 않는다.
다만 그래프 탐색시 어떤 노드를 방문했었는지를 반드시 체크해야한다. 그렇지 않으면 무한루프에 빠지게 된다.</p>
<h4 id="bfs-구현-java">BFS 구현 (java)</h4>
<pre><code>package DFS_BFS;

import java.util.LinkedList;
import java.util.Queue;

public class BFS {

    public static void main(String[] args) {
        // 그래프의 연결상태를 2차원 배열로 표현
        // 인덱스가 각각의 노드번호가 되고 0번 인덱스는 아무것도 없는 상태
        int[][] graph = {{}, {2,3,8}, {1,6,8}, {1,5}, {5,7}, {3,4,7}, {2}, {4,5}, {1,2}};
        // 방문처리를 위한 boolean 배열 선언
        boolean[] visited = new boolean[9];

        System.out.println(bfs(1, graph, visited));
    }

    static String bfs(int start, int[][] graph, boolean[] visited) {
        // 탐색순서 출력용 스트링빌더
        StringBuilder sb = new StringBuilder();
        // 노드 탐색을 위한 큐
        Queue&lt;Integer&gt; q = new LinkedList&lt;Integer&gt;();

        // 시작노드 삽입 및 방문처리
        q.offer(start);
        visited[start] = true;

        // 큐에 데이터가 없을때까지 반복
        while(!q.isEmpty()) {
            // 큐에서 노드 꺼냄
            int nodeIndex = q.poll();
            sb.append(nodeIndex + &quot; -&gt; &quot;);

            // 꺼낸 노드와 인접한 노드 순회
            for(int i = 0; i &lt; graph[nodeIndex].length; i++) {
                int temp = graph[nodeIndex][i];
                // 방문처리 안된노드는 방문처리후 큐에 넣기
                if(!visited[temp]) {
                    visited[temp] = true;
                    q.offer(temp);
                }
            }
        }
        return sb.toString();
    }
}</code></pre><p>참고자료
나동빈, 「이것이 코딩테스트다」
소스코드 : <a href="https://codingnojam.tistory.com/41">[Algorithm] BFS (Depth-first Search)를 Java로 구현해보자!</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[탐색 알고리즘 DFS]]></title>
            <link>https://velog.io/@lily_younk/%ED%83%90%EC%83%89-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-DFSBFS</link>
            <guid>https://velog.io/@lily_younk/%ED%83%90%EC%83%89-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-DFSBFS</guid>
            <pubDate>Sun, 05 Nov 2023 14:31:21 GMT</pubDate>
            <description><![CDATA[<h3 id="dfsdepth-first-search">DFS(Depth-First Search)</h3>
<p>깊이우선탐색이라고도 불린다. 그래프를 넓게 탐색하기 보다는, 깊은 부분을 우선적으로 탐색하는 알고리즘이다. 
DFS의 구체적인 동작과정은 다음과 같다.
<img src="https://velog.velcdn.com/images/lily_younk/post/5156df89-050f-4b55-bfa0-d5a7309ad86e/image.png" alt=""></p>
<ul>
<li>탐색 시작 노드를 스택에 삽입하고 방문처리를 한다.</li>
<li>스택의 최상단 노드에 방문하지 않은 인접노드가 있으면 그 인접 노드를 스택에 넣고 방문 처리를 한다. 방문하지 않은 인접노드가 없으면 스택에서 최상단 노드를 꺼낸다.</li>
<li>위 과정을 더이상 수행할 수 없을 때까지 반복한다. </li>
</ul>
<p>*&#39;방문처리&#39;는 스택에 한번 삽입되어 처리된 노드가 다시 삽입되지 않게 체크하는것을 의미. 방문처리를 함으로써 각 노드를 한번씩만 처리할 수 있다.</p>
<p>DFS는 스택 자료구조에 기초하며, 탐색을 수행함에 있어서 시간복잡도가 O(N)이 소요된다. 스택을 이용해서 DFS를 구현할 수 있지만, 재귀함수를 사용하면 코드를 더 간결하게 사용할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/f565214e-3381-4c14-ae29-8095e29a8eec/image.png" alt=""></p>
<h4 id="스택을-이용한-dfs-java">스택을 이용한 DFS (java)</h4>
<pre><code>package DFS_BFS;

import java.util.Stack;

public class StackDFS {

    // 방문처리 배열
    static boolean[] visited = new boolean[9];
    // 그래프의 연결상태를 2차원 배열로 표현
    // 인덱스가 각각의 노드번호가 되고 0번 인덱스는 아무것도 없는 상태
    static int[][] graph = {{}, {2,3,8}, {1,6,8}, {1,5}, {5,7}, {3,4,7}, {2}, {4,5}, {1,2}};

    //DFS 사용할 스택
    static Stack&lt;Integer&gt; stack = new Stack&lt;&gt;();

    public static void main(String[] args) {
        // 시작노드 1을 스택에 넣어주고 방문처리 해줌
        stack.push(1);
        visited[1] = true;

        // 스택에 더이상 값이 없을때까지 반복
        while(!stack.isEmpty()) {
            //스택에서 값 꺼냄
            int nodeIndex = stack.pop();
            System.out.println(nodeIndex + &quot; -&gt; &quot;);
            //꺼낸 노드와 인접한 노드 순회
            for(int LinkedNode : graph[nodeIndex]) {
                //방문처리 안된노드는 스택에 넣고 방문처리
                if(!visited[LinkedNode]) {
                    stack.push(LinkedNode);
                    visited[LinkedNode] = true;
                }
            }
        }
    }
}</code></pre><h4 id="재귀를-이용한-dfs-java">재귀를 이용한 DFS (java)</h4>
<pre><code>package DFS_BFS;

public class RecursionDFS {

    // 방문처리에 사용할 배열
    static boolean[] visited = new boolean[9];

    // 그래프의 연결상태를 2차원 배열로 표현
    // 인덱스가 각각의 노드번호가 되고 0번 인덱스는 아무것도 없는 상태
    static int[][] graph = {{}, {2,3,8}, {1,6,8}, {1,5}, {5,7}, {3,4,7}, {2}, {4,5}, {1,2}};

    public static void main(String[] args) {
        dfs(1);
    }

    static void dfs(int nodeIndex) {
        // 해당 노드 방문처리
        visited[nodeIndex] = true;
        System.out.print(nodeIndex + &quot; - &gt; &quot;);

        // 해당 노드에 인접한 노드 순회
        for(int node : graph[nodeIndex]) {
            //인접한 노드중 방문처리 안된게 있다면 재귀호출
            if(!visited[node]) {
                dfs(node);
            }
        }
    }
}</code></pre><p>참고자료
나동빈, 「이것이 코딩테스트다」
소스코드 : <a href="https://codingnojam.tistory.com/44">[Algorithm] DFS (Depth-first Search)를 Java로 구현해보자!</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Lombok] 생성자와 관련된 어노테이션]]></title>
            <link>https://velog.io/@lily_younk/Lombok-%EC%83%9D%EC%84%B1%EC%9E%90%EC%99%80-%EA%B4%80%EB%A0%A8%EB%90%9C-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@lily_younk/Lombok-%EC%83%9D%EC%84%B1%EC%9E%90%EC%99%80-%EA%B4%80%EB%A0%A8%EB%90%9C-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98</guid>
            <pubDate>Mon, 30 Oct 2023 02:21:34 GMT</pubDate>
            <description><![CDATA[<h3 id="생성자-관련-어노테이션">생성자 관련 어노테이션</h3>
<p>Lombok에서 제공하는 생성자 어노테이션은 3가지가 있다.</p>
<h4 id="noargsconstructor">@NoArgsConstructor</h4>
<p>아무 인자가 없는 생성자를 생성해준다.</p>
<h4 id="requiredargsconstructor">@RequiredArgsConstructor</h4>
<p>꼭 필요한 변수만 인자로 받아 생성자를 구현한다. 꼭 필요한 변수를 결정짓는 것은 final 혹은 @NotNull 어노테이션이다. 이렇게 필수적으로 정의되어야하는 변수를 인자로 받아 객체를 생성한다. 이때 @NotNull은 롬복이 아닌 다른 라이브러리의 것으로 사용하면 필수 변수로 인식되지 않는다.</p>
<h4 id="allargsconstructor">@AllArgsConstructor</h4>
<p>해당 객체에 있는 모든 변수들을 인수로 받는 생성자를 만든다. 이때 NotNull을 지정한 변수는 null인지 체크도 한다. 하지만 이 어노테이션으로 생성자를 만들땐 정적 팩토리메소드를 사용하는것이 가독성이 좋을때가 많기 때문에, 사용 전에 고려를 해봐야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹서비스와 WEB WAS 개념 2/2]]></title>
            <link>https://velog.io/@lily_younk/%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4%EC%99%80-WEB-WAS-%EA%B0%9C%EB%85%90-22</link>
            <guid>https://velog.io/@lily_younk/%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4%EC%99%80-WEB-WAS-%EA%B0%9C%EB%85%90-22</guid>
            <pubDate>Mon, 30 Oct 2023 01:59:08 GMT</pubDate>
            <description><![CDATA[<h3 id="tomcat의-3가지-구성요소">Tomcat의 3가지 구성요소</h3>
<p>1) Coyote
코요테는 웹서버의 역할을 한다.
클라이언트로부터 들어온 http 요청을 처리하며, 톰캣에 TCP를 통한 프로토콜을 생성하고 관리한다.</p>
<p>2) Catalina
카탈리나는 톰캣의 가장 코어 요소인 서블릿 컨테이너이다. 자바 서블릿을 호스팅하는 환경을 제공한다. 
카탈리나의 기본 설정은 6개의 config 파일을 편집하여 구현할 수 있는데, 이부분은 깊게 들어가면 시간이 걸릴것 같아 우선은 패스해둔다.</p>
<p>3) Jasper
톰캣의 jsp엔진이다. 재스퍼는 jsp파일을 파싱하여 서블릿 코드로 컴파일한다. jsp가 변경되면 리컴파일 작업도 수행한다.</p>
<h3 id="스프링부트의-내장-톰캣">스프링부트의 내장 톰캣</h3>
<p>스프링부트 프로젝트 생성시, spring-boot-starter-web 라이브러리를 사용하면 내장톰캣을 사용할 수 있다. 
이 내장톰캣은 빌드와 배포를 편리하게 해주며, 빌드시 하나의 jar를 사용한다.</p>
<pre><code>@SpringBootApplication
public class BootApplication {

    public static void main(String[] args) {
        SpringApplication.run(BootApplication.class, args);
    }

}</code></pre><p>스프링부트 실행시 main()메소드에서 SpringApplication.run()을 호출하게 되는데, 이때 컴포넌트 스캔을 비롯한 여러 작업을 실행한다.
그 중 중요한 작업 두가지는 이것이다.</p>
<ul>
<li>스프링 컨테이너 생성</li>
<li>내장톰캣 생성</li>
</ul>
<p>이때 생성된 내장 톰캣은 build된 스프링부트 애플리케이션 jar를 java 명령어로 실행한다.</p>
<h3 id="스프링부트-프로젝트에서-외장톰캣을-사용하는-경우">스프링부트 프로젝트에서 외장톰캣을 사용하는 경우</h3>
<p>과거 스프링 레거시에서는 내장톰캣이 없었기 때문에, 자바 웹애플리케이션을 외부톰캣에 포함시켜주어야 했다.
외장톰캣을 사용하는 스프링 애플리케이션을 실행하려면</p>
<ul>
<li>톰캣을 설치한다</li>
<li>톰캣 설정 파일을 구성한다</li>
<li>톰캣 webapp 디렉토리에 build된 스프링 애플리케이션 war파일을 포함시켜준다.</li>
<li>톰캣을 실행해준다.
딱봐도 외장톰캣을 사용하는것이 복잡해보인다. 스프링부트의 내장톰캣은 외장톰캣보다 성능이 좋지않다는 인식이 있는것 같던데,(회사 사수님께도 실제로 그런 소리를 들었다. 내장톰캣은 간단한 토이프로젝트에서만 사용한다는..) 객관적인 근거는 듣지못했다. 내장톰캣과 외장톰캣이 비슷한 성능을 가진다는 블로그글도 봤기에 확신하지는 못하겠다.</li>
</ul>
<p>하지만 내장톰캣과 외장톰캣의 분명한 차이점은 존재한다.
<strong>-  virtual host 기능</strong>
외장 톰캣은 main host 하위에 가상의 호스트를 소프트웨어적으로 둘수 있는 기능을 제공한다. 이는 여러 애플리케이션을 하나의 포트로 서비스할 수있게 한다. 내장톰캣도 이를 가능하게 할 수는 있지만, 굉장히 까다롭다고 한다. </p>
<p>참고자료
<a href="https://thxwelchs.github.io/EmbeddedTomcat%EA%B3%BCTomcat%EC%9D%98%EC%B0%A8%EC%9D%B4/">Embedded Tomcat과 Tomcat의 차이</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA 프로젝트 생성]]></title>
            <link>https://velog.io/@lily_younk/JPA-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@lily_younk/JPA-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Mon, 30 Oct 2023 00:38:30 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lily_younk/post/6a15b1a9-7d65-4a78-9778-ebf361223ce9/image.png" alt=""></p>
<p>토이프로젝트를 해야지 해야지 계속 미루다가...
회사 SI를 하면서 운동도 다니고 취미댄스도 하다보니, 토이프로젝트는 거들떠 볼 여유도 없이 회피하고만 있었다. 하지만 이제는 더이상 물러날수 없어서 하나하나씩 공부하며 프로젝트에 적용해보는 식으로 진행하려한다.</p>
<h3 id="개발환경">개발환경</h3>
<p>IDE: IntelliJ Community
backend : springBoot 3.1.4
frontend : vue.js
db : mysql
build : gradle</p>
<h3 id="환경세팅">환경세팅</h3>
<h4 id="프로젝트-dependency-추가">프로젝트 dependency 추가</h4>
<p><strong>build.gradle</strong></p>
<pre><code>dependencies {
    // JPA
    implementation &#39;org.springframework.boot:spring-boot-starter-data-jpa&#39;
    // Thymeleaf
    implementation &#39;org.springframework.boot:spring-boot-starter-thymeleaf&#39;
    // web
    implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;
    //lombok
    compileOnly &#39;org.projectlombok:lombok&#39;
    // devTools
    developmentOnly &#39;org.springframework.boot:spring-boot-devtools&#39;
    //MySql
    runtimeOnly &#39;com.mysql:mysql-connector-j&#39;
    annotationProcessor &#39;org.projectlombok:lombok&#39;
    testImplementation &#39;org.springframework.boot:spring-boot-starter-test&#39;
}</code></pre><p>*스프링부트 2.7.8 이후부터 mysql-connector-java 의존성을 찾지 못하는 에러가 뜨는데, 해당 버전에서부터는 더이상 mysql-connector-java를 관리하지 않기 때문이다. 기존 mysql:mysql-connector-java의 groupId가  &quot;mysql&quot; 단일 단어로 이루어진 채 오래 동안 유지된 레거시 형태인데 이를 reversed domain의 형태인 &quot;com.mysql&quot;로 변경을 하기 위해서라고 한다. 따라서 2.7.8 이후부터는 &#39;com.mysql:mysql-connector-j&#39;을 선언해주면 된다.</p>
<p>gradle에서는 의존성을 가져오는 방법을 여러 가지 제공한다. 
가져온 의존성을 컴파일 타임에만 사용하게 할 수도 있고, 런타임에만 사용하게 할 수도 있다. 의존성의 특성에 맞춰서 configuration을 잘 설정해 원하는 의존성을 적절하게 주입하면 배포 파일의 크기를 최적화할 수 있다.
configuration 목록은 문서에서 찾을 수 있다. 위에서 쓰인 것만 소개해 보자면 다음과 같다.</p>
<p>compileOnly: 컴파일 타임에만 의존성을 주입한다.
runtimeOnly: 런타임에만 의존성을 주입한다. (deprecated: runtime)
implementation: 컴파일 타임과 런타임에 모두 쓰이는 의존성을 주입한다. (deprecated: compile)
annotationProcessor: 어노테이션 프로세서로써 컴파일 시 사용되는 의존성을 주입한다.
testImplementation: 테스트 시에만 의존성을 주입한다. (deprecated: testCompile)
의존성은 그룹명:이름:버전 순으로 명시해서 주입할 수 있으며, 버전명은 optional로, 꼭 명시하지 않아도 된다.</p>
<p>build.gradle은 수정후 바로 적용할 수 없고, 업뎃을 해줘야한다.
<img src="https://velog.velcdn.com/images/lily_younk/post/60aa946c-ff6c-4f6e-9058-4df0cb7e655f/image.png" alt="">
인텔리제이에서 수정하면 오른편에 Load Gradle Changes를 눌러 수정된 파일을 적용시키면 dependencies가 반영된다.</p>
<h4 id="db-세팅">DB 세팅</h4>
<p><strong>application.properties</strong></p>
<pre><code>spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=pwd
spring.datasource.url=jdbc:mysql://localhost:3306/demo</code></pre><p>DB는 로컬에 생성한 곳으로 연결해주었다. 회사 프로젝트에선 id와 password 자리에 암호화된 문자열이 세팅되어 있었다. 추후 시간이 되면 나도 암호화된 값으로 수정해 봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[토큰기반 인증방식과 JWT]]></title>
            <link>https://velog.io/@lily_younk/%ED%86%A0%ED%81%B0%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D%EB%B0%A9%EC%8B%9D%EA%B3%BC-JWT</link>
            <guid>https://velog.io/@lily_younk/%ED%86%A0%ED%81%B0%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D%EB%B0%A9%EC%8B%9D%EA%B3%BC-JWT</guid>
            <pubDate>Sun, 15 Oct 2023 13:57:54 GMT</pubDate>
            <description><![CDATA[<h3 id="http의-비상태성stateless">HTTP의 비상태성(Stateless)</h3>
<p>HTTP는 비상태성이라는 특성을 가진다. 서버는 클라이언트의 상태를 저장하지 않으며, 따라서 이전 요청과 다음요청의 맥락이 이어지지 않는다. HTTP는 이전에 발생한 통신을 기억하지 못하기 때문에, 이전에 클라이언트가 인증과정을 거쳤는지 알 수 없다. 하지만 매번 화면을 조회할 때마다 사용자에게 인증을 요구할 수는 없기 때문에, 세션과 토큰을 이용하여 인가(Authorization)를 수행한다.</p>
<h3 id="세션기반-인증">세션기반 인증</h3>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/e5ce6504-77be-449b-899b-33cb30123bff/image.png" alt="">
세션기반 인증은 사용자의 인증정보가 서버의 세션 저장소에 저장되는 방식이다. 사용자가 로그인을 하면 해당 인증정보를 서버의 세션 저장소에 저장하고, 세션 id를 부여한다. 이 세션 id는 브라우저에 쿠키형태로 저장된다. 이후 브라우저는 HTTP 요청마다 쿠키 헤더에 세션 id를 함께 서버로 전송한다. 서버는 요청을 받으면 세션 id에 해당하는 정보가 세션 저장소에 존재한다면, 해당 사용자를 인증된 사용자로 판단한다.</p>
<h3 id="토큰기반-인증">토큰기반 인증</h3>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/e80fd16d-78c8-45aa-a5a9-2c035b2c01e0/image.png" alt="">
토큰기반 인증은 인증정보를 클라이언트가 직접 들고 있다. 이때 인증정보는 토큰의 형태로 브라우저의 로컬스토리지 혹은 쿠키에 저장된다. JWT의 경우, 디지털 서명이 존재해 토큰의 내용이 위변조 되었는지 서버측에서 확인 가능하다.
토큰기반 인증에서는 사용자가 가지고 있는 토큰을 HTTP의 Authorization 헤더에 담아 보낸다. 그럼 서버는 토큰의 위변조 유무와 만료시각을 체크한 후, 토큰에 담겨있는 사용자 인증정보를 확인한다.</p>
<h3 id="세션기반-인증-vs-토큰기반-인증">세션기반 인증 VS 토큰기반 인증</h3>
<p>1) 트래픽 효율
세션의 경우 쿠키 헤더에 세션 id만 실어 보내면 되기 때문에 트래픽을 적게 사용한다.
하지만 JWT는 사용자 인증 정보와 토큰의 발급시각, 만료시각, 토큰의 id 등 많은 정보를 전달하므로 더 많은 네트워크 트래픽을 사용한다.</p>
<p>2) 안정성과 보안문제
세션은 모든 인증정보를 서버에서 관리하기 때문에, 좀더 보안에 유리하다. 만약 세션 id가 해커에 의해 탈취된다 하더라도, 서버측에서 해당 세션을 무효처리하면 된다.
반면 토큰의 경우, 클라이언트가 모든 인증정보를 가지고 있기 때문에, 한번 해커에게 탈취되면 해당 토큰이 만료되기 전까지는 속수무책으로 피해를 입을 수 밖에 없다.</p>
<p>3) 확장성
그럼에도 불구하고 최근 웹 애플리케이션이 토큰 기반인증을 사용하는 것은 확장성 때문이다.
일반적으로 웹 애플리케이션의 서버 확장 방식은 수평 확장을 사용한다. 즉, 한 대가 아닌 여러 대의 서버가 요청을 처리한다. 이때 별도의 작업을 해주지 않는다면, 세션기반 인증방식은 세션 불일치 문제를 겪게된다. 이를 해결하기 위해 Sticky Session, Session Clustering, 세션 스토리지 외부분리 등의 작업을 해주어야 한다.
하지만 토큰기반 인증일 경우, 서버가 직접 인증방식을 저장하지 않기 때문에 세션 불일치 문제로부터 자유롭다. 그렇기에 토큰기반 인증은 HTTP의 비상태성을 그대로 활용할 수 있고, 따라서 높은 확장성을 가질 수 있다.</p>
<p>4) 서버의 부담
세션기반 인증은 서비스가 세션 데이터를 직접 저장하고 관리하기 때문에, 데이터 양이 많아지면 많아질수록 서버의 부담이 증가한다.
반면 토큰기반 인증은 클라이언트가 데이터를 가지고 있기 때문에, 유저 수가 증가해도 서버의 부담이 증가하지 않는다.</p>
<h3 id="jwtjson-web-token">JWT(Json Web Token)</h3>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/6ddde6e2-564e-4dd4-b601-6ee04a19b0e4/image.png" alt="">
jwt는 json 객체를 사용하여 정보를 전달하는 웹 토큰이다.
jwt는 토큰 자체를 정보로 사용하는 self-contained 방식으로 정보를 안전하게 전달한다.</p>
<ul>
<li>self-contained(자가수용적)이란 
: jwt는 필요한 모든 정보를 자체적으로 가지고 있다. jwt 시스템에서 발급된 토큰에 대한 기본 정보, 전달할 정보, 토큰이 검증됨을 증명하는 서명을 포함한다.</li>
</ul>
<p>jwt는 헤더, 내용, 서명이 &#39;.&#39;을 기준으로 나누어져있으며, 완성된 토큰은 다음과 같은 형태를 가진다.</p>
<blockquote>
<p>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c</p>
</blockquote>
<h4 id="jwt-구조">JWT 구조</h4>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/7b73ce70-7ef9-4ab4-8165-b9941e6f606e/image.png" alt="">
jwt의 헤더, 페이로드, 시그니처는 json 형태로 구성되며, 각 부분은 Base64로 인코딩되어 있다. </p>
<p><strong>1) Header</strong>
알고리즘과 토큰 타입을 저장한다.</p>
<pre><code>{
    &quot;alg&quot; : &quot;HS256&quot;,
    &quot;typ&quot; : &quot;JWT&quot;
}</code></pre><ul>
<li><p>typ : 토큰의 타입을 지정 ex) JWT</p>
</li>
<li><p>alg : 알고리즘 방식을 지정하며, 서명(Signature) 및 토큰 검증에 사용 ex) HS256(SHA256) 또는 RSA</p>
</li>
</ul>
<p><strong>2) Payload</strong>
토큰에서 사용할 정보들인 클레임(claim)이 저장된다.</p>
<pre><code>{
    &quot;sub&quot; : &quot;1234567&quot;,
    &quot;name&quot; : &quot;yunkyeong&quot;,
    &quot;iat&quot; : 1234567
}</code></pre><p>클레임은 총 3가지로 나뉜다.</p>
<ul>
<li><p>Register Claim
이미 등록된 클레임.
등록된 클레임은 토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터들이다.</p>
<blockquote>
<p>iss(issuer; 발행자), 
exp(expireation time; 만료 시간), 
sub(subject; 제목), 
iat(issued At; 발행 시간), 
jti(JWI ID)</p>
</blockquote>
</li>
<li><p>Public Claims 
사용자가 정의할 수 있는 클레임. 공개용 정보 전달을 위해 사용.</p>
</li>
<li><p>Private Claims 
해당하는 당사자들 간에 정보를 공유하기 위해 만들어진 사용자 지정 클레임. 외부에 공개되도 상관없지만 해당 유저를 특정할 수 있는 정보들을 담는다</p>
</li>
</ul>
<p><strong>3) Signature</strong>
시그니처는 (헤더 + 페이로드)와 서버가 갖고 있는 유일한 key 값을 합친 후, 암호화를 한다.이때 사용하는 암호화 알고리즘은 헤더에서 정의한 알고리즘 방식(alg)을 활용한다.
Header와 Payload는 단순히 인코딩된 값이기 때문에 제 3자가 복호화 및 조작할 수 있지만, Signature는 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화할 수 없다. 따라서 Signature는 토큰의 위변조 여부를 확인하는데 사용된다.</p>
<h3 id="acess-token과-refresh-token">Acess Token과 Refresh Token</h3>
<p>JWT도 제 3자에게 토큰 탈취의 위험성이 있기 때문에, 현업에서는 그대로 사용하는것이 아닌 Access Token, Refresh Token 으로 나누어 인증을 하는 방식을 취한다.</p>
<p>Access Token 과 Refresh Token은 둘다 똑같은 JWT이다. 다만 토큰이 어디에 저장되고 관리되느냐에 따른 사용 차이일 뿐이다.</p>
<ul>
<li><p>Access Token 
클라이언트가 갖고있는 실제로 유저의 정보가 담긴 토큰으로, 클라이언트에서 요청이 오면 서버에서 해당 토큰에 있는 정보를 활용하여 사용자 정보에 맞게 응답을 진행</p>
</li>
<li><p>Refresh Token
새로운 Access Token을 발급해주기 위해 사용하는 토큰으로 짧은 수명을 가지는 Access Token에게 새로운 토큰을 발급해주기 위해 사용. 해당 토큰은 보통 데이터베이스에 유저 정보와 같이 기록.</p>
</li>
</ul>
<p>*정리하자면, Access Token은 접근에 관여하는 토큰, Refresh Token은 재발급에 관여하는 토큰의 역할로 사용되는 JWT 이라고 말할 수 있다.
만약 Access Token 만을 이용하여 인증하면, Access Token은 발급된 이후 서버에 저장되지 않고 클라이언트에 저장되어 토큰 자체로 검증을 하며 사용자 권한 인증을 진행하기 때문에, Access Token이 탈취되면 토큰이 만료되기 전 까지, 토큰을 획득한 사람은 누구나 권한 접근이 가능해지는 문제점이 있었다. 
그래서 토큰의 유효 시간을 부여하여 탈취 문제에 대해 대응을 하기도 하지만, 만일 유효 기간이 짧을 경우 그만큼 사용자는 로그인을 자주해야 하는 번거로움이 있다.
따라서 이러한 문제를 해결하기 위해 Refresh Token 이라는 추가적인 토큰을 활용하여 토큰을 이중 장막을 쳐서 보다 보안을 강화하는 식으로 보면 된다.</p>
<p>참고
<a href="https://hudi.blog/session-based-auth-vs-token-based-auth/">세션 기반 인증과 토큰 기반 인증 (feat. 인증과 인가)</a>
<a href="https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-JWTjson-web-token-%EB%9E%80-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC#token_%EB%B0%A9%EC%8B%9D%EC%9D%98_%EB%8B%A8%EC%A0%90">JWT 토큰 인증 이란? (쿠키 vs 세션 vs 토큰)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[의존성 주입(Dependency Injection)]]></title>
            <link>https://velog.io/@lily_younk/%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85Dependency-Injection</link>
            <guid>https://velog.io/@lily_younk/%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85Dependency-Injection</guid>
            <pubDate>Tue, 10 Oct 2023 02:23:56 GMT</pubDate>
            <description><![CDATA[<h2 id="의존성-주입dependency-injection이란">의존성 주입(Dependency Injection)이란</h2>
<p>클래스간 의존성을 클래스 외부에서 주입하는 것을 의미한다.
객체지향 프로그래밍에서 클래스간 의존성이 있다는 것은, 클래스간의 의존관계가 존재함을 의미한다. 이는 한 클래스가 바뀔때 다른 클래스가 영향을 받는다는 의미이기도 하다. 
DI는 두 클래스간의 관계를 정의하기 위해 중간에 인터페이스를 두고, 클래스 레벨에서는 의존관계가 고정되지 않도록 하고, 런타임 시에 관계를 동적으로 주입해준다. 이는 코드의 유연성을 늘리고 결합도를 낮출 수 있도록 해준다. </p>
<h2 id="의존성-주입이-필요한-이유">의존성 주입이 필요한 이유</h2>
<pre><code>public class Computer {
    private Monitor monitor;

    public Computer() {
        this.monitor = new Monitor();
    }
}</code></pre><p>위 코드의 문제점은 크게 두가지가 있다.</p>
<ul>
<li><p>두 클래스가 강하게 결집되어 있다.
Monitor클래스가 Monitor15로 업그레이드 된다면, Computer 클래스는 2줄의 코드를 수정해야한다. 현 예시에선 2줄뿐이지만, 실제로 사용되는 클래스가 Monitor 뿐아니라 Mouse, KeyBoard, Speaker 등등 여러개라면, 수정해야할 코드는 10줄 100줄이 될 수도 있다.</p>
</li>
<li><p>객체들 간의 관계가 아니라 클래스 간의 관계가 맺어져 있다.
올바른 객체지향적 설계는 객체들 간의 관계를 지향한다. 객체들 간 관계가 맺어져있다면, 다른 객체의 구체 클래스를 전혀 알지 못하더라도 인터페이스 타입으로 사용할 수 있다.</p>
</li>
</ul>
<p>결국 클래스간 결합도가 높아지고, 코드가 유연해질수 없기에, 스프링에서는 DI를 적용하여 문제를 해결할 수 있다.</p>
<h2 id="다형성을-이용한-의존성-주입">다형성을 이용한 의존성 주입</h2>
<pre><code>public interface Element {
}

public class Monitor implements Element {
}

public class Keyboard implements Element {
}
...</code></pre><p>위 코드에선 Element라는 인터페이스를 두어 여러가지 구성품을 하나로 표현할수 있게 되었다.</p>
<pre><code>public class Computer {
    private Element element;

    public Computer(Element element) {
        this.element = element;
    }
}</code></pre><p>Computer 클래스에서 element를 의존하면, Computer 생성자의 매개변수로 어떤 구성품이 오든 코드를 변경하지 않을 수 있다. 또한 Computer에서 Element 객체를 주입하기 위해서는 애플리케이션 실행 시점에 필요한 객체를 생성해야하며, 의존성이 있는 두 객체를 연결하기 위해 한 객체를 다른 객체로 주입시켜야 한다. 이러한 역할을 스프링 프레임워크가 해준다. </p>
<pre><code>public class BeanFactory {

    public void computer() {
        // Bean의 생성
        Element monitor = new Monitor();

        // 의존성 주입
        Computer computer = new Computer(monitor);
    }

}</code></pre><p>위 코드와 같은 DI 컨테이너를 스프링이 지원하기 때문에, DI 컨테이너라고도 불린다. 또한 이런 개념은 제어의 역전(Inversion of Control, IoC)이라고 불리기도 한다. 어떤 객체를 사용할 지에 대한 책임은 프레임워크에게 넘어가고, 개발자는 수동적으로 주입받는 객체를 사용하기 때문이다.</p>
<h2 id="di-방식-세가지">DI 방식 세가지</h2>
<p>스프링의 DI 방식은 세가지가 있다.
Spring 3버전까지는 setter 주입을 권장했으나, 4.3부터는 생성자 주입을 권장하고 있다.</p>
<h3 id="1-생성자-주입">1) 생성자 주입</h3>
<p>생성자에 의존성 주입을 받고자하는 필드를 나열하는 방법이다.
@Autowired 어노테이션을 통해 생성자를 주입할 수 있다. 생성자가 하나만 있을 경우, @Autowired를 생략해도 주입이 가능하다.
final 키워드를 사용할 수 있기 때문에, 생성자로 인스턴스가 생성될때 1번만 할당되고, 객체의 불변성을 보장할 수 있다. 또한 초기에 할당되기때문에 NPE(Null Pointer Exception)이 발생하지 않는다.</p>
<pre><code>@Component
public class UserService {
    private final UserRepository userRepository;
    private final MemberService memberService;

    @Autowired //생략가능
    public UserService(UserRepository userRepository, MemberService memberService) {
        this.userRepository = userRepository;
        this.memberService = memberService;
    }
}</code></pre><h3 id="2-필드-주입">2) 필드 주입</h3>
<p>필드에 @Autowired를 붙여서 바로 주입하는 방법이다.
final 선언이 불가능하다.
의존 관계가 잘 보이지 않아 추상적이고, 이로 인해 의존성 관계가 복잡해질 수 있다.
생성자주입보다 코드가 간결하지만, 외부에서 변경이 불가능하여 테스트하기 어렵다.
DI 프레임 워크가 없다면 아무것도 할 수 없으며, SRP(단일책임원칙)에 위배되는 안티패턴이다.</p>
<pre><code>@Component
public class UserService {

    @Autowired
    private UserRepository userRepository;
    @Autowired
    private MemberService memberService;

}</code></pre><h3 id="3-setter-주입">3) setter 주입</h3>
<p>setter 메소드에 @Autowired 어노테이션을 선언하여 주입받는 방법이다. 
final 선언 불가능하다.
의존성이 선택적으로 필요한 경우 사용할 수 있기 때문에, 생성자 주입방법과 setter 주입 방법을 적절하게 분배하여 사용할 수 있다. </p>
<pre><code>public class UserService {

    private UserRepository userRepository;
    private MemberService memberService;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Autowired
    public void setMemberService(MemberService memberService) {
        this.memberService = memberService;
    }
}</code></pre><h3 id="참고-사이트">참고 사이트</h3>
<p><a href="https://mangkyu.tistory.com/150">[Spring] 의존성 주입(Dependency Injection, DI)이란? 및 Spring이 의존성 주입을 지원하는 이유</a>
<a href="https://mangkyu.tistory.com/125">[Spring] 다양한 의존성 주입 방법과 생성자 주입을 사용해야 하는 이유 - (2/2)</a>
<a href="https://devlog-wjdrbs96.tistory.com/166">[Spring] @Autowired란 무엇인가?</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로세스간 통신]]></title>
            <link>https://velog.io/@lily_younk/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EA%B0%84-%ED%86%B5%EC%8B%A0</link>
            <guid>https://velog.io/@lily_younk/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EA%B0%84-%ED%86%B5%EC%8B%A0</guid>
            <pubDate>Wed, 20 Sep 2023 05:11:25 GMT</pubDate>
            <description><![CDATA[<h3 id="ipc-interprocess-communication">IPC (Interprocess Communication)</h3>
<p>독립적인 프로세스는 프로세스끼리 자원을 공유할 필요가 없기 때문에, CPU스케줄링을 잘해주면 프로세스간에 영향을 미치지 않는다.
하지만 프로세스간 협력이 필요한 경우엔 자원을 공유해야한다.</p>
<p>IPC란 프로세스들 사이에 서로 데이터를 주고받는 행위나 방법 또는 그 경로를 말한다. 각 프로세스는 자신의 독립적인 메모리 공간을 가지고, 그것을 보호하고 있기 때문에 통신을 위한 별도의 매커니즘이 필요하다.</p>
<h4 id="프로세스간-협력이-필요한-이유">프로세스간 협력이 필요한 이유</h4>
<p>1) 정보 공유 : 여러 사용자가 동일한 정보를 필요로 함
2) 계산 가속화 : 특정 작업을 빠르게 실행하기 위해 해당작업을 서브 task로 나누어 병렬로 실행할 수 있음
3) 모듈성 : 특정한 시스템 기능을 별도의 프로세스로 구분하여 모듈식 형태로 시스템을 구성할 수 있음
4) 편의성 : 여러 사용자들이 동시에 많은 작업을 수행할 수 있음</p>
<h3 id="운영체제가-지원하는-ipc-모델">운영체제가 지원하는 IPC 모델</h3>
<h4 id="1-shared-memory공유-메모리">1) Shared Memory(공유 메모리)</h4>
<p> : 공유 메모리를 이용해 자원을 주고받는다. 커널 의존성이 낮기 때문에 속도가 빠르고, 유저레벨에서 사용 가능하기 때문에 통신이 자유롭다. 하지만 동일한 자원을 여러 프로세스가 바라보다보니, 동기화 이슈가 존재한다. 유효한 데이터를 잘 전달하기 위해선 동기화 기술이 필요하다.
 프로세스가 공유 메모리 할당을 커널에 요청하면 커널은 해당 프로세스에 메모리공간을 할당한다.
<img src="https://velog.velcdn.com/images/lily_younk/post/5e6c5aa1-4d87-45c5-8d26-bd09356831cf/image.png" alt=""></p>
<ul>
<li>공유 메모리 세그먼트 : 공유 메모리를 생성하는 프로세스 메모리 공간에 위치한다. 다른 프로세스는 자신의 주소에 연결</li>
</ul>
<h4 id="2-kernel-massage-queue-커널-메세지큐">2) Kernel Massage Queue (커널 메세지큐)</h4>
<p>: 메세지를 주고받는 방법이다. 커널을 통해 메세지를 전달하는 방식으로 자원을 주고받는다. 공유메모리처럼 무언갈 구축할 필요 없이 커널을 사용하기 때문에 구현이 쉽다. 하지만 커널을 사용하다보니 시스템콜이 필요하며, 이로인한 오버헤드가 발생할 수 있다. 큐는 버퍼의 역할을 하며 커널 레벨에 위치하기 때문에, 프로세스가 종료되어도 특정 함수를 사용하여 삭제하지 않는 한, 남아있다.
<img src="https://velog.velcdn.com/images/lily_younk/post/4d10f8af-72cf-4e8b-9494-4966f52bd0ae/image.png" alt="">
메세지 전달 방식은 프로세스가 직접 자원을 공유하지 않고 운영체제가 그 메커니즘을 제공한다.
커널은 send와 receive를 통해 channel 크기만큼의 메모리를 주고받는다.</p>
<h4 id="3-file">3) file</h4>
<p>하나의 프로세스가 파일을 쓰고 다른 프로세스에서 이를 읽는다.</p>
<h4 id="4-pipe">4) pipe</h4>
<p>통신을 위한 버퍼 메모리공간(버퍼)를 생성하여 프로세스가 데이터를 주고받을 수 있게한다. 이때 버퍼는 파일시스템에 생성되는 임시 공간이다
파이프의 특징으로는 입구와 출구가 없다는 점이 있다.
방향성이 없기 때문에, 두개의 프로세스가 통신할때는 읽기전용 파이프와 쓰기전용 파이프 총 두개를 쓰게 된다. </p>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/4522501e-feeb-452d-92a9-6e46c259e098/image.png" alt="">
주로 파이프는 부모 프로세스와 자식 프로세스가 데이터를 교환할때 사용된다.
부모프로세스는 fork()를 통해 자식프로세스를 생성하며 파이프를 통해 데이터교환을 할 수 있다.</p>
<h4 id="3-semaphore-세마포어">3) Semaphore (세마포어)</h4>
<p>메세지 전달 모델과 공유메모리 모델은 한번에 하나의 프로세스만 해당 자원에 접근하다는 단점이 있다. 세마포어는 이를 극복하기 위한 통신방법이다. 
세마포어는 상호배제 원리를 보장하는 알고리즘이다. 동기화 대상이 여러개일 경우에 주로 사용된다.</p>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/a77b1aa8-7d8a-4793-9d17-f6cbfbd56c7c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AOP란]]></title>
            <link>https://velog.io/@lily_younk/AOP%EB%9E%80</link>
            <guid>https://velog.io/@lily_younk/AOP%EB%9E%80</guid>
            <pubDate>Mon, 18 Sep 2023 05:32:34 GMT</pubDate>
            <description><![CDATA[<h3 id="aop-aspect-oriented-programming">AOP (Aspect-Oriented Programming)</h3>
<p>관점지향 프로그래밍
관점을 기준으로 다양한 기능을 분리하여 보는 프로그래밍이다. 여기서 관점이란, 공통 관심사(모듈) 정도로 스스로 해석했다.</p>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/7dc94e82-166a-45eb-8963-500ed0b83c1c/image.png" alt="">
웹 어플리케이션은 핵심 비즈니스 로직과 그 애플리케이션 전체를 관통하는 부가 기능 로직이 있다. (예를 들어 로깅, 보안, 트랜젝션 등이 있음) 
이를 횡단 관심사라고 한다. 횡단 관심사의 코드는 비즈니스 로직의 코드와 분리하여, 각 코드의 변경과 확장에 유연함을 주도록 하는것이 AOP의 목적이다.</p>
<h3 id="aop-관련-용어">AOP 관련 용어</h3>
<ul>
<li>Aspect : 흩어진 관심사를 모듈화한 것</li>
<li>Target : Aspect를 적용하는 곳 (클래스, 메소드 등)</li>
<li>Advice : 실질적으로 어떤 일을 해야할 지에 대한 것. 실질적 부가기능을 담은 구현체</li>
<li>Join Point : Advice가 적용될 위치 혹은 끼어들 수 있는 시점. (스프링에서 Join Point는 언제나 메소드 실행 시점을 의미)</li>
<li>Point Cut : 특정 조건에 의해 필터링된 조인 포인트. 수 많은 조인포인트 중 특정 메소드에서만 advice를 수행시키기 위해서 사용되는 곳</li>
<li>Weaving : 포인트컷에 의해 결정된 타겟의 조인포인트에 Advice를 삽입하는 과정</li>
</ul>
<h4 id="advice의-동작시점">Advice의 동작시점</h4>
<ul>
<li>Before : 메소드 실행 전 동작</li>
<li>After : 메소드 실행 후 동작</li>
<li>After-returning : 메소드 정상 실행 후 동작</li>
<li>After-throwing : 예외 발생 후 동작</li>
<li>Around : 메소드 호출 이전, 이후, 예외발생 등 모든 시점에서 동작</li>
</ul>
<h3 id="aop-적용-방식">AOP 적용 방식</h3>
<h4 id="컴파일-시점-적용">컴파일 시점 적용</h4>
<p>컴파일 시점 적용방식은 AspectJ 컴파일러가 .java파일을 컴파일 할때 부가기능을 넣어서 .class 파일로 컴파일 해주는 것을 의미한다. </p>
<h4 id="클래스-로딩-시점-적용">클래스 로딩 시점 적용</h4>
<p>JVM내 클래스로더에 .class 파일을 올리는 시점에 바이트 코드를 조작해 부가기능 로직을 추가하는 방식이다.</p>
<h4 id="런타임-시점-적용">런타임 시점 적용</h4>
<p>컴파일, 클래스 로딩, main() 메소드 실행 이후에 자바가 제공하는 범위내에 부가 기능을 적용하는 방식이다.
스프링 AOP가 주로 사용하는 방법. (A라는 클래스 타입의 Bean을 만들때 A타입의 Proxy Bean을 만들어 Proxy Bean이 Aspect코드를 추가하여 동작하게한다)</p>
<h3 id="스프링-aop">스프링 AOP</h3>
<p>스프링 AOP는 프록시 가반의 AOP구현체이다.
스프링 Bean에만 적용가능하다. 스프링 AOP는 순수 자바로 구현되어 있어, 특별한 컴파일 과정이 필요하지 않다.
<img src="https://velog.velcdn.com/images/lily_younk/post/31a1d760-a391-42f5-89a3-e3c134d6d8f5/image.png" alt="">
스프링은 빈을 등록할때, 빈 후처리기에서 모든 Advisor 빈을 조회한 뒤, 포인트컷으로 매칭해보면서 프록시 적용 대상인지 판단하고, 대상이라면 프록시를 빈으로 등록한다. 이때, 스프링에 AOP 의존성을 추가하게 된다면, 자동 프록시 생성기를 사용해 Advisor를 쉽게 구현할 수 있게 된다. 이는 Advisor기반으로 프록시를 생성한다. 또한 @Aspect를 보고 Advisor로 변환해서 저장하는 작업을 수행한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[그리디(Greedy) 알고리즘]]></title>
            <link>https://velog.io/@lily_younk/%EA%B7%B8%EB%A6%AC%EB%94%94Greedy-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@lily_younk/%EA%B7%B8%EB%A6%AC%EB%94%94Greedy-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Tue, 15 Aug 2023 05:20:27 GMT</pubDate>
            <description><![CDATA[<h3 id="그리디-알고리즘-탐욕법">그리디 알고리즘 (탐욕법)</h3>
<p>그리디는 이름에서 알 수 있듯, 단순하고 탐욕적으로 문제를 해결하는 방법이다.
이때 탐욕적이라는 말은 &#39;현재 상황에서 지금 당장 좋은것만 고르는 방법&#39;을 의미한다. 그리디 알고리즘은 순간마다의 선택에 대해 지역적으로 최적이지만, 그 선택들이 합쳐져 전역적으로도 최적이어야한다.</p>
<p>그리디 알고리즘이 최적으로 작동되는 문제는 greedy choice property(탐욕적 선택조건)과 optimal substructure(최적부분 구조조건)이라는 두가지의 조건이 만족된다.</p>
<ul>
<li>탐욕적 선택조건 : 선행된 선택이 이후의 선택에 영향을 주지 않는다.</li>
<li>최적부분 구조조건 : 문제에 대한 최적의 해가 부분문제에 대해서도 최적의 해여야한다.</li>
</ul>
<h3 id="거스름돈">거스름돈</h3>
<p>그리디의 가장 대표적인 예는 거스름돈이다. 거스름 돈을 줄때는 가장 큰 화폐단위부터 내려가며, 결국 최대한 적은 양의 화폐를 주게된다. 1260원을 동전으로 건네 줘야할때, 500원, 100원, 50원, 10원 순서로 주면 최소한의 갯수로 거스름돈을 줄수 있다.</p>
<pre><code>def getChange(totalMoney, count) :
    coinTypes = [500, 100, 50, 10]
    for coin in coinTypes :
        count += totalMoney // coin  #해당 화폐로 거슬러 줄수있는 동전의 갯수
        n %= coin     #해당화폐를 거슬러준 후 나머지값
    print(count)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[웹서비스와 WEB WAS 개념1/2]]></title>
            <link>https://velog.io/@lily_younk/SpringBoot%EC%99%80-tomcat</link>
            <guid>https://velog.io/@lily_younk/SpringBoot%EC%99%80-tomcat</guid>
            <pubDate>Tue, 08 Aug 2023 08:18:38 GMT</pubDate>
            <description><![CDATA[<p>스프링부트에는 내장 톰캣이 설정되어있다. 그런데 회사 프로젝트의 pom.xml에는 embedded tomcat을 사용하지 않고 외부 tomcat을 사용하도록 설정해두었다. 왜 이렇게 설정해두었는지 궁금해진 나는, 이 기회에 톰캣에 대하여, 웹서버에 대하여 제대로 공부해보고자 한다.</p>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/804f7865-ec72-4fae-8af6-101790f2057b/image.png" alt=""></p>
<h3 id="웹-서버">웹 서버</h3>
<p>웹서버는 Http 프로토콜을 사용하여 클라이언트와 Request, Response를 주고받는다. 사용자가 브라우저에 url을 입력하면 웹서버는 url에 맞는 웹페이지를 전송해준다. 웹서버는 정적인 요청을 처리하면서 동적인 요청들을 WAS에 요청한다.
웹서버의 종류에는 apache, NginX 등이 있다.</p>
<h3 id="was">WAS</h3>
<p>서블릿 컨테이너를 통해 비즈니스 로직을 처리하거나, db 조회 등의 동적인 자원을 제공한다. 
또한 was는 웹서버의 기능도 수행이 가능하다. 
하지만 was의 부하를 방지하기 위하여, 주로 웹서버의 기능과 was의 기능을 분리하여 사용하고있다.
WAS의 종류에는 Tomcat, JBOSS, JEUS 등이 있다.</p>
<h3 id="서블릿-컨테이너">서블릿 컨테이너</h3>
<p>서블릿 컨테이너는 소켓으로 웹서버와 통신하며, 서블릿들이 클라이언트 요청을 처리할수있도록 관리해준다.
컨테이너는 요청이 올때마다 새로운 자바 스레드를 하나씩 생성하는데, 내부에 <strong>스레드풀</strong>이 있어 스레드를 할당하고 요청할때 꺼내 재사용하게된다. 또한 서블릿 객체는 <strong>싱글톤</strong>으로 관리되어 메모리 비효율을 막는다.</p>
<h3 id="서블릿-객체">서블릿 객체</h3>
<p>서블릿은 javax.servlet.package에 정의된 인터페이스이다. 서블릿은 lifeCycle을 위한 세가지 필수적인 메소드를 정의하는데, <strong>init(), service(), destroy()</strong>이다.</p>
<ul>
<li><p>init() : 서블릿 생명주기 중 초기화 단계에 호출됨. javax.servlet.ServletConfig 인터페이스를 구현하는 오브젝트가 전달되며, 이를 통해 서블릿이 웹 애플리케이션에서 초기화 파라미터에 접근할수 있도록한다.</p>
</li>
<li><p>service() : 초기화 이후 각각의 요청들이 들어오면 호출되는 메소드. 각각의 요청들은 별도로 나누어진 스레드에서 처리된다. 웹 컨테이너는 모든 요청에 대해 서블릿의  service()메소드를 요청한다.  service() 메소드는 요청의 종류를 판별하고 요청을 처리할 적절한 메소드로 전달한다.</p>
</li>
<li><p>destroy() : 서블릿 객체가 파괴되어야할 때 호출된다.
해당 서블릿이 가지고 있던 자원(메모리)를 풀어준다.</p>
</li>
</ul>
<p>서블릿 객체는 동시에 <strong>여러개의 스레드를 제공</strong>할 수 있으며, 스레드가 더이상 사용되지 않을때는 JVM에 의해 가비지 컬렉팅된다. 서블릿들은 JVM내부에서 작동하며 복잡한 http요청들을 처리하기위해 <strong>서블릿 컨테이너에 의해 생성, 실행, 파괴</strong>된다.</p>
<h3 id="서블릿의-특징">서블릿의 특징</h3>
<p>1) 동적으로 작동하는 자바 웹 프로그래밍 기술이다. 
2) html을 사용하여 요청에 응답한다.
3) 자바 스레드를 이용하여 동작한다. 
4) HTTP 프로토콜 서비스를 지원하는 javax.servlet.http.HttpServlet 클래스를 상속받는다.
5) UDP에 비해 처리 속도가 느리다.
6) 서블릿은 MVC 패턴에서 controller로 이용된다.</p>
<h3 id="서블릿-동작-순서">서블릿 동작 순서</h3>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/fdddcad8-aa69-43d8-84fc-459f4140cc28/image.png" alt=""></p>
<p>1) 클라이언트가 url을 입력하면 웹서버가 서블릿 컨테이너로 요청을 전송한다.
2) 요청을 전송받은 컨테이너는 HttpServletRequest, HttpServletResponse 객체를 생성한다.
3) web.xml을 기반으로 사용자가 요청한 url이 어느 서블릿에 대한 요청인지 찾는다.
3-1) 서블릿이 컨테이너에 없다면, 서블릿을 동적으로 검색하여 컨테이너의 주소 공간에 로드한다.
3-2) 컨테이너가 서블릿의 init()메소드를 호출하면, 서블릿이 초기화된다. (처음 로드 됐을때 한번만 호출)
4) 컨테이너가 서블릿의 service() 메소드를 호출한다.
5) service() 메소드는 요청이 get인지, post인지 구별하여 doGet(), doPost()를 수행하여 HttpServletResponse 객체에 응답을 보낸다.
6) 웹서버는 이 응답을 올바른 위치에 반환한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[application.yml .properties 차이]]></title>
            <link>https://velog.io/@lily_younk/application.yml-.properties-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@lily_younk/application.yml-.properties-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Mon, 07 Aug 2023 14:52:42 GMT</pubDate>
            <description><![CDATA[<p>스프링부트를 공부하다보면 application.yml 혹은 application.properties 파일을 보게된다. 둘다 서버정보나 profile 등을 설정할 수 있는 외부설정파일이지만, 어떤 경우에 yml을 쓰고 properties를 쓰는지 궁금해서 공부해보고자 한다.</p>
<h3 id="외부설정파일">외부설정파일</h3>
<p>실제 프로젝트를 진행하다보면 dev, stg, prod 등 운영환경에 따라 서버 설정이나, 데이터베이스 설정 등을 다르게 설정한다. 이렇게 스프링부트 외적인 시스템과 연동할때 필요한 profile들을 정의하거나 프로그램이 실행되는데 필요한 속성들을 정의할때 application.yml application.properties를 사용하게된다. 스프링 initializr를 통해 프로젝트를 생성하면 application.properties가 생성되지만, 종종 yml파일로 변경해서 사용하는 경우가 있다.</p>
<h3 id="applicationproperties">application.properties</h3>
<p>properties파일은 key=value 형식으로 서술된다. 문자열만 사용가능하며, 그외 자료형은 사용할수 없다.properties 파일은 java에서만 활용된다.
하나의 properties파일은 하나의 profile만 가지며, 여러개의 properties 파일을 생성하여 쓸수있다.</p>
<pre><code># DB Setting
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:orcl
spring.datasource.username=username
spring.datasource.password=password</code></pre><h3 id="applicationyml">application.yml</h3>
<p>yml 파일은 properties 파일과 다르게 계층적 구조를 사용할 수 있다.
예를들어, db세팅을 할때 datasource라는 공통 구조는 상위에 한번만 작성하고, 하위에 driver, url, password 등의 구조를 선언하여 쓸 수 있다.
key,value,map,list 등을 사용할 수 있으며, java 외에도 파이썬이나 루비에서 활용될 수 있다.
하나의 properties 파일에 하나의 profile만 설정할 수 있었던것과 다르게, yml 파일에는 여러개의 profile을 정리해둘 수있다.</p>
<pre><code># DB Setting
spring:
  datasource:
    driver-class-name: oracle.jdbc.driver.OracleDriver
    url: jdbc:oracle:thin:@localhost:1521:orcl
    username: username
    password: password</code></pre><p>위와같이 계층적인 선언은 properties에서 같은 구조내부의 설정들을 여러번 작성해야했던 불편함을 해소할 수 있다.</p>
<h3 id="우선순위">우선순위</h3>
<p>yml 파일과 properties 파일을 함께 사용하면 properties 파일이 우선순위가 높아 yml 파일의 설정이 이를 덮어 쓰게 된다. 따라서 한 프로젝트내에선 둘중 하나의 파일만 쓰기를 권장한다고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Gradle과 Maven]]></title>
            <link>https://velog.io/@lily_younk/Gradle%EA%B3%BC-Maven</link>
            <guid>https://velog.io/@lily_younk/Gradle%EA%B3%BC-Maven</guid>
            <pubDate>Fri, 04 Aug 2023 07:25:45 GMT</pubDate>
            <description><![CDATA[<h3 id="gradle과-maven">Gradle과 Maven</h3>
<p>회사에서 스프링부트를 사용하는 si를 두번이나 진행했지만, 
gradle과 maven의 정확한 차이점을 알고있지 못했다.
스스로 토이프로젝트를 생성하려다 gradle을 선택해야할지, maven을 선택해야할지 몰라서 끄적여보는 공부일지.</p>
<h3 id="빌드-관리-도구build-tool">빌드 관리 도구(Build Tool)</h3>
<p>maven과 gradle은 빌드과정에서 필요한 여러 외부 라이브러리들을 자동으로 관리해주는 빌드관리 도구이다.</p>
<h4 id="빌드란">빌드란?</h4>
<p>빌드는 소스코드 파일을 컴퓨터에서 실행할 수 있는 독립적인 형태로 변환하는 과정과 결과를 말함.
java,xml,jsp등을 was가 인식할 수 있도록 패키징하는 과정 및 결과물</p>
<h4 id="빌드-관리도구의-기능">빌드 관리도구의 기능</h4>
<ul>
<li>종속성 다운로드(전처리)</li>
<li>소스코드를 바이너리 코드로 컴파일</li>
<li>바이너리 코드를 패키징</li>
<li>테스트 실행</li>
<li>프로덕션 시스템에 배포</li>
</ul>
<h3 id="maven">Maven</h3>
<p>maven은 Java 전용 프로젝트 관리도구이다. LifeCycle 관리 목적 빌드 도구이며, Apache Ant의 대안으로 만들어졌다. maven은 아파치 라이센스로 배포되는 오픈소스 소프트웨어이다.</p>
<p><img src="https://velog.velcdn.com/images/lily_younk/post/c61770c7-59ee-443a-b3af-4ba2d55d2e12/image.png" alt="">
=&gt; clean - validate - compile - test - package - verify - install - site - deploy의 라이프 사이클을 가진다.</p>
<p>(1)clean : 빌드 시 생성되어있었던 파일들을 삭제
(2) validate : 프로젝트가 올바른지 확인하고 필요한 모든 정보를 사용할 수 있는지 확인하는 단계
(3) compile : 소스코드를 컴파일 하는 단계
(4) test : 단위테스트를 수행하는 단계. 테스트 실패 시 빌드 실패로 처리하며, 스킵이 가능
(5) package : 실제 컴파일된 소스코드와 리소스들을 jar, war 등의 패키지로 만드는 단계
(6) verify : 통합 테스트 결과에 대한 검사를 실행. 품질기준을 충족하는지 확인
(7) site :  프로젝트 문서와 사이트 작성, 생성하는 단계
(8) deploy : 만들어진 패키지를 원격 저장소에 release하는 단계</p>
<p>maven은 필요한 라이브러리를 pom.xml에 정의한다. 이를 프로젝트 모델링이라고 함.</p>
<h4 id="maven-예시-pomxml">maven 예시 (pom.xml)</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;parent&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
        &lt;version&gt;2.5.2&lt;/version&gt;
        &lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt;
    &lt;/parent&gt;
    &lt;groupId&gt;com.example2&lt;/groupId&gt;
    &lt;artifactId&gt;demo-maven&lt;/artifactId&gt;
    &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
    &lt;name&gt;demo-maven&lt;/name&gt;
    &lt;description&gt;Demo project for Spring Boot&lt;/description&gt;
    &lt;properties&gt;
        &lt;java.version&gt;11&lt;/java.version&gt;
    &lt;/properties&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;

    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
                &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;

&lt;/project&gt;</code></pre><h3 id="gradle">Gradle</h3>
<p>Ant Builder와 Groovy script를 기반으로 구축되어 기존 Ant의 역할과 배포 스크립의 기능을 모두 사용가능함. 스프링부트와 안드로이드에서 사용된다. maven에 비해 빌드 속도가 10~100배 가량 빠르다. 자바, c/c++, 파이썬등을 지원한다.</p>
<h4 id="gradle-예시-buildgradle">Gradle 예시 (build.gradle)</h4>
<pre><code>plugins {
    id &#39;org.springframework.boot&#39; version &#39;2.5.2&#39;
    id &#39;io.spring.dependency-management&#39; version &#39;1.0.11.RELEASE&#39;
    id &#39;java&#39;
}

group = &#39;com.example&#39;
version = &#39;0.0.1-SNAPSHOT&#39;
sourceCompatibility = &#39;11&#39;

repositories {
    mavenCentral()
}

dependencies {
    implementation &#39;org.springframework.boot:spring-boot-starter&#39;
    testImplementation &#39;org.springframework.boot:spring-boot-starter-test&#39;
}

test {
    useJUnitPlatform()
}</code></pre><h3 id="maven과-gradle의-비교">Maven과 Gradle의 비교</h3>
<ol>
<li><p>가독성, 간결함
maven은 xml에 정의하지만, gradle은 스크립트 언어를 사용하기 때문에 변수선언, if else for등의 로직이 구현가능하고 가독성이 좋다.</p>
</li>
<li><p>빌드 속도
gradle은 이미 빌드된 파일들을 모두 다시 빌드하지 않고, 바뀐 파일들만 빌드한다. 캐시를 사용하기  때문에 테스트 반복시 속도차이가 더 크게 발생.
또한 gradle은 데몬 프로세스를 지원한다. 데몬 프로세스는 서비스의 요청에 응답하기위해 오랫동안 살아있는 프로세스인데, gralde의 데몬 프로세스는 메모리상에 빌드 결과물을 보관한다. 때문에 한번 빌드된 프로젝트는 다음 빌드에서 매우 적은 시간만 소요된다. </p>
</li>
<li><p>직관적인 사용
maven이 정적인 형태의 xml 기반으로 작성되기 때문에, 특정 설정을 다른 모듈에서 사용하려면 상속받아야한다. 
반면 gradle은 groovy를 사용하기 때문에 동적인 빌드는 groovy 스크립트로 플러그인을 호출하거나, 직접 코드를 짜면 된다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링부트 환경설정 - @SpringBootApplication]]></title>
            <link>https://velog.io/@lily_younk/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95-SpringBootApplication</link>
            <guid>https://velog.io/@lily_younk/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95-SpringBootApplication</guid>
            <pubDate>Sat, 08 Jul 2023 05:20:26 GMT</pubDate>
            <description><![CDATA[<h3 id="springbootapplication">@SpringBootApplication</h3>
<pre><code class="language-Java">@SpringBootApplication
public class BamduleApplication {

    public static void main(String[] args) {
        SpringApplication.run(BamduleApplication.class, args);
    }

}</code></pre>
<p>스프링부트는 main 메소드가 선언된 클래스를 기준으로 실행된다.
@SpringBootApplication 어노테이션은 스프링부트의 가장 기본적인 설정을 선언해준다.
해당 어노테이션을 상세히 보면, 아래와 같은 어노테이션들이 다시 선언되어 있다.</p>
<pre><code class="language-Java">@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class&lt;?&gt;[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = &quot;basePackages&quot;
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = &quot;basePackageClasses&quot;
    )
    Class&lt;?&gt;[] scanBasePackageClasses() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = &quot;nameGenerator&quot;
    )
    Class&lt;? extends BeanNameGenerator&gt; nameGenerator() default BeanNameGenerator.class;

    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}</code></pre>
<p>이 중에서 눈여겨볼 설정은 @ComponentScan과 @EnabledAutoConfiguration이다.</p>
<p>스프링부트는 Bean을 두번 등록한다.
먼저 @ComponentScan으로 등록한다음, @EnabledAutoConfiguration으로 수집된 Bean을 등록한다. 그래서 사실상, @EnabledAutoConfiguration없이 @ComponentScan만 있어도 기동이 가능하다.</p>
<h3 id="componentscan">@ComponentScan</h3>
<p>@ComponentScan은 @Component 어노테이션 및 @Service, @Repository, @Controller 등의 어노테이션을 스캔하여 Bean으로 등록해주는 어노테이션이다.
해당 패키지에 속해있는 클래스에 대해서만 스캔을 하기 때문에, 메인 패키지 바로 아래에 등록해주어야 사용되는 클래스들을 스캔할 수 있다.</p>
<h3 id="enabledautoconfiguration">@EnabledAutoConfiguration</h3>
<p>@EnabledAutoConfiguration은 사전에 정의한 라이브러리들(meta파일에서 읽어냄)을 Bean으로 등록해주는 어노테이션이다. 사전에 정의한 라이브러리들 모두가 등록되지는 않고, 특정 조건이 만족될 경우에 Bean으로 등록한다.</p>
<ul>
<li>사전정의 파일 위치
: Dependencies &gt; spring-boot-autoconfigure &gt; META-INF &gt; spring.factories
&quot;org.springframework.boot.autoconfigure.EnableAutoConfiguration=&quot;에 등록된 클래스들이 자동으로 등록되는 Bean이다.
각 Bean은 OnBeanCondition, OnClassCondition, OnWebApplicationCondition 어노테이션의 조건에 의해 등록 여부가 결정된다.</li>
</ul>
<p>@OnBeanCondition : 특정 Bean이 사전에 생성되어있지 않을 경우에 조건이 만족됨
@ConditionalOnBean : 특정 Bean이 이미 생성되어있을 경우에 조건이 만족됨
@ConditionalOnClass : Classpath에 특정 class가 존재할 경우에 조건이 만족됨</p>
]]></description>
        </item>
    </channel>
</rss>