<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>knu-kang.log</title>
        <link>https://velog.io/</link>
        <description>You must do the things you think you cannot do</description>
        <lastBuildDate>Fri, 20 Jun 2025 15:13:59 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. knu-kang.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/knu-kang" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[test]]></title>
            <link>https://velog.io/@knu-kang/test</link>
            <guid>https://velog.io/@knu-kang/test</guid>
            <pubDate>Fri, 20 Jun 2025 15:13:59 GMT</pubDate>
            <description><![CDATA[<p>test</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[서버와 애플리케이션에서 TimeZone(TZ) 문제 해결: System vs Application Level 설정 (feat. LocalDateTime 사용)]]></title>
            <link>https://velog.io/@knu-kang/%EC%84%9C%EB%B2%84%EC%99%80-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-TimeZoneTZ-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0-System-vs-Application-Level-%EC%84%A4%EC%A0%95-feat.-LocalDateTime-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@knu-kang/%EC%84%9C%EB%B2%84%EC%99%80-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-TimeZoneTZ-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0-System-vs-Application-Level-%EC%84%A4%EC%A0%95-feat.-LocalDateTime-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Mon, 09 Sep 2024 05:02:56 GMT</pubDate>
            <description><![CDATA[<p><code>LocalDateTime</code> 은 TZ(TimeZone)를 포함하지않습니다. 하지만, TZ로 인해서 문제가 생길 수도 있습니다.</p>
<h2 id="서버-환경-tz의-불일치">서버 환경 TZ의 불일치</h2>
<p>서버 환경 내에서의 <code>TZ</code>가 내가 예상한 TZ 가 아닐 경우, 우리는 타임머신을 탄 경험을 하게 됩니다.. 쉽게 말하면, 완전 일자가 달라진다는 얘기입니다.</p>
<p>내가 원하던 <code>LocalDateTime</code>의 정보가 <strong>2022-05-16T18:36:25.400357</strong> 라고 가정한다면, 서버 환경 내의 TZ로 인해서 <strong>1980-05-16T18:36:25.400357</strong> 이라는 아예 다른 시간 데이터가 도출될 수도 있다는 것입니다.</p>
<p>이를 막기 위해서는 시스템 내에서 TZ 를 적용할 수도 있지만, 그렇게 된다면 서버가 변경될 때마다 TZ 를 재적용해야하는 번거로움이 존재하게 됩니다.</p>
<p>그렇기 때문에 <strong>Application-Level</strong> 에서 이를 설정할 수 있습니다.</p>
<h2 id="system-레벨에서의-tz-설정">System 레벨에서의 TZ 설정</h2>
<p>Application 레벨에서의 설정을 설명하기 이전에 해당 절차가 얼마나 번거로운지를 보여주기 위해서 적용하는 방법에 대해서 소개하겠습니다.</p>
<blockquote>
<p>번거롭지않다면, 해당 방법을 사용해도 괜찮습니다. 이는 개발자에 대한 개인 선호도에 따라서 어떤 방식으로 TZ를 설정하는지에는 차이가 있습니다.</p>
</blockquote>
<h4 id="timedatectl-을-통해서-현재-tz-정보를-확인">timedatectl 을 통해서 현재 TZ 정보를 확인</h4>
<pre><code>$ timedatectl
               Local time: Fri 2023-03-24 07:28:18 UTC
           Universal time: Fri 2023-03-24 07:28:18 UTC
                 RTC time: Fri 2023-03-24 07:28:18
                Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no
$</code></pre><h4 id="시간대-변경">시간대 변경</h4>
<pre><code>$ sudo timedatectl set-timezone Asia/Seoul</code></pre><p>명령어 자체는 아주 심플하고 간단해보입니다. 하지만, 다른 서버에서 해당 환경을 적용하려고하면 다시 해당 명령을 작성해야합니다.</p>
<p>사람에 따라서 해당 동작이 굉장히 귀찮을 수도, 또는 굉장히 편할 수도 있습니다. 만약 이러한 동작을 Application 레벨에서 제어하고 이를 통해서 해당 Application 에 별도의 TZ 가 설정될 수 있다면 해당 작업을 반복할 필요가 없어집니다.</p>
<h2 id="application-레벨에서의-tz-설정">Application 레벨에서의 TZ 설정</h2>
<pre><code class="language-java">
import java.time.Clock;
import java.time.ZoneId;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ClockConfig {

    @Bean
    public Clock clock() {
        return Clock.system(ZoneId.of(&quot;Asia/Seoul&quot;));
    }
}</code></pre>
<p>해당 코드를 통해서 Spring Application 내에서 Clock 에 대한 TZ 를 명시적으로 선언해줄 수 있습니다. 이렇게 되면, Application 자체에서 해당 TZ를 가지게 되어 별도의 시스템에서의 설정은 필요하지 않게 됩니다.</p>
<blockquote>
<p><code>Clock</code> 클래스는 Java 8부터 도입된 <code>java.time</code> 패키지의 일부로, 시간과 날짜를 측정하는 데 사용되는 추상 클래스입니다. </p>
<p><code>Clock</code>은 <strong>시스템 시계(System Clock)</strong>, <strong>고정 시계(Fixed Clock)</strong>, 또는 <strong>오프셋 시계(Offset Clock)</strong>와 같은 다양한 구현을 제공하며, 시간과 날짜 관련 작업을 보다 유연하고 명확하게 할 수 있도록 설계되었습니다.</p>
</blockquote>
<h2 id="결론">결론</h2>
<p>TZ에 대한 문제는 생각보다 민감한 문제입니다. 테스트에 대한 오류를 유발하거나, 데이터의 무결성을 잃게 되는 원인으로써 작용하기도 합니다.</p>
<p>이를 막기 위해서 앞서 소개한 2가지의 방법에 대해서 잘 선택하여 사용하면 보다 무결성이 잘 보장된 어플리케이션을 구축할 수 있을 것 입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[유동적인 데이터의 위험: LocalDateTime.now() 대신 사용할 수 있는 접근법]]></title>
            <link>https://velog.io/@knu-kang/%EC%9C%A0%EB%8F%99%EC%A0%81%EC%9D%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%9D%98-%EC%9C%84%ED%97%98-LocalDateTime.now-%EB%8C%80%EC%8B%A0-%EC%82%AC%EC%9A%A9%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%A0%91%EA%B7%BC%EB%B2%95</link>
            <guid>https://velog.io/@knu-kang/%EC%9C%A0%EB%8F%99%EC%A0%81%EC%9D%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%9D%98-%EC%9C%84%ED%97%98-LocalDateTime.now-%EB%8C%80%EC%8B%A0-%EC%82%AC%EC%9A%A9%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%A0%91%EA%B7%BC%EB%B2%95</guid>
            <pubDate>Sun, 08 Sep 2024 14:15:33 GMT</pubDate>
            <description><![CDATA[<p><code>Tasksprints</code> 프로젝트를 하던 중 백엔드에서 테스트에서 <code>LocalDateTime.now()</code>을 사용하는 것은 위험하다는 피드백을 보게 되었습니다. 이에 대해서 왜 위험한지, 그리고 어떻게 이를 구성해야하는지에 대해서 간단하게 소개해 드리겠습니다.</p>
<h2 id="localdatetimenow로-인한-유동적인-데이터를-통한-테스트에-대해서-고려해야할-부분">LocalDateTime.now()로 인한 유동적인 데이터를 통한 테스트에 대해서 고려해야할 부분</h2>
<p>테스트라는 것은 여러 유명 개발자 분들의 말에 의거하면 <strong>고정된 데이터를 통해 언제든지 동일한 결과</strong>를 만들어 내야합니다. 이는 <strong>테스트의 신뢰성</strong>과도 큰 연관이 있습니다.</p>
<p>이는 더 명확하게 말하게 되면, 유동적인 데이터는 테스트의 예상치 못한 결과를 나타낼 수도 있습니다. 전혀 이상한게 없는 테스트 코드에서도, <strong>어떨 때는 성공</strong>, <strong>어떨 때는 실패</strong>하는 경우가 생길 수도 있다는 것 입니다.</p>
<p>간단히 말하면, 시스템의 시간 동기화 문제나 다른 시스템 의존성으로도 전혀 다른 결과가 나올 수 있습니다.</p>
<h2 id="해결책1--localdatetimeof를-통한-정적인-날짜를-설정하여-테스트를-진행">해결책(1) : LocalDateTime.of()를 통한 정적인 날짜를 설정하여 테스트를 진행</h2>
<p>이는 생각보다 매우 단순하게 해결할 수 있습니다. now 를 통해서 가지고 오는 것이 아닌, of를 통해서 특정한 날짜에 대한 설정을 해줄 수 있습니다. 이러한 형태로 테스트를 하게되면, 기본 테스트의 의미에 맞게 <strong>언제든지 동일한 결과를 내는 테스트코드</strong>를 작성할 수 있게 됩니다.</p>
<h2 id="해결책2--clock을-이용한-시간-제어">해결책(2) : Clock을 이용한 시간 제어</h2>
<p>테스트 환경에서 시간을 고정하거나 제어하는 또 다른 방법은 Clock 클래스를 활용하는 것입니다. Clock을 사용하면 LocalDateTime.now() 대신 Clock으로 시간을 조작할 수 있습니다. 이를 통해 더 유연하고 테스트 가능한 코드를 작성할 수 있습니다.</p>
<pre><code class="language-java">Clock fixedClock = Clock.fixed(Instant.parse(&quot;2023-09-08T12:00:00Z&quot;), ZoneId.of(&quot;UTC&quot;));
LocalDateTime now = LocalDateTime.now(fixedClock);</code></pre>
<p>이 방식은 특히, 다양한 시간대를 테스트하거나 특정 시간 흐름을 시뮬레이션하는 경우에 유용합니다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript와 React에서의 파일 명명 규칙: kebab-case vs PascalCase]]></title>
            <link>https://velog.io/@knu-kang/TypeScript%EC%99%80-React%EC%97%90%EC%84%9C%EC%9D%98-%ED%8C%8C%EC%9D%BC-%EB%AA%85%EB%AA%85-%EA%B7%9C%EC%B9%99-kebab-case-vs-PascalCase</link>
            <guid>https://velog.io/@knu-kang/TypeScript%EC%99%80-React%EC%97%90%EC%84%9C%EC%9D%98-%ED%8C%8C%EC%9D%BC-%EB%AA%85%EB%AA%85-%EA%B7%9C%EC%B9%99-kebab-case-vs-PascalCase</guid>
            <pubDate>Sun, 08 Sep 2024 13:56:08 GMT</pubDate>
            <description><![CDATA[<p>TaskSprints 프로젝트 진행 중 프론트 파트에서 흥미로운 주제가 생겨서 이에 대해서 간단하게 정리하려고 합니다.</p>
<h2 id="파일-이름-명명은-어떤-형식을-따라야할까">파일 이름 명명은 어떤 형식을 따라야할까?</h2>
<p>파스칼 케이스 vs 케밥 케이스 중에 많은 고민이 있습니다.
ts 공식 문서 상으로는 케밥 케이스를 따르도록 추천이 되어있고,
react 쪽 공식 문서 상에 tsx는 파스칼 케이스를 따르도록 추천이 되어 있습니다.</p>
<p>이 말을 간단하게 말하자면, ts를 통해서 만드는 부분은 kebab-case 처럼 파이프 라인으로 구분하고</p>
<p>tsx 를 통해 구성하는 부분은 PascalCase 처럼 단어의 첫문자를 대문자로써 표기하는 것이 좋다는 의미입니다.</p>
<blockquote>
<p>파이프 라인으로 구분하는 케밥케이스가 타입스크립트에 <strong>친화적인 형태</strong>라고 이해하시면 좋을 것 같습니다. 컴포넌트를 구성해야하는 tsx 파일 특성상 필연적으로 PascalCase를 사용하는 것이라고 생각하면 좋을 것 같습니다.</p>
</blockquote>
<h2 id="파일-명명-규칙이-여러개라면-오히려-난해해보이지-않을까">파일 명명 규칙이 여러개라면 오히려 난해해보이지 않을까?</h2>
<p>오히려 다양한 컨벤션을 적용하면 <strong>난해함</strong>이라는 문제를 만날 수 있다고 생각했다. 하지만, 반대로 이를 통해 <strong><em>순수 ts를 통한 기능의 구현</em></strong>과 <strong><em>React라이브러이에 의존적인 Component</em></strong>에 대한 부분을 직관적으로 분리하여 확인할 수 있다고 생각이 되었습니다.</p>
<p>물론 해당 명명 규칙에 대한 명확한 약속이 있는 것은 아닙니다. 하지만, ts나 js 개발자라면 기본적인 &quot;<del>.ts&quot;, &quot;</del>.js&quot; 의 파일이 파스칼 케이스로 명명 되는 것을 본적은 드물 것이라고 생각이 됩니다. </p>
<p>React를 직접 초기 셋팅을 하게 되면, 해당 기본 셋팅에서 조차, 2가지 컨벤션을 모두 사용합니다. 이러한 구조는 잘못된 것이 아닌 어떤 부분에 의존적인 기능인지에 대한 확실한 구분점을 나타낼 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[uuid 의 필요성과 효율적으로 사용하는 방법]]></title>
            <link>https://velog.io/@knu-kang/uuid-%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1%EA%B3%BC-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@knu-kang/uuid-%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1%EA%B3%BC-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sat, 07 Sep 2024 11:13:34 GMT</pubDate>
            <description><![CDATA[<p><code>TaskSprints</code> 스터디를 진행하면서 백엔드 내부에서 <code>uuid</code>를 사용하는 것에서 의문이 들었습니다. 그래서 이에 대해서 찾아보고 공부한 내용에 대한 정리입니다.</p>
<h2 id="서론">서론</h2>
<p><code>UUID</code> 관련해서 제가 조금 알아본 걸 토대로 간단하게 소개해드리겠습니다.</p>
<p>저는 <code>UUID</code>가 성능을 감소 시키는 주된 원인이라고 생각하고, 많이 쓰는 이유를 이해하지 못했습니다. 또한, <code>auto_increment</code>에 비해서 극악의 확률이라 하더라도 중복이 될 수도 있다는 점에서 굳이 필요한가 라는 생각을 가졌습니다.</p>
<h2 id="보안성-때문에-사용하는건가">보안성 때문에 사용하는건가?</h2>
<p>보안성을 지키기 위해서 사용한다는 말을 엄청 많이 들었는데, 실제로 pk를 특정해서 유저의 정보에 접속할 수 있다는 <code>concept</code>부터 말이 안된다고 생각했습니다.</p>
<p>서버 내부적인 보안처리, 토큰을 통한 접근 제어 등을 통해서도 충분히 방지가 될 것으로 보이는데 이것이 정말 보안에 중대한 상황 인지에 대해서 의문을 느꼈습니다.</p>
<p>그래서 여러 정보를 찾아보고 주변 사람들에게 물어보면서 알게 된 UUID 를 사용하는 목적은 숫자에 대한 추론을 불가하게 만드는 것에 있습니다. 생각을 해보면 &quot;거래의 양, 유저의 수&quot;등이 <code>auto_increment</code>의 경우에는 충분히 특정이 가능합니다. 하지만 특정 상황에서는 이러한 정보를 가리고 싶은 상황이 분명 존재합니다. 이러한 상황에서 <code>UUID</code>를 사용하게 됩니다. 그렇기에 민감한 데이터에 대한 접근을 막아주는 것이 주된 의미가 아니라, 해당 숫자를 통해서 <code>어떠한 지표를 추론하는 것을 막기 위해 사용</code>된다고 생각하면 좋을 것 같습니다.</p>
<blockquote>
<p>보안 자체에 큰 메리트를 생각하는게 아닌, 추론에 대한 차단을 좀 더 메리트 있게 생각 할 수 있음. </p>
</blockquote>
<h2 id="성능에서-이점을-가지는-경우">성능에서 이점을 가지는 경우</h2>
<p><code>UUID</code>가 성능상으로 더 높은 효과를 만들어내는 경우는 일반 단일 서버에서는 딱히 없긴 합니다. 하지만 분산 환경에 있다고 하면 조금 다릅니다.</p>
<p>각 분산 서비스가 각각의 샤딩된 DB 를 가지게 된다면, 이는 하나의 DB로 묶어서 처리를 해야 합니다. 이 때, <code>auto_increment 로 생성 된 pk</code>는 필연적인 충돌이 일어날 수밖에 없습니다. 이 때, <code>UUID</code>를 사용하게 됩니다. <code>UUID</code>의 특정으로는 해당 값은 전역에서 <code>Unique</code>하다는 특징을 가지고 있습니다. 그렇기 때문에 샤딩된 DB가 합쳐질 때 충돌이 발생할 확률을 줄여주게 됩니다.</p>
<img src="https://github.com/user-attachments/assets/623631ca-fab4-4216-a658-501d437b9a70" width="300px"/>

<blockquote>
<p>샤딩 관련 자료</p>
</blockquote>
<h2 id="uuid-가-유니크하지-않을-수-있음에도-사용하는-이유">UUID 가 유니크하지 않을 수 있음에도 사용하는 이유</h2>
<p><code>UUID</code>에서 중복된 값이 생성될 확률은 매우 극악에 확률입니다. <code>트레이드 오프</code>를 했을 때, 해당 부분을 감안하면서 사용할만한 부분에서는 충분히 가치가 있습니다.
 추가로 <code>UUIDv4</code> 라이브러리를 사용한다면, 라이브러리에서 어플리케이션 레벨에서 충돌 방지 로직을 가지고 있어서, <code>기존의 uuid</code>을 통한 키 발행 시 극악의 조건으로 생기는 충돌을 막을 수 있습니다.</p>
<h2 id="결론">결론</h2>
<p>확실히 무분별한 <code>UUID</code> 사용은 성능저하에서 문제가 생길 수 있습니다. 하지만 추론을 불가하고 싶게 조절하거나, 분산환경에 대한 계획이 있는 서비스라면 충분히 사용할만한 가치가 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[node] common.js로 견고한 node 프로젝트 설계하기]]></title>
            <link>https://velog.io/@knu-kang/node.-common.js%EB%A1%9C-%EA%B2%AC%EA%B3%A0%ED%95%9C-node-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%84%A4%EA%B3%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@knu-kang/node.-common.js%EB%A1%9C-%EA%B2%AC%EA%B3%A0%ED%95%9C-node-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%84%A4%EA%B3%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 29 Jun 2024 12:06:05 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/knu-kang/post/c3449a4c-d487-48b6-b938-a67832c66c8d/image.png" alt=""></p>
<h1 id="서론">서론</h1>
<p>&quot;common.js로 견고한 node 프로젝트 설계하기&quot;라는 주제로 github 를 작성하였다.</p>
<p><a href="https://github.com/KNU-K/bulletproof-node-common-js-project-architecture">https://github.com/KNU-K/bulletproof-node-common-js-project-architecture</a></p>
<p>node진형에서 살아남기위해서, 필수적인 기술이 무엇이 있을까? 라는 생각에서 찾아보던중 santiago라는 개발자가 작성한 &quot;bulletproof architecture&quot; 라는 구조를 알게되었다.</p>
<p>이를 보면서 느낀 첫 감정은 이런식으로도 코딩을 할 수 있구나.. 라는 생각을 많이 느꼈던 것 같다.</p>
<p>단순히 common.js를 통한 개발은 타입이 없기에 안정성이 없고 그렇기 때문에 사람들이 좋게 보지않는 언어이다. 그렇기에 보통은 typescript로 넘어가는 추세이다. </p>
<p>하지만, 나는 common.js 만큼 초기에 시작하기 좋은 js 문법이 또 어디있을까 라는 생각이 들었다. 이를 통해서 <code>백엔드</code>를 공부하는 개발자가 단시간에 여러 시너지를 만들어낼 수 있다고 확신할 수 있다.</p>
<p>하지만 여기에 추가로 <code>좋은 습관</code>을 만들어내면 훨씬 더 추후에 좋은 개발자로써 성장할 수 있다.</p>
<p>이는 아래와 같다.</p>
<ul>
<li><p>js도 <code>DI</code>를 사용한다. 모듈화로 테스트가 용이하기 때문에, DI를 적용해서 프로젝트를 진행하면 좋다. </p>
</li>
<li><p><code>JS DOC</code>과 같은 형태를 활용하여 타입을 지속적으로 명시하는 습관을 만들어라. 이는 추후 강타입 언어로 넘어가기 위한 준비이라고 생각하면 좋다. </p>
<blockquote>
<p>단순히 js를 타입이 없기 때문에 복잡한 타입의 도피처로 생각하면 안된다는 말이다.</p>
</blockquote>
</li>
<li><p>e2e 테스트보다는 unit test를 통해서 해당 코드가 테스트에 용이한 코드인지를 항상 생각하며 리팩토링</p>
<blockquote>
<p>unit test를 통해서 green이 뜨는 경우를 만들고, 코드를 유지보수를 하게 되면 코드가 좀 더 빠르게 리팩토링 될 수 있게 도움을 줄 수 있다.</p>
</blockquote>
</li>
</ul>
<p>단순히 이정도의 습관만 가지고 개발을 한다면, 분명 좋은 node 개발자가 될 수 있다고 생각한다.</p>
<p>bulletproof 아키텍처에 대해서 알아보고 싶으면, 위의 링크를 타고 문서를 확인하길 바란다. 해당 포스트에서는 node를 선택하는 많은 사람들에게 이는 결코 잘못되지 않았다고 말해주고 싶어서 글을 작성하려고 한다.</p>
<h1 id="nodejs의-오해">node.js의 오해</h1>
<p>사람들은 node.js기반 프레임워크가 단순히 Single-Thread 기반이라서 spring과 같은 프레임워크에 비해 많이 부족하다고 말을 많이한다.</p>
<p>이는 잘못된 생각이다. 현 시대에서 Single-Thread를 극복할 수 있는 여러 설계 기법들이 등장했고, 이는 더 이상 큰 단점으로 작용하지 않는다. 대표적인 설계 기법이 <code>MSA 방식</code>이다. </p>
<p>오히려 어떤 일부 기능에서는 spring에 비해서 node.js가 초기 메모리를 작게 차지해서 성능 향상에 도움을 줄 수도 있다.</p>
<p>명확하게는 내부적으로 <code>Batch 작업</code>이 어렵고, <code>대규모/대용량</code>을 처리하는 것에 적합하지 않아서 그렇지 이외의 작업에는 node.js기반 프레임워크는 상당히 경량화된 좋은 프레임워크라고 할 수 있다.</p>
<p>국내에서 Spring 과 같은 프레임워크가 주력을 이루는 이유는 초기 국내 생태계를 Spring으로 구성하였고, 현재 국내 시니어 개발자들이 가장 많이 분포해있는 것도 한 몫하고 있다고 생각한다.</p>
<p>하지만, 실력과 차별성이 있는 개발자라면 어떠한 언어를 하던지 크게 상관을 없을 것이라고 생각한다.</p>
<h1 id="아키텍처를-중요하게-생각해야하는-이유">아키텍처를 중요하게 생각해야하는 이유</h1>
<p>나 또한 아키텍처보다는 서비스를 구현하는 것에 급급하던 한 <code>백엔드 개발자</code>였다. express로 프로젝트를 진행하게 되면, nest.js나 spring에 비해서 정해진 부분이 없다보니 사람들마다 개발을 진행하는 방식이 천차만별이다. </p>
<p>하지만, 해당 개발이 시니어 라인으로 올라갈 수록, 테스트를 통한 안정성, 의존성에 대한 문제를 해결하기 위해서 통일화된 구조를 사용하게 된다. 나는 해당 구조를 무조건적으로 익히기보다는 해당 문서를 읽음으로써, 왜 이런 구조를 사용해야하는지에 대한 고민을 했으면 좋겠다.</p>
<p>좋은 아키텍처는 좋은 코드를 만드는 것은 아니다. 내가 생각하는 좋은 아키텍처는 좋은 코드로 가기 위한 좋은 길을 만들어 준다고 생각한다. 그렇기 때문에 해당 부분을 중요하게 생각하고 개발을 하면 좋을 것 같다.</p>
<h1 id="해당-문서화-작업을-하면서-느낀점">해당 문서화 작업을 하면서 느낀점</h1>
<p>부족한게 많은 한 대학생 개발자이지만, 원래는 부족한게 무엇인지 어떻게 성장해야하는지에 대한 생각을 전혀 못했었다. 단순히 문제을 해결할 수 있는 개발자라는 막연한 자신감에 가득차 있었던 것같다.</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/4b051179-cf69-47a2-9b9f-6e0e67e8f8f2/image.png" alt=""></p>
<p>하지만, 시니어 개발자와 메일을 통해서 연락을 하면서 느낀 것은 단순히 개발을 하는 것이 아닌 어떻게 개발해야하고 왜 이렇게 개발되어야하는지를 알아야한다고 느꼈다. </p>
<p>또한, Santiago의 자료는 5년전의 자료였던터라, 조금 더 수정을 하면 좋은 포인트 들을 살리려고 노력을 했고, 나는 ts가 아닌 js 문법을 통해서 이를 사람들에게 제공하고자 많은 노력을 했다.</p>
<p>그러면서, Open Source 시장에서 다른 사람들이 쓴 코드들도 많이 참조를 했고, 그러다보니 우연치않은 계기로 기여를 할 수 있는 기회도 생겼었다. 그러다보니 나의 답이 100% 정답은 아니더라도, 적어도 확실한 오답에 대해서 알 수 있는 능력이 생겼다.</p>
<h1 id="마무리feat-자기-성찰">마무리(feat. 자기 성찰)</h1>
<p>나는 신입 개발자이다. 아는 것이 많다 느꼈던 만큼 모르는 것은 훨씬 더 많다고 느낄 수 있었던 기회였던 것 같다. 이 기회를 통해서 나는 <code>우매함의 봉우리</code>에서 떨어져 앞으로 나아갈 힘을 얻었다.</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/8dc4e08a-60b1-4277-9949-07009b464b90/image.png" alt=""></p>
<p>가장 성장해있다고 믿는 그 시점이 어쩌면, 자만으로 가득 차있는 시기일 수도 있다. 그렇기 때문에 항상 자신을 의심하고 그에 따른 근거를 찾으려는 노력을 하면 좋을 것 같다는 생각을 했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Backend] Nginx 와 로드밸런서 (2)]]></title>
            <link>https://velog.io/@knu-kang/Backend-Nginx-%EC%99%80-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C-2</link>
            <guid>https://velog.io/@knu-kang/Backend-Nginx-%EC%99%80-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C-2</guid>
            <pubDate>Wed, 19 Jun 2024 06:55:47 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기에-앞서">들어가기에 앞서</h2>
<p>Nginx와 로드밸런서라는 단어는 알고만 있었지만, 실제로 프로젝트에 도입한게 이번년도가 처음이였다. 그렇기에 여러가지 trade-off에 대해서 고민이 많았었다. 이 글을 읽고 백엔드 개발로써 이러한 걱정을 조금이나마 해소할 수 있도록 하는 것을 목표로 글을 쓰게 되었다. 내가 경험한 것을 바탕으로 쓰는 만큼 부족한 부분도 잘못된 부분도 있을 수 있다.</p>
<h2 id="msa-vs-monolithic">MSA VS Monolithic</h2>
<p>먼저 로드밸런서를 도입하기 전에 해당 개념을 이해하고 가면 정말 좋을 것 같다는 생각이든다.</p>
<h3 id="monolithic-architecture-방식">Monolithic-Architecture 방식</h3>
<p>모노리틱 아키텍처는 전통적인 웹 시스템 개발 방식 중 하나이다. 간단히 말해서 하나의 Application 내에 모든 로직이 들어가 있는 구조를 말한다. </p>
<h4 id="monolithic에서-로드밸런싱이-굳이-필요해">Monolithic에서 로드밸런싱이 굳이 필요해?</h4>
<p>내가 찾아본 글에서 위와 같은 말을 하는 사람들이 분명 있었다. 근데 나는 이 말 자체가 대단히 오만하다고 느꼈다. 기본적은 Monolithic의 장단점을 알고있다면 할 수 없는 소리이다. 하지만 그렇다고 무조건적으로 필요하다는 것이 아니다. </p>
<p>이게 무슨 소리인가? 생각 들 수도 있지만, 이 것은 서버의 규모에 따라서 평가가 가능하다. 사람들이 얼마나 들어오고, 동시 접속량이 얼마고, 현재 내 서버의 스펙, 내 프레임워크의 스펙을 보면서 기준을 정할 수 있다고 생각된다.</p>
<p>내가 생각할 때는 Monolithic 방식에서 로드밸런싱을 필요로 하는 경우는 다음과 같다.</p>
<ol>
<li>사용자의 트래픽으로 인해서 프레임워크 요청 시, 병목이 발생할 때</li>
<li>서버를 중간에 교체를 해야한다고 했을 때, 중단하지 않고 서버를 가용해야할 때</li>
<li>모니터링을 중앙에서 관제하고자 할 때 (트래픽 자체가 Nginx가 다 관리하고 받기 때문에)</li>
<li>의도적인 성능의 확장이 필요로 할 때 (Scale에 대한 목적부여)</li>
<li>장애에 대한 대응체계 구비가 필요할 때<h3 id="msa-architecture-방식">MSA-Architecture 방식</h3>
마이크로서비스 아키텍처는 Application 하나 당 단일 서비스를 제공한다. 이를 하나로 묶어주기 위해서는 필연적으로 로드밸런싱이 필요하게 된다. </li>
</ol>
<p>왜 필연적인지를 말해주겠다. MSA 는 각각의 독립적인 서비스이다. 이는 그저 같은 로컬 단위에서 서비스들이 분리 되어있을 수도 있으며, 완전히 지역적으로 분리되어있는 경우 또한 존재한다. 이를 하나의 서버처럼 가용하기 위해서는 중간에 proxy를 잡아서 로드밸런싱을 할 필요가 있다는 것이다. 하지만, MSA는 데이터의 관리가 굉장히 어려워진다.</p>
<p>해당 부분에서는 로드밸런싱보다는 데이터의 통합에 대해서 많이 고려를 해야한다.</p>
<h4 id="msa에서-데이터의-관리와-통합에-대한-고려사항">MSA에서 데이터의 관리와 통합에 대한 고려사항</h4>
<ul>
<li><p><strong>데이터 일관성</strong>: 각 마이크로서비스가 자체 데이터베이스를 관리하기 때문에, 서비스 간 데이터 일관성을 유지하는 것이 중요하다. 이를 위해 이벤트 기반 통합 패턴이나 CQRS 패턴을 사용할 수 있다.</p>
</li>
<li><p><strong>분산 트랜잭션</strong>: 서비스 간의 데이터 일관성을 유지하기 위한 분산 트랜잭션 처리가 필요할 수 있다. 이는 시스템의 복잡성을 증가시킬 수 있으므로 신중히 접근해야 한다.</p>
</li>
</ul>
<h2 id="마무리하며">마무리하며</h2>
<p>결론적으로 로드밸런서는 무조건적으로 좋은 것이 아니다. 해당 로드밸런서의 사용을 감당할 만큼 서버 스펙이 준비되어있어야 하며, 애초에 로드밸런싱을 사용할만큼의 트래픽 수준인가를 고려해야한다. 만약에 이를 공부삼아서 구현하고자 한다면 스트레스 테스트를 진행하면서 자신이 목표한 수준의 부하를 잘 버티는지를 생각해야한다.</p>
<p>다음 게시글은 간단한 시나리오를 설계하고 스트레스 테스트를 하는 내용에 대해서 올릴 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Backend] Nginx 와 로드밸런서 (1)]]></title>
            <link>https://velog.io/@knu-kang/Backend-Nginx-%EC%99%80-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C-1</link>
            <guid>https://velog.io/@knu-kang/Backend-Nginx-%EC%99%80-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C-1</guid>
            <pubDate>Fri, 17 May 2024 06:39:58 GMT</pubDate>
            <description><![CDATA[<h2 id="서론">서론</h2>
<p>해당 게시글은 본 필자가 프로젝트를 진행하면서 생겼던 의문점과 해당 의문점으로 부터 배운 내용에 대한 것을 정리한 것이다.</p>
<p>때는 G-StartUp 이라는 기업협력 프로젝트를 진행함에 있어서 있었던 일이다. 서버의 부하를 줄인다는 것에 관심이 많았고 Nginx를 통한 로드밸런서를 구현했다. </p>
<p>docker를 통해 동일한 node container를 replica로 두고 nginx 로 로드밸런스를 진행하였다. 그리고 이후 개발자 멘토님에게 이러한 방법이 어떤 것 같냐고 여쭤봤고, 예상 외로 혹평을 들었다.</p>
<blockquote>
<p><strong>왜 굳이 Nignx를 사용했나?</strong>
docker를 통해서 외부 포트를 동일한 포트로 잡으면 딱히 Nginx가 필요없지 않냐? 부하를 줄이려고하는데 Nginx를 사용함으로써 Nginx가 그 부하를 오롯이 받고 있지않나? 라고 의문을 제기하였다. </p>
</blockquote>
<blockquote>
<p><strong>왜 동일 Replica를 사용하였나?</strong>
monolithic한 개발이 아닌 MSA 형태로 기능 별로 다른 Node Container를 두고 Nginx 로 로드밸런서를 두게되면 더 좋을텐데 동일 Replica로 한 이유가 있나?</p>
</blockquote>
<p>위의 질문을 받았을 때 나는 꿀먹은 벙어리가 되었다. Nginx에 대한 깊이있는 이해도가 없었기에 아직 부족함이 많다는 것을 느끼게 되었다. 그래서 Nginx 에 대해서 어떻게 사용하면 좋을지에 대한 공부와 방법론에 대한 부분에 대한 간략한 정리를 하려고 생각하여 게시물을 쓰게 되었다.</p>
<h2 id="nginx-도대체-뭐하는-녀석인가">Nginx 도대체 뭐하는 녀석인가..?</h2>
<p>이를 이해하기 위해서는 먼저 Reverse Proxy에 대한 개념을 이해해야한다.</p>
<h3 id="reverse-proxy">Reverse Proxy</h3>
<p>역방향 프록시라고 불리는 이 녀석은, 웹서버 앞에 위치해서 클라이언트의 요청을 해당 웹서버로 전달해주는 역할을 한다.</p>
<p>주로 사용하는 목적은 보안, 성능, 안정성 향상을 위해 구현된다고 생각하면 편한다. 조금 더 세부적인 목적을 말하면 아래와 같다.</p>
<blockquote>
<ol>
<li>부하 분산</li>
<li>DDOS와 같은 공격으로 부터 보호 (중개하여)</li>
<li>캐싱 기능</li>
<li>SSL 암호화</li>
</ol>
</blockquote>
<p>보통은 Nginx를 사용하는 주된 이유는 1번과 2번과 같은 경우로 인해서 사용하지않을까하고 필자는 생각한다.</p>
<h3 id="nginx-를-통한-로드밸런서가-오히려-비효율적인-경우">Nginx 를 통한 로드밸런서가 오히려 비효율적인 경우</h3>
<ol>
<li>단순한 API 서비스 로직 지원하는 경우</li>
<li>단일 서버로 구성된 경우</li>
<li>단일 데이터베이스를 쓰는 경우</li>
</ol>
<p>1번과 같은 케이스는 홉을 하나 더 늘려서 Nginx를 만든는 것이 오히려 전달을 해야하는 과정이 하나 더 늘어나기 때문에 오히려 비효율적일 수도 있다. 내부적인 복잡한 로직으로 wait 되는 시간이 길어지는 것이 아닐 때는 부하분산에 대한 의미가 없어질 수도 있다.</p>
<p>2번과 같은 케이스는 규모에 따른 얘기이다. 작은 규모의 어플리케이션을 개발함에 있어서 Nginx를 사용하는 것은 큰 의미가 없다는 얘기이다. 오히려 자원을 더 많이 먹게 될 수도 있다. 확장에 대한 잠재성이 클 경우에는 얘기가 달라질 수도 있다.</p>
<p>3번과 같은 케이스의 경우에는 병목에 대한 얘기인데, 요청에 따른 병목지점을 넓히기 위해서 Nginx를 가용하고, Single-Thread 기반인 express에 병목 발생을 막기 위해 Replica를 구성했다. 그런데 단일 데이터베이스를 사용하게 되면 오히려 database 쪽이 병목지점이 될 수 있기에 이런 경우를 잘 고려해야한다.</p>
<blockquote>
<p>위와 같은 경우로 인해서 side-effect에 대한 부분을 충분히 이해하고 로드밸런서에 대한 부분을 고려해야한다.</p>
</blockquote>
<h3 id="reverseproxy-와-로드밸런서를-사용함에-있어서-고려해야하는-사항">reverseProxy 와 로드밸런서를 사용함에 있어서 고려해야하는 사항</h3>
<ol>
<li><p>요청에 따른 병목지점을 극복한다는 것이 의미있는 행동인지 고려. 만약에 이를 통해서 요청량이 증가했을 때 받을 수 있다고 한들 데이터베이스와 같은 요소에서 병목이 발생할 수 있다.</p>
</li>
<li><p>홉이 하나 추가 된다는 것을 고려해도 좋은 선택인가?</p>
</li>
<li><p>Nginx 의 목적성이 분명한가?
캐싱, 설정에 대한 부분을 Nginx에서 하고 서버에서는 단순 서비스로직만 구현한다는 등의 Nginx의 분명한 목적성을 가져야한다는 것임.</p>
</li>
<li><p>해당 서비스의 트래픽 상태가 어떠한가?</p>
</li>
<li><p>배포할 때 무중단 배포가 되어야한가?</p>
</li>
</ol>
<p>이러한 간단한 부분을 고려했을 때, trade-off에 대한 것을 생각하여 괜찮다면 가용하면 된다고 생각된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[생활 속의 통계] 자료 모으기]]></title>
            <link>https://velog.io/@knu-kang/%EC%83%9D%ED%99%9C-%EC%86%8D%EC%9D%98-%ED%86%B5%EA%B3%84-%EC%9E%90%EB%A3%8C-%EB%AA%A8%EC%9C%BC%EA%B8%B0</link>
            <guid>https://velog.io/@knu-kang/%EC%83%9D%ED%99%9C-%EC%86%8D%EC%9D%98-%ED%86%B5%EA%B3%84-%EC%9E%90%EB%A3%8C-%EB%AA%A8%EC%9C%BC%EA%B8%B0</guid>
            <pubDate>Fri, 05 Apr 2024 14:11:33 GMT</pubDate>
            <description><![CDATA[<h1 id="측정과-척도">측정과 척도</h1>
<h2 id="측정measurement">측정(measurement)</h2>
<p>연구의 대상이 되는 속성이나 개념에 대해 일정한 규칙에 따라 수치를 부여하는 작업</p>
<blockquote>
<p>연구 주제와 관련된 개념을 쉽게 다룰 수 있도록 <strong>정량화</strong>하는 것</p>
</blockquote>
<blockquote>
<p>측정에서 사용되는 도구를 <strong>측정 도구</strong>라고 한다. </p>
</blockquote>
<h2 id="척도scale">척도(scale)</h2>
<p>측정 결과를 값으로 나타내며, 측정 척도(measurement scale)에 따라 4 가지 형태로 구분된다.</p>
<blockquote>
<p>척도의 종류에 따라 해당 값이 가지는 의미와 적용하는 통계 기법이 다르다.</p>
</blockquote>
<h3 id="질적-변수">질적 변수</h3>
<h4 id="---명목척도nominal-scale">-- 명목척도(nominal scale)</h4>
<p>단순히 <strong>분류</strong> 만을 목적으로 사용</p>
<blockquote>
<p>대소관계, 연산 성립 X</p>
</blockquote>
<h4 id="---순서척도ordinal-scale">-- 순서척도(ordinal scale)</h4>
<p>측정대상 사이의 <strong>대소 관계</strong>를 표현하는 목적으로 사용</p>
<blockquote>
<p>Only 대소관계만 성립</p>
</blockquote>
<h3 id="양적-변수">양적 변수</h3>
<h3 id="---구간척도interval-scale">-- 구간척도(interval scale)</h3>
<p>기준이 되는 값을 여러개 더하여 나타내기 위해 사용</p>
<blockquote>
<p>더하기와 빼기, 대소관계 성립</p>
<p>&#39;0&#39;이 절대적인 0을 의미하지  않는다. 즉 상대적인 기준으로 부터 증가하는 것이기에 비율의 의미는 적용되지 않는다.</p>
</blockquote>
<h3 id="---비율척도ratio-scale">-- 비율척도(ratio scale)</h3>
<p>기준이 되는 값의 여러 배수로 측정된 값</p>
<blockquote>
<p>사칙연산 모두 가능함.</p>
<p>절대적인 0을 의미, 비율이 성립한다.</p>
</blockquote>
<h2 id="측정과-관련된-기본-용어">측정과 관련된 기본 용어</h2>
<ul>
<li>모집단</li>
<li>표본추출단위</li>
<li>표본</li>
<li>전수조사</li>
<li>표본조사</li>
<li>모수</li>
<li>통계량</li>
<li>확률변수</li>
</ul>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/00cb2c2c-3a74-4f4b-ae2a-10eab6a81e96/image.png" alt=""></p>
<h2 id="편의와-정도">편의와 정도</h2>
<ul>
<li><p>편의(bias)
참값에서 벗어나는 양</p>
<blockquote>
<p>편의가 없을 때, <strong>불변성</strong>을 만족한다고 말함.</p>
<ul>
<li>편의가 없음 - 중간에 있음</li>
<li>편의가 있음 - 중간에서 떨어짐</li>
</ul>
</blockquote>
</li>
<li><p>정도(precision)
통계량의 변동(variation)을 나타냄</p>
<blockquote>
<ul>
<li>정도가 높음 - 모여있음.</li>
<li>정도가 낮음 - 흩어져 있음.</li>
</ul>
</blockquote>
</li>
</ul>
<h2 id="관측연구와-실험연구">관측연구와 실험연구</h2>
<ul>
<li><p>관측연구 (observational study) - 귀납적
표본에 속하는 개체들을 조사하여 표본을 있는 그대로 관측</p>
<blockquote>
<p>가만히 놔두면서, 관찰하는 방식</p>
</blockquote>
</li>
<li><p>실험연구 (experimental study) - 연역적
표본에 대해서 여러 통계적인 실험을 설게해서 결과 측정</p>
<blockquote>
<p>정확한 효과를 위해, 진실을 말하지않음. (플라시보 효과)</p>
</blockquote>
</li>
</ul>
<h2 id="올바른-측정-도구에-대한-기준">올바른 측정 도구에 대한 기준</h2>
<ul>
<li><p>타당성(validity)</p>
</li>
<li><p>신뢰성(reliability)</p>
</li>
</ul>
<h1 id="표본조사">표본조사</h1>
<p>모집단을 대상으로 하는 조사는 너무 오래 걸리고, 사실상 실현가능성이 낮다. 그렇기에 표본을 모집단과 최대한 비슷하게 &quot;대표할 수 있도록&quot; 구성하여 조사를 진행하여 모집단의 모수를 추론한다.</p>
<blockquote>
<p>표본조사에서는 어떻게 모집단을 잘 대표할 수 있는 표본을 추출할 수 있는가가 중요하다.</p>
</blockquote>
<h2 id="왜-표본조사가-필요해">왜 표본조사가 필요해!?</h2>
<ol>
<li>조사 환경에 따라 시간 및 비용에 한계 존재</li>
<li>전수조사보다 오히려 정확할 수도 있음 (오차에 대한 부분이 전수조사에서는 많이 존재)</li>
<li>현실적으로 전수조사가 훨씬 어려움.</li>
</ol>
<h2 id="오차">오차</h2>
<p>측정을 통해 얻은 <strong>통계량</strong>과 <strong>실제 모수</strong>의 차이</p>
<blockquote>
<p>통계량을 얻는 것은 모수를 추론하기 위함이기 때문</p>
</blockquote>
<p>대표적으로 아래와 같다.</p>
<h3 id="표본오차sampling-error">표본오차(sampling error)</h3>
<ul>
<li><p>전수조사를 하고않고 표본조사를 함으로써 발생하는 오차</p>
</li>
<li><p>모집단의 원소에서 임의로 선택하기에 실제 모집단과 괴리감 발생</p>
</li>
<li><p>표본집단이 <strong>모집단</strong>을 잘 대표하지 못해서 발생</p>
<h3 id="비표본오차non-sampling-error">비표본오차(non-sampling error)</h3>
<p>표본오차 이외에 발생하는 오차</p>
</li>
<li><p><strong>무응답 오차(non-response error)</strong>
대답을 하지않아서 특성값을 측정하지 못한 경우 발생</p>
</li>
<li><p><strong>응답 오차(response error)</strong>
조사원에 대한 불충분한 감독이나 경험미숙 등으로 응답자가 거짓으로 응답하는 경우 발생</p>
</li>
<li><p><strong>처리 오차(processing error)</strong>
수집된 데이터를 전처리하는 과정 중 발생하는 오차( 수작업 등에서)</p>
</li>
</ul>
<h2 id="오차를-줄이는-법">오차를 줄이는 법</h2>
<ul>
<li><p>표본오차에 해당</p>
<ul>
<li>적절한 표본추출법 선택 </li>
<li>표본의 크기 조절 - 크기가 크면 조사 결과의 변동이 줄어듬<blockquote>
<p>너무 크면 모집단과 다를 바가 없음. 적절한 선을 지켜야함.</p>
</blockquote>
</li>
</ul>
</li>
<li><p>비표본오차에 해당</p>
<ul>
<li>조사원에 대한 관리 감독을 강화 ( 거짓된 말 하지말게)</li>
</ul>
</li>
</ul>
<h2 id="표본추출방법">표본추출방법</h2>
<h3 id="확률표본추출-probability-sampling-">확률표본추출( probability sampling )</h3>
<ul>
<li><p>단순임의 추출 - 제일 일반적인 방법 
랜덤으로 하기 때문에, 표본으로 선택될 확률이 모두 동일함</p>
<blockquote>
<p><strong>난수(Random Number)</strong> : 임의로 추출한 수 </p>
<p>대규모 표본조사에 사용하기 어려움. 그래서 소규모 조사나 예비 조사에서 많이 사용</p>
</blockquote>
</li>
<li><p>층화 추출
모집단에 동일한 집단이 여러개 포함된 경우 대표성을 확보하기 위해 고안됨</p>
<p>각 층에서 단순임의추출로 표본을 뽑음 </p>
<blockquote>
<p>strata(층)로 나눔 (층 내는 동일, 층 간은 이질)
모든 층에서 조사가 진행됨</p>
</blockquote>
</li>
<li><p>계통 추출
표본 추출 단위를 차례대로 나열한 후에 표본을 일정한 간격으로 추출
이 때, 간격은 임의로 선택함</p>
<blockquote>
<p>단순임의추출을 대신해서 많이 활용됨</p>
</blockquote>
</li>
<li><p>집락 추출
집락이라는 말에서부터 알 수 있듯이, 뭉쳐져 있는 구조이다. 작은 모집단을 집락 1개가 형성한다. 그래서 동질적인 구조가 아닌 이질적인 구조로 집락을  형성한 후에 조사단위를 모두 조사</p>
<p>필요에 따라서 2단계 집락 추출을 할 수도 있다.
이는 임의로 집락을 몇개 추출한 후에, 해당 집락에서 다시 일부를 추출하는 방법이다.</p>
<blockquote>
<p>2단계 집락 추출 방법을 적용하는 것이 효율적
일부 집락에서만 조사 진행</p>
</blockquote>
</li>
<li><p>층화집락 추출
층화 추출법 이후에 동질적인 구조로 층을 만들고, 해당 층 내에서 다른 특성으로 집락을 구성한다.</p>
<blockquote>
<p>실제 조사에서 흔히 사용됨.</p>
</blockquote>
</li>
</ul>
<h3 id="비확률표본추출-non-probability-sampling----정도precision을-설명할-수-없음">비확률표본추출( non-probability sampling ) - 정도(precision)을 설명할 수 없음</h3>
<ul>
<li><p>편의 추출
손 쉽게 접촉할 수 있는 대상들을 표본 추출</p>
<blockquote>
<p>기준없이 조사원이 주관적인 생각으로 편하게 선택하여 조사</p>
<p>그렇기 때문에, 일반화하기 어려움</p>
<h4 id="눈덩이-추출법">눈덩이 추출법</h4>
<p>편의 추출법에 일종이고, 다단계의 느낌..
초기 접촉대상자들이 유사한 대상자들을 소개하여 조사를 진행!</p>
</blockquote>
</li>
<li><p>유의 추출
모집단을 잘 대표할 수 있다고 전문가가 주관적으로 판단되는 대상을 표본으로 추출</p>
<blockquote>
<p>전문가에 따라 표본이 달라지기 때문에 객관적으로 대표성을 확보할 수 없음.</p>
</blockquote>
<blockquote>
<p>표본 크기가 매우 작을 때 효과적
이 때는 확률추출법을 사용하면 오히려 대표성 확보를 못할 수도 있음.
유의 추출을 하면 편의는 발생할지만, 모집단을 대표하는 표본 확보 가능함.</p>
</blockquote>
</li>
</ul>
<ul>
<li><p>할당 추출
층화 추출에서 랜덤화 과정이 빠진 형태이다.</p>
<p>층을 나눠서 해당 층 내에서 전문가의 주관으로 대상을 선정하는 방식이다.</p>
<blockquote>
<p>선택 편의(selection bias)가 발생
최소한의 그룺 구성을 위해 반영한 특성에 있어서 조사원에 의한 선택 편의를 제거한다.</p>
</blockquote>
<p>여론 조사에서 주로 쓰이며, 무응답에는 굳이 재조사를 필요로 하지않는다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Trouble Shooting] Spring Security를 통한 인증 구현 중 CustomUserDetailsService 의 loadUserByUsername의 username이 없는 오류]]></title>
            <link>https://velog.io/@knu-kang/Trouble-Shooting-Spring-Security%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%9D%B8%EC%A6%9D-%EA%B5%AC%ED%98%84-%EC%A4%91-CustomUserDetailsService-%EC%9D%98-loadUserByUsername%EC%9D%98-username%EC%9D%B4-%EC%97%86%EB%8A%94-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@knu-kang/Trouble-Shooting-Spring-Security%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%9D%B8%EC%A6%9D-%EA%B5%AC%ED%98%84-%EC%A4%91-CustomUserDetailsService-%EC%9D%98-loadUserByUsername%EC%9D%98-username%EC%9D%B4-%EC%97%86%EB%8A%94-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Tue, 05 Mar 2024 11:36:58 GMT</pubDate>
            <description><![CDATA[<h1 id="사건-개요">사건 개요</h1>
<p>REST API 를 구현하기 위해서 Spring Security를 통해 </p>
<pre><code class="language-java">@Bean
public SeucurityFilterChain filterChain(HttpSecurity http)throws Exception{
    return http.csrf(CsrfConfigurer::disable)
             .cors(CorsConfigurer::disable)
             .authorizeHttpRequests(
                 authManagerConfig-&gt;
                    authManagerConfig.requestMatchers(&quot;/*&quot;).permitAll()
             )
             .formLogin(formLoginConfig-&gt;formLoginConfig
                .usernameParameter(&quot;userid&quot;)
                .passwordParameter(&quot;userpw&quot;)
                .loginProcessingUrl(&quot;/login&quot;)
             ).build();

}</code></pre>
<p>위와 같이 구성을 하면 postman으로 json 형식으로 body에 담아보내면 될 줄 알았다. </p>
<p>하지만, 이 것은 내가 formLogin에 대한 이해도가 떨어져서 이렇게 생각 했던 것이다. 기본적으로 Spring Security는 form data 형식을 통한 로그인을 지원하고 있기 때문에 body를 통한 접근을 했을 때, CustomUserDetailsService내의 파라미터에 아무것도 들어있지 않는 것을 알 수 있다.</p>
<h1 id="그렇다면-어떻게-해결할까">그렇다면 어떻게 해결할까?</h1>
<p> 이를 해결하는 것은 body를 통한 데이터를 처리할 수 있도록 filter를 가용해야한다. 
 즉 초기에 데이터를 받는 필터 부분을 커스텀마이징해야한다는 것이다.</p>
<p> 트러블 슈팅이라고 하기에 조금 뭐하지만.. </p>
<p>이러한 부분때문에 난항을 겪는 사람이 많을 것 같아서 올리게 되었다.</p>
<pre><code class="language-java">public class JsonUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private static final String DEFAULT_LOGIN_REQUEST_URL = &quot;/login&quot;;
    private static final String HTTP_METHOD = &quot;POST&quot;;
    private static final String CONTENT_TYPE = &quot;application/json&quot;;
    private static final AntPathRequestMatcher DEFAULT_LOGIN_PATH_REQUEST_MATCHER =
            new AntPathRequestMatcher(DEFAULT_LOGIN_REQUEST_URL, HTTP_METHOD);

    private final ObjectMapper objectMapper;

    public JsonUsernamePasswordAuthenticationFilter(ObjectMapper objectMapper,
                                                    AuthenticationSuccessHandler authenticationSuccessHandler, 
                                                    AuthenticationFailureHandler authenticationFailureHandler 
    ) {

        super(DEFAULT_LOGIN_PATH_REQUEST_MATCHER);  

        this.objectMapper = objectMapper;
        setAuthenticationSuccessHandler(authenticationSuccessHandler);
        setAuthenticationFailureHandler(authenticationFailureHandler);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {

        if (request.getContentType() == null || !request.getContentType().equals(CONTENT_TYPE)) {
            throw new AuthenticationServiceException(&quot;Authentication Content-Type not supported: &quot; + request.getContentType());
        }

        LoginDto loginDto = objectMapper.readValue(StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8), LoginDto.class);

        String username = loginDto.getUsername();
        String password = loginDto.getPassword();

        if (username == null || password == null) {
            throw new AuthenticationServiceException(&quot;DATA IS MISS&quot;);
        }

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    @Data
    private static class LoginDto {
        String username;
        String password;
    }
}</code></pre>
<p>위와 같은 filter를 만들고</p>
<pre><code class="language-java"> http.addFilterBefore(jsonUsernamePasswordAuthenticationFilter(), LogoutFilter.class);</code></pre>
<p>이런식으로 filter를 추가해주면 된다. 자세한 것은 AbstractAuthenticationProcessingFilter 를 다루는 게시물을 참조하면 좋을 것 같다. LogoutFilter 앞에 넣는 이유는 해당 게시물을 보면 바로 이해할 수 있을 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[회고록] 3학년 겨울방학을 마치며..]]></title>
            <link>https://velog.io/@knu-kang/%ED%9A%8C%EA%B3%A0%EB%A1%9D-3%ED%95%99%EB%85%84-%EA%B2%A8%EC%9A%B8%EB%B0%A9%ED%95%99%EC%9D%84-%EB%A7%88%EC%B9%98%EB%A9%B0</link>
            <guid>https://velog.io/@knu-kang/%ED%9A%8C%EA%B3%A0%EB%A1%9D-3%ED%95%99%EB%85%84-%EA%B2%A8%EC%9A%B8%EB%B0%A9%ED%95%99%EC%9D%84-%EB%A7%88%EC%B9%98%EB%A9%B0</guid>
            <pubDate>Tue, 05 Mar 2024 11:30:16 GMT</pubDate>
            <description><![CDATA[<h1 id="부족했던-것">부족했던 것..</h1>
<p>방학동안 여러 프로젝트를 진행하려 했지만, 중간에 기본적인 CS 지식이나 트레이드 오프에 대한 관점이 뚜렷하지않아서 하나의 토이 프로젝트를 마치고 개인 공부를 불태웠다.</p>
<p>얻은게 많은 만큼 참 짧은 한 해였다. </p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/2168ba55-a52e-484e-9449-486fd3b31add/image.png" alt=""></p>
<p>처음에는 무작정 많이 만들어보자는 생각을 많이 했던 것 같다. 이 개념이 나한테 들어오는지도 들오지않는지도 신경쓰지않고 만드는 것에만 의의를 두면서 한해를 보냈던 것같다.</p>
<p>이 것이 문제가 될 줄은 꿈에도 몰랐다... 허허</p>
<p>사용해본 것이 많은 만큼 부족한 부분도 많았고 최적화나 부하에 대한 개념을 이해할 때, 실질적으로 적용할 수 있는 내용이 거의 없었다. 쉽게 말해서 <strong>기초</strong> 가 부족했었다는 것이다. 이러한 과거 회상을 기반으로 회고록을 작성해 보려고한다.</p>
<h1 id="터닝-포인트">터닝 포인트</h1>
<p>내 개발 관점이 바뀌게 된 포인트가 올해 존재한다고 당연 확신할 수 있다.</p>
<ul>
<li>어떻게 효율적이고, 더 많은 트래픽을 처리할 수 있을까?</li>
<li>정말 내가 만든 프로젝트가 상업화 될 만큼 안정성이 있는가?</li>
<li>얼마나 많은 사람을 수용할 수 있을까?</li>
</ul>
<p>단순히 이런 생각들이 해커톤을 한 이후에 많이 들었던 것 같다.</p>
<p>좋은 프로젝트를 만들려면 기반에 대해서 이해하고 그 것을 토대로 최적화할 수 있는 방법에 대해서 이해해야하고, 또한 내가 만든 프로젝트가 안정성 있는지 확인하기 위해서는 부하에 대해서 견딜 수 있는지 확인을 해야하는데 그러한 호기심에서부터 시작된 머릿 속 질문들을 해결하기 위해서 많은 인터넷 방황을 하며 정보를 찾았고 내가 가야할 방향을 잡을 수 있게 되었다.</p>
<h2 id="feat-스프링부트와의-첫만남">feat. 스프링부트와의 첫만남..</h2>
<p>터닝포인트 중에 하나는 스프링부트를 처음 사용하면서 배우는 과정에서도 느꼈던 것 같다. 하나의 프레임워크에 조합으로 프로그래밍을 하다보니 단순한 작업은 일련의 프레임워크 내에 존재하는 라이브러리의 집합이였고, 이는 내가 성장하는 것에 걸림돌이 되었다.</p>
<p>걸림돌을 극복하기 위해서 내부적으로 도대체 어떻게 소통하고 내가 어떻게 커스텀마이징 할 수 있는지 생각을 했고, 해당 생각이 좀 더 좋은 코드를 만드는데 한 몫했다고 생각한다.</p>
<blockquote>
<h4 id="작은-에피소드--ep-spring-security-참--어려운-녀석">작은 에피소드 ( ep. spring security 참 .. 어려운 녀석)</h4>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/f51ec76d-1518-4c08-9438-ec7f0d2aa3aa/image.png" alt=""></p>
</blockquote>
<p>내가 스프링을 통해서 프로젝트를 진행하려고했는데, Spring Security를 통해 세션 쿠키를 통한 인증 방식을 구현하려고했다. 이미 존재하는 필터는 application/json 형식의 데이터를 파싱하는 것을 지원하지않았기 때문에, 많은 난항이 있었다.</p>
<blockquote>
<p>어떻게 해야하지 생각하던 찰나, &quot;직접 만들면 이런문제가 없잖아?&quot;라는 생각으로 조금 더 깊게 공부를 했더니 필터라는 개념이 있었고, 해당 개념을 공부하면 내가 직접 원하는 방식으로 파싱하고 처리할 수 있겠다고 생각해서 공부를 했다.</p>
<p>현재는 잘 만들지는 못하지만, 어지간한 인증방식은 스프링으로 간단하게 만들 수 있다고 자신할 수 있다.</p>
</blockquote>
<h1 id="향후-계획">향후 계획</h1>
<p>여전히 부족한 응애 개발자이지만, 내가 가야 할 방향을 방학동안에 찾았으니, 앞으로 나아가기만하면 된다고 생각한다.</p>
<p>내가 지향하는 개발자는 그저 구현만 하는 개발자가 아닌 코드와 소통하는 개발자로써, 좀 더 유연하고 최적화된 코드를 짤 수 있도록 나를 성장 시키는 것이 가장 주된 목표이다.</p>
<p>한 부분을 깊게 파는 것은 좋을 수도 있지만, 좁은 시야를 필연적으로 가지게 된다는 단점이 존재한다.
그렇기 때문에, 적당한 깊이에서 많은 것을 경험해 보는 것이 좋다고 생각이 들었다.</p>
<p>내 말대로라면 나는 매우 깊은 지식을 요하기 보다는 많은 것을 경험하며 내가 사용하는 라이브러리들이 왜(WHY?), 어떻게(HOW?) 작동하는지에 대한 생각을 가지며 적어도 내가 하는 부분에 있어서는 오롯이 나의 스타일로 녹여낼 수 있도록 성장하려 한다.</p>
<h1 id="이번-년도-목표">이번 년도 목표</h1>
<ul>
<li>인턴 경험</li>
<li>대규모 프로젝트 경험</li>
<li>많은 기술보다는 내가 하는 기술에 대해서 오롯이 완벽하게 이해하기</li>
</ul>
<p>이번 년도 목표는 그렇게 크지않다.
누가보면 꿈이 작다고 할 수도 있다. 근데 나는 절대 허황된 꿈은 꾸지않는다. 당장 내가 필요로하고 할 수 있는 일을 생각했고, 위와 같은 결과가 나왔다.</p>
<p>해당 목표는 내가 원하는 개발자가 되기 위한 밑기둥을 세우는 작업이라 생각된다.</p>
<p>가는 길이 힘들지, 아니면 여유로울지 아무도 모르지만 최선을 다해서 성장하는 개발자가 되려고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DevOps] Docker 컨테이너의 생명주기]]></title>
            <link>https://velog.io/@knu-kang/DevOps-Docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%9D%98-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0</link>
            <guid>https://velog.io/@knu-kang/DevOps-Docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%9D%98-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0</guid>
            <pubDate>Tue, 05 Mar 2024 08:39:27 GMT</pubDate>
            <description><![CDATA[<h2 id="도커-컨테이너의-생명주기">도커 컨테이너의 생명주기</h2>
<p>도커 컨테이너 생명주기에 대해서 공부한 내용에 대해서 정리하려고 한다.
일반적으로 생명주기(Life Cycle)이라고 하면<strong>&quot;어떤 기술이나 제품이 발표된 후에 다른 기술이나 제품에 의해 대체되는 때까지의 기간&quot;</strong>이라고 한다. </p>
<p>이는 간단히 말해서 환경 변경, 버전에 따른 마이그레이션 등에 이유로 생명주기가 생기는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/37173381-c98a-48e0-910e-c4de18e4b6d1/image.png" alt=""></p>
<p>위와 같은 형태로 생명 주기가 구성되는데, 다음과 같은 상태로 구분된다.</p>
<ul>
<li><p>생성(Create):
생성 단계에서는 도커 이미지를 기반으로 컨테이너를 만든다, (이때, 도커 이미지에 정의된 파일 시스템과 설정을 포함하는 새로운 컨테이너가 생성됨)</p>
</li>
<li><p>시작(Start):
생성된 컨테이너를 시작하여 프로세스가 실행되도록 한다.</p>
</li>
<li><p>정지(Stop):
정지 단계에서는 실행 중인 컨테이너의 프로세스를 일시 중지시킨다. 컨테이너는 정지 상태로 전환되며, 필요에 따라 재시작할 수 있다.</p>
</li>
<li><p>삭제(Remove):
삭제 단계에서는 더 이상 필요하지 않은 컨테이너를 제거한다. 컨테이너가 삭제되면 해당 컨테이너의 파일 시스템과 설정 등이 제거되며, 해당 리소스가 해제된다.</p>
<blockquote>
<p>이렇게 삭제되면 모든 설정이 쉽게 지워지게 때문에, 사람들은 Docker container를 다룰 때 파일을 유지하는 것이 어렵다라고 한다.</p>
<h3 id="너네들이-잘못한거잖아">너네들이 잘못한거잖아!!</h3>
<p>삭제를 하기 이전에 필요한 내용이 존재한다면 로컬로 마운트(mount)한 이 후에 삭제를 해야한다. 그래야 다른 컨테이너에 해당 내용을 다시 추가하여 지속성을 보장할 수 있기 때문이다.</p>
</blockquote>
</li>
</ul>
<p>보통 자신이 엄청 고생해서 컨테이너를 만들었는데, 이를 지운다는 것이 이해가 되지않을 수도 있다.
하지만 컨테이너는 영구적으로 사용되는 것이 아닌, 변경이 필요할 때 과감하게 내부 내용을 로컬에 마운트 한 이후에 삭제하고 새로운 컨테이너를 생성하여 로컬에 있는 내용을 그대로 올려서 사용할 수 있다. 이를 통해 간단하게 컨테이너를 관리할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/c0047ac5-c18b-47c2-94bd-26dab73429fd/image.png" alt=""></p>
<p>mount와 transfer(copy)을 통해서 설정을 유지하면서 컨테이너를 옮길 수 있다. 이를 조금 더 전문적인 말로 지속성을 유지한다라고 말한다.</p>
<p>이정도가 간단한 docker container의 생명주기이다. 이 후에는 볼륨과 해당 생명주기 작업을 실습형태로 정리해볼 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DevOps] Docker 그냥 넘어가지말고 한번 보자!]]></title>
            <link>https://velog.io/@knu-kang/DevOps-Docker%EB%9E%80</link>
            <guid>https://velog.io/@knu-kang/DevOps-Docker%EB%9E%80</guid>
            <pubDate>Sat, 24 Feb 2024 14:22:38 GMT</pubDate>
            <description><![CDATA[<p>개발자들이 공부를 할 때 Docker라는 기술을 많이 접하게 된다.
나 또한 많이 접해봤고, 더 이상 애매한 상태로 둘 수 없어서 이에 대해 제대로 정리하려고 한다.</p>
<h2 id="docker란-무엇인가">Docker란 무엇인가?</h2>
<p>Docker는 리눅스의 응용 프로그램들을 프로세스 격리 기술을 사용하여 컨테이너로 실행하고 관리하는 오픈 소스 프로젝트이다.</p>
<p>이 말이 확실하게 와닿지 않을 수도 있다. 어떤 배경으로 인해서 Docker가 만들어졌는지 이해해야 더 확실히 Docker에 대해서 알 수 있다.</p>
<h3 id="docker가-생기게-된-배경-feat-hypervisor">Docker가 생기게 된 배경 (feat. Hypervisor)</h3>
<h4 id="초반">초반</h4>
<p>초기에는 하나의 서버에 하나의 어플리케이션을 구동할 수 있었다.</p>
<p>하드웨어의 급격한 발전으로, 서버는 소프트웨어가 요구하는 사양에 비해 훨씬 더 높은 수준의 사양을 가질 수 있게 되었다.</p>
<p>이게 무슨 문제일까? 생각할 수도 있다. </p>
<p>쉽게 생각해서 <strong>낭비</strong>가 가장 큰 원인이었다. 하드웨어가 발전하면서 가격이 상승했는데, 당시의 서버는 하나의 어플리케이션만을 구동할 수 있었기 때문에 여러 어플리케이션을 구동하기 위해서는 어플리케이션 당 서버 하나를 가동해야 했다. 그 결과, 남아있는 리소스가 존재하는 서버에서도 낭비되는 <strong>리소스</strong>가 발생했다.</p>
<p>요약하면 아래와 같은 문제가 있었다 :</p>
<ul>
<li>서버 가격이 비싸짐.</li>
<li>하나의 어플리케이션만을 허용하였기 때문에, 낭비되는 리소스가 발생</li>
</ul>
<p>이러한 기술적인 문제를 어떻게 해결할 수 있을지 고안했고, 하나의 서버 내에서 여러 어플리케이션을 돌리기 위한 방법에 대한 연구가 지속되었다.</p>
<h4 id="중반-가상화--hypervisor-등장">중반 (가상화 &amp; Hypervisor 등장)</h4>
<blockquote>
<h4 id="hypervisor가-뭘까">Hypervisor가 뭘까?</h4>
<p>하이퍼바이저는 여러 개의 가상 머신(Virtual Machine, VM)을 생성하고 구동하는 소프트웨어이다.</p>
</blockquote>
<p>초반에 서버 내에서 하나의 어플리케이션을 구동하는 것에서 문제가 발생했다. 이를 해결하기 위해서 Hypervisor라는 기술이 등장했다.
<img src="https://velog.velcdn.com/images/knu-kang/post/0fda37ae-9ce4-4277-b48d-23ec13dd4061/image.png" alt="Hypervisor 아키텍처"></p>
<p>이 사진은 Hypervisor의 아키텍처이다. 하드웨어 위에 Hypervisor를 올려서 N개의 가상 머신을 생성하고, 해당 머신 내에 Guest OS를 생성하여 어플리케이션을 구동함으로써 여러 어플리케이션을 구동할 수 있게 했다.</p>
<p>이 것이 초기 가상화 기술이다.</p>
<p>여러 어플리케이션을 동시에 실행할 수 있는 혁신적인 기술이었지만, 가상 머신에 비해 무겁다는 단점이 있었다. 그 외에도 아래와 같은 대표적인 단점들이 존재한다:</p>
<ul>
<li>리소스 관리가 어려움.</li>
<li>가상 머신들의 동일한 수준의 성능 보장이 어려움.</li>
<li>가상 머신들이 하드웨어의 리소스를 나눠 가지기 때문에 성능 저하에 문제 발생.</li>
</ul>
<p>이를 해결하기 위해서는 Hypervisor에 비해 가볍고 빠른 형태의 가상화 방법이 필요했다.</p>
<h4 id="현재">현재</h4>
<p>그래서 나온 기술이 Docker이다.</p>
<p>Docker는 리눅스 기반으로 가상 머신보다 가볍고 빠르게 어플리케이션을 패키징하고 실행할 수 있어 리소스의 효율성과 빠른 배포가 가능해졌다.</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/afc272fd-f9bb-4d0b-9c9f-12dc5ebcb9f3/image.jpg" alt=""></p>
<p>왼쪽이 Docker에 대한 아키텍처이다.</p>
<p>미리 말해야하는 것은 원래 Docker는 Linux 기반의 서버에서 사용하는 용도로 만들어졌다. 그래서 Host Os(메인 운영체제) 의 Linux 기반 커널을 공유할 수 있었다. 그리고 각 컨테이너화 되어있는 App들은 커널이 제외된 Linux 껍데기로 구성되어있고, 내부적으로 필요에 따라서 NginX나 Node와 같은 소프트웨어가 포함된 형태의 껍데기를 받아올 수 있다.</p>
<blockquote>
<p>껍데기는 Linux 를 구성하는 최소한의 요소라고 생각하면 된다.</p>
</blockquote>
<blockquote>
<h3 id="그럼-windows에서는-docker가-안되나요">그럼 Windows에서는 Docker가 안되나요?</h3>
<p>Windows에서도 Docker를 지원한다. 그런데 이렇게 말하면  Host OS가 커널을 공유해준다고 했는데, Host OS가 윈도우가 되면 공유하지 못하는거 아닌가? 라고 생각할 수도 있다. 하지만 Windows 버전을 설치하면 Linux 커널과 함께 설치가 되거나, Linux 가 가상화된 형태로 커널을 공유할 수 있도록 해준다.</p>
</blockquote>
<h4 id="docker-가상화-vs-hypervisor-가상화">Docker 가상화 vs Hypervisor 가상화</h4>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/dba3483d-caff-4d31-9b88-b98f8eb5f1b7/image.jpg" alt=""></p>
<p>아키텍처로 확인할 수 있지만, Docker에서는 GuestOs가 없다. 또한 Hypervisor처럼 각 가상머신이 커널을 가질 필요없이 Host OS의 커널을 공유한다. 그렇기 때문에 상대적으로 Hypervisor에 비해서 가벼울 수밖에 없다.</p>
<p>또한 Container내에 Image를 주입하는 형태로 어플리케이션들을 구동하기 때문에, 원하는 버전의 Image를 가져와서 쉽게 환경을 만들 수 있다. 또한 다른 버전으로의 마이그레이션을 원할 때 Container를 날려버리고 새로운 Container에 기존의 데이터를 올리는 형태로 진행 할 수 있다. 즉 Hypervisor에 비해서 Docker는 짧은 라이프 사이클을 가진다는 장점 또한 존재한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring Security] AbstractAuthenticationProcessingFilter 격파하기!]]></title>
            <link>https://velog.io/@knu-kang/Spring-Security-AbstractAuthenticationProcessingFilter-%EA%B2%A9%ED%8C%8C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@knu-kang/Spring-Security-AbstractAuthenticationProcessingFilter-%EA%B2%A9%ED%8C%8C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 23 Feb 2024 08:02:06 GMT</pubDate>
            <description><![CDATA[<p>오늘은 AbstractAuthenticationProcessingFilter에 대해서 격파한 이후 내가 이해한 내용을 정리하려한다.</p>
<h1 id="abstractauthenticationprocessingfilter">AbstractAuthenticationProcessingFilter</h1>
<h2 id="🧐그게-뭔데">🧐그게 뭔데..?</h2>
<p>UsernamePasswordAuthenticationFilter 이라고 있다. 이게 뭔지 궁금할 것 같은데 아래 사진을 보면 이전에 설명에서 나왔던 사진인 것을 알 수 있다.
<img src="https://velog.velcdn.com/images/knu-kang/post/05967984-7be8-4c27-9b46-b2ae5249caec/image.png" alt=""></p>
<p>해당 SecurityFilterChain에 3번째에 존재하는 필터이다. </p>
<p>AbstractAuthenticationProcessingFilter에 대해서 알려준다고 했는데 다른 필터가 나와서 아마 의아할 것이다. </p>
<p>하지만 해당 UsernamePasswordAuthenticationFilter의 내부를 보면 왜 해당 Filter가 등장했는지 알 수 있을 것이다.</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/5b8575aa-9270-4531-8d18-72222fa341e1/image.png" alt=""></p>
<p>위와 같이 UsernamePasswordAuthenticationFilter는 AbstractAuthenticationProcessingFilter를 상속받고 있는 것을 알 수 있다 .</p>
<h3 id="그럼-usernamepasswordauthenticationfilter는-뭐하는-필터일까">그럼 UsernamePasswordAuthenticationFilter는 뭐하는 필터일까?</h3>
<p>UsernamePasswordAuthenticationFilter는 Spring Security에서 기본적으로 지원하는 필터이며, Username과 Password를 form-data 형식으로 받아서 인증로직을 처리하는 필터이다.</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/d25d841c-c526-46d7-983e-60bdc91046d1/image.png" alt=""></p>
<p>위 코드는UsernamePasswordAuthenticationFilter내에서 오버라이드된 함수이다.</p>
<p>함수명으로 추측하면 인증을 시도하는 메소드라는 것을 알 수있다. 해당 함수에서는 위에서 말한 것처럼 username과 pasword를 obtain~()함수를 통해서 받아온다.
<img src="https://velog.velcdn.com/images/knu-kang/post/349451c1-0136-4cbd-8fb0-f85012753d9a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/563f753b-9235-40e5-aa97-500d928ab449/image.png" alt=""></p>
<p>request.getParameter(form-data Key)를 통해 form-data형태의 데이터를 return 해주는 것을 알 수 있다. 해당 데이터를 UsernamePasswordAuthenticationToken으로 감싸서 AuthenticationManger내의 authenticate에 해당 Token을 넘겨주며 인증을 진행하게 된다.</p>
<p>여기까지가 UsernamePasswordAuthenticaitonFilter의 로직에 대한 간단한 설명이다.</p>
<h3 id="그럼-본론으로-넘어와서-abstractauthenticationprocessingfilter가-뭐야">그럼 본론으로 넘어와서 AbstractAuthenticationProcessingFilter가 뭐야?</h3>
<p>이름에서부터 볼 수 있듯이 추상클래스이다. 공통적인 작업은 해당 추상클래스 내부에서 동작될 수 있다. 예를 들어서 아래와 같이 AuthenticationManager를 get/set하는 작업이 있다. 그리고 추상 메소드로 인증을 시도하는 것에 대한 것을 주고 커스텀할 수 있는 기회를 제공한다.</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/023a3538-966f-4f2f-a9bb-d4410c274176/image.png" alt=""></p>
<blockquote>
<h3 id="authenticationmanager란">AuthenticationManager란?</h3>
<p>이전에 그림으로 한번 설명한 적이 있는데 간단하게 다시 설명해보겠다.
<img src="https://velog.velcdn.com/images/knu-kang/post/7ff9fe2a-1250-4102-af5a-7143767390c2/image.png" alt=""></p>
<p>이 처럼 인터페이스이고 ProviderManger가 해당 인터페이스의 구현체가 된다.
ProviderManger에서는 여러 인증에 대한 것을 대응할 수 있도록 AuthenticationProvider를 생성자로 가져간다.</p>
<p>주로 DaoAuthenticationProvider를 사용해서 데이터베이스 내에 Username과 Password가 존재하는지에 대한 인증 작업을 진행하게 된다.</p>
</blockquote>
<h2 id="🧱-어디에-활용-될까">🧱 어디에 활용 될까?</h2>
<p>주로 spring security는 form-data 기반의 로그인을 기본으로 지원하고 있다. 하지만 개발자가 api 형태에서 <code>context/json</code>형태로 데이터를 받아서 로그인을 처리하고 싶을 수도 있다. 그럴 때, AbstractAuthenticationProcessingFilter를 상속받아 커스텀마이징해서 만들 수있다.</p>
<h3 id="만들긴했는데-어떻게-써">만들긴했는데 어떻게 써?</h3>
<p>이런 질문이 아마 머릿 속에서 떠오를 수도 있다. </p>
<pre><code class="language-java">@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -&gt; authz
                .anyRequest().authenticated()
            )
            .formLogin(formLogin -&gt; formLogin
                        .loginPage(&quot;/login&quot;));
        return http.build();
    }

}</code></pre>
<p>해당 코드는 Spring Security 공식 문서에 있던 코드이다. 해당 Bean을 등록함으로써, filterChain동작을 제어할 수 있다.</p>
<p>위에 요소들은 이후 소개할 예정이고, 지금 중요하게 봐야할 포인트는 .formLogin(~) 쪽이다. 
람다식 형태로 지원되고 있고, 기본적인 loginPage를 <code>/login</code>이라는 것을 알려주고 있다. 해당 조건이 실행 되게 되면 Spring Security에서 기본적으로 지원하는 UsernamePasswordAuthenticationFilter가 진행되게된다.</p>
<p>그런데, 우리는 커스텀한 Filter를 사용하고 싶다. 그렇기 때문에 위의 코드를 아래와 같이 변경해준다.</p>
<pre><code class="language-java">@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -&gt; authz
                .anyRequest().authenticated()
            )
            .formLogin(AbstractHttpConfigurer::disable)
            .addFilterAfter(커스텀필터의 인스턴스, LogoutFilter.class);
        return http.build();
    }

}</code></pre>
<p>간단하게 2가지 요소를 살펴보면 된다.</p>
<ol>
<li><p><code>formLogin(AbstractHttpConfigurer::disable)</code>
해당 코드를 통해서 formLogin을 비활성화했고, 이에 따라 UsernamePasswordAuthenticationFilter가 실행되지 않게 된다.</p>
</li>
<li><p><code>addFilterAfter(커스텀필터의 인스턴스, LogoutFilter.class);</code>
해당 코드를 통해서 LogoutFilter 이후에 커스텀 필터를 배치하게 된다.</p>
</li>
</ol>
<blockquote>
<h4 id="근데-왜-logoutfilter-이후에-배치하는-걸까">근데 왜 LogoutFilter 이후에 배치하는 걸까?</h4>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/05967984-7be8-4c27-9b46-b2ae5249caec/image.png" alt="">
위에서 가져온 사진을 또 가져왔다. 해당 왼쪽에 있는 SeucrityFilterChain을 보면 LogoutFilter 이후에 UsernamePasswordAuthenticationFilter가 존재한다. </p>
<p>우리는 formLogin을 disable해주면서 해당 filter를 비활성화 했고 그 자리에 커스텀필터를 배치하기 위해서 해당 코드를 작성한 것이다.</p>
</blockquote>
<p>위의 작업을 진행했으면 추가적으로 @Bean으로 등록해야하는 요소들이 정말 많다.</p>
<pre><code class="language-java">@Bean //등록
public AuthenticationManager authenticationManager() throws Exception {
        DaoAuthenticationProvider provider = @Bean으로 등록된 Provider
        return new ProviderManager(provider);
    }

//json 형태로 username, password 를 받아서 인증 진행 예시
@Bean //등록
public JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordLoginFilter() throws Exception {
        JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordLoginFilter = new JsonUsernamePasswordAuthenticationFilter(objectMapper);
        jsonUsernamePasswordLoginFilter.setAuthenticationManager(authenticationManager());
        return jsonUsernamePasswordLoginFilter;
}</code></pre>
<p>위와 같이 셋팅했다면 jsonUsernamePasswordLoginFilter()를 <code>.addFilterAfter</code>함수의 첫번째 인스턴스로 호출해주면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring Data] 영속성 컨텍스트 (Persistence Context)]]></title>
            <link>https://velog.io/@knu-kang/Spring-Data-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-Persistence-Context</link>
            <guid>https://velog.io/@knu-kang/Spring-Data-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-Persistence-Context</guid>
            <pubDate>Wed, 21 Feb 2024 09:57:26 GMT</pubDate>
            <description><![CDATA[<h2 id="영속성-컨텍스트-그게-뭔데">영속성 컨텍스트.. 그게 뭔데..</h2>
<p>영속성 컨텍스트는 간단하게 말해서 어플리케이션과 데이터베이스의 중간다리 역할을 하는 논리적 가상 공간이라고 생각하면 된다.</p>
<blockquote>
<p>간단히 말하면, 영속성 컨텍스트는 어플리케이션과 데이터베이스 간의 데이터 교환을 관리하며, 데이터를 영구적으로 저장하는(persisting) 역할을 한다.</p>
</blockquote>
<h2 id="왜-굳이-영속성-컨텍스트를-사용하는거야">왜 굳이 영속성 컨텍스트를 사용하는거야?</h2>
<p>영속성 컨텍스트를 왜 사용하고, 배워야하는지 궁금해서 그에 대해서 알아 봤다.</p>
<ol>
<li>캐싱처리</li>
<li>트랜잭션 관리</li>
<li>지연로딩</li>
</ol>
<p>위와 같은 형태때문에 주로 영속성 컨텍스트를 가용하게 된다.</p>
<p>영속성 컨텍스트는 개념에서부터 &quot;응용프로그램과 데이터베이스 사이의 가상의 저장 공간&quot;이다. </p>
<p>즉 <code>flush</code> 처리가 되지않는다면, 데이터베이스 내에 반영하기 않고, 메모리 내에서만 처리하게 할 수 있다는 것이다. </p>
<blockquote>
<h4 id="플러시flush">플러시(Flush):</h4>
<p>영속성 컨텍스트에서의 변경사항을 데이터베이스에 동기화하는 것이 플러시이다. </p>
<p>트랜잭션을 커밋할 때 자동으로 플러시가 발생하거나, 명시적으로 flush() 메서드를 호출할 수 있다. </p>
</blockquote>
<p>플러시는 데이터베이스에 변경사항을 반영하는 중요한 단계이며, 트랜잭션이 롤백될 때도 발생할 수 있다.</p>
<p>이를 통해서 하나의 트랜잭션 단위로 관리를 할 수 있게 되며, 데이터베이스에 지속적인 참조를 하는 것이 아닌 메모리 내에 저장된 정보를 통한 빠른 접근을 가능하게 한다.</p>
<p>이 외에도 많은 이유에서 영속성 컨텍스트를 사용하지만, 보통 이러한 이유로 인해서 사용하여 보다 안정적인 데이터 관리를 할 수 있도록 유도한다.</p>
<h2 id="엔티티-생명주기">엔티티 생명주기</h2>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/3308ef97-762b-4c12-881c-479f6f863bf7/image.png" alt=""></p>
<p>엔티티 생명주기에서는 아래와 같이 총 4가지의 상태를 가진다.</p>
<ul>
<li><p>비영속(new/transient)
이는 말그대로 영속성 컨텍스트에 포함되지않은 상태이다.</p>
</li>
<li><p>영속(managed)
이는 영속성 컨텍스트에 저장된 상태이다.</p>
</li>
<li><p>준영속(detached)
이는 영속성 컨텍스트에 저장되어있다가 분리된 상태이다.</p>
</li>
<li><p>삭제(remove)
이는 영속성 컨텍스트에서 삭제된 상태이다.</p>
</li>
</ul>
<h3 id="엔티티의-4가지-상태">엔티티의 4가지 상태</h3>
<pre><code class="language-java">@Entity
class User{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Setter
    private String userName;

    @Setter
    private String nickName;

    //constructor(shouldn&#39;t include PK in constructor)
}</code></pre>
<p>위와 같은 Entity가 있다고 가정하고 설명을 하겠다.</p>
<h4 id="비영속">비영속</h4>
<pre><code class="language-java">User user = new User(&quot;a&quot;,&quot;b&quot;); </code></pre>
<p>이는 영속성 컨텍스트에 포함되지않은 상태이다.</p>
<h4 id="영속">영속</h4>
<pre><code class="language-java">User user = new User(&quot;a&quot;,&quot;b&quot;); 
em.persist(user);</code></pre>
<p>이는 영속성 컨텍스트에 저장되어, 영속성 컨텍스트 내에서 관리가 되는 상태이다.</p>
<h4 id="준영속">준영속</h4>
<pre><code class="language-java">User user = new User(&quot;a&quot;,&quot;b&quot;); 
em.detach(user);
em.clear();
em.close();</code></pre>
<p>이는 영속성 컨텍스트에서 분리된 상태이다.</p>
<h4 id="삭제">삭제</h4>
<pre><code class="language-java">User user = new User(&quot;a&quot;,&quot;b&quot;); 
em.remove(user);</code></pre>
<p>이는 영속성 컨텍스트에서 삭제되는 상태이다.</p>
<h4 id="em-이-뭐야">em 이 뭐야?</h4>
<p>위에서 쓰여진 em은 entityManger로써, 영속성 컨텍스트에 따른 상태를 관리할 수 있게 도와준다.</p>
<pre><code class="language-java">@PersistenceContext
private EntityManager em;</code></pre>
<p>위의 형태로 선언해 놓으면 간단하게 사용할 수 있다.</p>
<h2 id="영속성-컨텍스트의-기능">영속성 컨텍스트의 기능</h2>
<h3 id="1차-캐시">1차 캐시</h3>
<p>영속성 컨텍스트는 데이터베이스로부터 읽어온 엔티티들을 메모리에 캐싱한다. 
이를 통해 동일한 엔티티에 대한 반복된 조회를 최소화하고 성능을 향상시킨다.</p>
<blockquote>
<p>간단히 말하면 n번의 데이터 요청이 있다 가정하게 된다면,</p>
<p>데이터베이스에서의 요청은 1번, 이후에는 해당 요청 데이터를 1차 캐시 내에서 저장되며 요청에 대한 처리를 한다는 의미이다.</p>
</blockquote>
<blockquote>
<p>이는 영속성 컨텍스트내에서만 존재한다.</p>
<p>reference locality의 개념이 적용된 예시이다.</p>
</blockquote>
<h3 id="지연로딩">지연로딩</h3>
<p>연관된 엔티티를 실제로 사용할 때까지 데이터베이스에서 로딩을 지연시키는 전략이다. 필요한 시점에 연관된 엔티티를 로딩하여 성능을 최적화할 수 있다.</p>
<p>이를 확인하기 위해서는 쿼리 로그를 확인하면 편하다.</p>
<blockquote>
<p>한번만에 데이터를 가지고 오는지, 아니면 필요 시에 쿼리를 통해 가지고 오는지에 따라서 <strong>지연로딩</strong>과 <strong>즉시로딩</strong>을 구분한다.</p>
</blockquote>
<p>지연 로딩은 성능에 부하를 줄일 수는 있지만, 오히려 데이터 접근 시 N+1 오류가 발생해서 부하가 더 발생 할 수도 있다. </p>
<p>이이는 trade-off 가 되는 경우라서 필요에 따라 좋은 선택을 할 수 있어야한다.</p>
<h3 id="쓰기지연">쓰기지연</h3>
<p>엔티티의 상태 변경이나 쿼리를 데이터베이스에 즉시 반영하지 않고, 트랜잭션이 커밋(flush)될 때 변경 사항을 일괄적으로 적용하는 전략이다. 꼭 필요한 부분만을 반영하면서, 성능을 향상시킬 수 있다.</p>
<h3 id="트랜잭션-범위-캐시">트랜잭션 범위 캐시</h3>
<p>트랜잭션 내에서만 존재하는 캐시를 의미한다.</p>
<p>동일한 트랜잭션 내에서 여러 번 조회되는 엔티티를 캐시하여 중복 조회를 방지한다.</p>
<blockquote>
<p>1차 캐시와 유사하지만, 이는 트랜잭션 내에서만 유효하며 트랜잭션이 종료되면 해당 캐시도 비워진다.</p>
</blockquote>
<h3 id="동일성-보장">동일성 보장</h3>
<p>같은 식별자를 가진 엔티티는 동일한 인스턴스로 관리됨을 의미한다. </p>
<p>나는 이를 알아보면서, 싱글톤 개념이 떠올랐다. </p>
<p>객체 1개당 인스턴스 한개만을 가질 수 있고 이를 통해서 자원을 아낄 수 있다는 맥락이 동일성 보장과 매우 유사하다고 생각했다.</p>
<blockquote>
<p>간단하게 말하자면 싱글톤과 비슷하게 한 번 로딩된 엔티티는 두 번째 로딩 시에는 캐시된 인스턴스를 반환하여 동일성(동일 인스턴스)을 보장합니다.</p>
</blockquote>
<h3 id="dirty-checking">dirty checking</h3>
<p>엔티티의 상태 변경 여부를 감지하는 메커니즘이다. </p>
<p>트랜잭션이 커밋될 때 엔티티의 변경 사항이 자동으로 데이터베이스에 반영된다.</p>
<p>개념이 이런식으로 나와있는데, 원래는 자동으로 반영이 안되었나? 하는 생각이 들 것이다.</p>
<p>이는 그런 차원의 문제가 아니라, commit 작업을 거치게 되면 별다른 query 필요없이 변경 사항이 DB내에 적용된다는 의미로 받아들이면된다. 이는 ORM적인 특성을 띄고있는 기능이다.</p>
<h2 id="commit-과-flush의-차이점">commit 과 flush의 차이점</h2>
<p>이 부분을 개발자들이 가장 어렴풋이 넘어가는 부분이 아닐까 생각이 든다. ( 개인적으로 나는 그냥 어렴풋이 알고 넘어갔다.. 하하)</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/759d0d6b-2cb5-4e95-87ed-cf889ff1b943/image.jpg" alt=""></p>
<p>이는 <strong>해맥의 IT기술정보</strong>의 트랜잭션 상태도 이미지를 인용하였다.</p>
<p>위에서 보는 것처럼 commit은 하나의 트랜잭션이 끝나서 트랜잭션이 데이터베이스에 영구적으로 반영되는 작업이다.</p>
<p>flush는 commit과 다르게 트랜잭션이 끝났기 때문이 아닌, 데이터의 최신성 때문에 최신의 데이터를 통해 변경하고자 flush를 통해 업데이트를 진행하게 된다. 또한 데이터 오류 발생시에 rollback이 가능하다. flush 자체가 트랜잭션을 종료하지않았기 때문에, flush를 포함한 트랜잭션 내에서 오류 발생 시에 flush작업을 취소할 수 있다는 것이다.</p>
<blockquote>
<p>commit 은 rollback이 불가하지만, flush는 rollback이 가능하다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 옵저버 패턴 / 프록시 패턴]]></title>
            <link>https://velog.io/@knu-kang/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4-%ED%94%84%EB%A1%9D%EC%8B%9C-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@knu-kang/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4-%ED%94%84%EB%A1%9D%EC%8B%9C-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Wed, 21 Feb 2024 07:32:35 GMT</pubDate>
            <description><![CDATA[<h1 id="옵저버-패턴">옵저버 패턴</h1>
<h2 id="옵저버-패턴이-뭘까">옵저버 패턴이 뭘까?</h2>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/4778ad2d-a8b7-44a3-9b9d-d8a81b014160/image.png" alt="">
옵저버 패턴은 주체가 어떤 객체의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메소드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 패턴이다.</p>
<blockquote>
<h4 id="주체란">주체란</h4>
<p>상태 변화를 지켜보는 관찰자이다.</p>
<h4 id="옵저버란">옵저버란?</h4>
<p>이 객체의 상태 변화에 따라 전달되는 메서드 등을 기반으로 추가 변화 사항이 생기는 객체들을 의미한다.</p>
</blockquote>
<h2 id="옵저버-패턴이-어디서-사용돼">옵저버 패턴이 어디서 사용돼?</h2>
<p>옵저버 패턴은 위의 설명을 들으면 알겠지만, SNS 서비스에서 주로 사용된다.</p>
<p>인스타나 트위터와 같이 팔로우한 사람이 새로운 정보를 갱신하면, 그 정보를 옵저버들이 바로 알 수 있다.</p>
<p>그리고 옵저버 패턴은 주로 이벤트 기반 시스템에서 사용하기 떄문에 MVC 패턴에도 사용된다.</p>
<blockquote>
</blockquote>
<ul>
<li>주체 -  model</li>
<li>옵저버 - view<blockquote>
<p>즉, model에 변경이 생겨 메소드를 통해서 옵저버인 &quot;View&quot;에 알려주고 이를 기반으로 &quot;Controller&quot;가 동작한다.</p>
</blockquote>
</li>
</ul>
<h2 id="어떻게-구현할-수-있어">어떻게 구현할 수 있어?</h2>
<p>이는 자바와 자바스크립트에서의 구현이 조금 차이가 있기 때문에 두 형식 모두 보여주겠다.</p>
<h3 id="java-code">[JAVA CODE]</h3>
<pre><code class="language-java">interface Subject{
    public void register(Observer obj);
    public void unregister(Observer obj);
    public void notifyObservers();
    public Object getUpdate(Observer obj);
}
interface Observer{
    public void update();
}
class Topic implements Subject{
   private List&lt;Observer&gt; observers;
   private String message;

    public Topic() {
        this.observers = new ArrayList&lt;&gt;();
        this.message = &quot;&quot;;
    }

    @Override
    public void register(Observer obj) {
        if(!observers.contains(obj)) observers.add(obj);
    }

    @Override
    public void unregister(Observer obj) {
        observers.remove(obj);
    }

    @Override
    public void notifyObservers() {
        this.observers.forEach(Observer::update);
    }

    @Override
    public Object getUpdate(Observer obj) {
        return this.message;
    }

    public void postMessage(String msg){

        System.out.println(&quot;Message sent to Topic : &quot;+msg);
        this.message = msg;
        notifyObservers();
    }
}
class TopicSubscriber implements  Observer{
    private String name;
    private Subject topic;

    public TopicSubscriber(String name, Subject topic) {
        this.name = name;
        this.topic = topic;
    }

    @Override
    public void update() {
        String msg = (String) topic.getUpdate(this);
        System.out.println(name+&quot;:: got message &gt;&gt; &quot; + msg);
    }
}
public class Main {
    public static void main(String[] args) {
        Topic topic = new Topic();
        Observer a = new TopicSubscriber(&quot;a&quot;, topic);
        Observer b = new TopicSubscriber(&quot;b&quot;, topic);
        Observer c = new TopicSubscriber(&quot;c&quot;, topic);
        topic.register(a);
        topic.register(b);
        topic.register(c);

        topic.postMessage(&quot;amumu is op champion&quot;);
    }
}</code></pre>
<p>일단 동일 topic에 대해 a,b,c 가 구독하고 있는 상황이다.</p>
<ul>
<li><p>해당 topic은 observer를 리스트 형태로 가지고 있다.</p>
</li>
<li><p>topic 을 구독하는 a,b,c 옵저버를 만든다.</p>
</li>
<li><p>register 메소드를 통해서 a,b,c 유저를 가입 시킨다.</p>
<ul>
<li>topic 에 observer 리스트에 포함된다.</li>
</ul>
</li>
<li><p>모두가 구독한 topic에서 메세지를 보내게 된다.</p>
<ul>
<li>notifyObservers를 통해서 모든 옵저버에게 메세지를 보내게된다.<blockquote>
<pre><code class="language-java">  this.observers.forEach(Observer::update);</code></pre>
</blockquote>
<pre><code>라는 부분이 조금 이해가 안될 수도 있어서 가지고 왔다.
* this.observers 는 Observer 리스트이다. 
  * new TopicSubsciber()라는 Observer의 구현체가 내부에 담긴다.(DI) - 주입
* forEach로 해당 list 요소마다 접근한다.
  * Observer::update는 해당 요소의 update 라는 구현체를 실행하라는 의미이다.

</code></pre></li>
</ul>
</li>
</ul>
<p>이와 같이 자바에서 옵저버 패턴을 구현할 수 있다.</p>
<blockquote>
<h3 id="상속과-구현의-차이">상속과 구현의 차이</h3>
<p>이전에 어디에는 상속, 어디에는 구현을 사용해서 이 둘이 비슷한게 아닐까? 라는 생각을 할 수도 있다.</p>
<p>그래서 명확한 차이를 제시하기 위해서 이를 정리하고자 한다.</p>
<ul>
<li><strong><em>상속</em></strong>
  자식 클래스가 부모 클래스의 메서드 등을 상속받아 사용하며 자식 클래스에서 추가 및 확장을 할 수 있다.</li>
</ul>
</blockquote>
<ul>
<li><strong><em>구현</em></strong>
  구현은 부모 인터페이스를 자식 클래스에서 재정의하여 구현하는 것을 말하며, 상속과는 달리 오버라이드가 필수적이다.<blockquote>
<p>즉, 상속은 일반 클래스고 abstract를 기반으로 구현하며, 구현은 인터페이스를 기반으로 구현한다.</p>
</blockquote>
</li>
</ul>
<h3 id="-java-script-code-">[ JAVA SCRIPT CODE ]</h3>
<p>먼저 자바스크립트에서 옵저버 패턴을 적용시키기 위해서는 프록시 객체라는 개념을 알고 있어야한다.
프록시 객체없이도 만들 수 있지만, 이를 통해서 옵저버와 같은 동작을 만들어 낼 수 있다.그래서 해당 패턴은 옵저버 패턴이 아닌 프록시 패턴으로 불린다.</p>
<h4 id="프록시-패턴은-그럼-다른거야">프록시 패턴은 그럼 다른거야?</h4>
<p>엄밀이 말하면 같은 패턴이라고는 할 수 없다.  비슷하다 정도로 생각하면 좋을거 같다. </p>
<p>프록시 패턴은 주체 접근하기 전에 그 접근에 대한 흐름을 가로채 해당 접근을 필터링 하거나 수정하는 계층적 구조를 가진 디자인 패턴이다.</p>
<blockquote>
<p>설명에서도 보이지만, 큰 차이는 없다.</p>
</blockquote>
<h4 id="프록시-객체-그게-뭔데">프록시 객체? 그게 뭔데?</h4>
<p>프록시 객체는 어떤 대상(target)의 기본적인 동작의 작업을 가로챌 수 있는 객체이다.
아래와 같은 매개변수를 가질 수 있다.</p>
<ul>
<li>target : 프록시할 대상</li>
<li>handler : target 동작을 가로채고 어떠한 동작을 할 것인지</li>
</ul>
<pre><code class="language-js">const handler = {
  get: function (target, name) {
    //get으로 name을 요청 했냐?
    return name === &quot;name&quot; ? `${target.a} ${target.b}` : target[name];
  },
};

const test = new Proxy({ a: &quot;I&quot;, b: &quot;am a student&quot;, x: &quot;asd&quot; }, handler);
console.log(test.x); //x는 name이 아니므로 target[name] 원래 하던 행동
console.log(test.name); //name는 name이 맞으므로, a와 b를 합쳐서 return</code></pre>
<p>이 형태가 프록시의 기본이다. 기존 로직을 가로채서 원하는 로직으로 강제 수행하는 것이 프록시 객체이다.</p>
<h4 id="이제-그럼-본격적으로-만들어보자">이제 그럼 본격적으로 만들어보자!</h4>
<pre><code class="language-js">const createReactiveObj = (target, callback)=&gt;{
    const proxy = new Proxy(target, {
      set(obj, prop, value){
          if(value !== obj[prop]){
            const prev = obj[prop];
            obj[prop] = value;
            callback(`${prop} is changed to [${prev}] from [${value}]  `);
        }
      }
    })
       return proxy;
}

const a = {
    &quot;kim&quot;, &quot;solo&quot;
}
const b = createReactiveObj(a, console.log)
b.kim = &quot;solo&quot;;
b.kim = &quot;couple&quot;;
// kim is changed to solo from couple</code></pre>
<p>이런식으로 동작된다.</p>
<ul>
<li>obj 는 a를 가지고 있다. </li>
<li>prop은 참조하되는 이름을 말한다. 위 코드에서는 kim </li>
<li>value 는 내가 넣을 데이터를 말한다. (solo, couple</li>
</ul>
<p>원래 동작은 </p>
<pre><code class="language-js">obj[prop] = value</code></pre>
<p>  와 같이 동작 된다고 생각하면 된다.</p>
<ul>
<li>value 와 obj[prop]이 같으면 변동사항이 없다는 것이고, 만약 변동이 있다면</li>
<li>이전 데이터 저장하고 현재 데이터로 변경해서</li>
<li>Callback을 통해 console.log 로 가게된다.</li>
</ul>
<p>일들 통해 변경되는 작업에 대한 감시를 할 수 있다.</p>
<h2 id="프록시-서버가-프록시-패턴을-이용한거라고">프록시 서버가 프록시 패턴을 이용한거라고!?</h2>
<h3 id="프록시-서버">프록시 서버</h3>
<p>프록시 서버는 서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크에 <strong>간접적으로</strong> 접근할 수 있게 해주는 컴퓨터 시스템이나 응용 프로그램을 말한다.</p>
<blockquote>
<p>Nginx가 그 대표적인 예이다.</p>
</blockquote>
<h4 id="프록시-서버가-중요햬">프록시 서버가 중요햬?</h4>
<p>중요하냐고 묻는다면, 당연하다. </p>
<p>Node.js 창시자 라이언 달의 말을 인용해보겠다.</p>
<blockquote>
<p>You just may be hacked when some yet-unkown butter overflow is discovered. Not that couldn&#39;t happend behind nginx, but somehow having a proxy in front makes me happy&quot;</p>
</blockquote>
<p>이는 간단히 말해서 Node.js 의 버퍼 오버플로우 취약점을 극복하기 위해서 nginx를 프록시 서버 앞에 두고 Node.js를 뒤에 두는 것이 좋다고 말한 것이다.</p>
<p>이렇게 만 말하면 조금 &quot;엥?&quot;한 반응을 보일 수도 있다.</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/e8ef92e5-8fe5-46c4-9fee-0d73a60747dd/image.png" alt=""></p>
<p>이 그림을 한번 보면 이해가 될 것이다. NginX라는 것은 없으면 절대 안되는 요소는 아니다.
하지만, 사람들이 계속 많아지는 현재의 시점에서는 없으면 디메리트되는 요소인 것은 확실하다.</p>
<p>Node.js 특성상 단일 쓰레드이기 때문에 서버의 부하를 받을 수 밖에 없다. 이를 여러개의 서버를 두고 NiginX Proxy를 통해서 제일 바쁘지않은 노드 서버와 맵핑시켜준다. </p>
<p>이 것은 기능적 요소이고, 프록시 측면으로 바라보면 NginX는 </p>
<ul>
<li>실제 포트를 숨겨준다.</li>
<li>서버 앞단에서의 로깅을 처리할 수 있다.</li>
</ul>
<p>등이 있다.</p>
<p>또한 저건 서버에서의 프록시이다. 반대로 클라이언트에서도 프록시를 만들기도 한다. </p>
<p>프론트와 백엔드가 서로 통신할 때 주로 CORS 에러를 마주치는데 이를 극복 할 수도 있다.</p>
<blockquote>
<h3 id="orgin">orgin</h3>
<p>오리진이라는 것은 프로토콜과 호스트이름 포트만을 사용한 조합이다.</p>
<p><a href="http://127.0.0.1:8080/api">http://127.0.0.1:8080/api</a> 에서 origin 은 <a href="http://127.0.0.1:8080%EC%9D%B4">http://127.0.0.1:8080이</a> 된다.</p>
</blockquote>
<p>서버와 클라이언트에서 테스트를 하는데 클라이언트는 3000, 서버는 8080번의 포트를 가지면 오리진내에 포트가 다르기 때문에 CORS 에러가 발생한다. 이 때 클라이언트에서 프록시를 둠으로써 서버에 요청되는 오리진을 변경하여 8080번 포트로 맵핑 될 수 있게 한다.</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/24871a00-d2b8-4605-b73a-d4d5d1a1261a/image.png" alt=""></p>
<p>이와 같이 CORS 문제를 극복 할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring Security] 스프링 시큐리티 아키텍처]]></title>
            <link>https://velog.io/@knu-kang/Spring-Security-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98</link>
            <guid>https://velog.io/@knu-kang/Spring-Security-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98</guid>
            <pubDate>Mon, 12 Feb 2024 22:52:59 GMT</pubDate>
            <description><![CDATA[<h1 id="스프링-시큐리티-아키텍처">스프링 시큐리티 아키텍처</h1>
<h2 id="필터-체인-filter-chain">필터 체인 (Filter Chain)</h2>
<p>스프링 시큐리티의 핵심 디자인 패턴이다.
<img src="https://velog.velcdn.com/images/knu-kang/post/a98210b1-7a71-492f-8ae7-d38efd2c446c/image.png" alt=""></p>
<p>위와 같은 구조를 띄고있다. </p>
<p>처음에 나오는 Filter는 FilterProxy 전에 진행되는 Filter이다.
마지막에 나오는 Filter는 FilterProxy 후에 진행되는 Filter이다.</p>
<p>최종적으로 DelegatingFilterProxy 는 스프링 애플리케이션 컨텍스트내에  기본적으로 제공하는 필터들을 필요에 따라 대리로 진행 시켜준다.</p>
<p>아래는 DelegatingFilterProxy가 대리로 진행시킬 수 있는 필터 목록이다.</p>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/7e31afb3-9b4e-4d8a-99dc-32115b19625b/image.png" alt="">
이 중에서 인증과 인가에 대한 아키텍처에 대해서 간단하게 소개하겠다.</p>
<h2 id="스프링-시큐리티의-인증-아키텍처">스프링 시큐리티의 인증 아키텍처</h2>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/78ee4ebd-e90d-4a15-b928-6a693e9f59cc/image.png" alt=""></p>
<p>위는 실제 아키텍처는 내가 좀 더 보기 편하게 그린 그림이다.</p>
<p>간단하게 설명하자면</p>
<ol>
<li><p>클라이언트로 부터 요청이 들어온다.</p>
</li>
<li><p>AuthenticationFilter가 해당 요청을 받아 UsernamePasswordAuthenticationToken 을 통해 Authentication 인스턴스를 생성한다.</p>
</li>
<li><p>AuthenticationFilter가 해당 Authentication 인스턴스를을 ProviderManager로 보낸다.</p>
<blockquote>
<p>이 때, ProviderManger는 AuthenticationManger의 구현체이다.</p>
</blockquote>
</li>
<li><p>이후 ProviderManger는 AuthenticationProvider 를 선택해서 해당 Provider에 authenticate함수를 호출한다.</p>
<blockquote>
<p>이 때, ProviderManger는 AuthenticationManger의 구현체이다.
또한, authenticate함수 호출을 할 때 인자로 Authentication 인스턴스가 들어간다.</p>
</blockquote>
<blockquote>
<p>AuthenticationProvider는 Dao,Jwt와 같은 어떤 인증 제공자를 사용할 것인지에 따라 다르게 작용한다.
즉, AuthenticationProvider는 인터페이스이다.</p>
</blockquote>
</li>
<li><p>해당 authenticate 함수를 호출하게되면 CustomUserDetailsService에서 사용자를 비교하여, 유효한 사용자이면 해당 사용자를 CustomUserDetails에 담아 return 한다.</p>
<blockquote>
<p>이 때, CustomUserDetailsService 는 UserDetailsService의 구현체이다.
GrantedAuthority 또한 이 때 설정할 수 있다.
@override loadUserByUserName 내에서 설정가능하다.</p>
</blockquote>
<blockquote>
<p>이 때, CustomUserDetails는 UserDetails의 구현체이다. </p>
</blockquote>
</li>
<li><p>이를 통해서 모든 과정이 완료되면, AuthenticationFilter에 Authentication 인스턴스가 다시 반환된다.</p>
</li>
<li><p>해당 Authentication 인스턴스는 SecurityContext에 담기게 된다.</p>
</li>
</ol>
<p>여기까지가 간단하게 정리한 스프링 시큐리티의 내부 동작이다.</p>
<h2 id="스프링-시큐리티-인가-아키텍처">스프링 시큐리티 인가 아키텍처</h2>
<p>여기는 그림보다는 AccessDecisionManager만 잘 이해하면 될 것 같아서 AccessDecisionManager에 대해서 설명하겠다.
<img src="https://velog.velcdn.com/images/knu-kang/post/e3ba9f36-c6df-4b60-b2e8-25eb250a34c0/image.png" alt=""></p>
<h3 id="accessdecisionmanager">AccessDecisionManager</h3>
<p>해당 인터페이스의 구현체는 주어진 사용자의 권한과 요청된 리소스에 대한 권한 설정(ConfigAttribute)을 기반으로 실제로 접근을 허용할지 또는 거부할지를 결정한다.</p>
<p>주로 여러 AccessDecisionVoter를 통해 투표를 진행하고, 투표 결과를 종합하여 최종 결정을 내린다.</p>
<p>AccessDecisionVoter를 구현하여 커스텀 권한 평가 로직을 만들 수 있다.</p>
<h3 id="accessdecisionvoter의-함수원형">AccessDecisionVoter의 함수원형</h3>
<ul>
<li>boolean supports(ConfigAttribute attribute)
해당 voter가 지정된 ConfigAttribute를 지원하는지 여부를 반환한다. 일반적으로는 voter의 동작에 필요한 권한 속성을 지원하는지 확인하는 용도로 사용된다.</li>
<li>boolean supports(Class&lt;?&gt; clazz)
해당 voter가 지정된 클래스를 지원하는지 여부를 반환한다. 일반적으로는 AccessDecisionManager가 사용하는 객체의 클래스를 지원하는지 확인하는 용도로 사용된다.</li>
</ul>
<ul>
<li><p>int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes)
현재 사용자의 인증 정보, 접근 대상 객체, 그리고 권한 속성들을 기반으로 실제 접근 결정을 내린다. </p>
<blockquote>
<p>반환값
ACCESS_GRANTED (0): 접근을 허용.
ACCESS_DENIED (1): 접근을 거부.
ACCESS_ABSTAIN (-1): 해당 Voter가 접근 결정에 참여하지 않음을 나타냄.</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 전략 패턴]]></title>
            <link>https://velog.io/@knu-kang/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@knu-kang/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 12 Feb 2024 10:18:20 GMT</pubDate>
            <description><![CDATA[<h1 id="전략-패턴">전략 패턴</h1>
<h2 id="전략-패턴이-뭐야">전략 패턴이 뭐야?</h2>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/884a9231-6ce8-42c0-aab1-953b242312d2/image.png" alt=""></p>
<p>해당 패턴은 전략 패턴 외에도 정책 패턴이라는 말을 가지고 있다. </p>
<p>이 패턴은 객체의 행위를 바꾸고 싶은 경우에 직접 수정하지않고 전략이라고 부르는 캡슐화한 알고리즘을 컨텍스트안에서 바꿔주면서 상호교체가 가능하게 만드는 패턴이다.</p>
<blockquote>
<h3 id="컨텍스트">컨텍스트</h3>
<p>컨텍스트라는 말을 너무 많이 듣는데, 마음으로는 알지만 머리로는 이해하지 못해서 따로 정리한다.</p>
<p>컨텍스트는 상황, 맥락, 문맥을 의미하며 개발자가 어떠한 작업을 완료하는데 필요한 모든 관련 정보를 말한다.</p>
</blockquote>
<h2 id="그게-보통-어디서-쓰이는데">그게 보통 어디서 쓰이는데?</h2>
<p>Express 기반으로 웹을 만들어본 사람들은 이 패턴을 보기만 했어도 어디서 본 적이 있는 형태라고 생각했을 것이다.
<img src="https://velog.velcdn.com/images/knu-kang/post/4d27ac56-ee0b-4d0f-9a73-485854215cbd/image.png" alt="">
passport가 그 대표적인 예시다.</p>
<p>각 인증에 맞게 전략을 넣어서 사용하게 된다.</p>
<pre><code class="language-js">const passport = require(&quot;passport&quot;)
const LocalStrategy = require(&#39;passport-local&#39;).Strategy

passport.use(new LocalStrategy(...))</code></pre>
<p>위와 같은 형태로 전략을 주입해서 로직을 수행 하는 것을 볼 수 있다.</p>
<h2 id="기본적인-틀을-만들어보자">기본적인 틀을 만들어보자!</h2>
<pre><code class="language-java">interface AuthenticationStrategy{
    public void authentication();
}
class NaverAuthenticationStrategy implements AuthenticationStrategy{
    @Override
    public void authentication() {
        System.out.println(&quot;Naver auth&quot;);
    }
}

class KakaoAuthenticationStrategy implements  AuthenticationStrategy{
    @Override
    public void authentication() {
        System.out.println(&quot;Kakao auth&quot;);
    }
}
class AuthenticationService{
    public void authentication(AuthenticationStrategy authenticationStrategy){
        authenticationStrategy.authentication();
    }
}

public class Main {
    public static void main(String[] args) {
        AuthenticationService authenticationService = new AuthenticationService();
        authenticationService.authentication(new KakaoAuthenticationStrategy());
        authenticationService.authentication(new NaverAuthenticationStrategy());
    }
}</code></pre>
<p>인터페이스를 통해 추상 레이어를 구성하고 이를 이용해 필요에 따라서 구현체를 만들게 된다.</p>
<p>사용자가 서비스 로직을 수행할 때, 주입을 받아서 여러 전략에 대응할 수 있게한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 팩토리 패턴]]></title>
            <link>https://velog.io/@knu-kang/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4-r48bdj5n</link>
            <guid>https://velog.io/@knu-kang/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4-r48bdj5n</guid>
            <pubDate>Mon, 12 Feb 2024 09:16:04 GMT</pubDate>
            <description><![CDATA[<h1 id="팩토리-패턴">팩토리 패턴</h1>
<h2 id="팩토리-패턴이-뭐야">팩토리 패턴이 뭐야?</h2>
<p><img src="https://velog.velcdn.com/images/knu-kang/post/060baf9b-9903-41cd-a731-4bd3f4ce65f6/image.png" alt=""></p>
<p>이 그림을 보면서 이해해보자.</p>
<ul>
<li>먼저 상속관계에 있는 <strong>상위 클래스(Abstract Layer)</strong> 는 주요 뼈대를 생성한다.</li>
<li><strong>하위 클래스</strong>에서 세부적인 기술사항에 대해서 구성한다.</li>
<li><strong>Factory 클래스</strong>에서는 사용자에 요청에 따라 선택적으로 하위클래스를 생성해준다.</li>
</ul>
<h2 id="그래서-어떻게-만드는거야">그래서 어떻게 만드는거야?</h2>
<h3 id="-java-code-">[ JAVA CODE ]</h3>
<pre><code class="language-java">enum CoffeeType{
    LATTE,
    ESPRESSO
}
abstract class Coffee{ //커피 뼈대 (공통적 요소)
    protected  String name;
    public String getName() {
        return name;
    }
}
class Latte extends Coffee{
    public Latte() { //라떼를 만드는기술
        name = &quot;latte&quot;;
    }
}
class Espresso extends Coffee{
    public Espresso() { //에스프레소를 만드는 기술
        name = &quot;Espresso&quot;;
    }
}
class CoffeeFactory {
    public static Coffee createCoffee(CoffeeType type){ //enum으로 LATTE, ESSPRESSO를 고른다.
        switch (type) { //요청에 맞게 해당 객체를 생성시켜준다.
            case LATTE:
                return new Latte();
            case ESPRESSO:
                return new Espresso();
            default:
                throw new IllegalArgumentException(&quot;invalid coffee type : &quot; + type);
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Coffee coffee = CoffeeFactory.createCoffee(CoffeeType.LATTE);
        // 상위 클래스에 반환 받는다.
        System.out.println(coffee.getName());
    }
}</code></pre>
<p>사용자 관점에서 해설</p>
<ul>
<li>커피머신이 있다. 나는 어떤 커피를 먹을지 고민 중이다.</li>
<li>라떼(에스프레소)를 먹기로 결정했다.</li>
<li>사용자가 선택한 커피가 만들어진다.</li>
<li>해당 커피가 사용자에서 제시된다. ( return new Latte() / return new Esspresso)</li>
<li>사용자가 커피를 받는다.</li>
</ul>
<p>이런 느낌으로 생각하면서 코드를 오밀조밀 이해하면 된다.</p>
<p>그러나 우리는 개발자이다. 개발자로써의 관점에서 몇가지를 살펴 보겠다.</p>
<ul>
<li>createCoffee 함수가 정적 메소드다.<blockquote>
<p>객체생성을 하지않고, 호출이 가능하기 때문에 해당 메서드에 대한 메모리 할당을 한 번만 할 수 있다.</p>
<h4 id="정적메소드를-하지않았을-때-생기는-문제">정적메소드를 하지않았을 때 생기는 문제</h4>
<p>이 것은 간단히 말하면 커피를 먹을 때마다 커피머신을 새로 만드는 행위랑 같다. 상식선에 이 것은 당연히 효율적이지 않다.</p>
</blockquote>
</li>
</ul>
<ul>
<li><p>enum 클래스를 통해서 구분한다.</p>
<blockquote>
<p>이는 단순히 가독성 및 enum 특성 상 구분에 용이하기 때문에 이를 활용하여 Factory 클래스 내부를 구성했다.</p>
<h4 id="enum-간단한-설명">Enum 간단한 설명</h4>
<p>Enum는 상수의 집합을 정의할 때 사용되는 타입이다.</p>
</blockquote>
<p>예를 들어서 월, 일, 색상 등의 상수 값을 담는다. 자바에서는 enum는 다른 언어보다 더 활발히 사용되고, 상수가 아니라도 <strong>메소드 자체를 집어넣을 수 있다.</strong> </p>
<blockquote>
<p>Enum을 기반으로 상수를 관리한다면, 로직 수정시 이 부분만 수정하면 된다는 장점이 있고, Enum 생성시 스레드세이프(Thread Safe)하기 때문에 싱글톤 패턴을 만들 때 도움이 된다. --&gt; Enum 으로 싱글톤을 만들면 더 효율적일 수 있다는 말</p>
</blockquote>
<blockquote>
<p><strong>스레드 안전(Thread-Satety)</strong>란 멀티 스레드 프로그래밍에서 일반적으로 어떤 함수나 변수, 혹은 객체가 <strong>여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제가 없는 것을 말</strong>한다.</p>
</blockquote>
</li>
<li><p>Coffee는 Abstract 클래스이다.</p>
<blockquote>
<p> 공통된 특성(Common elements)에 대해서 집합요소로써 묶기 위해서  구성했고, 이를 상속 받아 고유의 특성을 추가 시켜 LATTE 그리고 ESSPRESSO 클래스를 구성할 수 있다.</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 싱글톤 패턴]]></title>
            <link>https://velog.io/@knu-kang/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@knu-kang/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Sun, 11 Feb 2024 12:31:17 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/knu-kang/post/a8e30cbf-d8f8-4b17-99c7-4f8cd76228d9/image.jpg" alt=""></p>
<h1 id="싱글톤-패턴">싱글톤 패턴</h1>
<h2 id="싱글톤-패턴이-뭐야">싱글톤 패턴이 뭐야?</h2>
<p>싱글톤 패턴은 하나의 클래스에 오직 하나의 인스턴스만들 가지게하는 패턴이다.</p>
<p>위의 내용을 보고 사람들이 <strong>정적 클래스(Static class) 나 정적 메소드(Static Method)</strong>와 비슷한거 같은데 뭐가 말만 다른거 아니냐고 생각 할 수 있다. 일단 싱글톤 패턴과 정적 클래스의 가장 큰 차이점은 <strong>싱글톤</strong>은 인스턴스를 하나만 생성하는 것을 목표로 한다. 그런데, <strong>정적 클래스</strong>는 인스턴스를 생성하지않는다.</p>
<p>또한 싱글톤에서는 <strong>정적 메소드</strong>가 분명 들어가지만, 이는 인스턴스를 관리하는 부분만 정적으로 관리가 된다. 나머지는 일반 인스턴스 메소드(Instance Method) 로 기능이 구현되게 된다.</p>
<blockquote>
<p>싱글톤 패턴과 정적 클래스는 전혀 다른 존재이다.</p>
</blockquote>
<blockquote>
<p>싱글톤 패턴은 인스턴스 관리를 위해 정적 메소드를 사용한다.</p>
</blockquote>
<p>이 관계에 대해서 잘 이해하고 있어야지 좋은 싱글톤 패턴을 프로젝트에 적용할 수 있다.</p>
<h3 id="사용사례">사용사례</h3>
<p>DB 연결 동작 만들 때 주로 사용된다.</p>
<h2 id="왜-그렇게-만드는거야">왜 그렇게 만드는거야?</h2>
<p>보통 일반적으로는 하나의 클래스에 여러개의 개별 인스턴스를 생성할 수 있지만, 데이터베이스 연결 모듈과 같은 것을 직접 구현할 때는 동일 인스턴스를 사용하는 것이 훨씬 효율적이기 때문에 이런식으로 구성한다.</p>
<h2 id="어떻게-만들어">어떻게 만들어?</h2>
<p>크게 자바스크립트와 자바로 그 예시를 들어보겠다.</p>
<h3 id="javascript">JAVASCRIPT</h3>
<pre><code class="language-javascript">//싱글톤을 적용하지 않은 예시
class Singleton{
    constructor(){}
}

const a = new Singleton();
const b = new Singleton();
console.log(a==b);
//false


//싱글톤을 적용한 예시
class Singleton{
     constructor(){
        if(!Singleton.instance){
            Singleton.instance = this;
        }
      return Singleton.instance;
    }
}

const a = new Singleton();
const b = new Singleton();
console.log(a==b);
//true</code></pre>
<p>생성자 return에 대해서 궁금해할 수도 있어서 먼저 설명하겠다.
자바스크립트는 <strong>생성자가 묵시적으로 this를 return 한다.</strong>
this는 현재 인스턴스라 생각하면 된다. 이를 return 해 줌으로써, 인스턴스 생성이 되는 것이다.</p>
<p>하지만, 위에서는 return Singleton.instance를 return 해줬다. 즉 생성된 인스턴스가 있으면, 그 인스턴스를 return 해주는 것이다.</p>
<h3 id="java">JAVA</h3>
<pre><code class="language-java">//싱글톤 사용하지않은 예시
class Singleton{
    public Singleton(){}
}

public class Main{
    public static void main(String[] args){
        Singleton a = new Singleton();
       Singleton b = new Singleton();
       if(a!=b){
               System.out.println(false);
       }
    }
}
//싱글톤 사용한 예시
class Singleton{
    private static class singletonInstanceHoldeer{
        private static final Singleton singleton = new Singleton();
    }
    public static Singleton getInstance(){
        return singleInstanceHolder.singleton;
    }
}

public class Main{
    public static void main(String[] args){
        Singleton a = Singleton.getInstance();
       Singleton b = Singleton.getInstance();
       if(a==b){
               System.out.println(true);
       }
    }
}</code></pre>
<p>여기서 보게되면, 자바에서의 싱글톤 패턴은 간단하게 아래 2가지로 나누어진다.</p>
<ul>
<li><p>인스턴스는 정적 메소드나 클래스로 관리 (저장 및 return)</p>
<blockquote>
<p>이 때, return 시에 만들어둔 인스턴스를 반환</p>
</blockquote>
</li>
<li><p>나머지는 인스턴스 메소드로써 기능 구현</p>
<blockquote>
<p>생성된 인스턴스가 이를 사용할 수 있게 하기 위함.</p>
</blockquote>
<h2 id="그럼-싱글톤이-무조건-좋은거야">그럼 싱글톤이 무조건 좋은거야?</h2>
</li>
</ul>
<blockquote>
<p>일단 결론부터 말하면 아니다.</p>
</blockquote>
<p>싱글톤은 큰 단점이 존재한다. 
주로 TDD를 할 때는 유닛 테스트로 하는데, 이 때 테스트가 서로 독립적이여야한다. </p>
<p>하지만, 싱글톤 패턴은 하나의 인스턴스만을 사용하기에 각 테스트마다 독립적인 인스턴스를 만들기 어렵다. 또한, 모듈 간의 강한 결합이 생길 수 있다.</p>
<p>이는 의존성 주입(DI)를 통해 느슨한 결합을 만들어서 극복할 수 있다.</p>
<h3 id="그럼-전부-의존성-주입하면-되는건가">그럼 전부 의존성 주입하면 되는건가?</h3>
<p>의존성 주입을 하게되면 물론 추상화 레이어(Abstract Layer)가 중간에 들어가는 구조 상 모듈들을 쉽게 교체할 수 있어서 테스트 및 마이그레이션에 용이하다. 또한 구현 시 추상화 레이어(Abstract Layer)를 넣고, 이를 기반해 구현체를 넣어주기 때문에 의존성의 플로우가 일관되고, 모듈 간의 관계가 조금 더 명확해진다.</p>
<p>그럼에도 불구하고 의존성 주입은 필연적으로 모듈들이 분리되기 때문에, 클래스 수가 늘어난다. </p>
<p>결론적으로는 복잡성이 증가할 수 있다.</p>
<blockquote>
<p>이로 인해서 런타임 패널티가 생기기도 한다.</p>
</blockquote>
<blockquote>
<h4 id="런타임-패널티란">런타임 패널티란?</h4>
<p>프로그램이 실행 중에 발생하는 성능 저하나 부하를 나타내는 말이다.</p>
</blockquote>
<p>결론적으로 <strong>필요에 따른 싱글톤 패턴은 리소스의 낭비를 막을 수 있기 때문에 매우 추천</strong>된다. 하지만 사용을 위해서는 의존성 주입 원칙을 지켜서 만들게 되면 보다 효율적인 싱글톤 패턴을 만들 수 있다.</p>
<p>공부하면서 적은 내용이기 때문에 잘못되게나 틀린 부분은 댓글로 명시해주시면 수정하겠습니다. :)</p>
]]></description>
        </item>
    </channel>
</rss>