<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ssan_to__.log</title>
        <link>https://velog.io/</link>
        <description>내일이 다른</description>
        <lastBuildDate>Tue, 30 May 2023 11:00:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ssan_to__.log</title>
            <url>https://velog.velcdn.com/images/ssan_to__/profile/9862f007-86e6-4d41-b7d3-90007b9be06c/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ssan_to__.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ssan_to__" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[코틀린에서 null을 다루는 방법]]></title>
            <link>https://velog.io/@ssan_to__/%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-null%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@ssan_to__/%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-null%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 30 May 2023 11:00:32 GMT</pubDate>
            <description><![CDATA[<h2 id="kotlin에서의-null-체크">kotlin에서의 null 체크</h2>
<pre><code class="language-java">//안전하지 않다
public boolean startsWithA(String str){
    return str.startsWith(&quot;A&quot;);
}</code></pre>
<ol>
<li>str이 null일 경우 Exception을 낸다.<pre><code class="language-java">public boolean startsWithA1(String str){
 if(str == null) {
  throw new IllegalArgumentException(&quot;null이 들어왔습니다&quot;);
  }
  return str.startsWith(&quot;A&quot;);
}</code></pre>
</li>
<li>str이 null일 경우 null을 반환한다.<pre><code class="language-java">public Boolean startsWithA2(String str){
 if(str == null) {
     return null;
 }
 return str.startsWith(&quot;A&quot;);
}</code></pre>
</li>
<li>str이 null일 경우 false를 반환한다.<pre><code>public boolean startsWithA3(String str){
 if(str == null) {
     return false;
 }
 return str.startsWith(&quot;A&quot;);
}</code></pre></li>
</ol>
<h2 id="safe-call과-elvis-연산자">Safe Call과 Elvis 연산자</h2>
<pre><code class="language-kotlin">//safe call
val str: String? = &quot;ABC&quot;
str.length //불가능
str?.length //가능</code></pre>
<h2 id="널-아님-단언">널 아님 단언!!</h2>
<p>nullable type이지만, 아무리 생각해도 null이 될 수 없는 경우</p>
<pre><code>fun startsWtih(str: String?): Boolean {
    return str!!.staartsWith(&quot;A&quot;)
}</code></pre><blockquote>
<p>코틀린이 플랫폼타입 즉 다른 언어의 타입 중 null 관련 정보를 알 수 없는 타입 Runtime 시 Exception이 날 수 있다</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[코틀린에서 변수를 다루는 방법]]></title>
            <link>https://velog.io/@ssan_to__/%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-%EB%B3%80%EC%88%98%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@ssan_to__/%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-%EB%B3%80%EC%88%98%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 30 May 2023 10:49:37 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">java
long number1 = 10L;
final long number2 = 10L;

Long number3 = 1_000L;
Person person = new Person(&quot;조현재&quot;);</code></pre>
<pre><code class="language-kotlin">kotlin
var number1 = 10L  가변
val number2 = 10L  불변
</code></pre>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/c56cc06a-55da-4af6-a3c8-6253094ce044/image.png" alt=""></p>
<blockquote>
<p>java와 다르게 long과 Long을 걱정할 필요없다.즉, 프로그래머가 boxing / unboxing을 고려하지 않아도 되도록 Kotlin이 알아서 처리 해준다.</p>
</blockquote>
<pre><code class="language-kotlin">var number: Long? = 1_000L
number4 = null</code></pre>
<blockquote>
<p>java처럼 Long을 쓰게 되면 값이 null이 들어갈 수 있다는 건데 kotlin에서는 null이 들어가는걸 알려주기 위해서는 ? 와 null을 추가해준다</p>
</blockquote>
<pre><code class="language-kotlin">kotlin
var person = Person(&quot;조현재&quot;);</code></pre>
<blockquote>
<p>코틀린에서는 객체 인스턴스화를 할 때는 new를 붙이지 않아야 한다.</p>
</blockquote>
<blockquote>
<p><strong>코틀린에서 변수를 다루는 방법 최종 정리</strong><br>
모든 변수는 var / val을 붙여 주어야한다.
-var: 변경 가능하다 / val: 변경 불가능하다(read-only)
타입을 명시적으로 작성하지 않아도, 타입이 추론된다.
Primitive Type과 Reference Type 을 구분하지 않아도 된다.
Null이 들어갈 수 있는 변수는 타입 뒤에 ? 를 붙여주어야한다.
-아예 다른 타입으로 간주된다.
객체를 인스턴스화 할 때 new를 붙이지 않아야 한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[web api 차이]]></title>
            <link>https://velog.io/@ssan_to__/web-api-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@ssan_to__/web-api-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Mon, 22 May 2023 02:05:06 GMT</pubDate>
            <description><![CDATA[<p>Laravel 프로젝트에서 <code>web.php</code>와 <code>api.php</code>는 모두 라우팅 파일입니다. 
두 파일의 주요 차이점은 다음과 같다:</p>
<ol>
<li><p><strong>web.php</strong>: 이 파일은 웹 인터페이스를 통해 들어오는 HTTP 요청을 처리하는 라우트를 정의하는 곳 이 파일에 정의된 라우트는 Laravel의 웹 미들웨어 그룹에 의해 처리되며, 이 그룹에는 세션 상태 유지, CSRF 보호 등의 미들웨어가 포함되어 있다. 웹 페이지를 반환하는 라우트를 <code>web.php</code>에 정의하게 된다.</p>
</li>
<li><p><strong>api.php</strong>: 이 파일은 API를 통해 들어오는 HTTP 요청을 처리하는 라우트를 정의하는 곳이다. 이 파일에 정의된 라우트는 API 미들웨어 그룹에 의해 처리되며, 이 그룹에는 레이트 제한, 상태 없음 등의 미들웨어가 포함되어 있다. API 응답을 반환하는 라우트를 <code>api.php</code>에 정의하게 된다.</p>
</li>
</ol>
<p>따라서, 웹 인터페이스에서 제공하려면 <code>web.php</code>에 라우트를 정의하고, API를 통해 제공하려면 <code>api.php</code>에 라우트를 정의해야 한다. </p>
<p>다시 말해, 클라이언트가 웹 브라우저를 통해 다운로드 요청을 보낼 경우 <code>web.php</code>에 라우트를 정의해야 하고, HTTP 클라이언트(예: Postman, curl 등)나 다른 서버를 통해 다운로드 요청을 보낼 경우 <code>api.php</code>에 라우트를 정의해야 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[laravel .ignore]]></title>
            <link>https://velog.io/@ssan_to__/laravel-.ignore</link>
            <guid>https://velog.io/@ssan_to__/laravel-.ignore</guid>
            <pubDate>Tue, 16 May 2023 23:45:43 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p><code>/vendor</code>와 <code>/node_modules</code>: 각각 PHP와 JavaScript의 의존성이 저장되는 디렉터리. 
<code>composer install</code> 또는 <code>npm install</code>을 통해 쉽게 재생성될 수 있기 때문에 굳이 Git에서 추적할 필요가 없다.</p>
</li>
<li><p><code>/public/hot</code>, <code>/public/storage</code>:  Laravel 프로젝트에서 생성되는 임시 파일이나 스토리지 파일을 저장하는 디렉터리다.</p>
</li>
<li><p><code>/storage/*.key</code>: Laravel 프로젝트의 암호화 키가 저장되는 파일입니다. 이것은 보안상 중요한 정보이므로 공유해서는 안된다.</p>
</li>
<li><p><code>/.idea</code>, <code>/.vscode</code>: IntelliJ IDEA와 Visual Studio Code 같은 IDE(통합 개발 환경)에서 생성되는 설정 파일을 저장하는 디렉터리다.</p>
</li>
<li><p><code>/.env</code>, <code>/.env.backup</code>, <code>/.env.example</code>: 환경 변수를 저장하는 파일. <code>.env</code> 파일은 각 환경마다 다른 설정을 가질 수 있으므로 Git에 올리지 않는 것이 좋다. 대신 <code>.env.example</code> 파일을 이용해 템플릿을 제공하고 각 개발자가 자신의 환경에 맞게 <code>.env</code> 파일을 설정하도록 할 수 있다.</p>
</li>
<li><p><code>/phpunit.xml</code>, <code>/.phpunit.result.cache</code>: PHPUnit 테스트 프레임워크와 관련된 설정 파일 및 캐시 파일이다.</p>
</li>
<li><p><code>/docker-compose.override.yml</code>: Docker Compose 설정을 오버라이드하는 파일이다. </p>
</li>
<li><p><code>/_ide_helper.php</code>, <code>/_ide_helper_models.php</code>: Laravel IDE Helper 패키지에 의해 생성되는 파일들이다.</p>
</li>
<li><p><code>/PhpStorm-*</code>: PhpStorm에서 생성하는 설정 파일들을 나타낸다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[JAVA&SpringBoot 
PHP&Laravel 비교]]></title>
            <link>https://velog.io/@ssan_to__/JAVASpringBoot-PHPLaravel-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@ssan_to__/JAVASpringBoot-PHPLaravel-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Sun, 30 Apr 2023 12:07:15 GMT</pubDate>
            <description><![CDATA[<p>java를 배우고 spring boot까지 배운 상태에서 어쩌다가 php와 laravel을 배우게되었다. 배우게 되면서 비교하고 느꼈던 점에서 써보겠다.</p>
<h4 id="1-클래스와-메서드">1. 클래스와 메서드</h4>
<p>PHP에서 클래스는 <code>class</code> 키워드를 사용하고, 메서드는 <code>public</code>, <code>private</code>, <code>protected</code>와 같은 접근 제한자를 사용한다.  Java와 동일한 방식</p>
<h4 id="2-네임스페이스">2. 네임스페이스</h4>
<p>PHP에서 네임스페이스는 <code>namespace</code> 키워드로 선언한다. 이는 Java의 <code>package</code>와 유사</p>
<h4 id="3-객체-생성-및-메서드-호출">3. 객체 생성 및 메서드 호출</h4>
<p>PHP에서 객체를 생성할 때 <code>new</code> 키워드를 사용하며, 객체의 메서드는 <code>&gt;</code> 연산자를 사용하여 호출한다. Java에서는 <code>.</code> 연산자를 사용한다. 예를 들어, <code>$post = new Post();</code>는 Java에서 <code>Post post = new Post();</code>와 유사</p>
<h4 id="4-정적-메서드-호출">4. 정적 메서드 호출</h4>
<p>PHP에서 정적 메서드는 <code>::</code> 연산자를 사용하여 호출한다. Java에서는 <code>.</code> 연산자를 사용한다. 예를 들어, <code>User::where(&#39;userId&#39;, $userId)</code>는 Java에서</p>
<pre><code class="language-java">public class UserUtil {
    public static boolean isUserIdEmpty(String userId) {
        return (userId == null || StringUtils.isEmpty(userId));
    }
}
</code></pre>
<p>와 유사</p>
<h4 id="5-변수-선언">5. 변수 선언</h4>
<p>PHP에서 변수는 <code>$</code> 기호로 시작. Java에서는 변수 타입을 명시해야 하지만, PHP에서는 필요하지 않다.</p>
<h4 id="6-라우팅-routing">6. 라우팅 (Routing)</h4>
<p>Laravel에서 라우팅은 웹 요청을 애플리케이션에서 처리할 수 있는 메서드와 연결한다. 이는 Java에서 Spring 프레임워크의 <code>@RequestMapping</code>이나 <code>@GetMapping</code>, <code>@PostMapping</code> 등의 어노테이션과 비슷한 기능을 한다.</p>
<pre><code class="language-php">Laravel의 
Route::post(&#39;register&#39;, [JWTController::class, &#39;register&#39;]);</code></pre>
<pre><code class="language-java">java의
@PostMapping(&quot;/register&quot;)
public ResponseEntity&lt;?&gt; register(@RequestBody RegisterRequest registerRequest) {
    // ...
}</code></pre>
<h4 id="매개변수">매개변수</h4>
<pre><code class="language-php">public function update(Request $request, Post $post)</code></pre>
<p>(<code>Request $request</code>, <code>Post $post</code>) 부분은 이 메서드의 매개변수를 정의</p>
<ol>
<li><p><code>Request $request</code>: 이 매개변수는 클라이언트로부터의 HTTP 요청 정보를 담고 있는 <code>Request</code> 객체입니다. 이 객체를 사용하여 요청에 포함된 데이터를 가져올 수 있습니다. 예를 들어, <code>$request-&gt;title</code>과 <code>$request-&gt;content</code>는 클라이언트가 전송한 게시물의 제목과 내용이다</p>
</li>
<li><p><code>Post $post</code>: 이 매개변수는 업데이트할 게시물 객체입니다. 이 객체는 Laravel 의 Route Model Binding 기능을 사용하여 URL에서 전달된 게시물 ID에 해당하는 게시물 객체를 자동으로 검색하고 주입한다.</p>
<p>즉, 이 메서드가 호출되기 전에 이미 데이터베이스에서 게시물 객체가 조회하고 이 객체를 사용하여 게시물의 속성을 업데이트하고 저장할 수 있다.</p>
</li>
</ol>
<h3 id="java의-spring-boot와-비교">Java의 Spring Boot와 비교</h3>
<ol>
<li><p><code>Request $request</code>: 이것은 Spring Boot에서 <code>@RequestBody</code> 어노테이션을 사용하여 클라이언트로부터의 요청 본문(JSON)을 객체로 변환하는 것과 유사하다. </p>
<p>예를 들어, <code>public ResponseEntity&lt;?&gt; update(@RequestBody UpdateRequest updateRequest)</code>와 같은 메서드에서 <code>UpdateRequest</code> 객체는 클라이언트로부터의 요청 데이터를 담고 있다.</p>
</li>
<li><p><code>Post $post</code>: 이것은 Spring Boot에서 <code>@PathVariable</code> 어노테이션을 사용하여 URL에서 전달된 변수를 가져오는 것과 유사하다. </p>
<p>하지만, Spring Boot에서는 URL에서 가져온 변수를 사용하여 직접 데이터베이스에서 게시물을 조회해야 한다. </p>
<p>예를 들어, <code>public ResponseEntity&lt;?&gt; update(@PathVariable Long postId)</code>에서 <code>postId</code>는 URL에서 전달된 게시물 ID이다. </p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스]]></title>
            <link>https://velog.io/@ssan_to__/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@ssan_to__/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Mon, 20 Mar 2023 04:12:39 GMT</pubDate>
            <description><![CDATA[<h3 id="databasedb">database(DB)</h3>
<p>전자적으로 저장되고 사용되는 관련있는 데이터들의 <strong>조직화된</strong> 집합</p>
<h3 id="dbms">DBMS</h3>
<p>-database management systems
사용자에게 DB를 정의하고 만들고 관리하는 기능을 제공하는 소프트웨어 시스템
-DB를 정의하다 보면 부가적인 데이터가 발생한다</p>
<p>부가적인 데이터 : metadata</p>
<h3 id="metadata">metadata</h3>
<p>-database를 정의하거나 기술하는 data
-e.g)데이터 유형, 구조 제약조건, 보안, 저장, 인덱스, 사용자 그룹 등등 
-metadata 또한 DMBS를 통해 저장/관리 된다</p>
<h3 id="database-system">database system</h3>
<p>-database + DBMS + 연관된 applications
-줄여서 database라고도 부름</p>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/ef11f5f6-6b25-4ffc-8b57-52a995ac6938/image.png" alt=""></p>
<h3 id="data-models모델링">data models(모델링)</h3>
<p>-DB의 구조를 기술하는데 사용될 수 있는 개념들이 모인 집합
-DB구조를 <strong>추상화</strong>해서 표현할 수 있는 수단을 제공한다
db의 구조를 원하는 형태로 혹은 원하는 추상화 수준으로 모델링 하기위해서는 model이 필요하다</p>
<h4 id="data-models의-분류">data models의 분류</h4>
<p>-conceptual(or high-level) data models</p>
<ul>
<li>일반 사용들이 쉽게 이해할 수 있는 개념들로 이뤄진 모델(추상화 수준이 가장 높음)</li>
<li>비즈니스 요구사항을 추상화하여 기술할 때 사용
ex) entity-relationship model( ER diagram)</li>
</ul>
<p>-logical(or representational) data models</p>
<ul>
<li>이해하기 어렵지 않으면서도 디테일하게 DB를 구조화 할 수 있는 개념들을 제공</li>
<li>데이터가 컴퓨터에 저장될 때의 구조와 크게 다르지 않게 DB구조화를 가능하게 함</li>
<li>특정 DBMS나 storage에 종속되지 않는 수준에서 DB를 구조화 할 수 있는 모델
ex) <strong>relational data model</strong>, object data model, object-relational data model</li>
</ul>
<p>-physical(or low-level) data models</p>
<ul>
<li>컴퓨터에 데이터가 어떻게 파일 형태로 저장되는지를 기술 할 수 있는 수단을 제공</li>
<li>data format, data orderings, access path 등등 </li>
<li><ul>
<li>access path: 데이터 검색을 빠르게 하기 위한 구조체 e.g) index</li>
</ul>
</li>
</ul>
<h3 id="database-schema">database schema</h3>
<p>-data model을 바탕으로 database의 구조를 기술한 것
-schema는 database를 설계할 때 정해지며 한번 정해진 후에는 자주 바뀌지 않는다</p>
<h3 id="database-state">database state</h3>
<p>-database에 있는 실제 데이터는 꽤 자주 바뀔 수 있다
-특정 시점에 database에 있는 데이터를 database state 혹은 snapshot이라고 한다
-혹은 database에 있는 현재 instances의 집합이라고도 한다</p>
<h3 id="three-schema-architecture">three-schema architecture</h3>
<p>-database system을 구축하는 architecture 중의 하나
-user application으로 부터 물리적인(physical) database를 분리시키는 목적
-세 가지 level이 존재하며 각각의 level마다 schema가 정의되어 있다</p>
<ul>
<li><p>external schemas(or user views) at external(or view) level</p>
</li>
<li><p>실제 사용자가 바라보는 스키마</p>
</li>
<li><p>특정 유저들이 필요로 하는 데이터만 표현</p>
</li>
<li><p>그 외 알려줄 필요가 없는 데이터는 숨김 </p>
</li>
<li><p>logical data model을 통해 표현</p>
</li>
<li><p>conceptual schemas at conceptual level</p>
</li>
<li><p>원래는 external schemas와 internal schemas만 있었는데 문제점이 생겼는데 internal schemas가 유저에 맞춰 조금씩 다른 internal schema가 생기면서 데이터 불일치가 발생하게 되어 생김</p>
</li>
<li><p>전체 database에 대한 구조를 기술 즉, internal schema를 한번 추상화 하여 표현</p>
</li>
<li><p>물리적인 저장 구조에 관한 내용은 숨김</p>
</li>
<li><p>entities, data types, relationships, user operations, constraints에 집중</p>
</li>
<li><p>logical data model을 통해 기술</p>
</li>
<li><p>internal schemas at internal level</p>
</li>
<li><p>실제로 물리적인 저장장치에 가장 가깝게 위치</p>
</li>
<li><p>물리적으로 데이터가 어떻게 저장되는지 physical data model을 통해 표현</p>
</li>
<li><p>data storage, data structure, access path 등등 실체가 있는 내용 기술</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/05f285ca-d067-4739-ad9d-453593a6d813/image.png" alt=""></p>
<p>결론적으로 three-schema architecture는 안정적인 데이터베이스를 운영하기 위해서 사용한다.
각 레벨을 독립시켜서 어느 레벨에서의 변화가 상위 레벨에 영향을 주지 않기 위함.
대부분의 DMBS가 three level을 완벽하게 혹은 명시적으로 나누지는 않는다.
데이터가 존재하는 곳은 internal level에만 있다.</p>
<h3 id="database-language">database language</h3>
<ul>
<li><p>data definition language(DDL)</p>
</li>
<li><p>conceptual schema를 정의하기 위해 사용되는 언어</p>
</li>
<li><p>internal schema까지 정의할 수 있는 경우도 있음</p>
</li>
<li><p>storage definition language(SDL)</p>
</li>
<li><p>internal schema를 정의하는 용도로 사용되는 언어</p>
</li>
<li><p>요즘은 특히 relational DBMS에서는 SDL이 거의 없고 파라미터 등의 설정으로 대체됨</p>
</li>
<li><p>view definition language(VDL)</p>
</li>
<li><p>external schemas를 정의하기 위해 사용되는 언어</p>
</li>
<li><p>대부분의 DMBS에서는 DDL이 VDL 역할까지 수행</p>
</li>
<li><p>data manipulation language(DML)</p>
</li>
<li><p>database에 있는 data를 활용하기 위한 언어</p>
</li>
<li><p>data 추가, 삭제, 수정, 검색 등등의 기능을 제공하는 언어</p>
</li>
</ul>
<p>오늘날의 DBMS는 DML, VDL, DDL이 따로 존재하기 보다는 통합된 언어로 존재
-대표적인 예가 relational database language: SQL</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SPRING과 SPRINGBOOT]]></title>
            <link>https://velog.io/@ssan_to__/SPRING%EA%B3%BC-SPRINGBOOT</link>
            <guid>https://velog.io/@ssan_to__/SPRING%EA%B3%BC-SPRINGBOOT</guid>
            <pubDate>Wed, 15 Mar 2023 01:05:13 GMT</pubDate>
            <description><![CDATA[<h4 id="srping-boot-란">SRPING BOOT 란</h4>
<blockquote>
<p>스프링을 기반으로 실무 환경에 사용 가능한 수준의 <strong>독립실행형 애플리케이션</strong>을 복잡한 고민 없이 빠르게 작성할 수 있게 도와주는 여러가지 도구의 모음이다.
☞ 스프링 ≠ 스프링부트</p>
</blockquote>
<h4 id="스프링부트를-왜-쓰는가">스프링부트를 왜 쓰는가?</h4>
<blockquote>
</blockquote>
<ul>
<li>매우 빠르고 광범위한 영역의 스프링 개발 경험을 제공</li>
<li>강한 주장을 가지고 즉시 적용 가능한 기술 조합을 제공</li>
<li>프로젝트에 필요로하는 다양한 비기능적인 기술
(내장형서버, 보안, 메트릭, 상태체크, 외부설정 방식 등)</li>
<li>코드 생성이나 XML 설정을 필요로 하지 않음</li>
</ul>
<h4 id="containerless-웹-개발-아키텍처란">Containerless 웹 개발 아키텍처란?</h4>
<blockquote>
</blockquote>
<p>우선 Container가 무엇인지 알아보자
Container란? 
Servlet Container는 일반적으로 Servlet을 관리하며 WEB CLIENT의 WEB REQUEST가 들어오는걸 담당 Servlet로 안내해주는 역할을 한다.
(Serverless랑 비슷)
Spring은 IOC Container이다. 
<img src="https://velog.velcdn.com/images/ssan_to__/post/4a346f76-4480-4ff6-9508-6e1ab4717e79/image.png" alt="">
위와 같은 ServletContainer에서 web.xml, war, deploy 등등 설정의 수고 없이 Containerless가 되면 Srping Container 설정이 알아서 된다. 물론 커스텀 할 수 있다. SpringBoot를 이용하면 전체가 다 실행이 된다 그래서 독립실행형 어플리케이션이란걸 알 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[클린 코드(Clean Code) 요약 (진행중)]]></title>
            <link>https://velog.io/@ssan_to__/%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9CClean-Code-%EC%9A%94%EC%95%BD</link>
            <guid>https://velog.io/@ssan_to__/%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9CClean-Code-%EC%9A%94%EC%95%BD</guid>
            <pubDate>Wed, 08 Mar 2023 10:45:31 GMT</pubDate>
            <description><![CDATA[<p><strong>프로젝트 준비하면서 막 만들었던 코드들에 대한 피드백을 받다보니 책을 추천 받아 읽기 시작하여 제 방식대로 요약하였습니다.
한달 안에 다 읽을 예정이며 읽으면서 틈틈이 저의 생각과 책 내용을 정리할 예정입니다.</strong></p>
<h2 id="1장-깨끗한-코드">1장. 깨끗한 코드</h2>
<ul>
<li>제 3자가 읽기 쉽고 고치기 쉬운 코드</li>
<li>저자라 생각하고 독자와 잘 소통하는 코드</li>
</ul>
<p>첫 장에서는 나만 알아보는 코드가 아닌 다른 사람도 읽거나 작업해도 바로 이해할 수 있는 코드를 짜라고 조언한다.</p>
<h2 id="2장-의미있는-이름">2장. 의미있는 이름</h2>
<h4 id="의도를-분명히-밝혀라">의도를 분명히 밝혀라</h4>
<pre><code class="language-java">public List&lt;int[]&gt; getThem(){
    List&lt;int[]&gt; list1 = new ArrayList&lt;int[]&gt;();
    for (int[] x : theList)
        if (x[0] == 4)
            list1.add(x);
    return list1;        

}</code></pre>
<blockquote>
</blockquote>
<p>위 코드를 보면 무슨 일을 하는지 짐작하기 힘들다.
코드의 단순성이 아니라 코드의 함축성이다</p>
<blockquote>
<blockquote>
<p>위 코드 샘플엔 밑에 정보가 드러나지 않는다.</p>
</blockquote>
</blockquote>
<ol>
<li>theList에 무엇이 들었는가?</li>
<li>theList에서 0번째 값이 어째서 중요한가?</li>
<li>값 4는 무슨 의미인가?</li>
<li>함수가 반환하는 리스트 list1 을 어떻게 사용하는가?</li>
</ol>
<pre><code class="language-java">public List&lt;int[]&gt; getFlaggedCells(){
    List&lt;int[]&gt; flaggedCells = new ArrayList&lt;int[]&gt;();
    for (int[] cell : gameBoard)
      if (cell[STATUS_VALUE] == FLAGGED)
        flaggedCells.add(cell);
    return flaggedCells;
}    </code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[RestFul Service 기능 확장]]></title>
            <link>https://velog.io/@ssan_to__/RestFul-Service-%EA%B8%B0%EB%8A%A5-%ED%99%95%EC%9E%A5</link>
            <guid>https://velog.io/@ssan_to__/RestFul-Service-%EA%B8%B0%EB%8A%A5-%ED%99%95%EC%9E%A5</guid>
            <pubDate>Sun, 19 Feb 2023 06:14:15 GMT</pubDate>
            <description><![CDATA[<h3 id="validation">Validation</h3>
<ul>
<li>JDK에 포함된 Validation API</li>
<li>Hibernate library에 포함된 Hibernate Validation <pre><code class="language-java">import javax.validation.constraints.*;</code></pre>
<pre><code class="language-java">controller
</code></pre>
</li>
</ul>
<p>@PostMapping(&quot;/&quot;)
public ResponseEntity&lt;&gt; create(@Valid @RequestBody User user){ 
    return ResponseEntity.status(HttpStatus.CREATED).body(responseUser);}</p>
<pre><code>
```java
@RestController
@ControllerAdvice
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    //유효성 검사에 문제가 생겼을 때 화면에 오류 메세지를 출력하기 위한
    @Override
    protected ResponseEntity&lt;Object&gt; handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex,
            HttpHeaders headers, HttpStatus status,
            WebRequest request) {
        ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(),
                ex.getMessage(), ex.getBindingResult().toString());
        return new ResponseEntity(exceptionResponse, HttpStatus.BAD_REQUEST);
    }

}</code></pre><h3 id="internationalization">Internationalization</h3>
<p>다국어 처리</p>
<pre><code class="language-java">@Bean
public LocaleResolver localeResolver(){
    SessionLocaleResolver localeResolver = new SessionLocaleResolver();
    localeResolver.setDefaultLocale(Locale.KOREA);
    return localeResolver;
}</code></pre>
<p>application.yml</p>
<pre><code class="language-java">spring:
  messages:
    basename: messages //다국어 파일의 이름을 &quot;messages&quot; 로 하겠다는 의미</code></pre>
<p>resources에 위에 적은 이름으로
*messages.properties
    greeting.message=안녕하세요
*messages_en.properties
    greeting.message=Hello
*messages_fr.properties
    greeting.message=Bonjour</p>
<pre><code class="language-java">//메세지소스를 반환하기 위해서
import org.springframework.context.MessageSource;
private final MessageSource messageSource;

@GetMapping(&quot;/hello&quot;)
public String hello(@RequestHeader(name=&quot;Accept-Language&quot;, required=false) Locale locale){
    return messageSource.getMessage(&quot;greeting.message&quot;,null,locale);
}</code></pre>
<h3 id="xml-format으로-반환하기">XML format으로 반환하기</h3>
<pre><code>&lt;dependency&gt;
    &lt;groupId&gt;com.faster xml.jackson.dataformat&lt;/groupId&gt;
    &lt;artifactId&gt;jackson-dataformat-xml&lt;/artifactId&gt;
    &lt;version&gt;2.10.2&lt;/version&gt;
&lt;/dependency&gt;</code></pre><p><img src="https://velog.velcdn.com/images/ssan_to__/post/bc36f8b8-f80f-4da6-92c1-0e5ee3530952/image.png" alt=""></p>
<h3 id="filtering">Filtering</h3>
<p>중요한 데이터를 클라이언트가 바로 받아볼 수 있는 것이 아니라 이런 데이터 값을 제어하는 방법</p>
<p>방법 1.</p>
<pre><code class="language-java">@JsonIgnore
private String password;

@JsonIgnore
private String ssn;</code></pre>
<p>방법 2.</p>
<pre><code>@JsonIgnoreProperties(value={&quot;password&quot;, &quot;ssn&quot;})
public class User{
    ...
    private String password;
    private String ssn;
}</code></pre><h4 id="개별-사용자-조회-filtering방법">개별 사용자 조회 Filtering방법</h4>
<pre><code class="language-java">@JsonFilter(&quot;UserInfo&quot;)
public class User{
    ...
}</code></pre>
<p>관리자 유저컨트롤러</p>
<p>*개인 사용자 보기</p>
<pre><code class="language-java">@GetMapping(&quot;/admin/users/{id}&quot;)
public MappingJacksonValue retreveUser(@PathVariable int id){
    SimpleBeanPropertyFilter filter = simpleBeanPropertyFilter.filterOutAllExcept(&quot;id&quot;, &quot;name&quot; &quot;joinDate&quot; &quot;ssn&quot;);
    FilterProvider filters = new SimpleFilterProvider().addFilter(&quot;UserInfo&quot;, filter);
    MappingJacksonValue mapping = new MappingJacksonValue(user);
    mapping.setFilters(filters);

      return mapping;
}    </code></pre>
<p>*유저 리스트</p>
<pre><code class="language-java">@GetMapping(&quot;/admin/users&quot;)
public MappingJacksonValue retrieveAllUsers(){
    List&lt;User users = service.findAll();

    SimpleBeanPropertyFilter filter = simpleBeanPropertyFilter.filterOutAllExcept(&quot;id&quot;, &quot;name&quot; &quot;joinDate&quot; &quot;ssn&quot;);
    FilterProvider filters = new SimpleFilterProvider().addFilter(&quot;UserInfo&quot;, filter);
    MappingJacksonValue mapping = new MappingJacksonValue(users);
    mapping.setFilters(filters);

      return mapping;

}
</code></pre>
<br>

<h3 id="version관리">Version관리</h3>
<pre><code class="language-java">UserV1 class

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonFilter(&quot;UserInfo&quot;)
public class UserV1{
    private Integer id;
    @Size(min=2, message = &quot;이름은 두글자 이상 입력해주세요.&quot;)
    private String name;

    @Past
    private Date joinDate;

    private String ssn;</code></pre>
<pre><code class="language-java">UserV2 class

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonFilter(&quot;UserInfoV2&quot;)
public class UserV2 extends UserV1 {
    private String grade;
}
</code></pre>
<pre><code class="language-java">@GetMapping(&quot;/admin/v2/users/{id}&quot;)
public MappingJacksonValue retreveUserV2(@PathVariable int id){

    //user -&gt; user2
    UserV2 userV2 = new UserV2();
    BeanUtils.copyProperties(userV1, userV2);
    userV2.setGrade(&quot;VIP&quot;);


SimpleBeanPropertyFilter filter = simpleBeanPropertyFilter.filterOutAllExcept(&quot;id&quot;, &quot;name&quot; &quot;joinDate&quot; &quot;ssn&quot;, &quot;grade&quot;);
    FilterProvider filters = new SimpleFilterProvider().addFilter(&quot;UserInfoV2&quot;, filter);
    MappingJacksonValue mapping = new MappingJacksonValue(user);
    mapping.setFilters(filters);

      return mapping;
}    </code></pre>
<h4 id="request-parameter와-header를-이용한-api-version관리">Request Parameter와 Header를 이용한 API Version관리</h4>
<blockquote>
<p>*Request Parameter</p>
</blockquote>
<pre><code class="language-java">@GetMapping(value= &quot;/users/{id}&quot;, params = &quot;version=1&quot;)
public MappingJacksonValue retreveUserV1(@PathVariable int id){...}</code></pre>
<pre><code class="language-java">@GetMapping(value= &quot;/users/{id}&quot;, params = &quot;version=2&quot;)
public MappingJacksonValue retreveUserV2(@PathVariable int id){...}</code></pre>
<blockquote>
<p>*Header</p>
</blockquote>
<pre><code class="language-java">@GetMapping(value = &quot;/users/{id}&quot;, headers=&quot;X-API-VERSION=1&quot;)
public MappingJacksonValue retreveUserV1(@PathVariable int id){...}</code></pre>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/444cb1bd-a62c-4c16-84c5-4aacde2a0bf6/image.png" alt=""></p>
<blockquote>
<p>mine</p>
</blockquote>
<pre><code class="language-java">@GetMapping(value=&quot;/users/{id}&quot;, produces = &quot;application/vnd.company.appv1+json&quot;)
public MappingJacsonvalue retreveUserV1(@PathVariable int id){...}

![](https://velog.velcdn.com/images/ssan_to__/post/8c9fb625-2ad7-48f7-b57b-1ea062ab34f6/image.png)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[큐와 힙의 개념과 차이]]></title>
            <link>https://velog.io/@ssan_to__/%ED%81%90%EC%99%80-%ED%9E%99%EC%9D%98-%EA%B0%9C%EB%85%90%EA%B3%BC-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@ssan_to__/%ED%81%90%EC%99%80-%ED%9E%99%EC%9D%98-%EA%B0%9C%EB%85%90%EA%B3%BC-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Fri, 17 Feb 2023 00:29:49 GMT</pubDate>
            <description><![CDATA[<h4 id="priority-queue우선순위-큐">Priority queue(우선순위 큐)</h4>
<p>큐와 유사하지만 우선순위가 높은 아이템이 먼저 처리됨
-insert : 집어넣는다(우선순위 정보도 같이) 
-delete : 가장 우선 순위가 높은 아이템을 빼낸다.
-peek : delete와 유사하지만 우선순위 큐에서는 제거하지 않는다.</p>
<h4 id="heap">Heap</h4>
<p>주로 이진트리(binary tree)기반으로 구현 / 힙은 max heap과 min heap
-insert : 집어넣는다(아이템 key값도 같이) 
-delete : maxheap이라면 key값이 가장 큰 아이템을 반환
          minheap이라면 key값이 가장 작은 아이템을 반환
-peek : delete와 유사하지만 실제로 heap에서 꺼내지 않는다</p>
<blockquote>
<p>트리 :부모-자녀처럼 계층적인 형태를 가지는 구조
이진트리 : 부모가 자녀를 최대 두개만 가질 수 있는 트리</p>
</blockquote>
<blockquote>
<p>max heap : 부모 노드의 키(key)가 자식 노드(들)의 키(key)보다 크거나 같은 트리
min heap : 부모 노드의 키(key)가 자식 노드(들)의 키(key)보다 작거나 같은 트리</p>
</blockquote>
<p>Priority queue 와 Heap의 관계
힙(heap)의 키(key)를 우선순위(priority)로 사용한다면 힙은 우선순위 큐(priority queue)의 구현체가 된다.</p>
<p>Priority queue = ADT (After data type) 
-실제로 구현을 설명하지 않음
-어떤 동작들이 있는지 개념적인것만 설명
Heap = data structure
-구현까지 있는거</p>
<blockquote>
<p>그래서 결국 Heap을 Priority queue에 구현체라고 많이들 표현함
사실 Priority queue에는 많은 구현체들이 있지만 Heap이 제일 성능이 좋기때문에 대부분 많이 쓰기 때문에 
&#39;Priority queue랑 Heap이랑 똑같다&#39; 라는 인식이 조금 생겼지만 엄밀히 놓고 보면 서로 다른 개념이다.</p>
</blockquote>
<h4 id="사용사례">사용사례</h4>
<p>-프로세스 스케줄링(process scheduling)
-heap sort(힙 정렬)</p>
<p>※힙(heap) 메모리의 힙은 위에 적힌 힙과는 관련 없음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스택과 큐]]></title>
            <link>https://velog.io/@ssan_to__/%EC%8A%A4%ED%83%9D%EA%B3%BC-%ED%81%90</link>
            <guid>https://velog.io/@ssan_to__/%EC%8A%A4%ED%83%9D%EA%B3%BC-%ED%81%90</guid>
            <pubDate>Wed, 15 Feb 2023 01:02:34 GMT</pubDate>
            <description><![CDATA[<h4 id="adtabstract-data-type">ADT(abstract data type)</h4>
<p>-추상자료형
-개념적으로 어떤 동작이 있는지만 정의
-구현에 대해서는 다루지 않음</p>
<h4 id="dsdata-structure">DS(data structure)</h4>
<p>-자료구조
-ADT에서 정의된 동작을 실제로 구현한 것</p>
<h3 id="스택stack">스택(stack)</h3>
<p>LIFO(Last In First Out) 형태로 데이터를 저장하는 구조
-push : 넣기
-pop : 빼내기
-peek : 아이템을 빼내지는 않지만 가장 최상단에 있는게 뭔지 알 수 있는거</p>
<p>사용사례 : stack memory &amp; stack frame</p>
<h3 id="큐queue">큐(queue)</h3>
<p>FIFO(First In First Out) 형태로 데이터를 저장하는 구조
-enqueue : 큐에 아이템 넣기
-dequeue : 큐에서 아이템 빼기
-peek : 아이템을 빼내지는 않지만 아이템의 값을 알 수 있는거</p>
<p>사용사례 : producer  / consumer architecture
※항상 FIFO를 의미하지는 않는다.
문서를 잘 보고 파악해야한다. 뭔가를 저장하고 있는 대기열같은 느낌일 수도 있음</p>
<h3 id="error">Error</h3>
<h4 id="stackoverflowerror">StackOverflowError</h4>
<p>스택 메모리 공간을 다 썼을 때 발생하는 에러
-재귀함수(recursive function)에서 탈출 못해서 발생(탈출 조건을 안 썼을 경우)</p>
<pre><code>def recur_fibo(n):
    if n &lt;=1:
        return n
    else:
        return(recur_fibo(n-1) + recur_fibo(n-2))</code></pre><h4 id="outofmemoryerror">OutOfMemoryError</h4>
<p>Java의 힙(heap) 메모리를 다 썻을 때 발생
-heap: 객체가 거주하는 메모리 영역
큐에 데이터가 계속 쌓이기만 해서 메모리를 다 잡아먹을 경우
이를 해결하기 위해서는 큐 사이즈를 조정</p>
<p><strong>큐가 다 찼을 때는 어떻게 해야할까?</strong>
-예외(exception) 던지기
-특별한 값(null or false)을 반환
-성공할 때까지 영원히 스레드 블락(block)
-제한된 시간만 블락되고 그래도 안되면 포기</p>
<p>실제로 java에서 이런 4가지 방식을 구현한 몇몇 클래스들이 있는데 
그 중 하나가 LinkedBlockingQueue
<img src="https://velog.velcdn.com/images/ssan_to__/post/b0ceb55b-9338-40dc-894b-214ea2891686/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[시간복잡도]]></title>
            <link>https://velog.io/@ssan_to__/%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</link>
            <guid>https://velog.io/@ssan_to__/%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</guid>
            <pubDate>Mon, 13 Feb 2023 12:00:54 GMT</pubDate>
            <description><![CDATA[<h3 id="시간복잡도">시간복잡도</h3>
<blockquote>
<p>Time Complexity
●시간 복잡도를 이용하면 작성한 코드가 시간이 대략 얼마나 걸릴지 예상할 수 있다.
●표기법으로 대문자 O를 사용한다. (다양한 시간 복잡도가 많지만, 보통 Big-O만 사용한다.)
●영어로는 Big O Notation
●Big O Notation에서 상수는 버린다.
●두 가지 항이 있을 때, 변수가 같으면 큰 것만 빼고 다 버린다.
●입력의 크기 N에 대해서 시간이 얼마나 걸릴지 나타내는 방법
즉, 최악의 경우에 시간이 얼마나 걸릴지 알 수 있다.
●시간복잡도는 소스를 보고 계산할 수 있도 있고, 소스를 작성하기 전에 먼저 계산해볼 수 있다.
●문제를 풀기 전에 먼저 생각한 방법의 시간 복잡도를 계산해보고 이게 시간 안에 수행될 것 같은 경우에만 구현하는 것이 좋다.</p>
</blockquote>
<h4 id="시간복잡도예시">시간복잡도예시</h4>
<blockquote>
<p>총 N명의 사람이 식당에 방문했다.
식당에는 메뉴가 M가 있고, 메뉴판이 1개 있다.
사람 1명이 메뉴판을 읽는데 걸리는 시간은 O(M)이다.
주문한 모든 메뉴는 동시에 나왔고, 각 사람 i가 식사를 하는데 걸리는 시간은 $$A_{i}$$이다.
각 사람이 계산을 하는데 걸리는 시간은 O(P)이다.</p>
</blockquote>
<blockquote>
<p>각 사람이 메뉴판에 있는 모든 메뉴를 읽는 시간 복잡도는 O(NM)
모든 사람이 식사를 마치는데 걸리는 시간 = O(max($$A_{i}$$))
식사를 모두 마친 다음 한 줄로 서서 각자 계산을 하는 시간 복잡도는 = O(NP)</p>
</blockquote>
<p>N번 O(N)</p>
<pre><code class="language-java">int sum = 0;
for(int i =1; i&lt;=N; i++){
    sum += i;
}</code></pre>
<h4 id="on2">$$O(N^2)$$</h4>
<pre><code class="language-java">int sum =0;
for(int i=1; i&lt;N; i++){
    for(int j=1; j&lt;=N; j++){
        if(i==j){
            sum += j;
        }
    }
}</code></pre>
<h4 id="o1">O(1)</h4>
<pre><code class="language-java">int sum = 0;
sum = N*(N+1)/2;</code></pre>
<p>O(1)
O(N)
$$O(N^2)$$
여기에 값을 넣었을때 1억이 나오면 보통 1초 나온다고 본다.
 N=&lt;10만
 O(1) =&gt;1
O(N) =&gt; 10만
$$O(N^2)$$  =&gt; 100억 /100초</p>
<h3 id="메모리">메모리</h3>
<blockquote>
</blockquote>
<p>●보통 가장 많은 공간을 사용하는 것은 보통 배열이다.
●배열이 사용한 공간: 배열의 크기 X 자료형의 크기 B
●보통 배열의 크기가 크면 시간 초과를 받는 경우가 많다.
●불필요한 공간이 없다면, 대부분 메모리 제한은 알아서 지켜진다.</p>
<h3 id="입출력">입/출력</h3>
<blockquote>
<p>JAVA
●Java는 입력은 Scanner, 출력은 System.out을 사용한다.
●Scanner sc = new Scanner(System.in);
●입력이 많은 경우에는 속도가 느리기 때문에, BufferedReader를 사용한다.
●BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
●출력이 많은 경우에는 StringBuilder를 사용해서 한 문자열로 만들어서 출력을 한 번만 사용하거나 BufferedWrtier를 사용한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[OSI 7계층]]></title>
            <link>https://velog.io/@ssan_to__/OSI-7%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@ssan_to__/OSI-7%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Mon, 13 Feb 2023 09:40:09 GMT</pubDate>
            <description><![CDATA[<h4 id="port">Port</h4>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/8025ac08-4277-4689-a730-c135f265de21/image.png" alt=""></p>
<h4 id="osi-7계층">OSI 7계층</h4>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/2ff554ab-a7ff-4197-a499-2a1a18eca072/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/ac3a5c71-d764-4947-9ade-80a31b4d3056/image.png" alt=""></p>
<h4 id="tcp헤더">TCP헤더</h4>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/86850580-eb22-406a-bfeb-2b910e8367f9/image.png" alt="">
<img src="https://velog.velcdn.com/images/ssan_to__/post/879c07d4-3930-4df8-b421-0537651715ce/image.png" alt="패킹스위치">
<img src="https://velog.velcdn.com/images/ssan_to__/post/7114bf55-546c-4ee2-ad46-7e07cb0ec9d8/image.png" alt=""></p>
<h4 id="http헤더">Http헤더</h4>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/776e86d6-839b-4935-b63e-e35a1aa4b8a3/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stateful과 Stateless]]></title>
            <link>https://velog.io/@ssan_to__/Stateful%EA%B3%BC-Stateless</link>
            <guid>https://velog.io/@ssan_to__/Stateful%EA%B3%BC-Stateless</guid>
            <pubDate>Mon, 13 Feb 2023 03:19:47 GMT</pubDate>
            <description><![CDATA[<h4 id="stateful-상태-지속">stateful 상태 지속</h4>
<blockquote>
<p>A와 B가 서로 전송할 수 있다.</p>
</blockquote>
<h4 id="stateless-상태지속x">stateless 상태지속x</h4>
<blockquote>
<p>A가 요청하면 B가 응답할 수 있다. 그리고 응답이 되면 선이 끊긴다.
<img src="https://velog.velcdn.com/images/ssan_to__/post/4c3b866e-7355-4fa8-927b-8d9a30690c65/image.png" alt=""></p>
</blockquote>
<h4 id="웹서버는">웹서버는?</h4>
<blockquote>
<p>stateless -&gt; 상태 지속할 필요가 없음</p>
</blockquote>
<h4 id="웹-인증-시-어려운-이유">웹 인증 시 어려운 이유</h4>
<blockquote>
<p>웹서버 자체가 stateless라서 인증 시 응답이 완료되면 선이 끊긴다. 그러면서 새로운 요청이 왔을때는 인증되지 않은 사람으로 판단한다.</p>
</blockquote>
<h4 id="세션">세션</h4>
<blockquote>
<p>논리적으로 stateful
 <img src="https://velog.velcdn.com/images/ssan_to__/post/f9c747cb-be9c-4794-97ef-574fef07c4a9/image.png" alt=""></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[설정 정보의 암호화 처리]]></title>
            <link>https://velog.io/@ssan_to__/%EC%84%A4%EC%A0%95-%EC%A0%95%EB%B3%B4%EC%9D%98-%EC%95%94%ED%98%B8%ED%99%94-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@ssan_to__/%EC%84%A4%EC%A0%95-%EC%A0%95%EB%B3%B4%EC%9D%98-%EC%95%94%ED%98%B8%ED%99%94-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 06 Feb 2023 09:28:15 GMT</pubDate>
            <description><![CDATA[<p>암호화 처리하는 방법</p>
<p>암호화 작업 인크립션</p>
<p>인크립션 데이터를 복호화 디크립션</p>
<p>인크립션 디크립션 key를 같은걸 쓰는걸 대칭키값 쓰는걸</p>
<p>다르게 쓰는걸 비대칭 RSA</p>
<h4 id="대칭키">대칭키</h4>
<p>Config-service -bootstrap.yml</p>
<pre><code class="language-java">encrypt:
  key: abcdefghijklmopqrstuvwyz0123456789</code></pre>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/c0c499da-6e3a-4319-9371-6737961a6e7d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/1cbc2920-0f4d-4656-b581-609a8f36c612/image.png" alt=""></p>
<p>user-service에 bootstrap.yml 추가</p>
<pre><code class="language-java">spring:
  cloud:
    config:
      uri: http://127.0.0.1:8888
      name: user-service</code></pre>
<p>config파일에 bootstrap.yml</p>
<pre><code>encrypt:
  key: abcdefghijklmopqrstuvwyz0123456789</code></pre><pre><code>server:
  port: 8888

spring:
  application:
    name: config-service
  rabbitmq: #rabbitmq 설정 추가
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
  profiles:
    active: native
  cloud:
    config:
      server:
        native:
          search-locations: file://#yaml 파일이 있는 폴더 위치를 넣어주면 된다.</code></pre><p><img src="https://velog.velcdn.com/images/ssan_to__/post/aec7e3b3-ec80-4bfc-95a9-95ba2fb2e6a0/image.png" alt=""></p>
<p>이렇게 옮겨두면 복호화 되는걸 알 수 있다.</p>
<h4 id="비대칭">비대칭</h4>
<p>암호화할때는 privateKey
암호 풀때는 publicKey</p>
<p>apiEncryptionKey
<img src="https://velog.velcdn.com/images/ssan_to__/post/a3dad51a-70af-4665-83f8-a63bb5fcc31f/image.png" alt="apiEncryptionKey"></p>
<p>trustServer.cer
<img src="https://velog.velcdn.com/images/ssan_to__/post/fe0bca66-82bc-4baf-98e7-498de45f8b3f/image.png" alt="인증서파일"></p>
<p>인증서파일을 다시 jks파일로
<img src="https://velog.velcdn.com/images/ssan_to__/post/9d22b519-818b-4373-8657-2d4987217db9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/5dc8dcbf-610a-48e4-8bc4-62614489216d/image.png" alt=""></p>
<p>똑같이 복호화 시켜주면된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Cloud Bus (RabbitMQ)]]></title>
            <link>https://velog.io/@ssan_to__/Spring-Cloud-Bus-RabbitMQ</link>
            <guid>https://velog.io/@ssan_to__/Spring-Cloud-Bus-RabbitMQ</guid>
            <pubDate>Fri, 03 Feb 2023 07:39:12 GMT</pubDate>
            <description><![CDATA[<p>세번째 방법인 AMQP(Advanced Message Queuing Protocol)이라는 메세지 큐잉 프로토콜을 사용하여야한다. 유명한 프로젝트로는 RabbitMQ, Kafka 등이 있지만 대용량 처리나 속도보다는 정확성이 더 중요한 설정이니 RabbitMQ를 사용하여 진행해보려고 한다.</p>
<p>RabbitMQ를 사용하기 위해서는 Erlang 설치을 먼저 설치해야한다.</p>
<p><a href="https://www.erlang.org/downloads">Erlang 홈페이지</a>
windows installer를 눌러 설치</p>
<p>개인 환경변수를 Path에 넣어주고 RabbitMQ설치하면 된다.</p>
<p><a href="https://www.rabbitmq.com/download.html">RabbitMQ 홈페이지</a>
window용으로 Chocolatey package 나 Windows Installer 다운</p>
<p>다음 서비스에서 RabbitMQ가 들어가 있는지 확인 후 바로 환경 변수도 등록</p>
<p>마지막으로 Management Plugin을 설치해주면 더 편하게 사용 가능한데
C:\Program Files\RabbitMQ Server\rabbitmq_server-3.11.8\sbin에서
powershell을 열고</p>
<pre><code>rabbitmq-plugins enable rabbitmq_management</code></pre><p><a href="http://localhost:15672/">웹페이지 접근 가능해짐</a>
로그인 정보 : guest/guest</p>
<p>userservice / gateway / config 가 RabbitMQ의 client역할로서 다 등록을 해줄것이다 RabbitMQ에 변경사항이 생기면 해당하는 변경사항을 AMQP이라는 프로토콜로 받기 위한 클라이언트 역할이라고 보면 된다.</p>
<h4 id="프로젝트-적용">프로젝트 적용</h4>
<p>config server</p>
<pre><code class="language-java">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-bus-amqp&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-bootstrap&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
<p>config server에 각 actuator, bus amqp, bootstrap의 의존성을 추가한다.</p>
<pre><code class="language-java">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-bus-amqp&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
<p>config application</p>
<pre><code class="language-java">server:
  port: 8888

spring:
  application:
    name: config-service
  rabbitmq: #rabbitmq 설정 추가
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
  cloud:
    config:
      server:
        native:
          search-locations: file://${user.home}/Desktop/Program/0_workspace/0_project/individual/ecommerce
        git:
          uri: file://C:/Users/82107/Desktop/Program/0_workspace/0_project/individual/ecommerce

management: #actuator 설정
  endpoint:
    web:
      exposure:
        include: health, busrefresh</code></pre>
<p>user-service gateway에도 추가</p>
<pre><code class="language-java">  rabbitmq: #rabbitmq 설정 추가
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest

management: #actuator 설정
  endpoints:
    web:
      exposure:
        include: refresh, health, beans, busrefresh</code></pre>
<p>userservice bootstrap.yml를</p>
<pre><code class="language-java">spring:
  cloud:
    config:
      uri: http://112.1.1.2:8888
      name: config-service</code></pre>
<p>추가</p>
<p>busrefresh postman으로 post실행하면 http상태코드가 204로 반환되는걸 알 수있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Config - Gateway]]></title>
            <link>https://velog.io/@ssan_to__/Spring-Config-Gateway</link>
            <guid>https://velog.io/@ssan_to__/Spring-Config-Gateway</guid>
            <pubDate>Thu, 02 Feb 2023 19:40:37 GMT</pubDate>
            <description><![CDATA[<h4 id="gateway-설정">Gateway 설정</h4>
<pre><code class="language-java">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-config&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-bootstrap&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
<p>bootstramp.yml파일</p>
<pre><code class="language-java">spring:
  cloud:
    config:
      uri: http://112.1.1.2:8888
      name: ecommerce #파일명 yml</code></pre>
<p>application.yml파일</p>
<pre><code class="language-java">management:
  endpoints:
    web:
      exposure:
        include: refresh, health, beans, httptrace</code></pre>
<p>httptrace을 쓰기 위해서는 설정을 추가해줘야한다.</p>
<p>GatewayApplication에 bean추가</p>
<pre><code class="language-java">@Bean
    public HttpTraceRepository httpTraceRepository(){
        return new InMemoryHttpTraceRepository();
    }</code></pre>
<p>application.yml 추가</p>
<pre><code class="language-java">- id: user-service
  uri: lb://USER-SERVICE
  predicates:
    - Path=/order-service/actuator/**
    - Method=GET,POST
  filters:
    - RemoveRequestHeader=Cookie
    - RewritePath=/user-service(?&lt;segment&gt;.*), /$\{segment}</code></pre>
<h4 id="application-file을-여러개-생성-시">application file을 여러개 생성 시</h4>
<p>이름을 서로 정보를 다르게 해보자</p>
<pre><code>token:
  expiration_time: 86400000
  secret: user_token_dev</code></pre><pre><code>token:
  expiration_time: 86400000
  secret: user_token_prod</code></pre><p>근데 이렇게 하면 두개의 정보가 다르기에 jwt가 판단한다.</p>
<h4 id="git-repo--native-file-repo">git repo &amp; native file repo</h4>
<pre><code>githb github 주소 로 이어지는것도 가능 합니다. github repository 값을 넣어 가능 합니다.
profiles:
   active: native #cloud.config.server.native 와 같이 연계되어 사용한다.
native:
  search-locations:  yaml 파일이 있는 폴더 위치를 넣어주면 된다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Config]]></title>
            <link>https://velog.io/@ssan_to__/Spring-Config</link>
            <guid>https://velog.io/@ssan_to__/Spring-Config</guid>
            <pubDate>Wed, 01 Feb 2023 16:59:51 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-config란">Spring Config란</h2>
<p>각각의 구성정보파일 application.yml파일 정보가 바뀌면 다시 빌드하고 해야하는데 그걸 개선하기 위해 외부에 있는 시스템을 통해서 구성파일 정보를 관리할 수 있는 기능</p>
<p>git을 통해서</p>
<pre><code>$ ~/Desktop/work/git-local-repo 디렉토리 생성
$ cd init
ecmmerce.yml 파일 생성
$ git add ecommerce.yml
$ git commit -m &quot;upload an application yaml file&quot;</code></pre><p>후 code ecommerce.yml 치면 vs가 켜지는데</p>
<p>기존 yml파일에 있던 중복되던 내용을</p>
<pre><code class="language-java">token:
  expiration_time: 86400000
  secret: user_token</code></pre>
<h4 id="config-server-생성">Config Server 생성</h4>
<p>프로젝트 Spring Cloud Config - Config server만 선택 후 생성</p>
<pre><code class="language-java">@SpringBootApplication
@EnableConfigServer
public class ConfigServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigServiceApplication.class, args);
    }
</code></pre>
<p>바로 @EnableConfigServer를 써주면서 Config 서버로 구동시켜준다</p>
<pre><code class="language-java">server:
  port: 8888

spring:
  application:
    name: config-service
  cloud:
    config:
      server:
        git:
          uri: file://{등록한 y폴더경로}/ecommerce</code></pre>
<p><a href="http://112.1.1.2:8888/ecommerce/default">http://112.1.1.2:8888/ecommerce/default</a> 입력하면</p>
<pre><code class="language-java">// 20230201234421
// http://112.1.1.2:8888/ecommerce/default
{
  &quot;name&quot;: &quot;ecommerce&quot;,
  &quot;profiles&quot;: [
    &quot;default&quot;
  ],
  &quot;label&quot;: null,
  &quot;version&quot;: &quot;e95f07fb2851a044c0db619517a279b877c69ce9&quot;,
  &quot;state&quot;: null,
  &quot;propertySources&quot;: [
    {
      &quot;name&quot;: &quot;file://C:/l/ecommerce/ecommerce.yml&quot;,
      &quot;source&quot;: {
        &quot;token.expiration_time&quot;: 86400000,
        &quot;token.secret&quot;: &quot;user_token&quot;,
        &quot;gateway.ip&quot;: &quot;112.1.1.2&quot;
      }
    }
  ]
}</code></pre>
<p>겹치는 user Service에서 yml파일 주석 처리 후 </p>
<pre><code class="language-java">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-config&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-bootstrap&lt;/artifactId&gt;
&lt;/dependency&gt;
</code></pre>
<p>bootstrap.yml</p>
<pre><code class="language-java">spring:
  cloud:
    config:
      uri: http://112.1.1.2:8888
      name: ecommerce #파일명 yml</code></pre>
<p>pom.xml과 application.yml과 동일한 위치의 resources 폴더 아래에 bootstrap.yml 파일을 생성한 후 우리가 실행한 서버의 uri와 접근할 name을 적어준다.</p>
<p>내용을 확인하고자 기존에 있던 usercontroller에 health_check부분을 수정</p>
<pre><code class="language-java">    @GetMapping(&quot;/health_check&quot;)
    public String status(){
        return String.format(&quot;유저 서비스&quot;
                + &quot;, port(local.server.port) = &quot; + env.getProperty(&quot;local.server.port&quot;)
                + &quot;, port(server.port) = &quot; + env.getProperty(&quot;server.port&quot;)
                + &quot;, token secret = &quot; + env.getProperty(&quot;token.secret&quot;)
                + &quot;, token expiration time = &quot; + env.getProperty(&quot;token.expiration_time&quot;)
        );
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/0757cdc7-248a-4bc8-80bb-e682adde43e5/image.png" alt="">
ecommerce 정보들을 가져오는걸 볼 수 있다</p>
<p>여기서 이런 설정들이 변경이 자주 있을텐데 이런 방법을 가져오는데는 3가지 방법이 있는데</p>
<p>＊서버 재기동</p>
<ul>
<li>Actuator refresh</li>
<li>Spring cloud bus 사용 세가지있다</li>
</ul>
<h4 id="actuator-refresh">Actuator refresh</h4>
<p>솔직히 서버 재기동을 하기에는 너무 번거로움이 많다 그래서 제외하고 바로 Actuator refresh부터 해보았다.</p>
<pre><code class="language-java">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
<pre><code class="language-java">@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        AuthenticationManager authenticationManager = getAuthenticationFilter(http);

        http.csrf().disable();
        http.authorizeRequests()
                .antMatchers(&quot;/actuator/**&quot;).permitAll() //actuator는 모두 허용
                .antMatchers(&quot;/error/**&quot;).permitAll()
   //             .antMatchers(&quot;/**&quot;).hasIpAddress(&quot;112.1.1.2&quot;)
                .and()
                .authenticationManager(authenticationManager)
                .addFilter(getAuthenticationFilter(authenticationManager));

        //h2 console error 해결을 위해
        http.headers().frameOptions().disable();

        return http.build();
</code></pre>
<p>application.yml</p>
<pre><code class="language-java">management:
  endpoints:
    web:
      exposure:
        include: refresh, health, beans

#refresh: 현재 마이크로서비스에 있는 config-service에서 가져와야하는 정보를 refresh하겠다</code></pre>
<p>등록후 health나 beans는 web으로 가능하지만 refresh는 안된다
POSTMAN에서 POST <a href="http://112.1.1.2:52750/actuator/refresh">http://112.1.1.2:52750/actuator/refresh</a>
를 보내 변경하는 식으로 해야한다.</p>
<p>다시 VS로 돌아가서 yml파일 정보를 바꾼 후 
$ git add ecommerce.yml
$ git commit -m &quot;changed values&quot;</p>
<p>로 정보를 바꾼 후
<a href="http://112.1.1.2:52750/actuator/refresh">http://112.1.1.2:52750/actuator/refresh</a> postman으로 실행하면 
서버 재가동 없이 정보가 바뀌는걸 알 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JWT]]></title>
            <link>https://velog.io/@ssan_to__/JWT</link>
            <guid>https://velog.io/@ssan_to__/JWT</guid>
            <pubDate>Wed, 01 Feb 2023 12:48:23 GMT</pubDate>
            <description><![CDATA[<p>AuthenticationFilter 에 로그인 성공했을 때 로직에서 UserDto를 반환받기</p>
<pre><code class="language-java">    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        log.debug(((User)authResult.getPrincipal()).getUsername());

        String userName = ((User)authResult.getPrincipal()).getUsername();
        UserDto userDetails = userService.getUserDetailsByEmail(userName);

    }</code></pre>
<p>기존의 log만 찍었던 로직을 다음과 같이 입력 받은 userName 값으로 userId값을 받아올 수 있도록 변경한다.</p>
<p>여기서 userService에서 getUserDetailsByEmail(userName)이 정의 되지 않았기 때문에 정의해줘야한다
생성자에 UserService와 Enviroment값을 매개변수로 설정했기 때문에
WebSecurity에서 생성자로 Filter를 생성하는 부분에도 값을 넣어줘야한다.</p>
<pre><code class="language-java">    private AuthenticationFilter getAuthenticationFilter(AuthenticationManager authenticationManager) {
        return new AuthenticationFilter(authenticationManager, userService, env);
    }</code></pre>
<p>UserServiceImpl에서 </p>
<pre><code class="language-java">    @Override
    public UserDto getUserDetailsByEmail(String email) {

        UserEntity userEntity = userRepository.findByEmail(email);

        if(userEntity == null)
            throw new UsernameNotFoundException(email);

        return new ModelMapper().map(userEntity, UserDto.class);
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/3ea64108-9046-496c-bf7c-0a6bfeddaad4/image.png" alt="">
<img src="https://velog.velcdn.com/images/ssan_to__/post/4ed5987c-342e-424e-ba74-1a1019ec47ca/image.png" alt=""></p>
<h3 id="jwt-추가">Jwt 추가</h3>
<pre><code class="language-java">        &lt;dependency&gt;
            &lt;groupId&gt;io.jsonwebtoken&lt;/groupId&gt;
            &lt;artifactId&gt;jjwt&lt;/artifactId&gt;
            &lt;version&gt;0.9.1&lt;/version&gt;
        &lt;/dependency&gt;</code></pre>
<p>application.yml</p>
<pre><code class="language-java">token:
  expiration_time: 86400000 #ms단위 하루설정
  secret: user_token</code></pre>
<p>toeken을 이용해준다</p>
<pre><code class="language-java">//로그인 성공했을 때 로직
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        log.debug(((User)authResult.getPrincipal()).getUsername());
        String userName = ((User)authResult.getPrincipal()).getUsername();
        UserDto userDetails = userService.getUserDetailsByEmail(userName);

        String token = Jwts.builder()
                .setSubject(userDetails.getUserId()) //token 내용
                .setExpiration(new Date(System.currentTimeMillis() +
                        Long.parseLong(env.getProperty(&quot;token.expiration_time&quot;)))) //파기 날짜
                .signWith(SignatureAlgorithm.HS512, env.getProperty(&quot;token.secret&quot;)) //token 생성 알고리즘과 키 값
                .compact();

        response.addHeader(&quot;token&quot;, token);
        response.addHeader(&quot;userId&quot;, userDetails.getUserId()); //원래는 반환하지 않는 데이터이지만 확인용

    }</code></pre>
<p>또한 
userservice에서 jwt를 통해 토큰을 발급 받는 기능을 추가했으니 gateway에도
jwt dependency추가해준다.</p>
<pre><code class="language-java">@Component
@Slf4j
public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory&lt;AuthorizationHeaderFilter.Config&gt; {

    private Environment env;

    //생성시 Config class를 상속받은 Factory로 넘겨줘야해서 lombok을 사용하지 않고 다음과 같이 처리
    public AuthorizationHeaderFilter(Environment env) {
        super(Config.class);
        this.env = env;
    }

    public static  class Config{}

    //login -&gt; token -&gt; users (with token) -&gt; header(include token)
    @Override
    public GatewayFilter apply(Config config) {
        return ((exchange, chain) -&gt; {
            ServerHttpRequest request = exchange.getRequest();

            //header에 HttpHeaders.AUTHORIZATION 값이 존재하는지 확인
            if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
                return onError(exchange, &quot;no authorization header&quot;, HttpStatus.UNAUTHORIZED);
            }

            String authorizationHeader = request.getHeaders().get(HttpHeaders.AUTHORIZATION).get(0);//배열이라서
            String jwt = authorizationHeader.replace(&quot;Bearer&quot;, &quot;&quot;);

            if(!isJwtValid(jwt)){
                return onError(exchange, &quot;JWT Token is not valid&quot;, HttpStatus.UNAUTHORIZED);
            }

            return chain.filter(exchange);
        });

    }

    //token이 유효한지 확인
    private boolean isJwtValid(String jwt) {
        boolean returnValue = true;

        String subject = null;

        try {
            subject = Jwts.parser().setSigningKey(env.getProperty(&quot;token.secret&quot;))
                    .parseClaimsJws(jwt).getBody()
                    .getSubject();
            //token의 내용을 가져옴
            //jws로 파싱하고 그 앞에서 subject값
        } catch (Exception ex){
            returnValue = false;
        }

        if(subject == null || subject.equals(&quot;&quot;)){
            returnValue = false;
        }

        return returnValue;
    }

    //에러 발생시 에러 값을 response
    //Mono, Flux -&gt; Spring WebFlux / 데이터 단위 단일=Mono, 복수=Flux
    private Mono&lt;Void&gt; onError(ServerWebExchange exchange,
                               String err,
                               HttpStatus httpStatus) {

        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(httpStatus);

        log.error(err);
        return response.setComplete();

    }


}</code></pre>
<p>그리고 yml에 AuthorizationHeaderFilter 설정해준다</p>
<pre><code class="language-java">        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/login
            - Method=POST
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/user-service/(?&lt;segment&gt;.*), /$\{segment}
        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/users
            - Method=POST
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/user-service/(?&lt;segment&gt;.*), /$\{segment}
        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/**
            - Method=GET
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/user-service/(?&lt;segment&gt;.*), /$\{segment}
            - AuthorizationHeaderFilter</code></pre>
<p>user-service중 GET방식에 AuthorizationHeaderFilter filter를 추가</p>
<p>Authenticate 필요X post-&gt; /user-service/users (회원가입)
                   post-&gt; /user-service/login (로그인)
Authenticate 필요O GET-&gt; /user-service/users (회원정보)</p>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/462158a0-b1a7-4e35-bf4d-29bb6bdbb5f5/image.png" alt=""></p>
<p>Bearer Token에 발급 받은 토큰을 입력 후 진행 과정을 디버그로 확인해보자</p>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/b768b36d-e07a-4f45-9ca7-1a29be466d3f/image.png" alt=""></p>
<p>AuthorizationHeaderFilter의 apply()로 요청이 들어가고 </p>
<pre><code class="language-java">//header에 HttpHeaders.AUTHORIZATION 값이 존재하는지 확인
            if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
                return onError(exchange, &quot;no authorization header&quot;, HttpStatus.UNAUTHORIZED);
            }</code></pre>
<p>이걸로 인해 Header에 Authorization : value 가 존재하는지 확인
<img src="https://velog.velcdn.com/images/ssan_to__/post/38a39349-9845-4022-a672-e46c63274b2c/image.png" alt="">
value에 Bearer + token으로 값이 넘어오는데 해당 값을 replace()로 처리해주고 isJwtValid()를 통해 유효성을 체크</p>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/e937b831-c602-4ad2-84c5-92b49bdc7e67/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[인증 권한 흐름 파악]]></title>
            <link>https://velog.io/@ssan_to__/%EA%B6%8C%ED%95%9C-%EC%9D%B8%EC%A6%9D-%ED%9D%90%EB%A6%84-%ED%8C%8C%EC%95%85</link>
            <guid>https://velog.io/@ssan_to__/%EA%B6%8C%ED%95%9C-%EC%9D%B8%EC%A6%9D-%ED%9D%90%EB%A6%84-%ED%8C%8C%EC%95%85</guid>
            <pubDate>Wed, 01 Feb 2023 10:39:03 GMT</pubDate>
            <description><![CDATA[<h3 id="디버그-체크">디버그 체크</h3>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/86aabe77-7acf-4259-82db-99b02ca4e381/image.png" alt="">
<img src="https://velog.velcdn.com/images/ssan_to__/post/45028131-e388-4ad2-93d3-30d2a6c72b4d/image.png" alt="">
<img src="https://velog.velcdn.com/images/ssan_to__/post/6bcdcbe0-5fb1-4965-86a9-f711176bb89d/image.png" alt=""></p>
<blockquote>
<p>AuthenticationFilter 파일에서 두개의 브레이크 포인트를 찍었다. attemptAuthentication 인증 요청시 요청이 되는지, successfulAuthentication 로그인 성공시 로그가 찍히는지 확인을 위해서 찍었다. </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/60522c07-8a3e-4c8c-90b3-7a5f0a144f0e/image.png" alt="">
postman으로 로그인 시도하면</p>
<p>첫 번째 디버그 포인트
<img src="https://velog.velcdn.com/images/ssan_to__/post/9d6abf05-0acd-4faa-88cd-ba3fa191ab12/image.png" alt="">
으로 먼저 인증 요청 부분인 AuthenticationFilter의 attemptAuthentication로 실행이 된다.
request를 통해 RequestLogin 객체를 만들고 그 객체를 UsernamePasswordAuthenticationToken을 통해 세번째 디버그포인트인 
loadUserByUsername 전달
<img src="https://velog.velcdn.com/images/ssan_to__/post/cead7bd6-da7c-4b39-8ac2-ef651db78687/image.png" alt=""></p>
<p>userEntity에서 정보 실제 존재여부 확인 후 없을 시엔 exception을 터트리고 
있을경우 다시 User객체로 반환한다</p>
<p><img src="https://velog.velcdn.com/images/ssan_to__/post/57bdd74f-9b91-466c-bdad-efe878853863/image.png" alt=""></p>
<p>이럼 이전에 실행했던 retrieveUser의 catch에 걸리게 되고 var4로 UsernameNotFoundException Exception
<img src="https://velog.velcdn.com/images/ssan_to__/post/c79f78c1-6f73-407d-8fa4-337b6cbc1a91/image.png" alt="">
authenticate에서도 catch에 걸리게 되고 debug로 로그를 찍고 BadCredentialsException Exception을 생성하여
<img src="https://velog.velcdn.com/images/ssan_to__/post/70e49394-a744-482b-963e-4efce1234e6d/image.png" alt="">
<img src="https://velog.velcdn.com/images/ssan_to__/post/181f62e4-6c15-4cd3-bf2d-1d0a290a20bd/image.png" alt="">
<img src="https://velog.velcdn.com/images/ssan_to__/post/43aaee6e-f362-4111-b849-e36e5a73a58e/image.png" alt="">
<img src="https://velog.velcdn.com/images/ssan_to__/post/15c9b349-981f-4c15-8934-1a49d3cfdae0/image.png" alt="">
<img src="https://velog.velcdn.com/images/ssan_to__/post/4b28ebae-9045-4cb9-95e0-20d147899593/image.png" alt="">
그후에는 AuthenticationException을 ProviderManager의 authenticate 메서드에서 던지게 된다. 그리고 계속 filter를 타고 처리가 되어지는데
<img src="https://velog.velcdn.com/images/ssan_to__/post/65057ef0-4608-405c-ba81-8a238b51c9e0/image.png" alt="">
결국 401이 뜨고 만다.</p>
]]></description>
        </item>
    </channel>
</rss>