<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sujeong</title>
        <link>https://velog.io/</link>
        <description>계속해서 공부하는 개발자입니다 :)</description>
        <lastBuildDate>Wed, 29 Mar 2023 08:26:57 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sujeong</title>
            <url>https://images.velog.io/images/0_sujeong/profile/584fa3f6-36b5-4763-8e3e-cd7a71de10b4/KakaoTalk_20211017_165552625.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sujeong. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/0_sujeong" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Tomcat] rotatelogs 사용 시tomcat pid가 제대로 저장되지 않는 문제]]></title>
            <link>https://velog.io/@0_sujeong/Tomcat-rotatelogs-%EC%82%AC%EC%9A%A9-%EC%8B%9Ctomcat-pid%EA%B0%80-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%A0%80%EC%9E%A5%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@0_sujeong/Tomcat-rotatelogs-%EC%82%AC%EC%9A%A9-%EC%8B%9Ctomcat-pid%EA%B0%80-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%A0%80%EC%9E%A5%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Wed, 29 Mar 2023 08:26:57 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-상황">문제 상황</h2>
<p>rotatelogs를 이용시, tomcat pid가 저장되어야하는 경로에 tomcat pid가 아닌 rotatelogs 데몬의 pid가 저장됨
<code>shutdown.sh</code>이나 <code>catalina.sh stop</code> 을 통해 종료할 때에 프로세스가 종료되지 않는 문제</p>
<h2 id="해결-방안">해결 방안</h2>
<p><code>catalina.sh</code> 파일내용 중 tomcat.pid 저장하는 부분을 수정</p>
<ul>
<li>원본 <pre><code>if [ ! -z &quot;$CATALINA_PID&quot; ]; then
  echo $! &gt; &quot;$CATALINA_PID&quot;
fi</code></pre></li>
<li>변경 후<pre><code>if [ ! -z &quot;$CATALINA_PID&quot; ]; then
  #echo $! &gt; &quot;$CATALINA_PID&quot;
  `/bin/ps -ef | grep Tomcat_instance_Name | grep java | grep -v grep | awk &#39;{print $2}&#39; &gt; $CATALINA_PID`
fi</code></pre><blockquote>
<p>종료시에 <code>-force</code> 옵션추가 하면, 정상적으로 종료가 되지 않았을 때 해당 pid를 가지고 kill -9 명령어를 입력해서 종료함</p>
</blockquote>
</li>
</ul>
<p><em>출처</em>
<a href="https://siron89.tistory.com/23">rotatelogs 사용시 Tomcat PID 문제</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring] 환경에 맞는 properties 파일 구성]]></title>
            <link>https://velog.io/@0_sujeong/spring-%ED%99%98%EA%B2%BD%EC%97%90-%EB%A7%9E%EB%8A%94-properties-%ED%8C%8C%EC%9D%BC-%EA%B5%AC%EC%84%B1</link>
            <guid>https://velog.io/@0_sujeong/spring-%ED%99%98%EA%B2%BD%EC%97%90-%EB%A7%9E%EB%8A%94-properties-%ED%8C%8C%EC%9D%BC-%EA%B5%AC%EC%84%B1</guid>
            <pubDate>Sat, 25 Feb 2023 17:24:02 GMT</pubDate>
            <description><![CDATA[<h1 id="환경에-맞는-properties-파일-구성이란">환경에 맞는 properties 파일 구성이란?</h1>
<p>개발을 하다보면 여러가지 환경(=개발, 배포, 테스트 등)에 맞춰 properties 파일의 값을 변경해줘야할 일이 있습니다.</p>
<blockquote>
</blockquote>
<ul>
<li>DB 접속 정보</li>
<li>log 파일의 위치</li>
</ul>
<p>이런 내용들을 각 환경에 맞는 properties로 구성해놓는 뒤, 각 환경에서 가져다 쓰는 properties 파일이 다르면 편리하겠죠?</p>
<blockquote>
</blockquote>
<p>ex)</p>
<ul>
<li>개발 서버(window) : DB - 개발DB, 파일 경로 등의 위치 - <code>D:/dev/...(윈도우 폴더구조에 맞게)~</code></li>
<li>실서버(linux) :  DB - 운영DB, 파일 경로 등의 위치 - <code>/home/...(리눅스 디렉토리 구조에 맞게)~</code></li>
</ul>
<h1 id="구성하는-방법">구성하는 방법</h1>
<ol>
<li>각 필요한 환경에 맞춰 properties파일을 구성한다
변경 전 :  <code>application.properties</code> 
변경 후 : <code>application-dev.properties</code>, <code>application-test.properties</code>, <code>application-prod.properties</code> 등
이 때, dev, test, prod은 정해진 이름이 아니고 원하는 이름으로 정하면 된다.<blockquote>
</blockquote>
<code>application.properties</code> 파일이 존재해도 되고, 존재하지 않아도 된다. 다만 <code>default</code> 값이 <code>application.properties</code> 파일이기 때문에, 존재하지 않는다면 default 파일도 설정해야한다.</li>
</ol>
<ol start="2">
<li><p>프로젝트 실행 시, 어떤 환경인지 설정한다.
 아래의 방법 중 하나를 선택해서 진행하면 된다. </p>
<ul>
<li>intelliJ와 같은 IDE 툴이라면, 실행 구성 편집에 들어가서 active profile 값에 <code>prod</code> 와 같이 옵션 설정</li>
<li><code>SpringApplication.setAdditionalProfiles(...)</code>을 애플리케이션이 실행되기전에 호출해 설정</li>
<li>properties 파일에서 <code>spring-profiles-active=prod</code> 와 같이 설정</li>
<li>OS 환경변수에 <code>SPRING-PROFILES-ACTIVE</code> 설정</li>
<li>jar 파일을 실행할때, <code>-Dspring.profiles.active=prod</code> 옵션을 주어서 실행<blockquote>
<p>설정을 <code>application.properteis</code>파일로 하고싶다면, <code>default</code> 값으로 설정하면 된다.</p>
</blockquote>
</li>
</ul>
</li>
<li><p>확인하는 방법
프로젝트를 실행할 때, 로그에서
<code>No active profile set, falling back to 1 default profile: &quot;default&quot;</code> 와 같은 문구나 <code>The following 1 profile is active: &quot;dev&quot;</code> 와 같은 문구가 초반에 뜨게 된다.
첫번째 문장은 active된 profile이 없어서 <code>default profile(=application.properties)</code>
두번째 문장은 dev profile 로 설정되었다는 로그다.</p>
</li>
</ol>
<blockquote>
<p>스프링 버전 2.4 이하에서는 각 properties 파일마다 <code>spring.profile.active=[현재 프로필]</code> 과같이 명시를 해주어야 했는데, 이 이후에는 그렇지 않다고 한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] 테이블 데이터 스캔 방식]]></title>
            <link>https://velog.io/@0_sujeong/DB-%ED%85%8C%EC%9D%B4%EB%B8%94-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%8A%A4%EC%BA%94-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@0_sujeong/DB-%ED%85%8C%EC%9D%B4%EB%B8%94-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%8A%A4%EC%BA%94-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Mon, 12 Dec 2022 09:14:17 GMT</pubDate>
            <description><![CDATA[<h1 id="스캔-종류">스캔 종류</h1>
<ul>
<li>테이블 스캔 : 테이블 <strong>데이터로 직접</strong> 접근</li>
<li>인덱스 스캔 : <strong>인덱스를 통해</strong> 테이블 데이터에 접근</li>
</ul>
<h2 id="테이블-스캔">테이블 스캔</h2>
<h3 id="테이블-풀-스캔--table-full-scan-">테이블 풀 스캔 ( Table Full Scan )</h3>
<p><img src="https://velog.velcdn.com/images/0_sujeong/post/5cdbac19-0047-4d56-a132-9b028db895ab/image.png" alt=""></p>
<ul>
<li>테이블에 포함된 레코드를 처음부터 끝까지 읽어들임</li>
<li><code>where</code> 조건문에 기준으로 활용할 인덱스가 없는 경우</li>
<li>대량의 데이터의 접근시 테이블 풀 스캔을 쓰면, 인덱스를 통한 랜덤 엑세스가 발생하지 않는 장점</li>
</ul>
<h2 id="인덱스-스캔">인덱스 스캔</h2>
<h3 id="인덱스-풀-스캔--index-full-scan-">인덱스 풀 스캔 ( Index Full Scan )</h3>
<p><img src="https://velog.velcdn.com/images/0_sujeong/post/de5bc450-bc6e-4d4a-911f-715212a5d81c/image.png" alt=""></p>
<ul>
<li>인덱스만 풀 스캔해 원하는 데이터를 가져오는 것</li>
<li>인덱스는 테이블의 일부 데이터를 사용</li>
<li>검색 되는 데이터 극히 일부면 좋음</li>
</ul>
<h3 id="인덱스-범위-스캔--index-range-scan-">인덱스 범위 스캔 ( Index Range Scan )</h3>
<p><img src="https://velog.velcdn.com/images/0_sujeong/post/2d587fdd-f9da-42c9-8664-74a0ebf7161e/image.png" alt=""></p>
<ul>
<li>인덱스를 범위 기준으로 스캔한 뒤 스캔 결과를 토대로 테이블에 접근( BETWEEN ~ AND, LIKE, &lt; 등을 사용시 )</li>
<li>좁은 범위로는 좋지만 넓은 범위로는 비효율적</li>
</ul>
<h3 id="인덱스-고유-스캔--index-unique-scan-">인덱스 고유 스캔 ( Index Unique Scan )</h3>
<p><img src="https://velog.velcdn.com/images/0_sujeong/post/5278b3b2-b39e-4bd3-8e98-81dc90916e37/image.png" alt=""></p>
<ul>
<li>수직적 탐색만으로 찾는 방식, 기본키나 고유 인덱스로 테이블에 접근( = 사용시 )</li>
<li>인덱스를 사용하는 방식 중 가장 효율적</li>
</ul>
<h3 id="인덱스-루스-스캔--index-loose-scan-">인덱스 루스 스캔 ( Index Loose Scan )</h3>
<p><img src="https://velog.velcdn.com/images/0_sujeong/post/fb3b1670-dc0d-4c5d-824c-f140176eaca4/image.png" alt=""></p>
<ul>
<li>인덱스에서 필요한 부분만 선택하여 스캔하는 방식, <code>where</code> 조건문을 기준으로 불필요한 인덱스 키는 무시 ( GROUP BY, MAX(), MIN() 함수 포함 시 )</li>
</ul>
<h3 id="인덱스-병합-스캔--index-merge-scan-">인덱스 병합 스캔 ( Index Merge Scan )</h3>
<p><img src="https://velog.velcdn.com/images/0_sujeong/post/aeffa8c4-4051-4564-8ebc-da62d9b67111/image.png" alt=""></p>
<ul>
<li>테이블 내에 생성되 인덱스를 통합하여 스캔하는 방식</li>
<li>WHERE문의 조건 열이 서로 다른 인덱스에 존재할 때 사용됨.</li>
<li>인덱스 병합 스캔은 물리적으로 존재하는 개별 인덱스를 각각 접근</li>
</ul>
<h3 id="index-fast-full-scan">index fast full scan</h3>
<ul>
<li>index full scan 보다 빠름, 인덱스 구조를 따라서 스캔하지 않고 세그먼트 전체를 스캔</li>
<li>결과 집합 순서 보장이 안됨</li>
</ul>
<p><em>참조</em></p>
<ol>
<li><a href="https://velog.io/@jooh95/DB-Scan-%EC%A2%85%EB%A5%98-%EC%A0%95%EB%A6%AC">[DB] 테이블 데이터 스캔 방식 및 디스크 접근 정리</a></li>
<li><a href="https://blackhairdeveloper.tistory.com/4">인덱스 풀 스캔(INDEX FULLSCAN)</a></li>
<li><a href="https://pakker.tistory.com/m/113">인덱스 스캔 방식 및 인덱스 쿼리튜닝</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[API] REST resource naming]]></title>
            <link>https://velog.io/@0_sujeong/web-REST-resource-naming</link>
            <guid>https://velog.io/@0_sujeong/web-REST-resource-naming</guid>
            <pubDate>Fri, 29 Jul 2022 01:07:58 GMT</pubDate>
            <description><![CDATA[<h1 id="resource란">resource란?</h1>
<p>REST에서 resource란 기본 데이터 표현으로 일관된 REST resource 명명 전략을 갖는 것은 장기적으로 최고의 설계 결정 중 하나일 것입니다.</p>
<blockquote>
</blockquote>
<p>리소스는 특정 시점에 해당하는 엔티티가 아니라 엔티티 집합에 대한 개념적 매핑입니다.
— Roy Fielding’s dissertation</p>
<h2 id="1-singleton--collection">1. Singleton &amp; Collection</h2>
<p>리소스는 싱글톤 또는 컬렉션인데, 이때 싱글톤은 <code>customer</code>이고, <code>customers</code>는 리소스입니다.
<code>/customers/{customerId}</code>와같은 방법을 사용해 컬렉션 리소스를 식별할 수 있습니다.</p>
<h2 id="2-collection--sub-collection">2. Collection &amp; Sub-collection</h2>
<p>리소스에는 하위 컬렉션 리소스도 포함될 수 있습니다.
<code>customers/{customerId}/accounts</code>와 같은 방식으로 컬렉션 리소스 내부에 싱글톤 리소스를 식별 할 수 있습니다.</p>
<h2 id="3-uri">3. URI</h2>
<p>REST API는 URI를 사용해 리소스를 처리하기 때문에 리소스의 이름을 직관적이고 사용하기 쉽게 만들어야 사용하기 쉽습니다.</p>
<hr>
<h1 id="naming-tip">naming tip</h1>
<h2 id="1-명사-사용">1. 명사 사용</h2>
<p>: 명사에는 동사에 없는 속성이 있는데, 리소스에도 속성이 있어 이를 표현하기에 명사가 좀 더 적합</p>
<pre><code class="language-javascript">http://api.example.com/device-management/managed-devices 
http://api.example.com/device-management/managed-devices/{device-id} 
http://api.example.com/user-management/users
http://api.example.com/user-management/users/{id}</code></pre>
<h2 id="2-일관성">2. 일관성</h2>
<ul>
<li><p>슬래시(/)를 사용해 계층관계를 표현</p>
<pre><code class="language-javascript">http://api.example.com/device-management
http://api.example.com/device-management/managed-devices
http://api.example.com/device-management/managed-devices/{id}
http://api.example.com/device-management/managed-devices/{id}/scripts
http://api.example.com/device-management/managed-devices/{id}/scripts/{id}</code></pre>
</li>
<li><p>URI에 마지막에 슬래시(/)사용 X -&gt; 혼동을 줄 수 있음</p>
<pre><code class="language-javascript">👎http://api.example.com/device-management/managed-devices/
👍http://api.example.com/device-management/managed-devices</code></pre>
</li>
<li><p>하이픈(-) 사용해 가독성 향상</p>
<pre><code class="language-javascript">👎http://api.example.com/device-management/managed-devices
👍http://api.example.com/devicemanagement/manageddevices</code></pre>
</li>
<li><p>밑줄(_) 사용 X, 밑줄 대신 하이픈(-)사용</p>
<pre><code class="language-javascript">👎http://api.example.com/inventory-management/managedEntities/{id}/installScriptLocation
👍http://api.example.com/inventory-management/managed-entities/{id}/install-script-location</code></pre>
</li>
<li><p>URI에 소문자 사용</p>
<pre><code class="language-javascript">👍http://api.example.org/my-folder/my-doc
👎HTTP://API.EXAMPLE.ORG/my-folder/my-doc
👎http://api.example.org/My-Folder/my-doc</code></pre>
</li>
</ul>
<h2 id="3-파일-확장자-사용-x">3. 파일 확장자 사용 X</h2>
<p>파일 확장자는 <code>Content-Type</code>에 의존해 처리</p>
<pre><code class="language-javascript">👎http://api.example.com/device-management/managed-devices.xml
👍http://api.example.com/device-management/managed-devices</code></pre>
<h2 id="4-uri에-crud-사용-x">4. URI에 CRUD 사용 X</h2>
<p>어떤 CRUD 기능을 하는지는 HTTP 요청 방법을 사용해 표현</p>
<pre><code class="language-javascript">//Get all devices
HTTP GET http://api.example.com/device-management/managed-devices
//Create new Device
HTTP POST http://api.example.com/device-management/managed-devices

//Get device for given Id
HTTP GET http://api.example.com/device-management/managed-devices/{id}
//Update device for given Id
HTTP PUT http://api.example.com/device-management/managed-devices/{id}
//Delete device for given Id
HTTP DELETE http://api.example.com/device-management/managed-devices/{id}</code></pre>
<h2 id="5-쿼리-구성요소를-사용해-uri-컬렉션-필터링">5. 쿼리 구성요소를 사용해 URI 컬렉션 필터링</h2>
<pre><code class="language-javascript">http://api.example.com/device-management/managed-devices
http://api.example.com/device-management/managed-devices?region=USA
http://api.example.com/device-management/managed-devices?region=USA&amp;brand=XYZ
http://api.example.com/device-management/managed-devices?region=USA&amp;brand=XYZ&amp;sort=installation-date</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[java] 정규식 사용법]]></title>
            <link>https://velog.io/@0_sujeong/java-%EC%A0%95%EA%B7%9C%EC%8B%9D-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@0_sujeong/java-%EC%A0%95%EA%B7%9C%EC%8B%9D-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Thu, 21 Jul 2022 07:05:17 GMT</pubDate>
            <description><![CDATA[<h1 id="정규식이란">정규식이란?</h1>
<p>문자열에서 특정 문자 조합을 찾기 위한 패턴</p>
<h1 id="java에서-정규식-사용하기">java에서 정규식 사용하기</h1>
<p>java에서 정규식을 사용하기 위해서는 <code>java.util.regex</code>의 <code>Pattern</code>, <code>Matcher</code> 두 가지의 클래스를 이용한다.</p>
<h2 id="pattern">Pattern</h2>
<p>Perl에서 사용되는 구문과 유사한 구문으로 문자열 형식으로 지정된 정규식을 나타냄
Pattern을 통해 문자열로 지정된 정규식을 컴파일한 후, 결과 패턴을 이용해 Matcher를 생성한다.</p>
<h3 id="pattern-사용-방법">Pattern 사용 방법</h3>
<h4 id="matcher-생성-하는-방법">Matcher 생성 하는 방법</h4>
<pre><code class="language-java">Pattern p = Pattern.compile(&quot;a*b&quot;);
Matcher m = p.matcher(&quot;aaaaab&quot;);
boolean b = m.matches();</code></pre>
<h4 id="matcher-생성-하지-않고-하는-방법">Matcher 생성 하지 않고 하는 방법</h4>
<pre><code class="language-java">boolean b = Pattern.matches(&quot;a*b&quot;, &quot;aaaaab&quot;);</code></pre>
<h4 id="차이">차이</h4>
<p>위의 두 코드는 결과가 같다. 이때 이 둘의 차이점은 <code>정규식의 재사용</code>을 두고 얘기할 수 있다.
Matcher를 생성하면 여러 텍스트에 대해서 하나의 생성된 Matcher를 계속 반복해서 사용할 수 있지만, 두번째와 같이 Matcher를 생성하지 않으면, 사용된 정규식은 한번만 사용된다.</p>
<h4 id="주요-메서드">주요 메서드</h4>
<table>
<thead>
<tr>
<th>이름</th>
<th>리턴</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>static</strong> compile(String regex)</td>
<td>void</td>
<td>지정된 정규식을 패턴으로 컴파일</td>
</tr>
<tr>
<td><strong>static</strong> compile(String regex, int flags)</td>
<td>void</td>
<td>지정된 정규식을 패턴으로 컴파일</td>
</tr>
<tr>
<td>matcher(CharSequence input)</td>
<td>Matcher</td>
<td>Matcher 생성</td>
</tr>
<tr>
<td><strong>static</strong> matches(String regex, CharSequence input)</td>
<td>boolean</td>
<td>지정된 정규식 패턴과 일치하는지의 여부</td>
</tr>
<tr>
<td>split(CharSequence input)</td>
<td>String[]</td>
<td>지정된 정규식을 기준으로 텍스트를 분할</td>
</tr>
</tbody></table>
<h4 id="백슬래시-이스케이프">백슬래시, 이스케이프</h4>
<p>정규식을 만들다 보면 <code>(</code>,<code>[</code>,<code>.</code>등 처럼 특수문자를 그대로 인식해야하는 경우나 <code>\d</code>등 같은 정규식 문법을 사용해야하는데, java에 <code>&quot;\(&quot;</code>으로 입력해 사용하면 오류가 난다.</p>
<ul>
<li>이스케이프
이때는 특수문자 앞에는 <code>\\</code>이렇게 백슬래시를 2번 붙여서 사용한다. <code>\\(</code>
<code>(</code> -&gt; <code>\\(</code></li>
<li>백슬래시
이때는 <code>\</code> 앞에 한번 더 붙여 사용한다.
<code>\d</code>  -&gt; <code>\\d</code></li>
</ul>
<h2 id="matcher">Matcher</h2>
<p>주어진 패턴과 텍스트가 일치하는지 확인하는 엔진</p>
<h3 id="matcher-사용-방법">Matcher 사용 방법</h3>
<h4 id="확인-작업과-주요-메서드를-조합해서-사용">확인 작업과 주요 메서드를 조합해서 사용</h4>
<blockquote>
</blockquote>
<p>만약 <code>matches()</code>, <code>lookingAt()</code>, <code>find()</code>를 하지 않고 바로 <code>group()</code>와 같은 메서드를 호출하면 오류 발생</p>
<pre><code class="language-java">String orgFileName = &quot;가나다(1)&quot;;//테스트용 텍스트
Pattern pattern = Pattern.compile(&quot;\\([0-9]+\\)$&quot;);//정규식 컴파일
Matcher matcher = pattern.matcher(orgFileName);//Matcher 생성
while (matcher.find()) {//확인 작업
    String group = matcher.group();//찾아진 텍스트 
    System.out.println(&quot;Found: &quot; group);
}</code></pre>
<h4 id="확인-작업">확인 작업</h4>
<ul>
<li><p>matches()
: 정규식을 전체 문자열에 대해서 <code>완전히</code> 일치하는지 여부</p>
<blockquote>
</blockquote>
<p>ex) 정규식이 <code>\[a-zA-Z\\s\]</code>인 경우,
&quot;aaa 123 bbb&quot;  -&gt;  false
&quot;aaa bbb&quot;  -&gt;  true</p>
</li>
<li><p>lookingAt()
: 정규식에 일치하는 문자열로 시작하는지 여부</p>
<blockquote>
</blockquote>
<p>ex) 정규식이 <code>123</code>인 경우,
&quot;abd 123&quot; -&gt;false
&quot;123 abc&quot; -&gt; true</p>
</li>
<li><p>find()
: 정규식에 해당하는 문자열이 있는지 여부</p>
<blockquote>
</blockquote>
<p>ex) 정규식이 <code>123</code>인 경우,
&quot;abd 123&quot; -&gt; true
&quot;123 abc&quot; -&gt; true
&quot;가나다 text 123 가나다 12 abc&quot; -&gt; true
&quot;가나다 text 111 가나다 12 abc&quot; -&gt; false</p>
</li>
</ul>
<h4 id="주요-메서드-1">주요 메서드</h4>
<table>
<thead>
<tr>
<th>이름</th>
<th>리턴</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>group()</td>
<td>String</td>
<td>지정된 정규식과 일치하는 텍스트</td>
</tr>
<tr>
<td>group(int group)</td>
<td>String</td>
<td>지정된 정규식과 일치하는 텍스트 중 입력한 순번의 텍스트 반환</td>
</tr>
<tr>
<td>groupCount()</td>
<td>int</td>
<td>지정된 정규식과 일치하는 텍스트의 개수</td>
</tr>
<tr>
<td>start()</td>
<td>int</td>
<td>지정된 정규식과 일치하는 텍스트 시작 인덱스</td>
</tr>
<tr>
<td>end()</td>
<td>int</td>
<td>지정된 정규식과 일치하는 텍스트 끝 인덱스</td>
</tr>
</tbody></table>
<p><em>참고</em></p>
<ol>
<li><a href="https://docs.oracle.com/javase/8/docs/api/">java 공식 문서</a></li>
<li><a href="https://medium.com/stackera/java-regex-part-5-matches-lookingat-find-8033fa9cdebc">Java RegEx: 파트 5 - match(), lookAt(), find()</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[java] 정규식 No match found]]></title>
            <link>https://velog.io/@0_sujeong/java-%EC%A0%95%EA%B7%9C%EC%8B%9D-No-match-found</link>
            <guid>https://velog.io/@0_sujeong/java-%EC%A0%95%EA%B7%9C%EC%8B%9D-No-match-found</guid>
            <pubDate>Thu, 21 Jul 2022 01:38:58 GMT</pubDate>
            <description><![CDATA[<h1 id="오류-상황">오류 상황</h1>
<pre><code class="language-java">@Test
void 정규식_테스트() {
    String text = &quot;가나다(2)&quot;;

    Pattern pattern = Pattern.compile(&quot;\\([0-9]+\\)$&quot;);
    String res = pattern.matcher(text).group();
    log.info(&quot;res = {}&quot;, res);
}</code></pre>
<p><code>가나다(2)</code>에서 <code>(2)</code>와 같이 괄호 안에 숫자만 있는 부분을 정규식으로 잡아서 처리하고 싶었는데, 어째서인지 자꾸 <code>No match found</code>라는 메시지가 나오면 오류가 나왔으며, <code>find()</code>를 실행 했을 때에는 <code>true</code>값이 나오는 이상한 상황이였다.</p>
<p>자바 정규식은 <code>Pattern</code>과 <code>Matcher</code>를 이용하는데, Matcher에서 <code>group()</code>은 매칭된 부분을 반환해주고, <code>find()</code>는 대상 문자열과 패턴이 일치하는 지를 boolean값으로 반환해준다.
당연히 <code>group()</code>을 했을때, 반환되는 문자열이 없으면 <code>&#39;&#39;</code>와 같이 리턴이 될 줄 알았는데, 계속해서 오류로 처리가 되었다.</p>
<h1 id="문제-원인">문제 원인</h1>
<p><code>find()</code>를 먼저 실행해서 찾게 하고, 그 이후에 <code>end()</code>, <code>group()</code>등과 같은 메서드를 실행해야 하는데, 하지 않아서 문제가 되었다.</p>
<h1 id="해결방법">해결방법</h1>
<pre><code class="language-java">@Test
void 정규식_테스트() {
    String text = &quot;가나다(2)&quot;;

    Pattern pattern = Pattern.compile(&quot;\\([0-9]+\\)$&quot;);
    Matcher matcher = pattern.matcher(text);
    if(matcher.find()){
        String res = matcher.group();
        log.info(&quot;res = {}&quot;, res);
    }
}</code></pre>
<p>위와 같이 <code>find()</code>를 먼저 실행해서 찾고, 그 이후에 <code>group()</code>, <code>end()</code>와 같은 메서드를 실행한다.</p>
<p><em>참고</em></p>
<ol>
<li><a href="https://hbase.tistory.com/160">[Java] 정규표현식 사용법 및 예제 - Pattern, Matcher</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring] thymeleaf navbar active]]></title>
            <link>https://velog.io/@0_sujeong/spring-thymeleaf-navbar-active</link>
            <guid>https://velog.io/@0_sujeong/spring-thymeleaf-navbar-active</guid>
            <pubDate>Thu, 07 Jul 2022 01:51:52 GMT</pubDate>
            <description><![CDATA[<h1 id="현재-url-가져오기">현재 url 가져오기</h1>
<pre><code class="language-html">${#request.requestURI}</code></pre>
<h1 id="navbar에-현재-위치-표시하기">navbar에 현재 위치 표시하기</h1>
<pre><code class="language-html">&lt;li class=&quot;nav-item&quot; th:classappend=&quot;${#request.requestURI.equals(&#39;/test.do&#39;)}? &#39;active&#39;: &#39;&#39;&quot;&gt;
&lt;/li&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[datatables] 사용법]]></title>
            <link>https://velog.io/@0_sujeong/datatables-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@0_sujeong/datatables-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Fri, 01 Jul 2022 05:49:36 GMT</pubDate>
            <description><![CDATA[<h1 id="테이블-설정">테이블 설정</h1>
<h2 id="언어한글화">언어(한글화)</h2>
<pre><code class="language-javascript">language : {
  emptyTable: &quot;데이터가 없습니다.&quot;,
  lengthMenu: &quot;_MENU_&quot;,
  info: &quot;현재 _START_ - _END_ / 총 _TOTAL_건&quot;,
  infoEmpty: &quot;데이터 없음&quot;,
  infoFiltered: &quot;( _MAX_건의 데이터에서 필터링됨 )&quot;,
  loadingRecords: &quot;로딩중...&quot;,
  processing:     &quot;잠시만 기다려 주세요...&quot;,
  paginate: {
      &quot;next&quot;: &quot;다음&quot;,
    &quot;previous&quot;: &quot;이전&quot;
  },
  select: {
    rows: &quot;&quot;
  }
}</code></pre>
<h2 id="columns-와-columndefs">columns 와 columnDefs</h2>
<pre><code class="language-javascript">columns: [
{ data: &quot;id&quot; , width: &#39;10%&#39;},
{ data: &quot;phrase&quot; , width: &#39;75%&#39;},
{
  defaultContent: &quot;-&quot;, // 값이 없을때
  orderable: false, //정렬
  className: &#39;button&#39;,
  width: &#39;10%&#39;, // 가로 값
  targets:   2, // 몇번째 컬럼
  render: function (data, type, row){// data: 데이터, row: 표현되는 줄에 다른 값 접근 가능
    const keywordId= row.id;
    return &#39;&lt;button onclick=\&quot;alert(&quot;아이디는 &#39;+keywordId+&#39;&quot;);\&quot;&gt;버튼&lt;/button&gt;&#39;;// 이 부분이 실제로 표현됨
    }
  }
]

columnDefs = [
  { targets: 0, width: &#39;6%&#39;,}, // 0번째를 6%로 설정
  { targets: 1, width: &#39;7%&#39;,},
  { targets: 2, width: &#39;11%&#39;,}
]</code></pre>
<p>columns만 쓸 수도 있고, columnDefs와 같이 쓸 수도 있음</p>
<table>
<thead>
<tr>
<th>속성이름</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>width</td>
<td>가로길이 설정, ex)  <code>59%</code> , <code>110</code>(px)</td>
</tr>
<tr>
<td>targets</td>
<td>어떤 컬럼인지 지정,  ex) <code>1</code></td>
</tr>
<tr>
<td>responsivePriority</td>
<td>반응형에서 사용되는 순서 값  ex)  <code>59%</code></td>
</tr>
<tr>
<td>visible</td>
<td>해당 컬럼을 보이게 할지 ex)  <code>true</code></td>
</tr>
<tr>
<td>data</td>
<td>받아온 데이터의 컬럼명 ex)  <code>id</code> , <code>username</code></td>
</tr>
</tbody></table>
<h2 id="반응형">반응형</h2>
<ul>
<li>기능 설정<pre><code class="language-javascript">//반응형 화면이 작을때 너무 많은 내용이면 몇개의 컬럼만 남기고 안보이게함 (+버튼을 누르면 보이게됨)
responsive:false</code></pre>
</li>
<li>반응형이 될 때 화면에 맞춰서 사라질 컬럼 순서 지정<pre><code class="language-javascript">columnDefs:[
{responsivePriority : -3 , targets: 1},
{responsivePriority : -2 , targets: 2},
{responsivePriority : 10009 , targets: 3},
{responsivePriority : 10008, targets: 4},
{responsivePriority : 10007 , targets: 5},
{responsivePriority : 10006 , targets: 6},
{responsivePriority : 10005 , targets: 7},
{responsivePriority : 10004 , targets: 8},
{responsivePriority : 10003 , targets: 9},
]</code></pre>
<code>columnDefs</code>에 <code>responsivePriority</code>값을 지정
숫자가 큰 숫자일 수록! 먼저 사라짐, 음수는 사라지지 않게!
근데 정확하지 않은 내용인건지 음수인것도 화면이 너무 작으면 사라지거나 순서가 약간 다름</li>
</ul>
<h2 id="그-외-기능">그 외 기능</h2>
<pre><code class="language-javascript">//////// 기본기능
// 표시 건수(기본값 true)
lengthChange: false,
// 검색(기본값 true)
searching: false, //filter와 같은 듯?
// 정렬(기본값 true)
ordering: false, 
// 정보 표시(기본값 true)
info: false,
// 페이징(기본값 true)
paging: false,


// 그 외 설정
// 한 페이지에 보여줄 row 개수, 처음 화면
pageLength: 10,
//자동으로 width 변경(기본값 true)
autoWidth:false,
// 정렬 기준, 처음 화면
order: [[ 0, &quot;asc&quot; ]] // 다중시 [ [ 0, &quot;asc&quot; ], [ 1, &quot;desc&quot;] ]
// 기본 기능의 위치 변경 시
dom:&lt;&quot;#table_top.col-sm-12&quot;&lt;&quot;col-sm-10&quot;i&gt;&lt;&quot;col-sm-2&quot;l&gt;&gt;tp //i(info)f(filter)l(length change)t(table)p(paging)</code></pre>
<blockquote>
</blockquote>
<p>DOM 참고
<a href="https://datatables.net/reference/option/dom">https://datatables.net/reference/option/dom</a>
<a href="https://datatables.net/examples/basic_init/dom.html">https://datatables.net/examples/basic_init/dom.html</a></p>
<h1 id="데이터">데이터</h1>
<h2 id="ajax">ajax</h2>
<pre><code class="language-javascript">ajax: { 
  url: 실제url,
  type:&quot;GET&quot;, 
  dataSrc: &#39;&#39;
}</code></pre>
<pre><code class="language-javascript">ajax: &#39;../server_side/scripts/server_processing.php&#39; //json형식으로 데이터가 있는 file</code></pre>
<h2 id="html">HTML</h2>
<h2 id="data">data</h2>
<h1 id="이벤트">이벤트</h1>
<h2 id="자동으로-번호">자동으로 번호</h2>
<pre><code class="language-javascript">drawCallback: function( settings ) {
  var api = this.api();
  var num = 1;
  api.rows().every( function () { // rows({page:&#39;current&#39;})
    $(this.node()).find(&#39;td&#39;).eq(0).html(num);
    num = num + 1;
  } );
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] 주석 템플릿 설정]]></title>
            <link>https://velog.io/@0_sujeong/IntelliJ-%EC%A3%BC%EC%84%9D-%ED%85%9C%ED%94%8C%EB%A6%BF-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@0_sujeong/IntelliJ-%EC%A3%BC%EC%84%9D-%ED%85%9C%ED%94%8C%EB%A6%BF-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Tue, 28 Jun 2022 01:34:47 GMT</pubDate>
            <description><![CDATA[<h1 id="파일-템플릿">파일 템플릿</h1>
<ul>
<li><p>위치: <code>파일 및 코드 템플릿</code> - <code>파일</code>  - <code>class</code>(원하는 타입 선택) -<code>포함</code>
<img src="https://velog.velcdn.com/images/0_sujeong/post/866d66f9-f42f-4a6d-90b7-0418f5e31b18/image.png" alt="">
<img src="https://velog.velcdn.com/images/0_sujeong/post/c203a0a4-44ef-402b-8da3-3b03ffada217/image.png" alt=""></p>
</li>
<li><p>내용: 원하는 내용 작성  </p>
<ul>
<li>사용한 템플릿</li>
</ul>
<pre><code>/**
 * description    :
 * packageName    : ${PACKAGE_NAME}
 * fileName       : ${NAME}
 * author         : ${USER}
 * date           : ${DATE}
 * ===========================================================
 * DATE              AUTHOR             NOTE
 * -----------------------------------------------------------
 * ${DATE}        ${USER}       최초 생성
 */</code></pre><ul>
<li>템플릿 변수</li>
</ul>
<table>
<thead>
<tr>
<th>변수이름</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>${PACKAGE_NAME}</td>
<td>새 파일이 생성되는 패키지 이름</td>
</tr>
<tr>
<td>${USER}</td>
<td>현재 유저 시스템 로그인 이름</td>
</tr>
<tr>
<td>${DATE}</td>
<td>현재 시스템 날짜</td>
</tr>
<tr>
<td>${TIME}</td>
<td>현재 시스템 시간</td>
</tr>
<tr>
<td>${YEAR}</td>
<td>현재 년도</td>
</tr>
<tr>
<td>${MONTH}</td>
<td>현재 월</td>
</tr>
<tr>
<td>${MONTH_NAME_SHORT}</td>
<td>현재 월 이름 첫 세글자. 예: Jan, Feb 등등.</td>
</tr>
<tr>
<td>${MONTH_NAME_FULL}</td>
<td>현재 월의 이름 전체. 예: January, February 등등.</td>
</tr>
<tr>
<td>${DAY}</td>
<td>현재 일</td>
</tr>
<tr>
<td>${DAY_NAME_SHORT}</td>
<td>현재 요일의 첫 세 글자. 예: Mon, Tue 등.</td>
</tr>
<tr>
<td>${DAY_NAME_FULL}</td>
<td>현재 요일 이름 전체. 예: Monday, Tuesday 등.</td>
</tr>
<tr>
<td>${HOUR}</td>
<td>현재 시</td>
</tr>
<tr>
<td>${MINUTE}</td>
<td>현재 분</td>
</tr>
<tr>
<td>${PROJECT_NAME}</td>
<td>현재 프로젝트 이름</td>
</tr>
</tbody></table>
</li>
</ul>
<h1 id="메서드-템플릿">메서드 템플릿</h1>
<ol>
<li><p><code>JavaDoc</code> plugins 설치
<img src="https://velog.velcdn.com/images/0_sujeong/post/0feb8a04-814b-4243-8ba0-32bded7a9b1f/image.png" alt=""></p>
</li>
<li><p><code>도구</code> - <code>JavaDoc</code> - <code>Templates</code>
<img src="https://velog.velcdn.com/images/0_sujeong/post/69019f8a-395d-40a5-9cf0-da2a688de11c/image.png" alt=""></p>
</li>
<li><p><code>Method level</code> - <code>.+</code> 선택 및 편집 ( 더블 클릭 )
<img src="https://velog.velcdn.com/images/0_sujeong/post/144b8c6b-763a-44b0-ad94-3266df1ee5a7/image.png" alt=""></p>
</li>
</ol>
<p>상단에 내용 추가</p>
<pre><code class="language-java">* description : 
* methodName : ${element.getName()}
* author : GongSuJeong
* date : 2022.06.</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[window] 자동 업데이트 완전 끄기]]></title>
            <link>https://velog.io/@0_sujeong/window-%EC%9E%90%EB%8F%99-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EC%99%84%EC%A0%84-%EB%81%84%EA%B8%B0</link>
            <guid>https://velog.io/@0_sujeong/window-%EC%9E%90%EB%8F%99-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EC%99%84%EC%A0%84-%EB%81%84%EA%B8%B0</guid>
            <pubDate>Wed, 22 Jun 2022 02:21:08 GMT</pubDate>
            <description><![CDATA[<p>개인pc를 세팅하는 정보를 정리해 놓은 내용입니다.
참고만 해주세요 :) </p>
<h1 id="1-서비스">1. 서비스</h1>
<ul>
<li><code>서비스</code> - <code>window update</code> - <code>일반</code>
<img src="https://velog.velcdn.com/images/0_sujeong/post/a98f4749-76bd-4689-910c-0a5675f6f28b/image.png" alt=""><img src="https://velog.velcdn.com/images/0_sujeong/post/baba2faf-6a40-4233-8832-5509e7b66838/image.png" alt="">
<code>일반</code> - <code>시작유형</code>: <code>사용 안함</code>
<code>일반</code> - <code>서비스 상태</code>: <code>중지</code>
<code>복구</code> - 모든 실패를 <code>동작하지 않음</code>으로 설정</li>
</ul>
<h1 id="2-로컬-그룹-정책-편집기">2. 로컬 그룹 정책 편집기</h1>
<p><code>window키 + R</code> - <code>gpedit.msc</code> 로 <code>로컬 그룹 정책 편집기</code> 열기</p>
<h2 id="windows-업데이트">windows 업데이트</h2>
<p><code>로컬 컴퓨터 정책</code> - <code>컴퓨터 구성</code> - <code>관리 템플릿</code> - <code>windows 구성 요소</code> - <code>window 업데이트</code> 에서</p>
<ul>
<li><p><code>windows 업데이트에서 제공된 업데이트 관리</code> - <code>자동 업데이트 검색 주기</code>
<img src="https://velog.velcdn.com/images/0_sujeong/post/a32d29d6-ec7c-4d70-be02-65924df95ed1/image.png" alt="">
사용 안함</p>
</li>
<li><p><code>windows 업데이트에서 제공된 업데이트 관리</code> - <code>대상 기능 업데이트 버전 선택</code> 
<img src="https://velog.velcdn.com/images/0_sujeong/post/bd6b98ab-b9c7-469f-aa50-cfa0cf51d646/image.png" alt="">
사용
21H1</p>
</li>
<li><p><code>레거시 정책</code> - <code>자동 업데이트로 바로 설치 허용</code>
<img src="https://velog.velcdn.com/images/0_sujeong/post/3a02ca9a-2eec-45f2-a92b-cf3fa67c031d/image.png" alt="">
사용 안함</p>
</li>
<li><p><code>레거시 정책</code> - <code>자동 업데이트를 통한 권장 업데이트 설정</code>
<img src="https://velog.velcdn.com/images/0_sujeong/post/f4f58254-1d92-48b1-8f84-df1cc904fa40/image.png" alt="">
사용 안함</p>
</li>
<li><p><code>최종 사용자 환경 관리</code> - <code>자동 업데이트 구성</code>
<img src="https://velog.velcdn.com/images/0_sujeong/post/7a8b91c9-d696-439f-9963-69d266d3dcc4/image.png" alt="">
사용 안함</p>
</li>
<li><p><code>최종 사용자 환경 관리</code> - <code>사용 시간 중 업데이트를 위한 자동 다시 시작 끄기</code>
<img src="https://velog.velcdn.com/images/0_sujeong/post/bc693083-d5bb-4c1c-a8e5-c74c7a32111e/image.png" alt="">
사용 안함</p>
</li>
<li><p><code>최종 사용자 환경 관리</code> - <code>사용 시간 중 업데이트를 위한 자동 다시 시작 끄기</code>
<img src="https://velog.velcdn.com/images/0_sujeong/post/cf2bcb72-664e-486b-8a3c-bc7d4832a398/image.png" alt="">
사용 안함</p>
</li>
</ul>
<h2 id="인터넷-통신-관리">인터넷 통신 관리</h2>
<p><code>시스템</code> - <code>인터넷 통신 관리</code> - <code>인터넷 통신 설정</code> 에서</p>
<ul>
<li><code>모든 windows 업데이트 기능에 액세스 안 함</code>
<img src="https://velog.velcdn.com/images/0_sujeong/post/8c6d57e9-dcb1-409a-8484-dc828970cb23/image.png" alt="">
사용</li>
</ul>
<h1 id="3-레지스트리">3. 레지스트리</h1>
<p><code>window키 + R</code> - <code>regedit.exe</code> 로 <code>레지스트리 편집기</code> 열기</p>
<ul>
<li><code>HKEY_LOCAL_MACHINE</code> - <code>SOFTWARE</code> - <code>Policies</code> - <code>Microsoft</code> - <code>Windows</code> - <code>WindowsUpdate</code> - <code>AU</code>
<img src="https://velog.velcdn.com/images/0_sujeong/post/c694f555-f3ef-4bef-9402-4688cea2c975/image.png" alt="">
<code>NoAutoUpdate</code> 값을 <code>1</code>, <code>16 진수</code>로 설정<blockquote>
</blockquote>
만약 <code>DWORD(32비트)</code> 값이 없으면, 
<img src="https://velog.velcdn.com/images/0_sujeong/post/8fb54e53-79b9-4a6a-b4fd-099b0c4715c2/image.png" alt="">
<code>우클릭</code> - <code>새로 만들기</code> - <code>DWORD(32비트)</code> 선택
<img src="https://velog.velcdn.com/images/0_sujeong/post/76d71ccc-5171-4e76-8cd4-da05e0e515cc/image.png" alt="">
이름: <code>TargetReleaseVersionInfo</code>   (F2를 눌러서 이름 편집)
값: <code>21H1</code>
단위: <code>16진수(H)</code></li>
</ul>
<p><em>참고</em></p>
<ol>
<li><a href="https://itshareit.tistory.com/3">윈도우10 자동 업데이트 끄기 및 완전 해제하는 방법</a></li>
<li><a href="https://www.easeus.co.kr/partition-manager-software/stop-windows-11-update.html">Windows 11 업데이트를 중지하는 방법</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[intelliJ] client IP주소 ipv4으로 받기]]></title>
            <link>https://velog.io/@0_sujeong/intelliJ-client-IP%EC%A3%BC%EC%86%8C-ipv4%EC%9C%BC%EB%A1%9C-%EB%B0%9B%EA%B8%B0</link>
            <guid>https://velog.io/@0_sujeong/intelliJ-client-IP%EC%A3%BC%EC%86%8C-ipv4%EC%9C%BC%EB%A1%9C-%EB%B0%9B%EA%B8%B0</guid>
            <pubDate>Thu, 09 Jun 2022 06:09:36 GMT</pubDate>
            <description><![CDATA[<h1 id="client-주소-받기">client 주소 받기</h1>
<pre><code class="language-java">@PostMapping(&quot;form.do&quot;)
public String login(HttpServletRequest request){
    log.info(&quot;ip주소 = {}&quot; ,request.getRemoteAddr());
}</code></pre>
<p>ip주소를 받기 위해서는 위의 코드처럼 하면 되는데,
형식이 내가 알고 있던 것과 달라서 확인해보니 ipv6로 받고 있었다.</p>
<p>그래서 ipv4로 변경하고 싶어서 찾아보니 설정을 해주면 ipv4방식으로 받을 수 있다.</p>
<pre><code>-Djava.net.preferIPv4Stack=true
-Djava.net.preferIPv4Addresses=true</code></pre><p>이 코드를 <code>도움말</code> - <code>사용자 지정 VM 옵션 편집,,,</code>와 <code>구성 편집</code> - <code>spring Boot</code> - <code>프로그램 Application</code> - <code>빌드 및 실행</code> - <code>VM옵션</code>에 추가 해준다.</p>
<p><em>출처</em>
<a href="https://ooz.co.kr/138">이러쿵저러쿵:티스토리</a>
<a href="https://gaemi606.tistory.com/entry/IntelliJ-IPv4-%EC%84%A4%EC%A0%95">IntelliJ IPv4 설정</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring] Validation]]></title>
            <link>https://velog.io/@0_sujeong/spring-Validation</link>
            <guid>https://velog.io/@0_sujeong/spring-Validation</guid>
            <pubDate>Sun, 05 Jun 2022 06:39:12 GMT</pubDate>
            <description><![CDATA[<h1 id="검증">검증</h1>
<p>컨트롤러의 중요한 역할 중 하나는 HTTP요청이 정상인지 검증하는 것이다.</p>
<ul>
<li>클라이언트 검증
클라이언트 검증은 사용자에게 피드백이 빠르지만, 조작할 수 있어 보안에 취약</li>
<li>서버 검증
서버 검증은 보안에는 좋지만 피드백이 느림</li>
</ul>
<p>따라서 클라이언트 검증과 서버 검증을 적절하게 섞어서 사용해야한다.</p>
<p>또 검증을 할 때에 검증 오류 발생했다면 오류가 발생한 폼에 사용자가 입력한 값을 유지시키고, 오류 메시지를 같이 띄워 사용자가 인식 할 수 있게 하는 것이 좋은 방법이라고 생각한다.</p>
<h1 id="오류-종류">오류 종류</h1>
<h2 id="필드-오류">필드 오류</h2>
<p>필드 오류란 말 그대로 필드 값이 요구되는 범위 밖으로 벗어난 오류를 말한다.
예를 들면 상품 개수를 1000개까지만 입력 받는데, 이때 1001개를 입력 했다면 이는 필드 오류에 해당한다.</p>
<h2 id="객체-오류">객체 오류</h2>
<p>객체 오류란 필드 오류와 다르게 하나의 필드 값으로만 발생하는 오류가 아니라 이를 넘어서는 오류를 말한다.
예륻 들면 주문을 받을때 최소 주문금액을 15,000원이라고 했을 때 5천원짜리 짜장면 한 그릇과 6천원짜리 짬뽕 한 그릇만 주문했다면 이는 객체 오류에 해당한다.</p>
<h2 id="바인딩-오류">바인딩 오류</h2>
<p>바인딩 오류란 HTTP 요청시 들어온 데이터 값이 제대로 바인딩 되지 않았을때 발생하는 오류로 예를 들자면 상품 개수가 1,2 와 같은 int    타입이아니라 한개, 두개와 같은 string 타입이 들어온다면 이는 바인딩 오류에 해당한다.</p>
<h1 id="bindingresult">BindingResult</h1>
<p>만약 위에서 말한 바인딩 오류가 발생한다면, 컨트롤러가 호출 되기도 전에 오류로 인해서 예외처리가  실행 될 것이다. 이렇게 되면 오류가 난 값을 따로 관리 할 수 없다. 이럴 때 사용하는 것이 <code>BindingResult</code>이다.
<code>BindingResult</code>이란 검증 오류를 보관하는 객체로 오류를 낸 값까지 같이 보관을 하고 있다.
이 <code>BindingResult</code>를 사용한다면 바인딩 오류가 발생한다고 해도 컨트롤러가 호출된다.</p>
<blockquote>
</blockquote>
<p>다만 api방식에서는 컨트롤러가 호출 되지 않는다.
api방식은 http 컨버터가 메시지를 변환하지 못하는 오류로 처리를 하기 때문이다.</p>
<blockquote>
</blockquote>
<p><code>BindingResult</code>는 반드시 검증할 객체 뒤에 위치해야한다.</p>
<h2 id="검증-오류-적용-방법">검증 오류 적용 방법</h2>
<ul>
<li><code>@ModelAttribute</code>의 객체가 바인딩 실패하면 spring이 자동으로 <code>FieldError</code>를 생성해서 <code>BindingResult</code>에 넣어준다.</li>
<li>개발자가 직접 넣어준다.</li>
<li><code>@validated</code>를 사용한다.</li>
</ul>
<h1 id="오류-코드와-메시지-처리">오류 코드와 메시지 처리</h1>
<h2 id="메시지-처리">메시지 처리</h2>
<h3 id="설정">설정</h3>
<p>오류 메시지를 원하는 방식으로 정의 할 수있다. 이럴때 메시지를 정의하는 방법은 <code>errors.properties</code>파일을 만들어 원하는 오류 메시지를 적으면 된다.</p>
<p>그리고 이 파일을 적용시키기 위해서는 <code>application.properties</code>파일에 설정을 하나 해줘야한다.</p>
<pre><code>spring.messages.basename=errors</code></pre><p>이렇게 만들어진 파일이름을 적는 것이다. 만약 국제화를 위한 <code>message.properties</code>파일도 있다면 <code>,</code>로 구분해서 여러개를 적으면 된다. 또 에러 메시지도 국제화를 하고 싶다면 <code>errors_en.properties</code>등의 파일을 만들어 국제화를 하면 된다.</p>
<h3 id="우선순위">우선순위</h3>
<p>또 오류 메시지에는 우선순위가 있는데, 디테일한 것이 제일 우선순위가 높다.
만약 오류 메시지 파일에 내용이</p>
<pre><code>required=필요한 값이 제대로 입력되지 않았습니다.
required.item.itemName=아이템 이름이 입력되지 않았습니다.</code></pre><p>이런식으로 있다면, 아이템 이름이 입력되지 않은 상황에서는 2번째 줄인 <code>아이템 이름이 입력되지 않았습니다.</code>가 오류 메시지로 나오고, 나머지는 <code>필요한 값이 제대로 입력되지 않았습니다.</code>가 출력될 것이다.</p>
<p>이를 이용해 덜 디테일한 오류메시지를 기본으로 쓰고, 자세하게 오류 메시지를 알려줘야할 경우에는 해당 조건을 적어 그 부분은 자세히 따로 정의하면 된다.</p>
<h2 id="오류-코드">오류 코드</h2>
<p>이렇게 <code>errors.properties</code>를 작성할 때에 앞에    key값으로 적혀 있는 것들은 오류 코드라고하는데, 이러한 오류코드는 <code>BindingResult</code>에 같이 저장된다. 그래서 <code>errors.properties</code>를 작성 할 때에 이 규칙에 맞추어서 정의한다면 따로 기본 메시지라고 지정해 주지 않아도 spring이 읽는다.
만약 다르게 한다고 해서 사용 할 수 없는 것이 아니라 개발자가 직접 해당 오류에 이러한 메시지가 나와야한다고 지정해야한다.( 검증 오류 적용 방법에 따라 달라진다 )</p>
<h1 id="bean-validation">Bean Validation</h1>
<p>Bean validation방법은 위에서 말한 검증 오류 적용 방법중 <code>@validated</code>를 이용하는 방법이다. 그 외의 방법도 있지만 이 곳에서는 이 방법만 설명하려고 한다.</p>
<p>이 방식은 간단하다. <code>@Validated</code> 어노테이션을 검증할 객체 앞에 붙여주기만 하면된다. 또 이 검증 결과 또한 <code>BindingResult</code>에 담기기 때문에 검증할 객체 뒤에 당연히 있어야한다.그리고 각 필드의 검증 조건은 해당 객체에 정의한다.</p>
<ul>
<li><p>컨트롤러</p>
<pre><code class="language-java">@PostMapping(&quot;/test&quot;)
pulbic String test(@Validated @ModelAttribute(&quot;item&quot;) Item item, BindingResult bindingResult){
  if(bindingResult.hasResult()){
      //검증 실패로직
  }
  // 검증 성공 로직
  ,,,
}</code></pre>
</li>
<li><p>검증 객체</p>
<pre><code class="language-java">Public class Item{

  public Integer itemNum;

  @NotBlank
  public String itemName;

  @Range(min=100,max=9900)
  public Integer price;
}</code></pre>
</li>
</ul>
<p>이렇게 각 필드에 어노테이션을 통해서 필드 검증 조건을 설정 할 수 있다.</p>
<h2 id="한계와-극복">한계와 극복</h2>
<h3 id="한계">한계</h3>
<p>예를 들어서 상품 등록시 검증 조건과 상품 수정시에 검증 조건이 다르면 어떻게 처리할 것인가? 이렇게 검증을 한다면, 간편하지만 같은 객체에 다른 검증 조건을 요구한다면 해결하기가 어렵다. 왜냐면 <code>Item</code>이라는 객체에 아예 검증 조건을 정의했기 때문이다. </p>
<h3 id="극복">극복</h3>
<p>이를 극복하기위해 <code>groups</code>라는 기능을 사용하는 방법도 있지만, 코드가 복잡해지는 것 같아 추천하지 않는다. 대신 이런 경우에는 각 화면 별로 주고받는 데이터를 따로 구현해 검증을 한다.
예를 들어서 <code>Item</code>을 수정하거나 삭제한다고 할 때 <code>SaveItemForm</code> 과<code>UpdateItemForm</code>을 따로 만들어 클라이언트에게 입력 받은 데이터는 이러한 폼 객체를 사용해 검증하고, 폼 객체를 통해서 <code>Item</code>을 만드는 방식이다.</p>
<p>물론 컨트롤러에 이부분을 작성하는 것이 조금 번거롭겠지만 조금 더 직관적이라고 생각한다.</p>
<h1 id="apijson-검증">API(JSON) 검증</h1>
<p>지금까지는 <code>@ModelAttribute</code>로 받는 폼 데이터에 대해 주로 검증했다. 만약 폼 데이터가 아닌 <code>@RequestBody</code>로 받는 HTTP body 메시지라면 어떻게 검증을 적용해야할까?</p>
<p>이 검증은 위에서 말한 것과 달리 HTTP body 메시지는 HTTP 컨버터가 실행되어 객체로 변환이되는데, 만약 여기서 오류가 난다면 위에서 바인딩 오류에 대해 처리한 것과 달리 HTTP 컨버터 예외처리로 들어가기 때문에 컨트롤러가 호출되기 이전에 끝난다. 때문에 BindingResult에 값이 담기지 않는다.</p>
<p>이러한 차이가 나는 이유는 폼 객체가 바인딩되는 과정과 HTTP body 메시지가 HTTP 컨버터를 통해 변환이 되는 과정이 다르기 때문이다.</p>
<blockquote>
</blockquote>
<h2 id="차이">차이</h2>
<p><code>@ModelAttribute</code>는 필드 단위로 정교하게 바인딩이 적용된다. 특정 필드가 바인딩 되지 않아도 나머지 필드는 정상 바인딩 되고, Validator를 사용한 검증도 적용할 수 있다.
<code>@RequestBody</code>는 <code>HttpMessageConverter 단계</code>에서 JSON 데이터를 객체로 변경하지 못하면 이후 단계 자체가 진행되지 않고 예외가 발생한다. 컨트롤러도 호출되지 않고, Validator도 적용할 수 없다.</p>
<p>이 부분 이외에 컨트롤러가 호출된 이후부터는 똑같이 실행이 된다.</p>
<p><em>출처</em></p>
<ol>
<li><a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard">스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring] thymeleaf 와 문법]]></title>
            <link>https://velog.io/@0_sujeong/spring-thymeleaf-%EC%99%80-%EB%AC%B8%EB%B2%95</link>
            <guid>https://velog.io/@0_sujeong/spring-thymeleaf-%EC%99%80-%EB%AC%B8%EB%B2%95</guid>
            <pubDate>Wed, 01 Jun 2022 07:39:42 GMT</pubDate>
            <description><![CDATA[<h1 id="thymeleaf-란">Thymeleaf 란?</h1>
<ul>
<li>서버 사이드 HTML 렌더링 (SSR)
: 타임리프는 백엔드 서버에서 HTML을 동적으로 렌더링 하는 용도로 사용</li>
<li>네츄럴 템플릿
: jsp등과 다르게 타임리프는 html형식을 최대한 유지하기 때문에 html파일을 직접 열어도 내용을 확인할 수 있고, 뷰 템플릿을 거치면 동적인 화면을 확인할 수 있다. 이렇게 순수 HTML을 유지하면서 뷰 템플릿을 유지하면서 뷰 템플릿을 사용할 수 있는 특징을 네츄럴 템플릿 이라고 함</li>
<li>스프링 통합 지원
: 타임리프는 스프링과 자연스럽게 통합되고, 스프링의 다양한 기능을 편리하게 사용 할 수 있게 지원해준다. </li>
</ul>
<blockquote>
</blockquote>
<p>공식 사이트 : <a href="https://www.thymeleaf.org/">https://www.thymeleaf.org/</a>
공식 메뉴얼 - 기본 기능: <a href="https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html">https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html</a>
공식 메뉴얼 - 스프링 통합 : <a href="https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html">https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html</a></p>
<h1 id="문법">문법</h1>
<h2 id="사용-선언">사용 선언</h2>
<p><code>&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;</code>을 html파일 상단에 선언하면 사용할 수 있다. </p>
<h2 id="표현식">표현식</h2>
<ul>
<li>간단한 표현식<ul>
<li>변수 : ${,,,} <em>변수를 사용할 때 사용</em></li>
<li>선택 변수 : *{,,,} <em>th:Object와 함께 사용</em></li>
<li>메시지 표현식 : #{,,,}</li>
<li>링크 URL : @{,,,} <em>URL 링크를 표현 할 때</em></li>
<li>조각 표현식 : ~{,,,} <em>replace, insert 등을 사용 할 때</em></li>
</ul>
</li>
<li>리터럴<ul>
<li>텍스트 : &#39;one text&#39;, &#39;Another one!&#39; ,,,,</li>
<li>숫자 : 0, 34, 12.3, 14.0 ,,,,</li>
<li>불린 : true, false ,,,</li>
<li>널 : null</li>
<li>리터럴 토큰 : one, sometext, main </li>
</ul>
</li>
<li>문자 연산<ul>
<li>문자 합치기 : + </li>
<li>리터럴 대체 : |My name is ${name}!|<blockquote>
</blockquote>
타임리프에서 <strong>문자 리터럴</strong>에서 주의해야할 점이 있는데, 바로 모든 문자열은 <code>&#39;(싱글)</code> 으로 감싸져야한다는 것이다.
하지만 예외적으로 a-z, A-Z,0-9 으로만 이루어진 문자열은 감싸지 않아도 되지만, <strong>띄어쓰기</strong>가 들어가거나 <strong>특수 문자</strong> 등이 들어가거나 변수와 문자열 합치기를 해야하는 경우에는 반드시 <code>&#39;(싱글)</code>으로 감싸져 있어야한다.
혹은 위에서 말한 <code>|</code>으로 감싸져도 된다.</li>
</ul>
</li>
<li>산술 연산<ul>
<li>Binary operators : +, -, *, /, %</li>
<li>Minus sign (unary operator) : -</li>
</ul>
</li>
<li>불린 연산<ul>
<li>Binary operators : and, or</li>
<li>Boolean negation (unary operator) : !, not</li>
</ul>
</li>
<li>비교와 동등<ul>
<li>비교 :&gt;,&lt;,&gt;=,&lt;=(gt,lt,ge,le)</li>
<li>동등 연산 : ==, != (eq, ne)<blockquote>
</blockquote>
HTML Entity와 주의를 해야한다.</li>
</ul>
</li>
<li>조건 연산<ul>
<li>If-then: (if) ? (then)</li>
<li>If-then-else: (if) ? (then) : (else)</li>
<li>Default: (value) ?: (defaultvalue)</li>
</ul>
</li>
<li>특별한 토큰<ul>
<li>No-Operation : _</li>
</ul>
</li>
</ul>
<h2 id="사용-방법">사용 방법</h2>
<p>타임리프는 기본적으로 HTML 테그의 속성에 기능을 정의해서 동작한다.</p>
<pre><code class="language-html">&lt;span th:text=&quot;${data}&quot;&gt;원래 텍스트&lt;/span&gt;</code></pre>
<p>이런식으로 안에 <code>th:</code>를 먼저쓰고 뒤에 <code>text</code>나 <code>object</code> 등으로 어떤 값인지 구분을 하고, 그 값에 표현식을 넣는다.</p>
<p>아니면 html 태그 안에 값을 직접 넣고 싶다면, </p>
<pre><code class="language-html">&lt;span&gt;[[${data}]]&lt;/span&gt;</code></pre>
<p>이러한 방식으로 <code>[[]]</code>안에 표현식을 써서 사용할 수도 있다.</p>
<hr>
<h2 id="텍스트">텍스트</h2>
<p>data가 <code>&lt;b&gt;text&lt;/b&gt;</code>라고 상황을 두고 두 개를 비교해보자.</p>
<ul>
<li>text : 이스케이프 기능이 <strong>자동으로 적용되는</strong> text</li>
</ul>
<p>[타임리프]</p>
<pre><code class="language-html">&lt;span th:text=&quot;${data}&quot;&gt;원래 텍스트&lt;/span&gt;</code></pre>
<p>[결과] -&gt; 소스보기</p>
<pre><code>&lt;span&gt;&amp;lt;b&amp;gt;text&amp;lt;/b&amp;gt;&lt;/span&gt;</code></pre><ul>
<li>utext : 이스케이프 기능이 <strong>적용되지 않는</strong> text </li>
</ul>
<p>[타임리프]</p>
<pre><code class="language-html">&lt;span th:utext=&quot;${data}&quot;&gt;원래 텍스트&lt;/span&gt;</code></pre>
<p>[결과] -&gt; 소스보기</p>
<pre><code>&lt;span&gt;&lt;b&gt;text&lt;/b&gt;&lt;/span&gt;</code></pre><blockquote>
</blockquote>
<p>Escape
html문서는 <code>&lt;</code>, <code>&gt;</code> 같은 특수 문자를 기반으로 정의 되기 때문에 html 텍스트를 보여줄 때 주의 해야하는데, 이때 <code>&lt;</code>, <code>&gt;</code>와 같이 html 파일에서 사용하는 특수문자를 문자로 표현하는 문자를 <code>html entity</code>라고 한다.
이 html 파일에서 사용하는 특수문자를 <code>html entity</code>로 변경하는 것을 <code>escape</code>라고 한다.
기본적인 예시로는 
<code>&lt;</code> -&gt; <code>&amp;lt;</code> , <code>&gt;</code> -&gt; <code>&amp;gt;</code>가 있고 이 외에도 많은 <code>html entity</code>가 있다.</p>
<blockquote>
</blockquote>
<p>위에서 말한 <code>[[]]</code>안에 표현식을 써서 사용하는 방법은 text와 같고, <code>[()]</code>를 사용하면 utext와 같다.</p>
<hr>
<h2 id="변수">변수</h2>
<p>타임리프에서는 변수를 사용 할 때에는 <code>${,,,}</code>의 변수 표현식을 사용하는데, 여기 안에는 <code>springEL</code> 이라는 스프링이 제공하는 표현식을 사용할 수 있다.</p>
<blockquote>
</blockquote>
<p>SpringEL 다양한 표현식 사용</p>
<ol>
<li><strong>Object</strong>
user.username : user의 username을 프로퍼티 접근
user[&#39;username&#39;] : 위와 같음 user.getUsername()
user.getUsername() : user의 getUsername() 을 직접 호출</li>
<li><strong>List</strong>
users[0].username : List에서 첫 번째 회원을 찾고 username 프로퍼티 접근 -&gt; list.get(0).getUsername()
users[0][&#39;username&#39;] : 위와 같음
users[0].getUsername() : List에서 첫 번째 회원을 찾고 메서드 직접 호출</li>
<li><strong>Map</strong>
userMap[&#39;userA&#39;].username : Map에서 userA를 찾고, username 프로퍼티 접근 map.get(&quot;userA&quot;).getUsername()
userMap[&#39;userA&#39;][&#39;username&#39;] : 위와 같음 userMap[&#39;userA&#39;].getUsername() : Map에서 userA를 찾고 메서드 직접 호출</li>
</ol>
<ul>
<li>지역 변수 : th:with 를 사용해 지역 변수를 선언해서 사용할 수 있, 해당 지역 변수는 선언한 태그 안에서만 사용할 수 있다.</li>
</ul>
<p>[타임리프]</p>
<pre><code class="language-html">&lt;div th:with=&quot;userFirst=${users[0]}&quot;&gt;
    &lt;p&gt;1번째 유저 이름 : &lt;span th:text=&quot;${userFirst.username}&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</code></pre>
<h2 id="객체">객체</h2>
<h3 id="기본-객체">기본 객체</h3>
<ul>
<li><code>${#request}</code></li>
<li><code>${#response}</code></li>
<li><code>${#session}</code></li>
<li><code>${#servletContext}</code></li>
<li><code>${#locale}</code></li>
</ul>
<h3 id="유틸리티-객체">유틸리티 객체</h3>
<ul>
<li><code>#message</code> : 메시지, 국제화 처리</li>
<li><code>#uris</code> : URI 이스케이프 지원</li>
<li><code>#dates</code> : java.util.Date 서식 지원 </li>
<li><code>#calendars</code> : java.util.Calendar 서식 지원 </li>
<li><code>#temporals</code> : 자바8 날짜 서식 지원</li>
<li><code>#numbers</code> : 숫자 서식 지원</li>
<li><code>#strings</code> : 문자 관련 편의 기능</li>
<li><code>#objects</code> : 객체 관련 기능 제공</li>
<li><code>#bools</code> : boolean 관련 기능 제공</li>
<li><code>#arrays</code> : 배열 관련 기능 제공</li>
<li><code>#lists</code> , <code>#sets</code> , <code>#maps</code> : 컬렉션 관련 기능 제공 </li>
<li><code>#ids</code> : 아이디 처리 관련 기능 제공, 뒤에서 설명</li>
</ul>
<h2 id="url-링크">URL 링크</h2>
<p>타임리프에서 링크를 생성할 때 <code>@{...}</code>를 사용하면 된다.</p>
<ul>
<li>단순한 URL
<code>@{/hello}</code> -&gt; <code>/hello</code></li>
<li>쿼리 파라미터 : () 에 있는 부분은 쿼리 파라미터로 처리
<code>@{/hello(param1=${param1}, param2=${param2})}</code> -&gt; <code>/hello?param1=data1&amp;param2=data2</code></li>
<li>경로 변수 : URL 경로상에 변수가 있으면 () 부분은 경로 변수로 처리
<code>@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}</code> -&gt; <code>/hello/data1/data2</code></li>
<li>경로 변수 + 쿼리 파라미터 : 경로 변수와 쿼리 파라미터를 함께 사용할 수 있음
<code>@{/hello/{param1}(param1=${param1}, param2=${param2})}</code> -&gt; <code>/hello/data1?param2=data2</code></li>
</ul>
<hr>
<h2 id="속성-값-설정">속성 값 설정</h2>
<ul>
<li>속성 값 설정 :<code>th:*</code>속성을 지정하면 기존 속성을 <code>th:*</code> 로 지정한 속성으로 대체한다. (없으면 만듬)</li>
</ul>
<p>[타임리프]</p>
<pre><code class="language-html">&lt;input type=&quot;text&quot; name=&quot;mock&quot; th:name=&quot;userA&quot; /&gt;</code></pre>
<p>[결과]</p>
<pre><code class="language-html">&lt;input type=&quot;text&quot; name=&quot;userA&quot; /&gt;</code></pre>
<ul>
<li>속성 추가 : 원래 있던 속성 값을 대체 하는 것이 아니라 그 값에 추가<ul>
<li>th:attrappend : 속성 값의 뒤에 값을 추가한다.(띄어쓰기 필요)</li>
<li>th:attrprepend : 속성 값의 앞에 값을 추가한다.(띄어쓰기 필요)</li>
<li>th:classappend : class 속성에 자연스럽게 추가한다.(띄어쓰기 필요 없음, 자동으로 띄어쓰기까지 해줌)</li>
</ul>
</li>
</ul>
<p>[타임리프]</p>
<pre><code class="language-html">&lt;input type=&quot;text&quot; class=&quot;text&quot; th:attrappend=&quot;class=&#39; large&#39;&quot; /&gt;</code></pre>
<p>[결과]</p>
<pre><code class="language-html">&lt;input type=&quot;text&quot; class=&quot;text large&quot; /&gt;</code></pre>
<blockquote>
</blockquote>
<p>checked 
사실 <code>checked</code>는 <code>true</code>, <code>false</code> 값을 구분하는 것이 아니라 속성 값이 존재하는지 하지 않는지를 구분하기때문에 처리가 까다로운데, 타임리프는 <code>th:checked=&quot;false&quot;</code>라고 되어있으면 아예 <code>checked</code> 값을 넣지 않는다.</p>
<hr>
<h2 id="제어문">제어문</h2>
<h3 id="반복">반복</h3>
<ul>
<li>반복 1</li>
</ul>
<p>[타임리프]</p>
<pre><code class="language-html">&lt;tr th:each=&quot;user : ${users}&quot;&gt;
  &lt;td th:text=&quot;${user.username}&quot;&gt;유저이름&lt;/td&gt;
&lt;/tr&gt;</code></pre>
<p>[결과]</p>
<pre><code class="language-html">&lt;tr&gt;
  &lt;td&gt;username1&lt;/td&gt;
&lt;tr&gt;
&lt;/tr&gt;
  &lt;td&gt;username2&lt;/td&gt;
&lt;/tr&gt;</code></pre>
<p>반복시 오른쪽 컬렉션<code>${users}</code>의 값을 하나씩 꺼내서 왼쪽 변수<code>user</code>에 담아서 태그를 반복
반복 할 수 있는 객체 : <code>Map</code>, <code>List</code> ,<code>배열</code>, <code>java.util.Iterable</code> , <code>java.util.Enumeration</code>을 구현한 모든 객체를 반복에 사용할 수 있음</p>
<blockquote>
<p>Map 도 사용할 수 있는데 이 경우 변수에 담기는 값은 Map.Entry 입니다.</p>
</blockquote>
<ul>
<li>반복 2
반복의 두번째 파라미터를 설정해 반복의 여러 값을 확인 할 수 있음
이 때 두번째 파라미터를 지정하지 않아도 <code>첫번째 파라미터 이름</code> + <code>Stat</code> 으로 접근 가능
반복 상태 유지 기능<ul>
<li>index : 0부터 시작하는 값</li>
<li>count : 1부터 시작하는 값</li>
<li>size : 전체 사이즈</li>
<li>even , odd : 홀수, 짝수 여부( boolean )</li>
<li>first , last :처음, 마지막 여부( boolean ) </li>
<li>current : 현재 객체</li>
</ul>
</li>
</ul>
<p>[타임리프]</p>
<pre><code class="language-html">&lt;tr th:each=&quot;user, userStat : ${users}&quot;&gt;
  &lt;td th:text=&quot;${userStat.index}&quot;&gt;인덱스&lt;/td&gt;
  &lt;td th:text=&quot;${user.username}&quot;&gt;유저이름&lt;/td&gt;
&lt;/tr&gt;</code></pre>
<p>[결과]</p>
<pre><code class="language-html">&lt;tr&gt;
  &lt;td&gt;0&lt;/td&gt;
  &lt;td&gt;username1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;1&lt;/td&gt;
  &lt;td&gt;username1&lt;/td&gt;
&lt;/tr&gt;</code></pre>
<h3 id="조건">조건</h3>
<p>타임리프는 조건식을 비교했는데 맞지 않으면 아예 렌더링을 하지 않고 사라짐</p>
<ul>
<li>if, unless</li>
</ul>
<p>[타임리프]</p>
<pre><code class="language-html">&lt;span th:text=&quot;&#39;결과1&#39;&quot; th:if=&quot;${21 lt 20}&quot;&gt;&lt;/span&gt;
&lt;span th:text=&quot;&#39;결과2&#39;&quot; th:unless=&quot;${21 gt 20}&quot;&gt;&lt;/span&gt;</code></pre>
<p>[결과]</p>
<pre><code class="language-html">&lt;span&gt;결과2&lt;/span&gt;</code></pre>
<ul>
<li>switch
<code>th:case=&quot;*&quot;</code>는 만족하는 값이 없을때 사용하는 디폴트 값</li>
</ul>
<p>[타임리프]</p>
<pre><code class="language-html">&lt;td th:switch=&quot;&#39;10&#39;&quot;&gt;
    &lt;span th:case=&quot;10&quot;&gt;10살&lt;/span&gt;
      &lt;span th:case=&quot;20&quot;&gt;20살&lt;/span&gt;
      &lt;span th:case=&quot;*&quot;&gt;기타&lt;/span&gt;
&lt;/td&gt;</code></pre>
<p>[결과]</p>
<pre><code class="language-html">&lt;td&gt;
    &lt;span&gt;10살&lt;/span&gt;
&lt;/td&gt;</code></pre>
<hr>
<h2 id="주석">주석</h2>
<ul>
<li>표준 HTML 주석 : 타임리프가 렌더링 X , 주석 부분이 그대로 존재</li>
<li>타임리프 파서 주석 : 렌더링에서 주석 부분을 제거<ul>
<li>타임리프 프로토타입 주석 : HTML 파일 : 주석 / 타임리프를 렌더링 한 경우 : 보임</li>
</ul>
</li>
</ul>
<hr>
<h2 id="블록">블록</h2>
<p><code>&lt;th:block&gt;</code> 은 HTML 태그가 아닌 타임리프의 유일한 자체 태그
원래 타임리프는 html태그 안에 속성으로 기능을 정의해서 사용하는데, 그렇게 하기 애매한 경우에 사용
이 태그는 렌더링시에는 사라진다.</p>
<p>[타임리프]</p>
<pre><code class="language-html">&lt;th:block th:each=&quot;user : ${users}&quot;&gt; 
    &lt;div&gt;
        &lt;div th:text=&quot;${user.index}&quot;&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div&gt;
        &lt;div th:text=&quot;${user.name}&quot;&gt;&lt;/div&gt;
        &lt;div th:text=&quot;${user.age}&quot;&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/th:block&gt;</code></pre>
<p>[결과]</p>
<pre><code class="language-html">&lt;div&gt;
    &lt;div&gt;1&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;div&gt;유저1&lt;/div&gt;
    &lt;div&gt;10&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;div&gt;2&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;div&gt;유저2&lt;/div&gt;
    &lt;div&gt;20&lt;/div&gt;
&lt;/div&gt;</code></pre>
<hr>
<h2 id="자바스크립트-인라인">자바스크립트 인라인</h2>
<p>타임리프는 자바스크립트에서 타임리프를 사용할 수 있는 자바스크립트 인라인 기능을 제공한다.
적용하기 위해서는 <code>&lt;script th:inline=&quot;javascript&quot;&gt;</code>를 적으면 된다.</p>
<ul>
<li>텍스트 : 이스케이프까지 처리해서 <code>&quot;</code>로 감싸져서 들어감</li>
<li>내추럴 템플릿 : 주석 부분은 렌더링시 사라짐</li>
<li>객체 : 객체를 자동으로 JSON으로 변환 </li>
<li>each : 반복문 문법이 동작함</li>
</ul>
<p>[타임리프]</p>
<pre><code class="language-html">&lt;script th:inline=&quot;javascript&quot;&gt;
var username = [[${user.username}]];
var age = [[${user.age}]];
//자바스크립트 내추럴 템플릿
var username2 = /*[[${user.username}]]*/ &quot;test username&quot;;
//객체
var user = [[${user}]];


[# th:each=&quot;user, stat : ${users}&quot;]
var user[[${stat.count}]] = [[${user}]];
[/]
&lt;/script&gt;</code></pre>
<p>[결과]</p>
<pre><code class="language-html">&lt;script&gt;
var username = &quot;userA&quot;;
var age = 10; //자바스크립트 내추럴 템플릿
var username2 = &quot;userA&quot;; //객체
var user = {&quot;username&quot;:&quot;userA&quot;,&quot;age&quot;:10};

var user1 = {&quot;username&quot;:&quot;userA&quot;,&quot;age&quot;:10};
var user2 = {&quot;username&quot;:&quot;userB&quot;,&quot;age&quot;:20};
var user3 = {&quot;username&quot;:&quot;userC&quot;,&quot;age&quot;:30};
&lt;/script&gt;</code></pre>
<hr>
<h2 id="템플릿-조각">템플릿 조각</h2>
<p>템플릿 조각은 여러 페이지에서 공통적으로 사용하는 영역들을 해결하기 위해서 용한다. 예를 들어 footer와 header 등과 같은 부분은 여러 페이지에서 공통적으로 사용되면서 동시에 모든 페이지에서 똑같아야하는 부분등을 의미한다.</p>
<h3 id="부분-포함">부분 포함</h3>
<p><code>footer</code>를 따로 파일로 분리하고, 각 페이지에서 분리했던 <code>footer</code>를 가져다 사용하는 방식이다.</p>
<p>[타임리프]
<code>/template/fragment/footer.html</code></p>
<pre><code class="language-html">&lt;footer th:fragment=&quot;copy&quot;&gt; 푸터 자리 입니다.&lt;/footer&gt;

&lt;footer th:fragment=&quot;copyParam (param1, param2)&quot;&gt;
    &lt;p&gt;파라미터 자리 입니다.&lt;/p&gt;
    &lt;p th:text=&quot;${param1}&quot;&gt;&lt;/p&gt;
      &lt;p th:text=&quot;${param2}&quot;&gt;&lt;/p&gt;
&lt;/footer&gt;</code></pre>
<p><code>/template/fragment/main.html</code></p>
<pre><code class="language-html">&lt;body&gt;
    &lt;h2&gt;부분 포함 insert&lt;/h2&gt;
    &lt;div th:insert=&quot;~{template/fragment/footer :: copy}&quot;&gt;&lt;/div&gt;
    &lt;h2&gt;부분 포함 replace&lt;/h2&gt;
    &lt;div th:replace=&quot;~{template/fragment/footer :: copy}&quot;&gt;&lt;/div&gt;
    &lt;h1&gt;파라미터 사용&lt;/h1&gt;
    &lt;div th:replace=&quot;~{template/fragment/footer :: copyParam (&#39;데이터1&#39;, &#39;데이터 2&#39;)}&quot;&gt;&lt;/div&gt;
&lt;/body&gt;</code></pre>
<p>[결과]</p>
<pre><code class="language-html">&lt;body&gt;
    &lt;h2&gt;부분 포함 insert&lt;/h2&gt;
    &lt;div&gt;
        &lt;footer&gt; 푸터 자리 입니다.&lt;/footer&gt;
      &lt;/div&gt;
    &lt;h2&gt;부분 포함 replace&lt;/h2&gt;
    &lt;footer&gt; 푸터 자리 입니다.&lt;/footer&gt;
    &lt;h1&gt;파라미터 사용&lt;/h1&gt;
      &lt;footer&gt;
        &lt;p&gt;파라미터 자리 입니다.&lt;/p&gt;
        &lt;p&gt;데이터1&lt;/p&gt;
          &lt;p&gt;데이터2&lt;/p&gt;
    &lt;/footer&gt;
&lt;/body&gt;</code></pre>
<ul>
<li><code>th:insert</code> : 이 속성이 적혀 있던 태그의 자식 태그로 들어옴</li>
<li><code>th:replace</code> : 이 속성이 적혀 있던 태그가 사라지고, 그 위치로 들어옴</li>
</ul>
<p>또 파라미터를 사용 할 수 있고, 파라미터로는 태그도 들어 갈 수 있다.
이것을 이용해서 다양한 동적인 페이지를 만들 수 있다.</p>
<h3 id="레이아웃">레이아웃</h3>
<p>위에서는 공통된 부분을 부분별로 잘라내어 따로 저장하고 메인 페이지에서 이를 가져다 사용하는 방법이였다면, 이 방법은 오히려 반대로 한 html에 레이아웃을 짜놓고, 내용이 변경 되는 부분에만 내용을 넣어서 페이지를 구성하는 방식이다.</p>
<p>레이아웃을 짜놓을 파일에 
<code>&lt;html th:fragment=&quot;layout(title, content)&quot; xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;</code>를 추가해 해당 페이지를 <code>layout</code>이라고 정의하고, <code>title</code>과<code>content</code>를 받아서 레이아웃 안에 넣어 페이지를 보여준다.</p>
<p>[타임리프]</p>
<p><code>layout.html</code></p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html th:fragment=&quot;layout (title, content)&quot; xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
&lt;head&gt;
    &lt;title th:replace=&quot;${title}&quot;&gt;레이아웃 타이틀&lt;/title&gt; 
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;레이아웃 H1&lt;/h1&gt;
&lt;div th:replace=&quot;${content}&quot;&gt;
   &lt;p&gt;레이아웃 컨텐츠&lt;/p&gt; 
&lt;/div&gt;
&lt;footer&gt; 레이아웃 푸터&lt;/footer&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><code>main.html</code></p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html th:replace=&quot;~{template/layoutExtend/layoutFile ::layout(~{::title},~{::section})}&quot;xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
&lt;head&gt;
    &lt;title&gt;메인 페이지 타이틀&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;section&gt;
    &lt;p&gt;메인 페이지 컨텐츠&lt;/p&gt;
    &lt;div&gt;메인 페이지 포함 내용&lt;/div&gt;
&lt;/section&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>[결과]</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;메인 페이지 타이틀&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;레이아웃 H1&lt;/h1&gt;
&lt;section&gt;
    &lt;p&gt;메인 페이지 컨텐츠&lt;/p&gt;
    &lt;div&gt;메인 페이지 포함 내용&lt;/div&gt;
&lt;/section&gt;
&lt;footer&gt; 레이아웃 푸터&lt;/footer&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p><em>출처</em></p>
<ol>
<li><a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard">스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring] MyBatis log 설정]]></title>
            <link>https://velog.io/@0_sujeong/spring-MyBatis-log-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@0_sujeong/spring-MyBatis-log-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Mon, 30 May 2022 08:20:50 GMT</pubDate>
            <description><![CDATA[<h1 id="개발환경">개발환경</h1>
<ul>
<li>jdk : 1.8</li>
<li>IDE : intelliJ</li>
<li>DB : Oracle 11g</li>
<li>maven</li>
<li>spring boot</li>
<li>mybatis</li>
</ul>
<h1 id="의존성-추가---pomxml">의존성 추가 - pom.xml</h1>
<p>pom.xml 파일에 아래 코드를 추가해주세요.</p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.bgee.log4jdbc-log4j2&lt;/groupId&gt;
    &lt;artifactId&gt;log4jdbc-log4j2-jdbc4.1&lt;/artifactId&gt;
    &lt;version&gt;1.16&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<h1 id="logger-설정-파일-추가---log4jdbclog4j2properties">logger 설정 파일 추가 - log4jdbc.log4j2.properties</h1>
<p>propetties 파일을 새로 생성해 아래의 코드를 추가해주세요. </p>
<pre><code class="language-properties">log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0 # 최대 몇줄 출력에 관한 설정, 0으로 설정하면 제한 없이 설정</code></pre>
<h1 id="log-level-설정---application">log level 설정 - application</h1>
<pre><code>1. spring.datasource.driver-class-name 변경
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy

2. spring.datasource.url 변경 ( &#39;jdbc:&#39; 뒤에 &#39;log4jdbc:&#39; 추가 )
spring.datasource.url=jdbc:oracle:~~
spring.datasource.url=jdbc:log4jdbc:~~
</code></pre><p>위의 코드를 참고해서 <code>spring.datasource.driver-class-name</code>의 값과 <code>spring.datasource.url</code>값을 변경해주세요</p>
<p>출력하고 싶은 내용에 디버그 값을 설정해주세요
xml로 설정하는 방법도 있는데 xml파일을 최대한 사용하지 않기위해 <code>application.properties</code>에 작성했습니다.
만약 두 개 다 설정하셨다면 <code>application.properties</code>에 설정 한 내용이 우선 순위로 들어갑니다.</p>
<pre><code># 로그 관련 설정
logging.level.jdbc.sqlonly=OFF
logging.level.jdbc.sqltiming=INFO
logging.level.jdbc.resultsettable=INFO
logging.level.jdbc.audit=OFF
logging.level.jdbc.resultset=OFF
logging.level.jdbc.connection=OFF</code></pre><p><em>참고</em>
<a href="https://velog.io/@harpuria/Springboot-MyBatis-%EC%BF%BC%EB%A6%AC-%EB%A1%9C%EA%B7%B8-%EC%84%A4%EC%A0%95" >Springboot MyBatis 쿼리 로그 설정</a>
<a href="https://sosohanya.tistory.com/31" >MyBatis Logging 추가</a>
<a href="http://jmlim.github.io/spring/2018/12/26/spring-boot-logback-sql-pretty/" >스프링 부트(Spring boot) 마이바티스(Mybatis) 에서 쿼리 로그 출력 및 정렬하기.</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring] HTTP 요청]]></title>
            <link>https://velog.io/@0_sujeong/spring-HTTP-%EC%9A%94%EC%B2%AD</link>
            <guid>https://velog.io/@0_sujeong/spring-HTTP-%EC%9A%94%EC%B2%AD</guid>
            <pubDate>Sun, 29 May 2022 09:36:50 GMT</pubDate>
            <description><![CDATA[<h1 id="종류">종류</h1>
<p>클라이언트에서 서버로 요청 데이터를 전달할 때 가장 많이 사용하는 3가지 방식에 대해서 설명하고자한다.</p>
<h2 id="1-get---쿼리-파라미터">1. GET - 쿼리 파라미터</h2>
<p><code>/url?username=hello&amp;age=20</code>와 같이 HTTP 메시지 body없이 쿼리 파라미터에 데이터를 보내는 방식이다.</p>
<h2 id="2-post---html-form">2. POST - HTML Form</h2>
<p><code>html</code>에 form 태그에 작성된 내용을 전송 받는 방법이다.</p>
<h2 id="3-http-message-body">3. HTTP message body</h2>
<p>HTTP API에서 주로 사용하는 방식이다.</p>
<hr>
<h1 id="1-get---쿼리-파라미터--2-post---html-form">1. GET - 쿼리 파라미터 &amp; 2. POST - HTML Form</h1>
<p>이 두가지 방식을 합친 이유는 클라이언트에서 보내는 방식이 다르게 보일지 몰라도 HTTP 메시지에는 같은 형식으로 저장되기 때문에 구분 없이 사용되기때문에 같이 설명하려고한다.</p>
<h2 id="컨트롤러-작성">컨트롤러 작성</h2>
<p><code>/url?username=hello&amp;age=20</code>을 처리해야한다는 상황을 가정하고 컨트롤러를 작성해보자.</p>
<h3 id="1-httpservletrequest">1) HttpServletRequest</h3>
<p>Servlet을 사용할 때 처럼 <code>HttpServletRequest</code> 통해서 값을 입력 받는 방식이다.</p>
<pre><code class="language-java">@RequestMapping(&quot;/request-param-v1&quot;)
public void requestParamV1(
    HttpServletRequest request,HttpServletResponse response
) throws IOException {
    String username = request.getParameter(&quot;username&quot;);
    int age = Integer.parseInt(request.getParameter(&quot;age&quot;));
}</code></pre>
<h3 id="2-requestparam">2) @RequestParam</h3>
<p><code>@RequestParam</code>라는 어노테이션을 이용하는 방식으로, ()안에 쿼리 파라미터의 키값을 넣어주면 뒤에있는 데이터 형식으로 변환해서 변수에 넣어준다.
1번의 방법보다 훨씬 코드가 깔끔해지는 것을 볼 수 있다.</p>
<blockquote>
</blockquote>
<p>값을 받을 변수명과 쿼리 파라미터의 키 값이 같다면 ()안에 <code>쿼리 파라미터의 키값</code>을 넣는 것을 생략할 수 있다.
<code>@RequestParam(&quot;username&quot;) String memberName</code>
-&gt; <code>@RequestParam String memberName</code>
또 이때 변수의 형식이 단순 데이터(int, String, Integer 등 ) 이라면 <code>@RequestParam</code>까지 생략할 수 있다.
<code>@RequestParam(&quot;username&quot;) String memberName</code>
-&gt; <code>String memberName</code>
<em>(이렇게 되면 spring 내부에서 <code>required=false</code>를 적용한다.)</em>
하지만 어노테이션까지 생략하는 것은 명시적이지 않아 권하지 않는다.</p>
<blockquote>
</blockquote>
<h3 id="requestparam-추가-속성">@RequestParam 추가 속성</h3>
<ol>
<li>required : 파라미터 필수 여부 (기본 값 true)</li>
<li>defaultValue : 기본 값 세팅 (기본 값 없음)</li>
</ol>
<pre><code class="language-java">@RequestMapping(&quot;/request-param-v2&quot;)
public void requestParamV2(
    @RequestParam(&quot;username&quot;) String memberName,
    @RequestParam(&quot;age&quot;) int memberAge) {
    log.info(&quot;username={}, age={}&quot;, memberName, memberAge);
}</code></pre>
<h3 id="3-modelattribute">3) @ModelAttribute</h3>
<p>만약 값을 객체로 받고싶다면 <code>@ModelAttribute</code>를 사용해보자.
이 어노테이션을 사용한다면 객체도 자동으로 생성되고, 생성된 객체에 값도 자동으로 넣어준다.</p>
<blockquote>
</blockquote>
<p>이 어노테이션은 객체의 <code>setter()</code>를 통해서 값을 넣기 때문에 꼭 <code>setter()</code>가 정의되어있어야한다.</p>
<blockquote>
</blockquote>
<p>이 어노테이션도 생략할 수는 있지만 권장하지 않는다.</p>
<pre><code class="language-java">@ResponseBody
@RequestMapping(&quot;/model-attribute-v1&quot;)
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
    log.info(&quot;username={}, age={}&quot;, helloData.getUsername(), helloData.getAge());
    return &quot;ok&quot;;
}</code></pre>
<hr>
<h1 id="2-http-message-body">2. HTTP message body</h1>
<p>HTTP 메시지 body를 통해 데이터가 넘어오는 경우에는 위의 방식을 사용할 수 없다.</p>
<h2 id="컨트롤러-작성-1">컨트롤러 작성</h2>
<pre><code class="language-json">{&quot;username&quot;:&quot;hello&quot;, &quot;age&quot;:20}
content-type: application/json</code></pre>
<p>이러한 형식의 데이터를 받아서 처리한다고 가정하고 컨트롤러를 작성해보자</p>
<h3 id="1-httpservletresponse">1) HttpServletResponse</h3>
<p><code>HttpServletRequest</code>를 사용해서 직접 HTTP 메시지 바디에서 데이터를 <code>InputStream</code>을 이용해 직접 읽을 수 있다.</p>
<ul>
<li>string<pre><code class="language-java">@PostMapping(&quot;/request-body-json-v1&quot;)
public void requestBodyJsonV1(
  HttpServletRequest request,
  HttpServletResponse response
) throws IOException {
  ServletInputStream inputStream = request.getInputStream();
  String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
  log.info(&quot;messageBody={}&quot;, messageBody);
  response.getWriter().write(&quot;ok&quot;);
}</code></pre>
</li>
<li>json 등의 객체타입
문자로된 JSON 데이터를 변환하는 과정이 필요<pre><code class="language-java">private ObjectMapper objectMapper = new ObjectMapper();
</code></pre>
</li>
</ul>
<p>@PostMapping(&quot;/request-body-json-v1&quot;)
public void requestBodyJsonV1(
    HttpServletRequest request,
    HttpServletResponse response
) throws IOException {
    ServletInputStream inputStream = request.getInputStream();
    String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
    log.info(&quot;messageBody={}&quot;, messageBody);
    HelloData data = objectMapper.readValue(messageBody, HelloData.class);
    log.info(&quot;username={}, age={}&quot;, data.getUsername(), data.getAge());
    response.getWriter().write(&quot;ok&quot;);
}</p>
<pre><code>
### 2) InputStream
이 `InputStream`은 위의 `HttpServletResponse`에서 InputStream을 생성하던 것을 spring이 대신 해줘서 InputStream으로 바로 받을 수 있게 해주는 것이다. 나머지는 동일하다.

```java
@PostMapping(&quot;/request-body-string-v2&quot;)
public void requestBodyStringV2(
    InputStream inputStream, 
    Writer responseWriter
) throws IOException {
    String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
    log.info(&quot;messageBody={}&quot;, messageBody);
    responseWriter.write(&quot;ok&quot;);
}</code></pre><h3 id="3-httpentity">3) HttpEntity</h3>
<p><code>HttpEntity</code>란 HTTP 메시지를 편하게 조회할 수 있게 해준다.
<code>HttpEntity</code>를 상속받은 <code>RequestEntity</code>와 <code>ResponseEntity</code>도 같은 기능을 제공한다.</p>
<pre><code class="language-java">@PostMapping(&quot;/request-body-string-v3&quot;)
public HttpEntity&lt;String&gt; requestBodyStringV3(HttpEntity&lt;String&gt; httpEntity) {
    String messageBody = httpEntity.getBody();
    log.info(&quot;messageBody={}&quot;, messageBody);
    return new HttpEntity&lt;&gt;(&quot;ok&quot;);
}</code></pre>
<h3 id="4-requestbody">4) @RequestBody</h3>
<p><code>@RequestBody</code>어노테이션은 HTTP 메시지 바디 정보를 편리하게 조회할 수 있게 해준다. </p>
<ul>
<li>string<pre><code class="language-java">@ResponseBody
@PostMapping(&quot;/request-body-string-v4&quot;)
public String requestBodyStringV4(@RequestBody String messageBody) {
  log.info(&quot;messageBody={}&quot;, messageBody);
  return &quot;ok&quot;;
}</code></pre>
</li>
<li>json 등의 객체타입
만약 <code>@RequestBody</code> 데이터 타입이 객체라면 <code>@ModelAttribute</code>와 같이 객체가 생성되며 자동으로 값이 들어간다.<blockquote>
</blockquote>
@RequestParam나 @ModelAttribute`와 같이 생략은 불가능하다 <pre><code class="language-java">@ResponseBody
@PostMapping(&quot;/request-body-json-v3&quot;)
public String requestBodyJsonV3(@RequestBody HelloData data) {
  log.info(&quot;username={}, age={}&quot;, data.getUsername(), data.getAge());
  return &quot;ok&quot;;
}</code></pre>
물론 <code>HttpEntity</code>에서도 동일하게 동작된다.<pre><code class="language-java">@ResponseBody
@PostMapping(&quot;/request-body-json-v4&quot;)
public String requestBodyJsonV4(HttpEntity&lt;HelloData&gt; httpEntity) {
  HelloData data = httpEntity.getBody();
  log.info(&quot;username={}, age={}&quot;, data.getUsername(), data.getAge());
  return &quot;ok&quot;;
}</code></pre>
<blockquote>
</blockquote>
HTTP 요청시에 <code>content-type</code>이 <code>application/json</code>인지 <strong>꼭!</strong> 확인해야 한다. 그래야 JSON을 처리할 수 있는 HTTP 메시지 컨버터가 실행된다.</li>
</ul>
<p><em>출처</em></p>
<ol>
<li><a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard">스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring] HTTP 응답]]></title>
            <link>https://velog.io/@0_sujeong/spring-HTTP-%EC%9D%91%EB%8B%B5</link>
            <guid>https://velog.io/@0_sujeong/spring-HTTP-%EC%9D%91%EB%8B%B5</guid>
            <pubDate>Sun, 29 May 2022 06:13:30 GMT</pubDate>
            <description><![CDATA[<h1 id="종류">종류</h1>
<p>spring에서 만들 수 있는 응답 데이터는 크게 3종류로 나누어져 있다.
각 응답 데이터를 어떻게 만들어서 응답하는지에 대해 정리해보고자 한다.</p>
<h2 id="1-정적-리소스">1. 정적 리소스</h2>
<blockquote>
</blockquote>
<p>정적 리소스 : js, css, images 등과 같이 변화가 없는 리소스를 의미</p>
<h2 id="2-뷰-템플릿">2. 뷰 템플릿</h2>
<p>동적인 <code>html</code>을 리턴할 때에는 뷰 템플릿을 이용한다.</p>
<h2 id="3-http-메시지">3. HTTP 메시지</h2>
<p>HTTP API를 작성 할 때에는 <code>html</code>이 아니라 데이터를 전달해야해서 HTTP 메시지에 JSON과 같은 데이터를 실어보낼수 있도록 한다.</p>
<hr>
<h1 id="1-정적-리소스-1">1. 정적 리소스</h1>
<h2 id="기본경로">기본경로</h2>
<p>정적 리소스는 spring에서 코드를 따로 작성해서 응답을 하는 것이 아니라, 정적 리소스들을 미리 약속된 디렉토리에 넣어놓으면 알아서 spring이 정적 리소스를 제공한다.
이 때 미리 약속된 디렉토리는 클래스 패스의 시작 경로인 <code>src/main/resources</code>안에 있는  <code>/static</code> , <code>/public</code> , <code>/resources</code> , <code>/META-INF/resources</code> 이 있다.</p>
<blockquote>
</blockquote>
<p>ex <code>src/main/resources/static/js</code>에<code>example.js</code>가 있다면,
<code>/js/example.js</code>를 요청하면 된다.</p>
<hr>
<h1 id="2-뷰-템플릿-1">2. 뷰 템플릿</h1>
<h2 id="기본경로-1">기본경로</h2>
<p>뷰 템플릿을 거쳐서<code>html</code>을 생성하고, 뷰가 응답을 만들어서 전달한다.
이때 spring은 뷰 템플릿 기본 경로를 제공하는데 변경도 가능하다.
기본 경로는 <code>src/main/resources/templates</code> 이곳이다.</p>
<h2 id="컨트롤러-작성">컨트롤러 작성</h2>
<p>뷰 템플릿은 컨트롤러에서 리턴하는 방식이 3가지 정도가 있다. 
각 방식의 특징에 대해서 알아보자</p>
<h3 id="1-modelandview">1) ModelAndView</h3>
<p>이 방식은 컨트롤러 내에서 <code>ModelAndView</code>를 생성해서 반환하는 방법이다.
생성할 때에 경로를 넣고, 만약 같이 보내야할 데이터가 있다면 생성된 <code>ModelAndView</code>에 데이터를 추가해서 같이 전송한다.</p>
<pre><code class="language-java">@RequestMapping(&quot;/response-view-v1&quot;)
public ModelAndView responseViewV1() {
    ModelAndView mav = new ModelAndView(&quot;response/hello&quot;)
                            .addObject(&quot;data&quot;, &quot;hello!&quot;);
    return mav;
}</code></pre>
<h3 id="2-string">2) String</h3>
<p>이 방식은 보내야할 데이터가 있다면 model에 데이터를 넣고, 리턴은 경로만 리턴을 하는 방식이다. 앞에 <code>ModelAndView</code>와 기능은 같은데 <code>ModelAndView</code>를 직접 만들지 않아도 되서 코드가 훨씬 깔끔하다.</p>
<pre><code class="language-java">@RequestMapping(&quot;/response-view-v2&quot;)
public String responseViewV2(Model model) {
    model.addAttribute(&quot;data&quot;, &quot;hello!!&quot;);
    return &quot;response/hello&quot;;
}</code></pre>
<h3 id="3-void">3) Void</h3>
<p>이 방식은 앞의 <code>String</code>을 리턴하는 방식과 비슷한데, return을 void로 처리한 방식이다.
이것은 <code>RequestMapping</code>되는 경로와 뷰 템플릿의 경로가 같으면 사용할 수 있는데, 이 방식은 명시적이지 않아서 추천하지 않는다.</p>
<pre><code class="language-java">@RequestMapping(&quot;/response/hello&quot;)
public void responseViewV3(Model model) {
    model.addAttribute(&quot;data&quot;, &quot;hello!!&quot;);
}
</code></pre>
<hr>
<h1 id="3-http-메시지-1">3. HTTP 메시지</h1>
<p>이번에는 HTTP API를 제공하는 경우 <code>html</code>이나 뷰 템플릿이 아닌 데이터를 응답으로 보내야하기 때문에 HTTP 메시지에 데이터를 넣어야한다.</p>
<blockquote>
</blockquote>
<p>물론 정적 리소스, 뷰 템플릿을 사용해도 HTTP 메시지에 최종 데이터가 담겨서 가지만, 정적 리소스는 동적인 데이터를 전송할 수 없고, 뷰 템플릿은 뷰 템플릿을 거쳐서 가야한다. 이렇게 정적 리소스나 뷰 템플릿을 거치지 않고 직접 HTTP 응답 메시지를 전달하는 경우를 알아 보려고 한다.</p>
<h2 id="컨트롤러-작성-1">컨트롤러 작성</h2>
<h3 id="1-httpservletresponse">1) HttpServletResponse</h3>
<p>Servlet을 사용할 때 처럼 <code>HttpServletResponse</code>를 통해서 직접 전달하는 방식이다.</p>
<pre><code class="language-java">@GetMapping(&quot;/response-body-string-v1&quot;)
public void responseBodyV1(HttpServletResponse response) throws IOException
{
        response.getWriter().write(&quot;ok&quot;);
}</code></pre>
<h3 id="2-responseentity">2) ResponseEntity</h3>
<p><code>ResponseEntity</code>엔티티는 HTTP 메시지의 헤더, 바디 정보를 가지고<code>HttpEntity</code>를 상속 받은 객체이다. <code>ResponseEntity</code>는 여기에 더해서 HTTP 응답 코드를 설정할 수 있다.</p>
<blockquote>
</blockquote>
<p><code>HttpStatus</code>는 HTTP 응답 코드가 정의 되어있다.
전부 상수로 되어있는데 <code>200</code>이라고 적는 것보다 <code>HttpStatus.OK</code>라고 적는 것이 보기에 좀 더 직관적이라 사용한다.</p>
<ul>
<li>string<pre><code class="language-java"></code></pre>
</li>
</ul>
<p>@GetMapping(&quot;/response-body-string-v2&quot;)
public ResponseEntity<String> responseBodyV2() {
    return new ResponseEntity&lt;&gt;(&quot;ok&quot;, HttpStatus.OK);
}</p>
<pre><code>- json 등의 객체타입
```java
@GetMapping(&quot;/response-body-json-v1&quot;)
public ResponseEntity&lt;HelloData&gt; responseBodyJsonV1() {
    HelloData helloData = new HelloData();
    helloData.setUsername(&quot;userA&quot;);
    helloData.setAge(20);
    return new ResponseEntity&lt;&gt;(helloData, HttpStatus.OK);
}</code></pre><h3 id="3-responsebody">3) @ResponseBody</h3>
<p><code>@ResponseBody</code>어노테이션을 사용하면 리턴 값이 그대로 들어간다.
따로 생성을 하거나 하지 않기 때문에 코드가 가장 간결하다.</p>
<blockquote>
</blockquote>
<p>HTTP 응답 코드도 설정할 수 있는데, <code>@ResponseStatus(HttpStatus.OK)</code>를 사용해서 할 수 있다.
다만 위의 <code>ResponseEntity</code>방식처럼 동적으로 지정할 수는 없어서 동적으로 지정하고 싶다면 <code>ResponseEntity</code>을 사용해서 리턴시켜야한다.</p>
<ul>
<li>string<pre><code class="language-java">@ResponseBody
@GetMapping(&quot;/response-body-string-v3&quot;)
public String responseBodyV3() {
  return &quot;ok&quot;;
}</code></pre>
</li>
<li>json 등의 객체타입<pre><code class="language-java">@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping(&quot;/response-body-json-v2&quot;)
public HelloData responseBodyJsonV2() {
  HelloData helloData = new HelloData();
  helloData.setUsername(&quot;userA&quot;);
  helloData.setAge(20);
  return helloData;
}</code></pre>
</li>
</ul>
<blockquote>
</blockquote>
<p><code>ResponseEntity</code>엔티티나 <code>@ResponseBody</code>어노테이션을 사용해서 객체 타입의 데이터를 데이터로 보낸다면 <code>HTTP 메시지 컨버터</code>가 동작해서 JSON 형식으로 변환된다.</p>
<p><em>출처</em></p>
<ol>
<li><a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard">스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring] SSL 인증서 적용]]></title>
            <link>https://velog.io/@0_sujeong/spring-SSL-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EC%A0%81%EC%9A%A9</link>
            <guid>https://velog.io/@0_sujeong/spring-SSL-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EC%A0%81%EC%9A%A9</guid>
            <pubDate>Thu, 26 May 2022 06:45:13 GMT</pubDate>
            <description><![CDATA[<h1 id="ssl-발급">SSL 발급</h1>
<ol>
<li>openSSL 다운로드 </li>
<li>openSSL.exe 실행</li>
<li>다음 명령어 순서대로 입력<pre><code>openssl genrsa -des3 -out private.pem 2048
openssl req -new -key private.pem -out private.csr
openssl genrsa -aes256 -out rootCA.pem 2048
openssl req -x509 -new -nodes -key rootCA.pem -days 3650 -out rootCA.csr
openssl x509 -req -in private.csr -CA rootCA.csr -CAkey rootCA.pem -CAcreateserial -out private.crt -days 3650
openssl pkcs12 -export -in private.crt -inkey private.pem -out keystore -name tomcat</code></pre><em>ssl 적용 테스트 용이라 대충 만들었어요 :)</em></li>
</ol>
<h1 id="ssl-적용">SSL 적용</h1>
<h2 id="applicationproperties">application.properties</h2>
<ul>
<li><p>keyStore 파일을 이용</p>
<pre><code>server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
server.ssl.key-password=another-secret</code></pre></li>
<li><p>pem으로 인코딩된 인증서 및 개인 키 파일을 사용</p>
<pre><code>server.port=8443
server.ssl.certificate=classpath:my-cert.crt
server.ssl.certificate-private-key=classpath:my-cert.key
server.ssl.trust-certificate=classpath:ca-cert.crt
server.ssl.key-store-password=secret</code></pre><p>이렇게 설정을 하면 더이상 <code>8080</code> 포트에서 HTTP 연결을 지원하지 않음
<code>8080</code> 포트로 HTTP를, <code>8443</code> 포트로 HTTPS를 연결하고 싶다면 추가적으로 설정이 필요</p>
</li>
</ul>
<h2 id="class-application">Class Application</h2>
<pre><code class="language-java">/**
 * Redirect all traffic from port 8080 to 8443
 * @return
 */
@Bean
public ServletWebServerFactory servletContainer(){
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(){
        @Override
        protected void postProcessContext(Context context) {
            SecurityConstraint securityConstraint = new SecurityConstraint();
            securityConstraint.setUserConstraint(&quot;CONFIDENTIAL&quot;);
            SecurityCollection collection = new SecurityCollection();
            collection.addPattern(&quot;/*&quot;);
            securityConstraint.addCollection(collection);
            context.addConstraint(securityConstraint);
        }
    };
    tomcat.addAdditionalTomcatConnectors(redirectConnector());
    return tomcat;
}

private Connector redirectConnector() {
    Connector connector = new Connector(&quot;org.apache.coyote.http11.Http11NioProtocol&quot;);
    connector.setScheme(&quot;http&quot;);
    connector.setPort(8080);
    connector.setSecure(false);
    connector.setRedirectPort(8443);
    return connector;
}</code></pre>
<p>8080의 요청을 8443으로 redirect함으로써 8080 HTTP로도 접근이 가능하고,  8443 HTTPS으로도 접근이 가능</p>
<p><em>참고</em>
<a href="https://mkyong.com/spring-boot/spring-boot-ssl-https-examples/">Spring Boot SSL (HTTPS) examples</a>
<a href="https://docs.spring.io/spring-cloud-dataflow/docs/1.1.0.M1/reference/html/getting-started-security.html#getting-started-security-enabling-https">Spring Enabling HTTPS (공식매뉴얼)</a>
<a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-configure-ssl">Spring Configure SSL (공식매뉴얼)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[intelliJ] 자동 리로드 설정]]></title>
            <link>https://velog.io/@0_sujeong/intelliJ-%EC%9E%90%EB%8F%99-%EB%A6%AC%EB%A1%9C%EB%93%9C-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@0_sujeong/intelliJ-%EC%9E%90%EB%8F%99-%EB%A6%AC%EB%A1%9C%EB%93%9C-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Thu, 26 May 2022 06:15:31 GMT</pubDate>
            <description><![CDATA[<h1 id="devtools">devtools</h1>
<h2 id="의존성-추가">의존성 추가</h2>
<ul>
<li>maven <pre><code class="language-xml">&lt;dependency&gt;
  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
  &lt;artifactId&gt;spring-boot-devtools&lt;/artifactId&gt;
  &lt;optional&gt;true&lt;/optional&gt;
&lt;/dependency&gt;</code></pre>
<h2 id="applicationproperties">application.properties</h2>
<pre><code>spring.devtools.restart.enabled=true
spring.thymeleaf.cache=false</code></pre></li>
</ul>
<p>devtools는 </p>
<ul>
<li>파일 수정후 저장하면 자동으로 classpath에 존재하는 파일의 변경을 감지하고 자동으로 서버를 restart해주는 기능</li>
<li>thymeleaf는 기본적으로 캐싱 기능을 제공하지만, 이 기능이 활성화 되어있으면 thymeleaf파일을 수정해도 바로 적용되지 않기때문에 false로 수정</li>
</ul>
<hr>
<h1 id="intellij-설정">intelliJ 설정</h1>
<ul>
<li><code>설정</code> - <code>빌드, 실행, 배포</code> - <code>컴파일러</code> - <code>프로젝트 자동 빌드</code> 체크
<img src="https://velog.velcdn.com/images/0_sujeong/post/c4798083-8377-4d17-bb72-747a01f9ab8c/image.png" alt=""></li>
</ul>
<hr>
<h1 id="tomcat-설정">tomcat 설정</h1>
<ul>
<li>인텔리제이 화면에서 <code>ctrl + shift + A</code> - <code>구성편집</code> 검색 - <code>액션 탭</code>에서 <code>구성편집</code> 선택
<img src="https://velog.velcdn.com/images/0_sujeong/post/1c451305-d7a1-48e8-8372-50321543fb16/image.png" alt="">
<code>update 액션시 (U)</code> 와 <code>프레임 비활성화 시(F)</code> 둘 다 <code>클래스 및 리소스 업데이트</code>로 설정
<img src="https://velog.velcdn.com/images/0_sujeong/post/d9a246ca-bd23-41c1-97e0-ba072b082742/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring] HTTP 메시지 컨버터]]></title>
            <link>https://velog.io/@0_sujeong/spring-HTTP-%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%BB%A8%EB%B2%84%ED%84%B0</link>
            <guid>https://velog.io/@0_sujeong/spring-HTTP-%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%BB%A8%EB%B2%84%ED%84%B0</guid>
            <pubDate>Sun, 22 May 2022 06:31:15 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/0_sujeong/post/062c507a-df6d-4a7a-9f9d-d922c3a676e3/image.png" alt=""></p>
<h1 id="requestmappinghandleradapter">RequestMappingHandlerAdapter</h1>
<p><code>RequestMappingHandlerAdapter</code>는<code>@RequestMapping</code>은 를 처리하는 핸들러 어댑터이다.
<code>@RequestMapping</code> 기반의 컨트롤러는 다양한 파라미터를 사용할 수 있다. 
예를 들면 ``HttpServletRequest<code>,</code>Model<code>,</code>@RequestParam<code>,</code>@ModelAttribute<code>,</code>@RequestBody<code>,</code>HttpEntity<code>등이 있다. 
이렇게 많은 파라미터를 처리하는 등 매우 유연한 모습을 보여줄 수 있는 이유는</code>ArgumentResolver`를 호출해 컨트롤러(=핸들러)가 필요한 파마미터 값(객체)를 생성하기 때문이다.</p>
<hr>
<h2 id="argumentresolver">ArgumentResolver</h2>
<p><code>ArgumentResolver</code>란 정확히 <code>HandlerMethodArgumentResolver</code>이고, 줄여서 <code>ArgumentResolver</code>라고한다.
파라미터를 각 타입으로 변환시켜주는 리졸버이다. 
스프링에는 약30개 이상의<code>ArgumentResolver</code>가 존재함</p>
<h3 id="동작방식">동작방식</h3>
<ol>
<li><code>RequestMapping</code>핸들러 어댑터가 <code>ArgumentResolver</code>의 <code>supportsParameter()</code>를 호출해서 컨트롤러(=핸들러)가 원하는 파라미터를 지원하는지 확인</li>
<li>지원하면, <code>resolveArgument()</code>를 호출해 파라미터를 변환</li>
<li><code>RequestMapping</code>핸들러 어댑터가 변환된 파라미터를 넘겨주며 컨트롤러(=핸들러) 호출</li>
</ol>
<hr>
<h2 id="returnvaluehandler">ReturnValueHandler</h2>
<p><code>ReturnValueHandler</code>란 정확히<code>HandlerMethodReturnValueHandler</code>이다.
이것은 <code>ArgumentResolver</code>와 비슷한데, <code>ArgumentResolver</code>는 요청 값에 대한 변환을 처리했다면 <code>ReturnValueHandler</code>는 응답 값에 대한 처리를 한다.
응답 값을 <code>String</code>으로 리턴해도 뷰 이름으로 동작하게 해주고, <code>@ResponseBody</code>가 붙어있으면 그대로 반환하게 해주는 이유이다.
이것도 <code>ArgumentResolver</code>와 같이 spring에서 여러개의 <code>ReturnValueHandler</code>가 존재한다.</p>
<h3 id="동작방식-1">동작방식</h3>
<p><code>ArgumentResolver</code>의 동작 방식과 비슷하다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/0_sujeong/post/21b524c4-39d8-439b-9f5e-95922006a934/image.png" alt=""></p>
<h1 id="http-메시지-컨버터">HTTP 메시지 컨버터</h1>
<h2 id="위치">위치</h2>
<p>요청의 경우 <code>ArgumentResolver</code>를, 응답의 경우 <code>ReturnValueHandler</code>를 사용해서 각각의 값을 변환한다.
이렇게 변환을 할 때에 사용하는 것이 <code>HTTP 메시지 컨버터</code>이기때문에 <code>HTTP 메시지 컨버터</code>는 <code>ArgumentResolver</code>와 <code>ReturnValueHandler</code>안에서 호출되어 사용된다.</p>
<p><em>출처</em></p>
<ol>
<li><a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard">스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] 계정 설정]]></title>
            <link>https://velog.io/@0_sujeong/git-%EA%B3%84%EC%A0%95-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@0_sujeong/git-%EA%B3%84%EC%A0%95-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Fri, 20 May 2022 09:13:19 GMT</pubDate>
            <description><![CDATA[<h1 id="계정-설정-확인">계정 설정 확인</h1>
<pre><code>git config -l</code></pre><h1 id="이름-변경">이름 변경</h1>
<pre><code>git config --global user.name 공수정</code></pre><h1 id="이메일-변경">이메일 변경</h1>
<pre><code>git config --global user.email 0sujeong@naver.com</code></pre><h1 id="ssh-인증">SSh 인증</h1>
<p>아무나 커밋을 하는게 아니라 등록된 사람들만 커밋을 할 수 있게 하는 방식을 하기 위해서 인증서를 발급해 서버에 등록하고,
자신의 pc에도 가지고 있어서 이 인증서로 인증을해서 커밋을 할 수 있게 한다.</p>
<ul>
<li>키 생성 <pre><code>ssh-keygen -t ecdsa</code></pre>생성하면 비밀키와 공개키 2개의 파일이 생김
공개키(.pub확장자) 파일은 서버 관리자에게 주어서 등록을 해야하고, 하나는 가지고 있어야한다.
한번 해당 서버에서 클론을 하면 인증이 된 서버라는 등록정보가 또 파일로 저장이 된다</li>
</ul>
<blockquote>
</blockquote>
<p>메모하자 메모 :) </p>
]]></description>
        </item>
    </channel>
</rss>