<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hi</title>
        <link>https://velog.io/</link>
        <description>Sorbet is good...!</description>
        <lastBuildDate>Thu, 10 Mar 2022 15:32:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. hi. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/d-h-k" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[APS 22년 3월 문제집]]></title>
            <link>https://velog.io/@d-h-k/APS-22%EB%85%84-3%EC%9B%94-%EB%AC%B8%EC%A0%9C%EC%A7%91</link>
            <guid>https://velog.io/@d-h-k/APS-22%EB%85%84-3%EC%9B%94-%EB%AC%B8%EC%A0%9C%EC%A7%91</guid>
            <pubDate>Thu, 10 Mar 2022 15:32:40 GMT</pubDate>
            <description><![CDATA[<h2 id="3월-2일-예정">3월 2일 예정</h2>
<p><a href="https://leetcode.com/problems/minimum-absolute-difference-in-bst/">https://leetcode.com/problems/minimum-absolute-difference-in-bst/</a>
<a href="https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/">https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/</a>
<a href="https://leetcode.com/problems/detect-capital/">https://leetcode.com/problems/detect-capital/</a>
<a href="https://leetcode.com/problems/minimum-distance-between-bst-nodes/">https://leetcode.com/problems/minimum-distance-between-bst-nodes/</a>
<a href="https://leetcode.com/problems/check-array-formation-through-concatenation/">https://leetcode.com/problems/check-array-formation-through-concatenation/</a>
<a href="https://leetcode.com/problems/path-crossing/">https://leetcode.com/problems/path-crossing/</a>
<a href="https://leetcode.com/problems/degree-of-an-array/">https://leetcode.com/problems/degree-of-an-array/</a>
<a href="https://leetcode.com/problems/relative-ranks/">https://leetcode.com/problems/relative-ranks/</a>
<a href="https://leetcode.com/problems/find-winner-on-a-tic-tac-toe-game/">https://leetcode.com/problems/find-winner-on-a-tic-tac-toe-game/</a>
<a href="https://leetcode.com/problems/nim-game/">https://leetcode.com/problems/nim-game/</a>
<a href="https://leetcode.com/problems/ransom-note/">https://leetcode.com/problems/ransom-note/</a>
<a href="https://leetcode.com/problems/maximum-difference-between-increasing-elements/">https://leetcode.com/problems/maximum-difference-between-increasing-elements/</a>
<a href="https://leetcode.com/problems/kth-missing-positive-number/">https://leetcode.com/problems/kth-missing-positive-number/</a>
<a href="https://leetcode.com/problems/finding-3-digit-even-numbers/">https://leetcode.com/problems/finding-3-digit-even-numbers/</a>
<a href="https://leetcode.com/problems/max-consecutive-ones/">https://leetcode.com/problems/max-consecutive-ones/</a>
<a href="https://leetcode.com/problems/determine-whether-matrix-can-be-obtained-by-rotation/">https://leetcode.com/problems/determine-whether-matrix-can-be-obtained-by-rotation/</a>
<a href="https://leetcode.com/problems/binary-search/">https://leetcode.com/problems/binary-search/</a>
<a href="https://leetcode.com/problems/sum-of-left-leaves/">https://leetcode.com/problems/sum-of-left-leaves/</a>
<a href="https://leetcode.com/problems/check-if-string-is-a-prefix-of-array/">https://leetcode.com/problems/check-if-string-is-a-prefix-of-array/</a>
<a href="https://leetcode.com/problems/intersection-of-two-arrays-ii/">https://leetcode.com/problems/intersection-of-two-arrays-ii/</a>
<a href="https://leetcode.com/problems/minimum-difference-between-highest-and-lowest-of-k-scores/">https://leetcode.com/problems/minimum-difference-between-highest-and-lowest-of-k-scores/</a>
<a href="https://leetcode.com/problems/distance-between-bus-stops/">https://leetcode.com/problems/distance-between-bus-stops/</a>
<a href="https://leetcode.com/problems/count-odd-numbers-in-an-interval-range/">https://leetcode.com/problems/count-odd-numbers-in-an-interval-range/</a>
<a href="https://leetcode.com/problems/range-sum-query-immutable/">https://leetcode.com/problems/range-sum-query-immutable/</a>
<a href="https://leetcode.com/problems/cousins-in-binary-tree/">https://leetcode.com/problems/cousins-in-binary-tree/</a>
<a href="https://leetcode.com/problems/best-time-to-buy-and-sell-stock/">https://leetcode.com/problems/best-time-to-buy-and-sell-stock/</a>
<a href="https://leetcode.com/problems/longest-palindrome/">https://leetcode.com/problems/longest-palindrome/</a>
<a href="https://leetcode.com/problems/minimum-index-sum-of-two-lists/">https://leetcode.com/problems/minimum-index-sum-of-two-lists/</a>
<a href="https://leetcode.com/problems/minimum-moves-to-convert-string/">https://leetcode.com/problems/minimum-moves-to-convert-string/</a></p>
<h2 id="자료구조">자료구조</h2>
<p><a href="https://leetcode.com/problems/deepest-leaves-sum/">https://leetcode.com/problems/deepest-leaves-sum/</a>
<a href="https://leetcode.com/problems/merge-two-sorted-lists/">https://leetcode.com/problems/merge-two-sorted-lists/</a>
<a href="https://leetcode.com/problems/same-tree/">https://leetcode.com/problems/same-tree/</a>
<a href="https://leetcode.com/problems/diameter-of-binary-tree/">https://leetcode.com/problems/diameter-of-binary-tree/</a>
<a href="https://leetcode.com/problems/minimum-moves-to-convert-string/">https://leetcode.com/problems/minimum-moves-to-convert-string/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 고급 W7 - 스프링AOP ]]></title>
            <link>https://velog.io/@d-h-k/%EC%8A%A4%ED%94%84%EB%A7%81-%EA%B3%A0%EA%B8%89-W7-%EC%8A%A4%ED%94%84%EB%A7%81AOP</link>
            <guid>https://velog.io/@d-h-k/%EC%8A%A4%ED%94%84%EB%A7%81-%EA%B3%A0%EA%B8%89-W7-%EC%8A%A4%ED%94%84%EB%A7%81AOP</guid>
            <pubDate>Wed, 23 Feb 2022 07:39:10 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/d-h-k/post/2b1b2816-b5e0-4e21-b944-29f6301dbe4b/image.png" alt=""></p>
<ul>
<li>이번주에는 정말 너무너무 바빠서 손글씨로 정리했습니다ㅠ</li>
</ul>
<h2 id="추가">추가</h2>
<h2 id="프록시는-두가지-방식이-있다">프록시는 두가지 방식이 있다</h2>
<ul>
<li>두가지 프록시 구현방식의 장단점을 비교하면</li>
<li>프록시 사용예시<ul>
<li>Spring data jpa 프로젝트 : spring boot 와 무관하게 repostiroy를 자체적으로 jdk 동적 프록시를 이용해서 생성하고 있습니다</li>
</ul>
</li>
<li>참고로스프링은 CGLib을 쓴다 성능때문에<ul>
<li>성능비교 JDK-Dynamic vs CGLIB (코드추가 필요 @@)</li>
</ul>
</li>
</ul>
<h3 id="cglib-기반-dynamic-proxy-구현-방식">CGLIB 기반 Dynamic Proxy 구현 방식</h3>
<ul>
<li>코드 생성 라이브러리(Code Generator Library), 런타임에 동적으로 자바 클래스의 프록시를 생성해주는 기능</li>
<li>장점<ul>
<li>실제 바이트 코드를 조작하여 JDK Dynamic Proxy 보다 상대적으로 빠르다. </li>
<li>대상 객체가 인터페이스를 가지지 않았을 경우 사용한다. </li>
<li>인터페이스를 가져도 사용할 수 있다. aop:config의 proxy-target-class를 true로 설정. </li>
<li>대상 객체가 정의한 모든 메서드를 프록시 하여야하는 경우 사용한다.</li>
</ul>
</li>
<li>제약<ul>
<li>버전별 호환성이 좋지 않다. (사용 라이브러리 내부에 내장시켜 제공한다.)</li>
<li>final이나 private 같이 상속된 객체에 Overriding을 제공하지 않는다면 해당 행위에 대해서 Aspect를 적용할 수 없다.</li>
</ul>
</li>
<li>CGLIB Dynamic Proxy 를 사용하는 라이브러리<ul>
<li>Spring Boot AOP (Proxy)</li>
<li>Hibernate</li>
</ul>
</li>
</ul>
<h3 id="jdk-dynamic-proxy-기반-dynamic-proxy-구현방식">JDK Dynamic Proxy 기반 Dynamic Proxy 구현방식</h3>
<ul>
<li>JDK Dynamic Proxy?<ul>
<li>Java 에서 지원하는 동적(런타임) Proxy 구현 방식이다.</li>
<li>특정 인터페이스들을 구현하는 클래스나 인스턴스를 만드는 기술이다.</li>
<li>Reflection API을 사용하여 Target Class의 method를 invoke()를 통해 동작시킨다.</li>
</ul>
</li>
<li>invoke?(해석 : 부르다)<ul>
<li>클래스의 이름과 인자 값을 넘겨서 객체의 메서드를 실행시키는 메서드이다.</li>
<li>인자 값을 이용하여 메서드를 실행시키게 된다.</li>
</ul>
</li>
<li>제약<ul>
<li>인터페이스 기반의 Proxy → 모든 Target Class 는 Interface를 implement 하고 있어야 한다.</li>
</ul>
</li>
<li>JDK Dynamic Proxy 단점<ul>
<li>Advise 대상이든 아니든 모든 Method Call 마다 reflection API의 invoke를 실시한다.</li>
<li>다시말해 Method invoke를 우선 진행하고 Advise 유무를 판단한다.</li>
</ul>
</li>
<li>JDK Dynamic Proxy 를 사용하는 라이브러리<ul>
<li>Spring AOP ProxyFactory ( JDK Dynamic Proxy 를 추상화하고 재정의한 Class)</li>
<li>Spring Data JPA RepositoryFactorySupport (BeanClassLoaderAware, BeanFactoryAware)</li>
</ul>
</li>
</ul>
<blockquote>
<p>출처: <a href="https://lob-dev.tistory.com/entry/Java%EC%97%90%EC%84%9C-%EA%B5%AC%ED%98%84%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-Proxy-%EB%93%A4-Pure-JDK-CGLIB">https://lob-dev.tistory.com/entry/Java에서-구현할-수-있는-Proxy-들-Pure-JDK-CGLIB</a></p>
</blockquote>
<ul>
<li>나중에 더 읽어보기 : <a href="https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html">https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker image 삭제가 필요해!]]></title>
            <link>https://velog.io/@d-h-k/Docker-image-%EC%82%AD%EC%A0%9C%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%B4</link>
            <guid>https://velog.io/@d-h-k/Docker-image-%EC%82%AD%EC%A0%9C%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%B4</guid>
            <pubDate>Wed, 16 Feb 2022 04:50:49 GMT</pubDate>
            <description><![CDATA[<h1 id="사이드-프로젝트에서-도커파일을-빌드했는데">사이드 프로젝트에서 도커파일을 빌드했는데..</h1>
<ul>
<li>사이드 프로젝트에서 도커를 도입했다. 찐하게 쓴건 아니고 간단히 repo 최상단 위치에 DockerFile 하나 얹은 정도로해서 실행하기 편하도록 만든건데, 이게 개발하는 입장에서는 당연히 테스트해봐야하니까 빌드를 많이 했었고, 빌드 결과물들이 어떻게 남아있을지는 신경쓰지 못했었다.</li>
</ul>
<br>
<br>
<br>

<h2 id="docker-build-이미지는-자동으로-삭제가-안되는구나">Docker build 이미지는 자동으로 삭제가 안되는구나</h2>
<ul>
<li>도커 base이미지에다가 + 내가 필요한 jar파일 등등을 올려서 나만의 Custom Docker file 을 빌드하는데, 매번 빌드할때마다 각각의 이미지파일들은 삭제되지 않고 그대로 내 컴퓨터에 남아있게 된다</li>
</ul>
<p><img src="https://images.velog.io/images/d-h-k/post/7fa5b875-7894-4158-afdb-7c0942b8aa93/image.png" alt=""></p>
<blockquote>
<p>480MB 짜리 도커파일들이 엄청 많은데 이게 다 생성되는동안 아무것도 몰랐다..</p>
</blockquote>
<p><img src="https://images.velog.io/images/d-h-k/post/cfd6c908-831b-48cb-b3d9-14f36e2a6270/image.png" alt=""></p>
<blockquote>
<p>22개니까 어림잡아 11기가 정도 공간낭비</p>
</blockquote>
<pre><code>docker images | grep 480 | wc -l</code></pre><ul>
<li>480 메가짜리 파일이 몇개인지 확인하는 명령어</li>
</ul>
<br>
<br>
<br>

<h2 id="명령어-11번-치는건-시간낭비이니까-좀더-우아하게-제거하자">명령어 11번 치는건 시간낭비이니까 좀더 우아하게 제거하자</h2>
<p><img src="https://images.velog.io/images/d-h-k/post/bdc3f7fd-e030-424a-b40a-87337153ae12/image.png" alt=""></p>
<pre><code> awk -F&#39; &#39; &#39;{print $3}&#39;</code></pre><ul>
<li>위 명령어를 쓰면 구분자가 &lt;space&gt; 로 나뉘어진 3번째 항목만을 추출하는 명령어인데, 이를 사용하면 Docker Image ID를 추출해낼수 있다</li>
</ul>
<ul>
<li>Image ID를 추출 해낸뒤 docker rmi 명령어 뒤에 붙여넣기를 해줬다.</li>
</ul>
<p><img src="https://images.velog.io/images/d-h-k/post/a8332664-4d99-4897-8854-be69f9c80518/image.png" alt=""></p>
<ul>
<li>좀더 멋지게 한줄명령어로 해결할수 있을꺼같긴 한데 요즘은 많이 안써서 생각이 안난다... ㅠㅠ</li>
</ul>
<pre><code>DOCKER_DEL=($(docker images | grep 480 | awk -F&#39; &#39; &#39;{print $3}&#39;))</code></pre><blockquote>
<p>DOCKER_DEL 이라는 변수에 Docker Image ID 를 쭉 저장하고</p>
</blockquote>
<pre><code>echo $DOCKER_DEL</code></pre><blockquote>
<p>Docker Image ID 를 읽어오는데, 쉘 특성상 개행문자를 자동으로 스페이스로 치환해서 저장해준다. </p>
</blockquote>
<ul>
<li>해당 echo 명령어의 결과로 삭제할 Docker Image ID 들이 반환되므로 이를 <code>docker rmi [images..]</code> 명령어의 옵션으로 사용하면 끝!</li>
</ul>
<br>
<br>

<h2 id="그-결과">그 결과</h2>
<p><img src="https://images.velog.io/images/d-h-k/post/c8ea068f-f2de-463d-8e5c-b04ed54b12f2/image.png" alt=""></p>
<blockquote>
<p>우아하게 삭제 완료!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[뉴스 클러스터링 Java풀이]]></title>
            <link>https://velog.io/@d-h-k/%EB%89%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-Java%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@d-h-k/%EB%89%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-Java%ED%92%80%EC%9D%B4</guid>
            <pubDate>Sun, 13 Feb 2022 05:52:05 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>원문 : <a href="https://programmers.co.kr/learn/courses/30/lessons/17677">https://programmers.co.kr/learn/courses/30/lessons/17677</a></p>
</li>
<li><p>이 문제를 풀지 못했습니다. 아래의 테스트케이스에서 실패합니다</p>
</li>
</ul>
<pre><code>테스트허쉴(&quot;FRANCE&quot;, &quot;french&quot;, 16384);
</code></pre><h2 id="작성한-코드">작성한 코드</h2>
<pre><code class="language-java">import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

// 못품
// https://programmers.co.kr/learn/courses/30/lessons/17677
public class Solution17677 {

    public int solution(String str1, String str2) {
        str2 = str2.toUpperCase();
        str1 = str1.toUpperCase();

        List&lt;String&gt; list1 = deCompositionAndSort(str1);
        List&lt;String&gt; list2 = deCompositionAndSort(str2);

        //교집합 구하기
        List&lt;String&gt; intersection = new ArrayList&lt;&gt;(list1).stream()
                .filter(s -&gt; Collections.binarySearch(list2, s) != -1)
                .sorted(Comparator.naturalOrder())
                .collect(Collectors.toList());

        //합집합 구하기 : 교집합은 a,b,집합의 단순합에서 합집합원소를 제거하면 됨
        List&lt;String&gt; union = new ArrayList&lt;&gt;(list1);
        union.addAll(list2);
        union.sort(Comparator.naturalOrder());
        intersection.forEach(s -&gt; {
            int index = Collections.binarySearch(union, s);
            if (index == -1) {
                throw new RuntimeException();
            }
            union.remove(index);
            union.sort(Comparator.naturalOrder());
        });

        //자카드 유사도 구하기
        return (union.size() == 0) ? 65536 : (int) (((double) intersection.size() / (double) union.size()) * 65536);
    }

    //함수형으로 짜고싶다.. &gt;&gt; reduce 사용해 개선
    /*
    0 1 2 3 :
     */
    private List&lt;String&gt; deCompositionAndSort(String str) {
        List&lt;String&gt; result = new ArrayList&lt;&gt;();
        for (int i = 0; i &lt; str.length() - 1; i++) {
            char head = str.charAt(i);
            char tail = str.charAt(i + 1);
            if (Character.isLetter(head) &amp;&amp; Character.isLetter(tail)) {
                String element = str.substring(i, i + 2);
                result.add(element);
            }
        }
        result.sort(Comparator.naturalOrder());
        return result;
    }

    public static void main(String[] args) {
        테스트허쉴(&quot;FRANCE&quot;, &quot;french&quot;, 16384);
        테스트허쉴(&quot;aa1+aa2&quot;, &quot;AAAA12&quot;, 43690);
        테스트허쉴(&quot;handshake&quot;, &quot;shake hands&quot;, 65536);
    }

    private static void 테스트허쉴(String in1, String in2, int expected) {
        Solution17677 s = new Solution17677();
        int result = s.solution(in1, in2);
        System.out.println(result == expected ? &quot;SUCCESS!!!&quot; : &quot;FAIL result: &quot; + result + &quot;,  but expected: &quot; + expected);
    }


}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[인증방식간 비교 : Session 기반 VS Token 기반]]></title>
            <link>https://velog.io/@d-h-k/%EC%9D%B8%EC%A6%9D%EB%B0%A9%EC%8B%9D%EA%B0%84-%EB%B9%84%EA%B5%90-Session-%EA%B8%B0%EB%B0%98-VS-Token-%EA%B8%B0%EB%B0%98</link>
            <guid>https://velog.io/@d-h-k/%EC%9D%B8%EC%A6%9D%EB%B0%A9%EC%8B%9D%EA%B0%84-%EB%B9%84%EA%B5%90-Session-%EA%B8%B0%EB%B0%98-VS-Token-%EA%B8%B0%EB%B0%98</guid>
            <pubDate>Fri, 28 Jan 2022 08:43:41 GMT</pubDate>
            <description><![CDATA[<h1 id="session-기반-인증">Session 기반 인증</h1>
<ul>
<li>Session 을 기반으로 하는 인증방식을 설명하면<h3 id="클라이언트">클라이언트</h3>
</li>
<li>서버에 로그인 요청을 보내서 성공하면, 서버에서는 session을 발급해줍니다.<ul>
<li>이때 response http에 Set-Cookie 로 저장되어있습니다</li>
</ul>
</li>
<li>발급받은 session 정보는  웹 브라우저(크롬이나 엣지같은)의 내부의 세션 저장소를 기반으로 사용자 정보를 저장하고 이후 요청시마다 서버에 인증 정보를 보냅니다</li>
</ul>
<h3 id="서버">서버</h3>
<ul>
<li>각 사용자 Session 마다 고유한 SessionID 를 발급해줍니다. 이를 HTTP 헤더에 실어 클라이언트에게 보냅니다. (쿠키 발급 시 Set-Cookie 헤더에 의해 클라이언트에 쿠키값이 세팅됨)</li>
</ul>
<h3 id="통신의-흐름">통신의 흐름</h3>
<ul>
<li>http Request 마다 서버에 의해 셋팅된 Set-Cookie 헤더 정보와 함께 보냄</li>
<li>서버에서는 Set-Cookie 와 함께 발급해놓고 저장한 SessionID 사용자를 인식하게 되며</li>
<li>클라이언트측의 웹브라우저는 Session 내부의 Cookie 라는 공간에 데이터를 저장해놓고 매 요청마다 쿠키를 보내고 받음</li>
</ul>
<h3 id="결론">결론</h3>
<ul>
<li>Session : 서버에서 가지고 있는 클라이언트의 정보, 고유한 SessionID 로 사용자를 식별</li>
<li>Cookie : 클라이언트가 서버로부터 발급받은 고유한 식별 key (Session ID) </li>
<li>모든건 브라우저의 Session 공간, 그중에서 Cookie 를 기반으로 하므로 Web에서만 동작한다. App에서는 Session 자체가 없음</li>
</ul>
<p><br><br></p>
<h1 id="token-기반">Token 기반</h1>
<h3 id="클라이언트-1">클라이언트</h3>
<ul>
<li>서버에 로그인 요청을 보내서 성공하면, 서버에서는 Access-Token을 발급해줍니다.<ul>
<li>이때 Access-Token은 그냥보면 의미를 알수 없도록 암호화해 발급해줍니다.</li>
<li>일종의 입장권이며, 이 입장권은 암호화되어있어 서버에서만 해석&amp;검증 할 수 있는 형태로 보내주는데, 많이 사용하는 토큰의 표준규격중 하나가 JWT이며, 암호화 알고리즘은 은 서버 마음대로 선택 가능합니다.</li>
<li>근데 암호화 알고리즘이 이미 파훼법이 나와있는 쉬운 간단한 방법이라면 보안에 심각한 문제가 일어나게 됩니다. 마치 위조지폐를 누구나 손쉽게 집에서 만들수 있는 상황 되어버리기네요</li>
<li>때문에 위조/해석이 어려운 암호화 방식을 사용 해야합니다. 지폐에 위조지폐를 식별할 수 있는 기술을 적용해놓으면 위조지폐를 만들더라도 이를 받은 사람이 위조되었음을 검증해낼수 있게요</li>
</ul>
</li>
<li>이후 클라이언트는 매 요청마다 서버에게 발급받은 Access-Token을 HTTP-header 에 포함해 보냅니다. <h3 id="서버-1">서버</h3>
</li>
<li>모든 http-request 마다 클라이언트로 부터 받은 Token 을 검증해(=복호화) 위조되지 않은 정상적인 Token 이라면 요청에 대한 정보를 클라이언트에게 제공해줍니다</li>
<li>서버가 클라이언트에게 발급해주는 Token 에는 조작여부를 판단하는 Verify Signature와 Secret key가 있고, 토큰의 유효기간 등이 담겨있습니다.</li>
<li>Token 에 유효기간이 있는 이유는, 모든 암호화알고리즘이 &quot;절대무적&quot;을 보장하지 않기때문에, 충분한 컴퓨팅 리소스만 투입한다면(리소스 = 컴퓨팅파워*동작시간) 누구나 복호화가 가능하기 때문입니다. 일반적으로 <code>10년</code>정도 걸린다고 보면 됩니다 <ul>
<li>10년 = (10년동안 지금기술로 열심히 풀거나  /  10년뒤 진보된 기술로 우아하게 뚫거나  /  남들보다 10년이상 기술이 앞서있는 NSA 같은데서 그냥 박살내버리거나 )</li>
</ul>
</li>
<li>또한, 암호화가 어려우면 남들이 깨는것도 어려운데, 복호화또한 서버 리소스를 많이 사용하기 어려운 문제가 있습니다. 이를 해결하기 위해 Token 을 2단계로 분리하는데요<ul>
<li>강력한 암호화로 해킹하기도 어렵고 복호화도 어렵지만 오랜기간 나름 안전하게 오래록 사용할수 있어 유효기간을 길가 가져가면서 사용하는 토큰이며 일반적으로 <code>refresh-token</code> 이라고 부르는것</li>
<li>가볍지만 빠른 암호화로 짧게 사용하며 일반적으로 <code>Access-Token</code> 이라고 부르는것</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[개발관련 정리할것]]></title>
            <link>https://velog.io/@d-h-k/%EA%B0%9C%EB%B0%9C%EA%B4%80%EB%A0%A8-%EC%A0%95%EB%A6%AC%ED%95%A0%EA%B2%83</link>
            <guid>https://velog.io/@d-h-k/%EA%B0%9C%EB%B0%9C%EA%B4%80%EB%A0%A8-%EC%A0%95%EB%A6%AC%ED%95%A0%EA%B2%83</guid>
            <pubDate>Thu, 27 Jan 2022 13:05:15 GMT</pubDate>
            <description><![CDATA[<h2 id="filter-내부의-exception-은controladvice-대상이-아님">filter 내부의 exception 은controladvice 대상이 아님</h2>
<p>filter에서 필터링할 대상은 exception으로 처리하지 말고 바로 리턴 만들기</p>
<p>익셉션을 위한 controladvice 대상이 아니다 - 왜그럴까</p>
<p>필터와 인터셉터 차이가 아닐까
<a href="https://goddaehee.tistory.com/154">https://goddaehee.tistory.com/154</a></p>
<h2 id="인터셉터">인터셉터</h2>
<p><a href="https://bamdule.tistory.com/149">https://bamdule.tistory.com/149</a>
<a href="https://myhappyman.tistory.com/199">https://myhappyman.tistory.com/199</a>
인터셉터 존나잘정리됨 ㅠㅠ</p>
<h2 id="filter-auth">filter auth</h2>
<p><a href="https://velog.io/@sa833591/Spring-Security-5-Spring-Security-Filter-%EC%A0%81%EC%9A%A9">https://velog.io/@sa833591/Spring-Security-5-Spring-Security-Filter-적용</a></p>
<ul>
<li>시큐리티에서 커스텀필터 쩐닷..</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[APS 22년 2월 문제집 ]]></title>
            <link>https://velog.io/@d-h-k/2022%EB%85%84-APS-%EB%AC%B8%EC%A0%9C%EC%A7%91-1</link>
            <guid>https://velog.io/@d-h-k/2022%EB%85%84-APS-%EB%AC%B8%EC%A0%9C%EC%A7%91-1</guid>
            <pubDate>Wed, 26 Jan 2022 14:18:10 GMT</pubDate>
            <description><![CDATA[<h4 id="지난달의-레거시">지난달의 레거시</h4>
<p><a href="https://leetcode.com/problems/partitioning-into-minimum-number-of-deci-binary-numbers/">https://leetcode.com/problems/partitioning-into-minimum-number-of-deci-binary-numbers/</a></p>
<h2 id="2월-2일">2월 2일</h2>
<p><a href="https://leetcode.com/problems/implement-queue-using-stacks/">https://leetcode.com/problems/implement-queue-using-stacks/</a>
<a href="https://leetcode.com/problems/reformat-the-string/">https://leetcode.com/problems/reformat-the-string/</a>
<a href="https://leetcode.com/problems/first-unique-character-in-a-string/">https://leetcode.com/problems/first-unique-character-in-a-string/</a>
<a href="https://leetcode.com/problems/three-divisors/">https://leetcode.com/problems/three-divisors/</a></p>
<h2 id="2월-23일">2월 23일</h2>
<p><a href="https://leetcode.com/problems/pascals-triangle-ii/">https://leetcode.com/problems/pascals-triangle-ii/</a>
<a href="https://leetcode.com/problems/make-the-string-great/">https://leetcode.com/problems/make-the-string-great/</a>
<a href="https://leetcode.com/problems/delete-characters-to-make-fancy-string/">https://leetcode.com/problems/delete-characters-to-make-fancy-string/</a></p>
<ul>
<li>metadata : sort by acceptence, page 8of11</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2021년 1월 APS ]]></title>
            <link>https://velog.io/@d-h-k/2021%EB%85%84-APS</link>
            <guid>https://velog.io/@d-h-k/2021%EB%85%84-APS</guid>
            <pubDate>Wed, 26 Jan 2022 14:11:20 GMT</pubDate>
            <description><![CDATA[<h2 id="1월-5일">1월 5일</h2>
<p><a href="https://leetcode.com/problems/x-of-a-kind-in-a-deck-of-cards/">https://leetcode.com/problems/x-of-a-kind-in-a-deck-of-cards/</a>
<a href="https://leetcode.com/problems/length-of-last-word/">https://leetcode.com/problems/length-of-last-word/</a></p>
<h2 id="1월-12일">1월 12일</h2>
<p><a href="https://leetcode.com/problems/number-of-different-integers-in-a-string/">https://leetcode.com/problems/number-of-different-integers-in-a-string/</a>
<a href="https://leetcode.com/problems/check-if-n-and-its-double-exist/">https://leetcode.com/problems/check-if-n-and-its-double-exist/</a></p>
<h4 id=""><img src="https://images.velog.io/images/d-h-k/post/9492d31f-1774-4c80-845d-914d26dae9f0/image.png" alt=""></h4>
<h2 id="1월-19일">1월 19일</h2>
<p><a href="https://leetcode.com/problems/long-pressed-name/">https://leetcode.com/problems/long-pressed-name/</a>
<a href="https://leetcode.com/problems/valid-mountain-array/">https://leetcode.com/problems/valid-mountain-array/</a></p>
<h2 id="1월-26일">1월 26일</h2>
<p><a href="https://leetcode.com/problems/can-place-flowers/">https://leetcode.com/problems/can-place-flowers/</a>
<a href="https://leetcode.com/problems/remove-one-element-to-make-the-array-strictly-increasing/">https://leetcode.com/problems/remove-one-element-to-make-the-array-strictly-increasing/</a>
<a href="https://leetcode.com/problems/power-of-four/">https://leetcode.com/problems/power-of-four/</a>
<a href="https://leetcode.com/problems/partitioning-into-minimum-number-of-deci-binary-numbers/">https://leetcode.com/problems/partitioning-into-minimum-number-of-deci-binary-numbers/</a></p>
<h4 id="-1"><img src="https://images.velog.io/images/d-h-k/post/dfa8909a-eb12-4530-9bb7-3c62ad9155e0/image.png" alt=""></h4>
<h3 id="자료구조">자료구조</h3>
<p><a href="https://leetcode.com/problems/deepest-leaves-sum/">https://leetcode.com/problems/deepest-leaves-sum/</a>
<a href="https://leetcode.com/problems/merge-two-sorted-lists/">https://leetcode.com/problems/merge-two-sorted-lists/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[당근마켓 클론코딩 정리하는 긴글]]></title>
            <link>https://velog.io/@d-h-k/%EB%8B%B9%EA%B7%BC%EB%A7%88%EC%BC%93-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EC%A0%95%EB%A6%AC%ED%95%98%EB%8A%94-%EA%B8%B4%EA%B8%80</link>
            <guid>https://velog.io/@d-h-k/%EB%8B%B9%EA%B7%BC%EB%A7%88%EC%BC%93-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EC%A0%95%EB%A6%AC%ED%95%98%EB%8A%94-%EA%B8%B4%EA%B8%80</guid>
            <pubDate>Sun, 23 Jan 2022 09:41:17 GMT</pubDate>
            <description><![CDATA[<h1 id="당근마켓-클론코딩">당근마켓 클론코딩</h1>
<blockquote>
<p>당근마켓 클론코딩을 진행하면서 이번에 주로 어떤일들을 했는지 셀프피드백을 해봤습니다.</p>
</blockquote>
<ul>
<li>이번에는 코딩할시간이 많이 없어서 주로 도메인분석과 역으로 기획서를 작성하고 모델링하는데 시간을 많이 투여했는데요, </li>
</ul>
<p><img src="https://user-images.githubusercontent.com/31065684/150481671-27687c99-62ad-4b8a-a27a-c26a17978cd4.png" alt="사진"></p>
<h2 id="목차">목차</h2>
<ul>
<li><a href="">기획/Link</a> : 당근마켓 기획서 &amp; 유저 시나리오 분석</li>
<li><a href="">기술/Link</a> : 기술설명</li>
<li><a href="">에필로그/Link</a> : 당근마켓 도메인 분석하는 TMI 글 - 아이템이 아니라 글쓰기에 집중한 이유, 위치서비스를 제공하는 방법 분석하기</li>
</ul>
<p><br>성
<br>
<br>
<br></p>
<hr>
<br>
<br>
<br>
<br>


<h2 id="당근마켓-기획서--유저-시나리오-분석">당근마켓 기획서 &amp; 유저 시나리오 분석</h2>
<ul>
<li>이 페이지는 <code>당근마켓</code> 서비스의 클론코딩을 위해 백엔드개발자의 입장에서 기획서를 역으로 구성한 페이지 입니다</li>
</ul>
<h3 id="기술적인-부분">기술적인 부분</h3>
<ul>
<li>예외 상황에 대한 예외처리</li>
<li>필수 기능만 생각하고, 추가기능은 추가가 어렵지만 않게하고 YAGNI!</li>
<li>데이터 모델링 : 확장성 있게 Database 설계 Er-diagram 작성 및 제출</li>
<li>서버 배포와 Integration</li>
</ul>
<h3 id="유저-시나리오-기반의-동작-프로세스">유저 시나리오 기반의 동작 프로세스</h3>
<ul>
<li>http 가 stateless 하지만, 사용자들은 아래 상태들을 한정되는 FSM 으로 모델링 됩니다</li>
<li>고객 시나리오에 대응하는 프로세스를 짜고 이에 맞는 VIEW 와 API 를 작성하자</li>
</ul>
<h4 id="사용자-행동-시나리오">사용자 행동 시나리오</h4>
<ul>
<li>1 ) 회원가입<ul>
<li>1.1) 랜딩페이지</li>
<li>1.2) 회원가입 화면</li>
<li>1.3) 로그인 화면</li>
<li>1.4) (추가기능) 토큰만료 화면</li>
</ul>
</li>
<li>2 ) 상품등록(판매 글/글작성)<ul>
<li>2.1) 상품등록 페이지</li>
<li>2.2) 카테고리 페이지</li>
<li>2.3) 상품 상세 페이지</li>
<li>2.4) 이 판매자의 다른 판매상품</li>
</ul>
</li>
<li>3 ) 마이페이지<ul>
<li>3.1) 마이 페이지(나의 당근 페이지) 메인</li>
<li>3.2) 프로필 수정 페이지</li>
<li>3.3) 판매내역 페이지</li>
<li>3.4) 관심목록</li>
</ul>
</li>
<li>4 ) 댓글기능<ul>
<li>4.1) 댓글달기 기능</li>
<li>4.2) 댓글수정 기능</li>
<li>4.3) 댓글삭제 기능</li>
</ul>
</li>
<li>5 ) 추가기능<ul>
<li>5.1) 글 보호잠금 기능 / 사기피해예방</li>
<li>5.2) 유효한 이메일인지 확인 / 업자 필터</li>
<li>5.3) 중고 매물 검색기능</li>
<li>5.4) 알람 기능 (키워드, 관심상품, 내 작성글)</li>
<li>5.5) 대댓글 기능</li>
<li>5.6) 뱃지 기능</li>
<li>5.7) 매너온도 기능</li>
<li>5.8) 채팅 기능 추가</li>
<li>5.9) 글 끌어올림 기능</li>
</ul>
</li>
</ul>
<p><br><br><br><br></p>
<h2 id="1-회원가입">1) 회원가입</h2>
<h3 id="11-랜딩페이지">1.1) 랜딩페이지</h3>
<ul>
<li>로그인을 하지 않았으면 랜딩페이지로 아래와 같은 화면 노출</li>
<li>시작하기를 누르면 회원가입 페이지로 넘어갑니다.</li>
</ul>
<h3 id="12-회원가입-화면">1.2) 회원가입 화면</h3>
<ul>
<li>이메일, 비밀번호, 이름, 핸드폰번호, 닉네임을 입력받으면 가입완료!</li>
<li>가입완료시 메인으로 리다이렉트</li>
</ul>
<h3 id="13-로그인-화면">1.3) 로그인 화면</h3>
<ul>
<li>아이디, 비밀번호를 입력한 후 로그인을 누르면 정보확인 후 메인 페이지로 접속</li>
<li>회원정보가 잘못되면 아이디, 비밀번호가 올바르지 않다는 오류 팝업창<ul>
<li>추가기능 : 비밀번호 찾기나 아이디 찾기 등의 기능</li>
<li>추가기능 : 회원가입시 이메일 인증과정(계정 활성화/비활성화 기능)</li>
<li>당근마켓의 처음 버전은 판교장터로, 직장 메일이나 학교 메일만 가입 가능하게 추가적으로 설정해도 되고, 구글 오어스로 회사 구글계정 사용자만 로그인되게 해도 될듯</li>
</ul>
</li>
</ul>
<h3 id="14-추가기능-토큰만료-화면">1.4) (추가기능) 토큰만료 화면</h3>
<ul>
<li>로그인을 하면 로그아웃을 하거나 한 달이상의 시간이 지나기 전에는 회원정보를 들고있어 메인 url에 접속하면 다음의 페이지가 랜딩페이지가</li>
</ul>
<p><br><br><br><br></p>
<h2 id="2-상품등록">2) 상품등록</h2>
<h3 id="21-상품등록-페이지">2.1) 상품등록 페이지</h3>
<ul>
<li>다음의 메인페이지에서 + 버튼을 누르면</li>
<li>아래 사진의 <code>새로운 판매글 작성</code> 페이지가 나타납니다.</li>
<li>사진을 업로드할 수 있으며, 제목, 카테고리, 가격(원), 본문을 입력받습니다.<ul>
<li>사진은 실제 모바일 앨범에서 선택하는 것이 아닌, desktop에서 jpeg, png 등 이미지 파일을 업로드하도록 작성(MultipartFile 표준 사용하면 될듯)</li>
</ul>
</li>
<li>사진의 크기가 20MB 이하의 파일만 받도록 설정</li>
<li>(추가기능) 사진의 용량을 줄여서 저장하는 기능</li>
</ul>
<h3 id="22-카테고리-페이지">2.2) 카테고리 페이지</h3>
<ul>
<li>카테고리를 누르면 팝업창이 떠서 다음의 카테고리 중 하나를 선택할 수 있습니다.</li>
<li>카테고리는 아래 정도만<ul>
<li>‘디지털기기’, ‘생활가전’, ‘가구/인테리어’, ‘유아동’, ‘생활/가공식품’, ‘유아도서’, ‘스포츠/레저’, ‘여성잡화’, ‘여성의류’, ‘남성패션/잡화’, ‘게임/취미’, ‘뷰티/미용’, ‘반려동물용품’, ‘도서/티켓/음반’, ‘식물’, ‘기타 중고물품’, ‘중고차’</li>
</ul>
</li>
<li>내용을 모두 입력하면 완료 버튼이 활성화 되어 글을 입력할 수 있습니다. (사진은 업로드하지 않아도 완료 버튼이 활성화되어 글 입력이 가능)</li>
<li>검증은 프론트, 서버에서 더블체크</li>
</ul>
<h3 id="23-상품-상세-페이지">2.3) 상품 상세 페이지</h3>
<ul>
<li>랜딩페이지(메인페이지)에서 상품을 클릭하면 다음과 같은 상품페이지를 볼 수 있습니다.</li>
<li>보여지는 정보는<ul>
<li>상품사진, 판매자 프로필사진, 판매자 닉네임, 게시물 제목, 게시물 가격, 게시물 카테고리, 게시물 게시시간, 본문, 관심(하트 숫자), 동일판매자가 판매중인 상품, 글 조회수</li>
<li>img, seller profile, seller nick, title, price, category, time, contents, like, seller&#39;s other goods, views</li>
<li>조회수때문에 Redis 필요</li>
</ul>
</li>
<li>이미지는 없을수도 있음</li>
<li>게시시간 표시<ul>
<li>표시방식 : <code>n분전</code>, <code>n일전</code>, <code>n시간 전</code></li>
<li>1시간 전에는 n분전, 하루 전에는 n시간 전, 한 달 전에는 n일전, 1년 전에는 n달 전, 1년 이후에는 n년 전으로 나타납니다.</li>
</ul>
</li>
<li>거래가 완료된 상품은 제목 앞에 ‘거래완료’라는 문구가 붙습니다.</li>
<li>동일한 판매자의 다른 상품을 보기 위해서 <code>이 판매자가 판매중인 다른 상품 보기</code> 버튼이 있습니다<ul>
<li>개인적으로 이 기능을 상당히 좋아합니다. 중고물품 거래(ex 특히 게임기, 인두기, 버니어캘리퍼스 같은 물품 판매하시는 분들 보면 상당히 관심분야가 비슷하다는걸 느낍니다)</li>
</ul>
</li>
<li>해당 화면에서 <code>...</code> 버튼을 클릭하면 다음과 같이 <code>게시물 수정</code> 및 <code>게시물 삭제</code> 버튼이 표시되어야함<ul>
<li>수정 -&gt; 수정페이지 이동, 게시글 등록과 같은 화면이 뜨지만 글을 입력할 때 입력했던 입력값이 바로 보여짐</li>
<li>삭제 -&gt; 확인팝업 출력 후 삭제, 클릭하면 다음과 같은 화면 한번 거쳐서 삭제<h3 id="24-이-판매자의-다른-판매상품">2.4) 이 판매자의 다른 판매상품</h3>
</li>
</ul>
</li>
<li>seller&#39;s other goods</li>
<li>(상품 상세페이지에서 접근 가능) 판매 상품의 모두보기를 클릭하면 아래의 화면이 출력됩니다</li>
<li>댓글이나 관심(하트 수)가 0이면 해당 아이콘은 표시되지 않습니다.</li>
<li>거래가 완료된 상품은 표시되나 거래완료라는 상태가 함께 출력됩니다<ul>
<li>여기서는 거래 완료 여부와 상관없이 노출 되어야 함</li>
</ul>
</li>
<li>이 페이지에서 하트를 누르면 관심 물품으로 등록됨</li>
</ul>
<p><br><br><br><br></p>
<h2 id="3-마이페이지">3) 마이페이지</h2>
<h3 id="31-마이-페이지나의-당근-페이지-메인-줄여서-마페메">3.1) 마이 페이지(나의 당근 페이지) 메인 (줄여서 <code>마페메</code>)</h3>
<ul>
<li>랜딩페이지에서 나의 당근을 클릭하면 아래와 같은 페이지가 표시</li>
<li>프로필 화면에는 사진을 넣기 전에는 위와 같은 기본프사가 보이고, 별도 사진을 등록하면 사진이 표시</li>
<li>프로필 수정, 판매내역, 관심목록, Logout 으로 가는 기능이 존재해야함</li>
</ul>
<h3 id="32-프로필-수정-페이지">3.2) 프로필 수정 페이지</h3>
<ul>
<li><code>마페메</code> 에서 <code>프로필 수정</code> 버튼을 누르면 아래 화면 표시</li>
<li>이 페이지에서는 닉네임을 수정 가능</li>
<li>프로필 사진을 업로드기능<ul>
<li>프로필 사진은 중간 부분의 1:1 비율로 업로드</li>
<li>desktop에서 이미지 파일을 업로드하는 형태로 Multipart</li>
</ul>
</li>
</ul>
<h3 id="33-판매내역-페이지">3.3) 판매내역 페이지</h3>
<ul>
<li><code>마페메</code> 에서 <code>판매내역</code> 버튼을 누르면 아래 화면 표시</li>
<li>판매중인 상품 및 거래완료 상품을 확인</li>
<li>해당 화면에서 바로 예약중으로 변경 or 거래완료로 변경하며 상태변경이 가능</li>
<li>거래완료로 변경할 경우 해당 상품은 거래완료 탭으로 이동됨</li>
<li>거래완료에서도 판매중이나 예약중으로 상태변경이 가능합니다.</li>
<li>판매 상품을 클릭하면 다음과 같이 상품 상세페이지로 이동</li>
<li>판매자인 경우에만 판매물품의 상태변화가 가능<ul>
<li>판매중, 예약중, 거래완료 status</li>
</ul>
</li>
</ul>
<h3 id="34-관심목록">3.4) 관심목록</h3>
<ul>
<li>관심목록을 클릭하면 좋아요 누른 상품이 나타납니다.</li>
</ul>
<p><br><br><br><br></p>
<h2 id="4-댓글기능">4) 댓글기능</h2>
<h3 id="41-댓글달기-기능">4.1) 댓글달기 기능</h3>
<ul>
<li>상품 페이지에서 댓글 보기 및 댓글 남기기 기능을 사용할 수 있습니다.</li>
<li>하단의 댓글 보기를 클릭하면<ul>
<li>다음과 같이 닉네임, 시간, 댓글을 볼 수 있습니다.</li>
<li>시간은 n분 전, n시간 전, n일 전, n달 전, n년 전의 5가지 형태로 나타나며, 글을 쓴지 1시간 전에는 n분 전으로, 1일 전에는 n 시간 전으로, 1달 전에는 n일 전으로, 1년부터는 n년 전으로 표시</li>
<li>댓글은 시간이 빠른순서로 표시</li>
</ul>
</li>
<li>자신이 쓴 댓글에는 Edit 버튼이 떠서 수정이 가능, 삭제도 가능</li>
<li>(App) ← 버튼을 누르면 무조건 이전 product page로 돌아갑니다.</li>
<li>댓글 남기기를 클릭하면 다음과 같이 댓글을 남길 수 있습니다.</li>
</ul>
<h3 id="42-댓글-수정-기능">4.2) 댓글 수정 기능</h3>
<ul>
<li>Edit 버튼을 클릭할 경우 기존에 자신이 남겼던 글이 default 값으로 써져있으며 수정 후 작성완료를 누르면 수정됩니다.</li>
<li>작성완료할 경우 댓글 보기 페이지로 넘어갑니다.</li>
</ul>
<p>대댓글 기능은 여유가 되실경우 추가해주시기 바랍니다.</p>
<p>실제 거래는 댓글기능을 통해 판매자와 구매자가 거래 장소 및 시간을 정한 후 직거래 및 현금거래를 한다고 가정합니다. 모든 소통은 댓글을 통해서만 이루어집니다.</p>
<h3 id="43-댓글-삭제기능">4.3) 댓글 삭제기능</h3>
<ul>
<li>자신이 작성한 댓글만 삭제가 가능해야함</li>
<li>글 작성자는 댓글을 작성하지 않았지만 : 댓글을 삭제할수 없다</li>
<li>댓글이 달린 글은 삭제할 수 없다 : 사기피해 방지를 위해서<ul>
<li>이부분은 정책적인 결정 사항이라 바꿀 수 있어야함</li>
</ul>
</li>
</ul>
<p><br><br><br><br></p>
<h2 id="5-추가기능">5) 추가기능</h2>
<h3 id="51-글-보호잠금-기능--사기피해예방">5.1) 글 보호잠금 기능 / 사기피해예방</h3>
<ul>
<li>글 보호 기능(중고거래 사기방지) : 일정 기간동안 작성자조차 글 삭제가 불가능해집니다<ul>
<li>1명이 글 보호를 누르면 3시간동안 글이 보호</li>
<li>3명 이상이 글 보호시간이 3주간 보호</li>
<li>5명 이상이 글 보호 요청을 눌렀을 경우 1년간 보호</li>
<li>CS 직원이 확인해주고 보호잠금 해제 가능</li>
</ul>
</li>
</ul>
<h3 id="52-유효한-이메일인지-확인--업자-필터">5.2) 유효한 이메일인지 확인 / 업자 필터</h3>
<ul>
<li>회원가입 인증<ul>
<li>Email 인증 : 유효한 이메일인지 확인(장물아비, 도배업자 퇴치용)</li>
<li>지역인증 : 지역에 그 사람이 살고있는지 유효기간마다 인증하도록<ul>
<li>지역 인증이 한 달 동안만 유효</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="53-중고매물-검색기능">5.3) 중고매물 검색기능</h3>
<ul>
<li>검색 기능<ul>
<li>검색한 값을 제목이나 본문에 포함하고 있는 상품을 보여주는건 어떨까요?</li>
<li>어떤 상품을 상단에  노출하는지에 대한 로직 추가<ul>
<li>(판매중인 상품, 좋아요순, 댓글 많은순)</li>
</ul>
</li>
<li>검색에 Filtering 기능 : 카테고리 제한 설정, 가격범위(상한필터, 하한필터, 상하한필터)</li>
</ul>
</li>
</ul>
<h3 id="54-알람-기능-키워드-관심상품-내-작성글">5.4) 알람 기능 (키워드, 관심상품, 내 작성글)</h3>
<ul>
<li>알림기능을 추가해보는건 어떨까요?</li>
<li>알람 아이콘은 메인 페이지의 맨 위 상단의 검색 아이콘과 카테고리 아이콘 사이에 위치</li>
<li>키워드 알람<ul>
<li>키워드를 등록하면 알람이 발생한다</li>
<li>키워드는 제목, 본문, 제목or본문에 걸 수 있다</li>
</ul>
</li>
<li>관심 상품 상태 변경 알람(좋아요 누른 상품)<ul>
<li>판매중 -&gt; 예약중/판매완료 혹은 반대로 변경시 알림</li>
</ul>
</li>
<li>이외 중고나라, 당근마켓 알람 기능<ul>
<li>내 판매중 상품에 댓글, 좋아요 내가 댓글을 단 상품에 댓글이 달렸을 때</li>
<li>네이버 카페에도 유사한 시스템이 있음</li>
</ul>
</li>
<li>(의견) : 서비스가 커져서 마케팅팀이 주도하는 이벤트 또한 대비할 수 있음</li>
</ul>
<h3 id="55-대댓글-기능">5.5) 대댓글 기능</h3>
<ul>
<li>댓글의 댓글, depth 2단계까지는 대부분 지원하는듯 합니다</li>
<li>댓글의 댓글의 댓글의 .... 댓글 이렇게 무한히 가면 시스템적으로 구현은 가능할까? (물론 돈버는것과 관계는 없겠지만..)</li>
</ul>
<h3 id="56-뱃지-기능">5.6) 뱃지 기능</h3>
<ul>
<li>첫 거래 완료, 10번 거래 완료, 무료 나눔 완료 등 특정 이벤트를 수행하면 관련 뱃지를 지급</li>
<li>사용자들이 서비스에 더 애착을 갖을 수 있도록</li>
</ul>
<h3 id="57-매너온도-기능">5.7) 매너온도 기능</h3>
<ul>
<li>거래가 완료되면 서로 상대방의 매너에 대해 평가</li>
<li>따뜻한 사람이면 온도가 높아지고 비매너의 행동을 하면 온도 떨어트리기</li>
<li>거래완료 버튼을 누르면 &gt;&gt; 매너온도를 선택하는 버튼 or 판매완료 페이지에서 연락 온 사람들중 실제로 거래한사람을 선택해고 매너를 평가<ul>
<li>매너온도 기능은 기능 추가보다, 이전에 필요한 기능들이 많음</li>
<li>메너온도 기능을 위해 존재해야하는 시스템<ul>
<li>채팅기능</li>
<li>대댓글기능</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="58-채팅-기능-추가">5.8) 채팅 기능 추가</h3>
<ul>
<li>1:1 채팅기능이 있어서 거래약속(직거래) 혹은 택배거래를 진행할수 있도록</li>
<li>server pass 가 아니라, peer-to-peer 로 가야할듯 (WebSocket 기술을 사용해보자)</li>
<li>가보지않은길이라 모름..<ul>
<li>Web 환경에서 구현과 App 환경에서 구현에 차이가 있을지??</li>
<li>일단 WebSocket 이 뭔지부터 알아봐야함</li>
<li>서버의 역활은 오직 Broker (중개인 역할)</li>
</ul>
</li>
</ul>
<h3 id="59-글-끌어올림-기능">5.9) 글 끌어올림 기능</h3>
<ul>
<li>글을 올리고 하루이상 지난시점부터</li>
<li>게시글 끌어올리기 기능이 추가된다</li>
</ul>
<br>
<br>
<br>
<br>
<hr>
<br>
<br>
<br>
<br>




<h1 id="당근마켓-데이터-모델을-분석하고-er-d-작성하는-글">당근마켓 데이터 모델을 분석하고 ER-D 작성하는 글</h1>
<h2 id="당근마켓-도메인-모델링">당근마켓 도메인 모델링</h2>
<ul>
<li>이전에 도메인 모델링과 관련된 학습자료 몇개를 접하면서 어떻게 도메인을 모델링해야할까에 대한 생각을 조금씩 하고 있었는데요<ul>
<li><a href="https://youtu.be/4E1BHTvhB7Y">마틴 파울러 - 소프트웨어 아키텍처의 중요성 / 영상, 20분 Link</a></li>
<li><a href="https://opentutorials.org/course/3883">생활코딩 관계형 데이터모델링 / 강좌 Link </a></li>
<li><a href="http://www.yes24.com/Product/Goods/39497990">클린 소프트웨어 / YES24 책 Link </a></li>
</ul>
</li>
<li>이번에 당근마켓 백엔드 클론코딩을 진행하면서, 작고 간단하고 100% 끝가지 도달하지 못할 프로젝트일지라도 의식의 흐름대로 마구잡이식 난개발을 진행하지 말고 어느정도는 Minimum Viable 는 구현할수 있는 방향으로 데이터를 모델링하기로 결심했습니다. </li>
<li>복잡한 도메인 로직이 존재하는 당근마켓 서비를 클론코딩하기 전 Service Ownership 을 가질수 있도록 기획단계를 거쳤는데요</li>
<li>도메인 모델링에 상당히 비중높게 글을 작성하는 이유는 좋은 소프트웨어 아키텍쳐가 중요하기 때문이고, 아키텍쳐가 제대로 나오기 위해서는 모델링이 잘 되어있어야 하기 때문입니다. <ul>
<li>그전에 기획단계에서 잘 구성되어있어야 하고, 기획도 완벽하지 않으니까 trial&amp;error 가 필요합니다. 그래서 빠르게 도전하고 실패하는 agile process 가 필요한데, 돌고돌아 결국 협업이고 같이 일하는 사람을 얼마냐 잘 배려해주고 퍼포먼스를 낼수 있도록 협력하는게 중요한 시대~<blockquote>
<p><img src="https://user-images.githubusercontent.com/31065684/150246437-c19cebc3-c984-4f6d-8768-dfc958972692.png" alt=""></p>
</blockquote>
</li>
</ul>
</li>
<li>이 사진은 위에서 소개한 <a href="https://youtu.be/4E1BHTvhB7Y">소프트웨어 아키텍처의 중요성</a> 이라는 영상의 한 부분을 캡쳐한건데요, 기능이 많고 고도화될수록 아키텍쳐의 중요성이 더 커지게 된다는걸 설명한 부분입니다.</li>
<li>사족을 달아보면 모든 구성원들과 개발자들의 동작속도가 B(O) 가 log-N 이 될수도, n^2 이 될수도 있다는건데요</li>
<li>짧지만 회사다니는 2년동안 No-Design 의 코드를 유지보수를 하는데 대부분의 시간을 사용했고, Good-Design 으로 리팩토링이 끝나고 얼마 지나지 않아 회사가 어려워졌었던 경험이 있는데요</li>
<li>앞으로의 커리어에서는 Good-Design Code 에서 기획과 마케팅이 정교하게 결합된 고도화된 서비스를 만들어 제 목표인 <code>1억명이 사용하는 서비스 만들기</code> 를 달성하고 싶습니다. </li>
</ul>
<blockquote>
<p>요약 : 좋은 결정은 빨리 내려지길 원한다. 소프트웨어의 가격을 고려할때 가장 높은 valuation 은 미래가치(확장성, 유지보수성, 좋은 아키텍쳐와 모델링)에 두어야만 한다!</p>
</blockquote>
<h2 id="모델링-순서">모델링 순서</h2>
<ul>
<li>도메인을 모델링하기 위해 순서를 거쳤는데<ul>
<li>1] 구현할 <code>기능</code>을 먼저 생각한다</li>
<li>2] 기능에 따라 적절한 페이지의 <code>VIEW 와 API 의 URI 를 지정</code>한다</li>
<li>3] 저장해야할 <code>데이터</code>를 기능으로부터 생각해 <code>테이블</code>과 <code>필드변수</code>들을 생각해낸다</li>
<li>4] 만족스러울때까지 앞단계들을 반복한다</li>
</ul>
</li>
</ul>
<h2 id="view-와-api-의-uri-지정">VIEW 와 API 의 URI 지정</h2>
<h3 id="view-page-정리">VIEW Page 정리</h3>
<table>
<thead>
<tr>
<th align="center">번호</th>
<th align="left">설명</th>
<th align="left">VIEW</th>
<th align="left"></th>
</tr>
</thead>
<tbody><tr>
<td align="center">1</td>
<td align="left">회원가입</td>
<td align="left">??</td>
<td align="left"></td>
</tr>
<tr>
<td align="center">1.1</td>
<td align="left">랜딩페이지</td>
<td align="left">??</td>
<td align="left"></td>
</tr>
<tr>
<td align="center">1.2</td>
<td align="left">회원가입 화면</td>
<td align="left"><code>/login/join</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">1.3</td>
<td align="left">로그인 화면</td>
<td align="left"><code>/login</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">1.4</td>
<td align="left">(추가기능) 토큰만료 화면</td>
<td align="left"><code>/login/fail</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">2</td>
<td align="left">상품관련</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">2.1</td>
<td align="left">상품등록 페이지</td>
<td align="left"><code>/posts/new</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">2.2</td>
<td align="left">카테고리 페이지</td>
<td align="left">-</td>
<td align="left"></td>
</tr>
<tr>
<td align="center">2.3</td>
<td align="left">상품 상세 페이지</td>
<td align="left"><code>/posts/{id}</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">2.4</td>
<td align="left">이 판매자의 다른 판매상품</td>
<td align="left"><code>/posts/otherSales</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">3</td>
<td align="left">마이페이지</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">3.1</td>
<td align="left">마이 페이지 메인(나의 당근 페이지)</td>
<td align="left"><code>/accounts/</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">3.2</td>
<td align="left">프로필 수정 페이지</td>
<td align="left"><code>/accounts/userModify</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">3.3</td>
<td align="left">판매내역 페이지</td>
<td align="left"><code>/accounts/posts</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">3.4</td>
<td align="left">관심목록</td>
<td align="left"><code>/accounts/favoritePost</code></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">4</td>
<td align="left">댓글기능</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">4.1</td>
<td align="left">댓글달기 기능</td>
<td align="left">-</td>
<td align="left"></td>
</tr>
<tr>
<td align="center">4.2</td>
<td align="left">댓글수정 기능</td>
<td align="left">-</td>
<td align="left"></td>
</tr>
<tr>
<td align="center">4.3</td>
<td align="left">댓글삭제 기능</td>
<td align="left">-</td>
<td align="left"></td>
</tr>
<tr>
<td align="center">5</td>
<td align="left">추가기능</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">5.1</td>
<td align="left">글 보호잠금 기능 / 사기피해예방</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">5.2</td>
<td align="left">유효한 이메일인지 확인 / 업자 필터</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">5.3</td>
<td align="left">중고 매물 검색기능</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">5.4</td>
<td align="left">알람 기능 (키워드, 관심상품, 내 작성글)</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">5.5</td>
<td align="left">대댓글 기능</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">5.6</td>
<td align="left">뱃지 기능</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">5.7</td>
<td align="left">매너온도 기능</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">5.8</td>
<td align="left">채팅 기능 추가</td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center">5.9</td>
<td align="left">글 끌어올림 기능</td>
<td align="left"></td>
<td align="left"></td>
</tr>
</tbody></table>
<h2 id="전체-모델링">전체 모델링</h2>
<ul>
<li>전체 모델링은 이렇게 작성했습니다
<img src="https://user-images.githubusercontent.com/31065684/149715006-9b5058b0-e2b6-4106-ab2c-81027bd0a379.jpeg" alt=""></li>
</ul>
<br>
<br>
<br>
<br>
<hr>
<br>
<br>
<br>
<br>



<h1 id="기술설명">기술설명</h1>
<h2 id="0-개발환경--사용기술">0) 개발환경 &amp;&amp; 사용기술</h2>
<ul>
<li>IDE : IntelliJ</li>
<li>Build &amp; Package : Gradle</li>
<li>Language : Java (JDK 11)<ul>
<li>DB Access : JPA (Hibernate)</li>
</ul>
</li>
<li>DB : H2 (in-memory)</li>
</ul>
<br>
<br>

<h2 id="1-대용량-트래픽-고려">1) 대용량 트래픽 고려</h2>
<ul>
<li>대용량 트래픽이라는 요구사항을 듣고 <code>서버 보틀넥 제거</code>, <code>스케일 아웃</code> 두가지가 방식이 떠올랐는데, 여기서는 <code>RDB 에서의 보틀넥인 조회수 증가</code> 를 해결했습니다</li>
</ul>
<h3 id="조회수-증가가-보틀넥인-이유">조회수 증가가 보틀넥인 이유</h3>
<ul>
<li>상황 : 단시간에 트래픽이 몰리는 상황이라면<ul>
<li>1초에 1만번씩 특정 게시글에 get 요청이 들어오면, 조회수 단 하나의 필드값 증가를 위해 DB에 select문을 1초만에 1만번, 값이 1씩 변하는 insert 문을 DB에 보내줘야 합니다</li>
<li>DB 가 아닌 좀더 가볍고 빠른 저장소를 사용해 조회시마다 매번 insert 문을 내보내지 않고, 1초마다 한번씩만 조회수 변화를 반영하도록 제작하였습니다<ul>
<li>DB Insert 횟수가 줄어들고 (RDB의 삽입동작 시간복잡도는 log-N 이기 때문에)</li>
</ul>
</li>
<li><code>@readOnly</code> 사용으로 트랜잭션 종료시 flush 를 사용하지 않고, 더티체킹을 위한 스냅샷 비교를 하지 않아 성능이 개선됩니다</li>
</ul>
</li>
</ul>
<h2 id="구현하기까지의-과정">구현하기까지의 과정</h2>
<ul>
<li>처음에는 Redis를 사용해서 위 기능을 구현하려 하였으나, 구현에 실패했습니다</li>
<li>이를 대체하기위해 collection framework 중에서 HashMap을 사용해 해당 기능을 구현하였습니다</li>
<li>Key-Value 구조를 사용해 성능 최적화하기 위한 구조는 아래 그림과 같습니다</li>
<li><img src="https://user-images.githubusercontent.com/31065684/147671915-9f4846bc-b545-47dd-99c3-47a1fe273734.png" alt="설명"></li>
<li>위 버퍼에서 1초동안 조회횟수를 count 하고, 1초 이후 카운트에 반영해서 아무리 많이 요청되도 조회수 증가에 의한 쿼리는 PK 별로 1초에 한번만 insert문이 나가게 됩니다</li>
<li>이 로직을 <code>PostViewCountService</code> 클래스에 구현했고, <code>PostViewCountServiceTest</code> 클래스에서 테스트코드를 작성했습니다</li>
</ul>
<br>
<br>
<br>
<br>


<h2 id="2-첨부파일-기능">2) 첨부파일 기능</h2>
<ul>
<li>첨부파일 기능 구현을 위해서 아래의 구조를 생각했습니다</li>
<li><img src="https://user-images.githubusercontent.com/31065684/147674279-a81788da-8c37-4902-98e9-6768025fa99a.png" alt="대락사진"></li>
<li>엔티티패키지 <code>attachFile</code></li>
<li>storage 저장 기능을 <code>attachFile</code> 패키지에서 구현하지 않은 이유는, 게시판 특성상 미래에 다른 요구사항, 예를들어 본문에 포함된 사진을 자동으로 저장하는 기능을 대비하기 위해서, <code>메타데이터</code> 정보와 <code>실제 파일을 전송하는</code>
두가지 계층으로 분할해서 구현했습니다.</li>
<li>추후 클라이언트와 파일을 주고받을 다른 모듈에서도 <code>FileService</code> 를 사용하면 검증된 코드를 적은 작업으로 사용할수 있어 DRY 하게 구성하였습니다</li>
<li>뿐만 아니라, 추후 저장방식을 FileSystem이 아닌 AWS S3 같은 클라우드 저장소로 이동시에도 대응할수 있도록 FileServie 를 인터페이스로 분리해두었플므로  DIP 를 준수하려고 노력했습니다.</li>
</ul>
<br>
<br>
<br>
<br>
<hr>
<br>
<br>
<br>
<br>


<h2 id="당근마켓-분석하기-tmi">당근마켓 분석하기 TMI</h2>
<h2 id="당근마켓이-중고물건을-생각하는-방식">당근마켓이 중고물건을 생각하는 방식</h2>
<ul>
<li>당근마켓은 중고물건 판매글을, 물품을 올리는 goods 의 관점이 아니라, 글을 올린다는 관점의 article로 접근한다</li>
</ul>
<p><img src="https://user-images.githubusercontent.com/31065684/149781264-52104ef0-9e20-4e8d-a0aa-e67798f5d609.png" alt="판매글이미지"></p>
<ul>
<li>여기서 포인트는<ul>
<li>당근마켓은 중고판매글을 <code>article</code> 로 인식한다</li>
<li>관심, 채팅, 조회수가 표시되는데 여기서 채팅은 거래글을 중심으로 생성된다</li>
<li>게시시간은 끌어올림 글 기준으로 계산한다</li>
</ul>
</li>
</ul>
<h3 id="당근마켓과-중고나라의-전략차이">당근마켓과 중고나라의 전략차이</h3>
<ul>
<li>당근마켓은 중고판매글을 <code>article</code> 로 인식한다. 글올리는 행위에 중점을 두고 &gt;&gt; 지역사람들 끼리 글을 올리고 물건을 교환한다는</li>
</ul>
<h2 id="당근마켓은-지역을-어떻게-다룰까">당근마켓은 지역을 어떻게 다룰까?</h2>
<ul>
<li>서울시는 구단위, 나머지는 시군구로 크게 나뉘고 중간중간에 Dummy Number가 끼어있다. 노가다로 직접 구해본 데이터들을 소개하면</li>
</ul>
<h3 id="regions_id-는-primary-key-로-동작하고-동-단위로-인식한다">regions_id 는 Primary-Key 로 동작하고 &quot;동&quot; 단위로 인식한다</h3>
<table>
<thead>
<tr>
<th>regions_id</th>
<th>지역</th>
</tr>
</thead>
<tbody><tr>
<td>177</td>
<td>노원구 중계본동</td>
</tr>
<tr>
<td>178</td>
<td>노원구 중계1동</td>
</tr>
<tr>
<td>179</td>
<td>노원구 중계4동</td>
</tr>
<tr>
<td>180</td>
<td>노원구 중계2,3동</td>
</tr>
<tr>
<td>135</td>
<td>월곡2동</td>
</tr>
<tr>
<td>1604</td>
<td>별내동</td>
</tr>
<tr>
<td>6078</td>
<td>방학동</td>
</tr>
<tr>
<td>- 노원구에서 가장 가까운 서울시내 다른 구(월곡2동, 방학동), 경기도 인접(별내동) 번호는 크게 차이가 난다</td>
<td></td>
</tr>
<tr>
<td>- 결론 : regions_id 는 <code>고유키</code> 로 동작한다</td>
<td></td>
</tr>
</tbody></table>
<h3 id="regions_id-의-숫자값에-가중치나-의미가-없다-가까운-동네일지라도-광역시가-다르면-큰차이가-난다">regions_id 의 숫자값에 가중치나 의미가 없다. 가까운 동네일지라도 광역시가 다르면 큰차이가 난다</h3>
<table>
<thead>
<tr>
<th>regions_id</th>
<th>지역</th>
</tr>
</thead>
<tbody><tr>
<td>1351</td>
<td>성남시 삼평동(판교)</td>
</tr>
<tr>
<td>1347</td>
<td>성남시 야탑2동</td>
</tr>
<tr>
<td>1352</td>
<td>성남시 백현동</td>
</tr>
<tr>
<td>4538</td>
<td>수원시 이의동</td>
</tr>
<tr>
<td>4535</td>
<td>수원시 매탄동</td>
</tr>
</tbody></table>
<ul>
<li>당근마켓의 발상지(..?) 라고 할수 있는 판교와 그 옆 수원시를 찍어봤는데 regions_id 숫자에 큰 차이가 있었다. PK 의 Ordinal에 의미가 있지는 않은듯</li>
</ul>
<h3 id="regions_id-의-자릿수에도-의미가-없다">regions_id 의 자릿수에도 의미가 없다</h3>
<table>
<thead>
<tr>
<th>regions_id</th>
<th>지역</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>서울시 (더미)</td>
</tr>
<tr>
<td>11</td>
<td>중구 1234가동</td>
</tr>
<tr>
<td>111</td>
<td>중랑구 중랑동</td>
</tr>
<tr>
<td>1111</td>
<td>대전광역시 동구</td>
</tr>
<tr>
<td>2</td>
<td>중구 (더미)</td>
</tr>
<tr>
<td>22</td>
<td>중구 회현동</td>
</tr>
<tr>
<td>222</td>
<td>마포구 아현동</td>
</tr>
</tbody></table>
<ul>
<li>1,11,111,1111 사이의 지역들간 아무런 연관이 없다</li>
<li>2,22,222 도 마찬가지</li>
</ul>
<h3 id="연속된-숫자로-시군구-를-구분하며-중간중간에-더미pk가-끼여있다">연속된 숫자로 시/군/구 를 구분하며 중간중간에 더미PK가 끼여있다</h3>
<table>
<thead>
<tr>
<th>regions_id</th>
<th>지역</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>서울특별시 (더미)</td>
</tr>
<tr>
<td>2</td>
<td>중구 (더미)</td>
</tr>
<tr>
<td>3</td>
<td>중구 청운효자동</td>
</tr>
<tr>
<td>22</td>
<td>중구 회현동</td>
</tr>
<tr>
<td>22 ~ 33</td>
<td>중구 @@동</td>
</tr>
<tr>
<td>34</td>
<td>중구 황학동</td>
</tr>
<tr>
<td>35</td>
<td>중구 중림동</td>
</tr>
<tr>
<td>36</td>
<td>용산구 (더미)</td>
</tr>
<tr>
<td>37</td>
<td>용산구 후암동</td>
</tr>
<tr>
<td>38~51</td>
<td>용산구 @@동</td>
</tr>
<tr>
<td>52</td>
<td>용산구 보광동</td>
</tr>
<tr>
<td>53</td>
<td>서울 성동구 (더미)</td>
</tr>
<tr>
<td>54</td>
<td>서울 성동구 왕십리제2동</td>
</tr>
</tbody></table>
<ul>
<li>35,36,37번을 보면 중구 &gt;&gt; 용산구로 넘어가는데 중간에 35번 용산구더미가 끼여있다.</li>
<li>중간중간에 사용하지않는 지역을 끼워놓는 이유는 혹시나 모를 행정구 분할등을 대비함이 위함이 아닐까?!<ul>
<li>아니라면.. 왜 더미들이 끼여있는지는 잘 모르겠다. 뭐 물론 RDB 상에 저런 레코드 한두줄 더 끼여있는다고해서 큰 영향이 있을꺼같진 않다. 대충 계산해봐도 레코드가 0.3% 정도 늘어나는데 불과하므로</li>
</ul>
</li>
</ul>
<h3 id="번외--두-지점-사이의-거리근처인지-아닌지-판별하기-위해">번외 : 두 지점 사이의 거리(근처인지 아닌지 판별하기 위해)</h3>
<ul>
<li>성남시와 수원시는 인접시인데, 성남시 백현동과(1347) 수원시 이의동(4538)사이의 Ordinal 차이는 크다.</li>
<li>물리적인 두 지점 사이의 거리를 구하는 방법은 추측하건데 지도 API 로 기준점 좌표를 받아 &gt;&gt; 두점사이의 거리를 구하는 방식이 가장 쉽고 편하게 구할듯 하다..<ul>
<li>어차피 두 지점 사이의 거리는 변하지 않으니 Matrix 형식으로 요청해놓고 caching 해서 사용하면 될듯?</li>
</ul>
</li>
<li>당근마켓은 카카오맵 API를 사용하니 &quot;길이재기&quot; 기능을 사용하면 반경 X Km 까지 가능! 이런식의 서비스가 가능할꺼같다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring에서 file 다루기 (MultipartFile)]]></title>
            <link>https://velog.io/@d-h-k/Spring%EC%97%90%EC%84%9C-file-%EB%8B%A4%EB%A3%A8%EA%B8%B0-MultipartFile</link>
            <guid>https://velog.io/@d-h-k/Spring%EC%97%90%EC%84%9C-file-%EB%8B%A4%EB%A3%A8%EA%B8%B0-MultipartFile</guid>
            <pubDate>Fri, 24 Dec 2021 15:08:24 GMT</pubDate>
            <description><![CDATA[<h2 id="1-인트로">1) 인트로</h2>
<ul>
<li>스프링부트 서버에서 파일을 다룰때 편하게 쓸 수 있는 인터페이스  <code>MultipartFile</code> 를 간단히 사용해 보려고 합니다</li>
<li>테스트를 위해서 Spring MVC &amp;&amp; thymleafe 로 페이지를 구성했는데, 프론트 코드는 HTML이 대부분이라 다른 FE 기술스텍에서도 어렵지않게 사용 가능합니다. </li>
<li>이번 글에서는 minimal 한 글을 위해서 Client에게 파일 딱 하나만 업로드받아 서버 $Home 디렉토리에 저장하겠습니다</li>
</ul>
<h2 id="2-전체구조">2) 전체구조</h2>
<pre><code class="language-text">[front-end]
1) 파일 전송 요청
- 뷰 페이지
- &quot;/upload&quot;




[back-end]
2) 요청 받아서 파일을 서버에 저장
- Post로 파일전송받음
- &quot;api/v1/file&quot;
- 성공시 success페이지로 이동
- 실패시 예외발생, 에러리턴




[front-end]
3) 전송 성공 확인페이지
- 뷰 페이지
- &quot;/success&quot;

</code></pre>
<h2 id="3-filecontrollerjava">3) FileController.java</h2>
<pre><code class="language-java">@Controller
@RequiredArgsConstructor
public class FileController {

    private final FileService fileService;

    @GetMapping(&quot;/upload&quot;)
    public String upload() {
        return &quot;upload&quot;;
    }

    @GetMapping(&quot;/success&quot;)
    public String success() {
        return &quot;success&quot;;
    }

    @PostMapping(&quot;/api/v1/file&quot;)
    public String uploadFile(@RequestParam(&quot;file&quot;) MultipartFile file) {
        fileService.fileUpload(file);
        return &quot;redirect:/success&quot;;
    }
}</code></pre>
<ul>
<li>컨트롤러만 보면 어려울게 없습니당 그냥 이렇게 흘러가서 끝</li>
<li>뷰 페이지는 별거 없는데 공간을 많이 차지해서 생략<ul>
<li>궁금하신분은 아래 깃헙 링크에서 확인하실수 있습니다<ul>
<li><a href="https://github.com/d-h-k/bulletin-board/blob/main/src/main/resources/templates/success.html">https://github.com/d-h-k/bulletin-board/blob/main/src/main/resources/templates/success.html</a></li>
<li><a href="https://github.com/d-h-k/bulletin-board/blob/main/src/main/resources/templates/upload.html">https://github.com/d-h-k/bulletin-board/blob/main/src/main/resources/templates/upload.html</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="4-fileservice">4) FileService</h2>
<pre><code class="language-java">@Service
public class FileService {

    Logger log = LoggerFactory.getLogger(getClass());

    @Value(&quot;${user.home}&quot;)
    private String uploadDir;


    public void fileUpload(MultipartFile multipartFile) {
        Path serverPath = Paths.get(
                uploadDir +
                        File.separator +
                        StringUtils.cleanPath(multipartFile.getOriginalFilename()));

        try {
            Files.copy(multipartFile.getInputStream(), serverPath, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            log.error(&quot;fail to store file : name={}, exception={}&quot;,
                      multipartFile.getOriginalFilename(),
                      e.getMessage());
            throw new FileStorageException(&quot;fail to store file&quot;);
        }
    }

}</code></pre>
<ul>
<li><p>FileSystem이 OS 별로 다른 이유로  <code>File.seperator</code>  또한 다른데</p>
<ul>
<li>mac &amp; linux에선 <code>/</code> 문자인 반면, 윈도우에서는 <code>\</code> 구분자이므로 <code>StringUtils.cleanPath</code> 를 사용해 OS 종속성 제거 했습니다</li>
</ul>
</li>
<li><p>MultipartFile 이라는 인터페이스를 사용하는거 빼면 Local 개발컴퓨터에서 그냥 File 입출력이랑 다를게 없습니다</p>
<ul>
<li>multipartFile의 inputstream을 얻어서 파일을 네트워크로 ByteStream으로 전송받아 로컬에 그대로 작성합니다.</li>
</ul>
</li>
</ul>
<h2 id="5-tmi">5) TMI</h2>
<h3 id="굳이-mvc-까지-써야하는-이유가-있음">굳이 MVC 까지 써야하는 이유가 있음?</h3>
<ul>
<li><p>페이지의 form 요소의 코드 중 아래와 같은 부분이 있는데</p>
<pre><code> .... enctype=&quot;multipart/form-data&quot;&gt;
    &lt;input type=&quot;file&quot; name=&quot;file&quot; ...</code></pre></li>
<li><p>enctype이 &quot;multipart/form-data&quot; 인 경우 파일로 인식해서 SpringBoot 에서 알아서 <code>MultipartFile</code> 객체로 변환해주기 때문</p>
</li>
<li><p>Content-type  타입을 명시하고(예를 들어 text이면 text/plain, xml이면 text/xml, jpg이미지는 image/jpeg 등등)</p>
</li>
<li><p>Body의 해석 방식을 지정하는 타입으로 enctype 을 사용하는데, 대부분의 HTTP message가 ASCI 기반 인데</p>
</li>
<li><p>Filed의 경우 ASCI 방식의 인코딩이 아닌, Binary 방식의 인코딩을(사진, 음악, 암호화된 문서 등등)꼭 사용해야하며 그렇지 않는경우 파일이 깨질 수 있다</p>
<ul>
<li>form 에서 파일과 함께 전송하는 경우는 multipart/form-data 방식</li>
</ul>
</li>
<li><p>multipart 타입을 지정하면 HTTP Request에서 multipart의 Body를 전송하는 규약대로 서버-클라간 통신한다</p>
<ul>
<li>자세한 사항은 <a href="https://datatracker.ietf.org/doc/html/rfc7231#section-4.3.3">RFC 7231, section 4.3.3: POST</a> 표준을 참고해주세요</li>
</ul>
</li>
<li><p>아무튼 결론은 <code>HTTP 표준의 multipart</code> 규약을 이용하여 파일 업로드를 구현하는 것!</p>
</li>
</ul>
<h3 id="굳이-console-로만-돌리고싶다면">굳이 Console 로만 돌리고싶다면..</h3>
<ul>
<li>이 글을 참조하면 되는데 : <a href="https://www.baeldung.com/spring-rest-template-multipart-upload">https://www.baeldung.com/spring-rest-template-multipart-upload</a> </li>
<li>여기서 쓰는 RestTemplate 이 이미진즉에 Deprecated 라서 그닥 추천하지 않는데.</li>
</ul>
<h2 id="끝">끝</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[float 과 double의 차이]]></title>
            <link>https://velog.io/@d-h-k/float-%EA%B3%BC-double%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@d-h-k/float-%EA%B3%BC-double%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Thu, 23 Dec 2021 03:38:03 GMT</pubDate>
            <description><![CDATA[<ul>
<li>Single precision (float)과 Double precision의 차이점</li>
</ul>
<h2 id="차이">차이</h2>
<ul>
<li>float (Single)옵션 사용시 정밀도는 떨어지는 대신, 메모리, 생성되는 파일의 크기가 감소합니다. 이 때, 사용하는 메모리의 양이나 파일의 크기가 줄어들 경우, 그만큼 시뮬레이션이나 파일 처리 속도가 빨라집니다</li>
</ul>
<h3 id="floatsingle-precision">float(single precision)</h3>
<ul>
<li>소수점 유효숫자 6자리까지 : 3.14 1592</li>
<li>최대범위 : +/- 3.4e+38</li>
<li>32bit value</li>
</ul>
<h3 id="doubledouble-precision">double(double precision)</h3>
<ul>
<li>유효숫자 약 16개 : 3.14 1592 6535 8979 32</li>
<li>최대범위 : +/- 1.8e+308</li>
<li>64bit value</li>
</ul>
<h3 id="왜-float-는-single로-불릴까">왜 float 는 single로 불릴까</h3>
<ul>
<li>옜날옜적에 컴퓨터산업 초기에 소수점을 처음으로 도입될때 그시절 쓰던 <code>IBM 700/7000</code> 같은 컴퓨터들은 대부분 32비트 연산 시스템이였는데, float 32bit type을 한사이클에 처리할수 있어서 single이라고 부르기도 했는데</li>
<li>나중에 좀더 정밀한 소수점 표현을 위해 64비트 소수점 표현은 single의 2배만큼의 비트수인 64bit를 갖게 되므로 double 이라고 불린다고 한다</li>
</ul>
<h3 id="결론">결론</h3>
<ul>
<li>유효숫자가 7개 이상 필요하거나, 최대로 표현해야할 수가 3.4e+38 이상이라면 double을 쓰고</li>
<li>그렇지 않다면, float를 써도 별 문제가 없으며, 이경우 데이터 및 파일 크기에, 속도나 파일생성 속도에 있어서도 유리할꺼같지만</li>
<li>요즘 데스크탑들은 컴퓨터들은 대부분 64비트 시스템이고 CICS 기반 프로세서(X86, 인텔)들은 double precision 전용 하드웨어 &amp;&amp; Instruction Set 이 내장되어있다. </li>
<li>따라서 프로그래밍 언어에서는 모든 경우에 double 사용해도 큰 이상이 없고, 모델링/수치해석 프로그램(카티야, ProE, 등등..)은 툴 최적화에 따라 다름</li>
<li>적어도 X86 시스템에서 double type 자료형 사용에 부담을 느끼는건 1990년대에나 할법한 생각</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[CI 를 위한 테스트 성공시키는 이야기]]></title>
            <link>https://velog.io/@d-h-k/CI-%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%84%B1%EA%B3%B5%EC%8B%9C%ED%82%A4%EB%8A%94-%EC%9D%B4%EC%95%BC%EA%B8%B0</link>
            <guid>https://velog.io/@d-h-k/CI-%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%84%B1%EA%B3%B5%EC%8B%9C%ED%82%A4%EB%8A%94-%EC%9D%B4%EC%95%BC%EA%B8%B0</guid>
            <pubDate>Mon, 20 Dec 2021 13:46:50 GMT</pubDate>
            <description><![CDATA[<h2 id="테스트가-이상하다">테스트가 이상하다</h2>
<ul>
<li>분명히 로컬에서 테스트하면 잘 돌았는데.. 왜 서버에서 테스트를 돌리면 모든 테스트가 실패한다. 이상하다 이상하다~~
<img src="https://images.velog.io/images/d-h-k/post/b4a09949-7402-40dd-8f74-111376bc8d49/image.png" alt=""></li>
</ul>
<h2 id="이상하다-왜-안될까-왜-안될까">이상하다 왜 안될까 왜 안될까..</h2>
<ul>
<li>쿼리DSL 컴파일 에러랑 섞여가지고 헛스윙 날려보기도 하고, 나름의 생각으로 가능성있는 부분들을 하나씩 시도해봤다</li>
<li>결국 하루를 모두 갈아넣어서 찾았는데, 원인은 <code>auth관련 properties</code> 파일속 비밀키 정보때문에 gitignore에 등록해놓고 repo에 올리지 않았는데, Github-Action 입장에서는 이 <strong><code>auth관련 properties</code> 파일 정보가 없어서 Spring Context 를 로드하지 못한것..!</strong></li>
</ul>
<h2 id="문제는-해결하고-이제는-개선이다">문제는 해결하고 이제는 개선이다</h2>
<ul>
<li>Github-Action에서 이제 테스트는 잘 돌아가는데, 이렇게 된 이유를 되짚어봤다
<img src="https://images.velog.io/images/d-h-k/post/9ec16277-81b6-4e8d-b118-b2f52dfac586/image.png" alt=""></li>
<li>일단 프로퍼티 파일이 너무 난잡/복잡 하다. 개발 단계별 프로퍼티만 해도 test/local/staging 3단계에, 인증정보를 담고있는 oauth에, 무중단배포를 위한 real1,2 에.. 이 모든걸 switching 하기 위해 존재하는 &lt;&gt;. 파일까지</li>
<li><strong><code>이제는 프로퍼티 파일에 중복이 생기는걸 감수하고서라도 파일 갯수를 줄여서 심플하게</code></strong> 설정파일을 관리하는거에 초점을 맞춰야겠다</li>
<li>또, Jasypt와 Shell ENV 를 활용해서 gitignore에 의존적인 시크릿 키를 관리하지 않는 방향으로 가야겠다. 
<img src="https://images.velog.io/images/d-h-k/post/146aed8a-868c-40ae-86fb-44fe74085108/image.png" alt=""></li>
</ul>
<h2 id="난개발의-대가는-가혹했다">난개발의 대가는 가혹했다</h2>
<ul>
<li>막무가내로 기능 추가하는게 비용이 30 정도 든다면, 나중에 불필요한 기능과 코드를 도려내는데에는 100정도의 비용이 든다</li>
<li>개인플젝 혼자 개발하는데 비용이 무슨소린가 싶겠지만, 시간과 에너지도 결국 비용으로 환산된다</li>
<li>플젝에 코드가 덕지덕지 붙어서 덩치가 커지면서 테스트코드의 중요성도 같이 커지는데, 테스트가 있어서 CI 도 가능해지고, 리팩토링도 가능하고, 레거시 걷어내는것도 가능할꺼같다. </li>
</ul>
<h2 id="앞으로-탕감해야할-기술부채">앞으로 탕감해야할 기술부채</h2>
<ul>
<li>총 70개 정도 테스트코드가 있는데, 이중에서 20개가 @Ignored 처리 되어있다. 이걸 다 의미있는 테스트로 만들어야하고</li>
<li>build.gradle 파일의 90%는 인텔리제이 선생님께서 노란줄을 그어놓으셨다. 워닝을 없애야하고</li>
<li>이슈가 대충 40개쯤 있는데 하나씩 해결해야하고 뭐 그렇습니다.. </li>
</ul>
<h3 id="선생님-그거-다-빚이에요">선생님 그거 다 빚이에요</h3>
<ul>
<li>단기성과와는 상관없이, 챌린지나 대회 하는걸로 정해진 마감기한 안에 기능개발을 최대한 빨리 집어넣는건 마치 영끌해서 집사고, 전세놓은다음 또 집을 사는 갭투자 같은거다.</li>
<li>돈빌려준 사람이 받을돈을 까먹지 않고 수금하러 오는거처럼 기술부채도 언젠간 상환해야 한다. </li>
</ul>
<h2 id="아-초록점-찍는거-너무-힘들었다-ㅠㅠ">아 초록점 찍는거 너무 힘들었다.. ㅠㅠ</h2>
<p><img src="https://images.velog.io/images/d-h-k/post/07cff38b-bf7e-4899-8f91-71ae4cb309d1/image.png" alt=""></p>
<blockquote>
<p>이제 다음글에서는 CD 를 위한 자동배포를 포스팅 해보겠습니다!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[최근에 발견한 좋은 사이트]]></title>
            <link>https://velog.io/@d-h-k/%EC%B5%9C%EA%B7%BC%EC%97%90-%EB%B0%9C%EA%B2%AC%ED%95%9C-%EC%A2%8B%EC%9D%80-%EC%82%AC%EC%9D%B4%ED%8A%B8</link>
            <guid>https://velog.io/@d-h-k/%EC%B5%9C%EA%B7%BC%EC%97%90-%EB%B0%9C%EA%B2%AC%ED%95%9C-%EC%A2%8B%EC%9D%80-%EC%82%AC%EC%9D%B4%ED%8A%B8</guid>
            <pubDate>Sun, 19 Dec 2021 14:46:17 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/d-h-k/post/f74c7015-a391-4aa0-a2f4-a6cfe011ed9f/image.png" alt=""></p>
<p><a href="https://tecoble.techcourse.co.kr">https://tecoble.techcourse.co.kr</a></p>
<p>최근에 위 사이트를 자주 눈팅하는데, 포스트 하나하나가 퀄리티가 너무 좋다.</p>
<p>사실 내가 할말은 아니긴 한데.. 인터넷 정보글의 99% 는 어디서 퍼왔거나, 누군가가 잘 작성한 글을 따라하면서 복붙한 일종의 열화판이기 때문에 양질의 좋은 글과 그렇지 못한 글을 걸러내는 안목은 상당히 중요하다.</p>
<p>글의 퀄리티를 알아보는 안목이 있어도 하루에 집중해서 머리를 써서 일할수 있는 시간은 한정적이기 때문에, 나쁜글을 걸러내는것도 최소화하면 할수록 좋다. 그래서 좋은 포스트가 많은 유명인사의 블로그나(향로님) 유명 기업 테크블로그(우형블로그, 네이버 D2)등을 먼저 찾아보는 편인데, 위에서 소개한 tecoble.techcourse 도 상당히 좋다..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[12월 APS]]></title>
            <link>https://velog.io/@d-h-k/12%EC%9B%94-APS</link>
            <guid>https://velog.io/@d-h-k/12%EC%9B%94-APS</guid>
            <pubDate>Wed, 08 Dec 2021 09:57:52 GMT</pubDate>
            <description><![CDATA[<h2 id="12월-7일">12월 7일</h2>
<p><a href="https://leetcode.com/problems/add-digits/">https://leetcode.com/problems/add-digits/</a>
<a href="https://leetcode.com/problems/find-the-difference/">https://leetcode.com/problems/find-the-difference/</a>
<a href="https://leetcode.com/problems/excel-sheet-column-number/">https://leetcode.com/problems/excel-sheet-column-number/</a></p>
<h2 id="12월-15일">12월 15일</h2>
<p><a href="https://leetcode.com/problems/monotonic-array/">https://leetcode.com/problems/monotonic-array/</a>
<a href="https://leetcode.com/problems/two-sum-iv-input-is-a-bst/">https://leetcode.com/problems/two-sum-iv-input-is-a-bst/</a>
<a href="https://leetcode.com/problems/maximum-population-year/">https://leetcode.com/problems/maximum-population-year/</a></p>
<h2 id="12월-22일">12월 22일</h2>
<h4 id=""><img src="https://images.velog.io/images/d-h-k/post/2c3da6e0-c9ce-4c47-bcfa-f003242f9dbb/image.png" alt=""></h4>
<p><a href="https://leetcode.com/problems/buddy-strings/">https://leetcode.com/problems/buddy-strings/</a>
<a href="https://leetcode.com/problems/can-place-flowers/">https://leetcode.com/problems/can-place-flowers/</a>
<a href="https://leetcode.com/problems/minimize-maximum-pair-sum-in-array/">https://leetcode.com/problems/minimize-maximum-pair-sum-in-array/</a></p>
<h2 id="12월-29일">12월 29일</h2>
<p><a href="https://leetcode.com/problems/valid-mountain-array/">https://leetcode.com/problems/valid-mountain-array/</a>
<a href="https://leetcode.com/problems/excel-sheet-column-title/">https://leetcode.com/problems/excel-sheet-column-title/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 함 써보쉴]]></title>
            <link>https://velog.io/@d-h-k/%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%93%B0%EC%89%B4</link>
            <guid>https://velog.io/@d-h-k/%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%93%B0%EC%89%B4</guid>
            <pubDate>Fri, 03 Dec 2021 05:54:28 GMT</pubDate>
            <description><![CDATA[<h2 id="왜-사람들은-리눅스를-외면할까">왜 사람들은 리눅스를 외면할까</h2>
<ul>
<li>항상 왜 리눅스는 가상머신위에서만 돌아야할까</li>
<li>리눅스 쓰세요 진짜 좋아요</li>
</ul>
<p><a href="https://youtu.be/mPrmi0vrlQM">https://youtu.be/mPrmi0vrlQM</a></p>
<p>!youtube[mPrmi0vrlQM]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA Entity Class 에서 Primitive Type 을 써야할까 Wrapper Class 를 사용해야할까]]></title>
            <link>https://velog.io/@d-h-k/JPA-Entity-Class-%EC%97%90%EC%84%9C-Primitive-Type-%EC%9D%84-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C-Wrapper-Class-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@d-h-k/JPA-Entity-Class-%EC%97%90%EC%84%9C-Primitive-Type-%EC%9D%84-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C-Wrapper-Class-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Thu, 25 Nov 2021 07:51:02 GMT</pubDate>
            <description><![CDATA[<h2 id="java-언어에서-primitive-type-vs-wrapper-class-차이">Java 언어에서 <code>Primitive Type</code> VS <code>Wrapper Class</code> 차이</h2>
<ul>
<li>Primitive Type : 변수를 선언하면 Stack 저장공간에 데이터가 저장됨, <ul>
<li>변수명은 <code>Stack 저장공간</code> 이자 값 그 자체를 담고있음 (메타포 : 금괴를 손에 쥐고 있음) </li>
</ul>
</li>
<li>Wrapper Class : 변수를 선언하면 Stack 저장공간 Heap 영역의 주소를 알고있는 레퍼런스 (포인터 비스무리한것)가 Stack 에 생김<ul>
<li>실제 데이터는 Heap 영역에 따로 저장해놓고 JVM &amp; GC 의 관리를 받음</li>
<li>변수명은 데이터를 담고있지 않음 오직 레퍼런스 뿐</li>
<li>레퍼런스값은 바로 데이터가 존재하는 Heap 메모리공간의 주소임 (메타포 : 금괴의 위치가 표시된 지도만 있음)</li>
</ul>
</li>
<li>차이점<ul>
<li>메서드 호출시 : Primitive Type 은 값을 매번 복사하기 때문에 서로 영향을 주지 않지만, Wrapper Class 는 레퍼런스만 복사하기 때문에 서로 공유됨</li>
<li>재귀함수(Recursive Method) 사용시 :   Primitive Type은 StackFrame 에 데이터가 계속 생성되면서 서로 영향을 주고받지 않음</li>
<li>Immutable : Wrapper Classes 는 근본적으로 Immutable (변경 불가능한) 객체이고 Object 이므로 +1, ++ 같은 사칙연산이 안되는게 원칙이지만, 이걸 인식하지 않고 쓸수 있는 이유는 Java 에서 AutoBoxing/Unboxing 이 지원되고 Equals, HashCode를 재정의해놓았기 때문<pre><code class="language-java">@Test
public void test() {
  Integer i = 5;
  assertThat(i).isEqualTo(5); // i는 5이지만
  assertThat(System.identityHashCode(i)).isNotEqualTo(5); // 메모리 관점에서는 다르다
}</code></pre>
</li>
</ul>
</li>
</ul>
<h2 id="primitive-type-의-장점">Primitive Type 의 장점</h2>
<ul>
<li>공유참조문제가 없다 (하지만 Wrapper Class 도 이뮤터블 객체이다 )</li>
<li>성능이 조금이라도 더 좋다 (아니 좋을것같다)</li>
<li>nullPointException 발생이 원천봉쇄된다</li>
</ul>
<h2 id="wrapper-class-사용의-장점">Wrapper Class 사용의 장점</h2>
<ul>
<li>멀티스레드에서 사용할 수 있다</li>
<li>nullPointException 이 발생할수는 있지만<ul>
<li>null 임을 표시할수 있다. 숫자0이 의미를 갖는 경우도 분명히 있기때문</li>
</ul>
</li>
<li>하지만 명시적으로 vaildataion 을 사용할 수 있다<ul>
<li>@NonNull, @NotNull.. 등등</li>
</ul>
</li>
</ul>
<h2 id="결론">결론</h2>
<ul>
<li><p>PK에는 <code>Long</code>(Wrapper Class) 을 사용한다</p>
<ul>
<li>Wrapper Class 를 사용함으로써 Null을 대입해놓을 수 있는데, 명시적으로 PK가 아직 할당되지 않았음을 의미할수 있다. 반면 primivite type은 null 을 표시할 방법이 없다</li>
<li>We recommend that you declare consistently-named identifier attributes on persistent classes and that you use a nullable (i.e., non-primitive) type (<a href="https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#entity-pojo-identifier">출처링크</a>)</li>
</ul>
</li>
<li><p>판단기준은 Null을 원천봉쇄 하느냐 아니냐의 차이인데, 일부 필드에서 primitive Type 도 사용하고 있다.</p>
<ul>
<li>지금 코드에서는 PK 가 아닌 일부 필드에서 primitive Type을 사용하는 경우를 살펴보면</li>
<li>Item의 재고 수량 필드(stockCount)가 대표적인데, Null 이 필요한 경우도 없고, 따로 입력해 주지 않으면 기본값을 0으로 지정해도 문제가 없기 때문</li>
</ul>
</li>
<li><p>original from : <a href="https://github.com/d-h-k/just-board#jpa-entity-class-%EC%97%90%EC%84%9C-primitive-type-%EC%9D%84-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C-wrapper-class-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C">Link</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[아이템38 - 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라]]></title>
            <link>https://velog.io/@d-h-k/%EC%95%84%EC%9D%B4%ED%85%9C38-%ED%99%95%EC%9E%A5%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%98%EB%A9%B4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@d-h-k/%EC%95%84%EC%9D%B4%ED%85%9C38-%ED%99%95%EC%9E%A5%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%98%EB%A9%B4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</guid>
            <pubDate>Sat, 13 Nov 2021 01:44:12 GMT</pubDate>
            <description><![CDATA[<h1 id="아이템38---확장할-수-있는-열거-타입이-필요하면-인터페이스를-사용하라">아이템38 - 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라</h1>
<h2 id="열거형의-단점">열거형의 단점</h2>
<ul>
<li>우리가 쓰는 Enum의 특징으로는 클라이언트가 값을 더 추가해서 쓸 수 없다</li>
<li>예시로 Spring <code>HttpMethod httpMethod</code> 은 우리가 값을 추가할수 없는데 잘 생각해보면 그럴필요가 없다</li>
<li>생각해보면 확장할 필요가 있는경우가 드물다.. HttpMethod 같은걸 일개 개발자가 추가할리는 없으니..까?</li>
</ul>
<pre><code class="language-java">@Slf4j
@RestController
public class RequestHeaderController {

    @RequestMapping(&quot;/header&quot;)
    public String headers(HttpServletRequest request, HttpServletResponse response,
                          HttpMethod httpMethod, Locale locale,
                          @RequestHeader MultiValueMap&lt;String, String&gt; headerMap,
                          @RequestHeader(&quot;host&quot;) String host,
                          @CookieValue(value = &quot;myCookie&quot;, required = false) String cookie) {
</code></pre>
<pre><code class="language-java">
public enum HttpMethod {

    GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;


    private static final Map&lt;String, HttpMethod&gt; mappings = new HashMap&lt;&gt;(16);

    static {
        for (HttpMethod httpMethod : values()) {
            mappings.put(httpMethod.name(), httpMethod);
        }
    }

    @Nullable
    public static HttpMethod resolve(@Nullable String method) {
        return (method != null ? mappings.get(method) : null);
    }

    public boolean matches(String method) {
        return name().equals(method);
    }

}
</code></pre>
<h3 id="그럼에도-불구하고">그럼에도 불구하고</h3>
<ul>
<li>과거에 사용했던 타입안전 열거패턴은 가능했는데 이런게 필요한 경우가 있을까??</li>
<li>딱하나 있다면 연산자(Op code)같은게 있을수 있고, 이때는 Enum 으로 인터페이스를 구현하는 방식을 사용하면된다</li>
</ul>
<pre><code class="language-java">
public interface Operation {
    double apply(double x, double y);
}
</code></pre>
<pre><code class="language-java">public enum BasicOps implements Operation {
    PULS(&quot;+&quot;) {
        @Override
        public double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS(&quot;+&quot;) {

    },
    TIMES(&quot;*&quot;) {

    },
    DIVIDE(&quot;/&quot;) {

    };

    private final String symbol;

    BasicOps(String symbol) {
        this.symbol = symbol;
    }

}
</code></pre>
<h3 id="위와-같은-코드를-프레임워크가-만들어줬다고-하면-클라이언트가-아래-코드를-추가할-수-있다">위와 같은 코드를 프레임워크가 만들어줬다고 하면, 클라이언트가 아래 코드를 추가할 수 있다</h3>
<pre><code class="language-java">public enum ExtenedOps implements Operation {

    EXP(&quot;*&quot;) {
        @Override
        public double apply(double x, double y) {
            return x * y;
        }
    },
    REMAINDER(&quot;%&quot;) {
        //...
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[11월 APS]]></title>
            <link>https://velog.io/@d-h-k/11%EC%9B%94-APS</link>
            <guid>https://velog.io/@d-h-k/11%EC%9B%94-APS</guid>
            <pubDate>Wed, 10 Nov 2021 06:43:43 GMT</pubDate>
            <description><![CDATA[<h2 id="11월-10일-수요일">11월 10일 수요일</h2>
<p><a href="https://leetcode.com/problems/max-increase-to-keep-city-skyline/">https://leetcode.com/problems/max-increase-to-keep-city-skyline/</a>
<a href="https://leetcode.com/problems/surface-area-of-3d-shapes/">https://leetcode.com/problems/surface-area-of-3d-shapes/</a>
<a href="https://leetcode.com/problems/check-if-all-1s-are-at-least-length-k-places-away/">https://leetcode.com/problems/check-if-all-1s-are-at-least-length-k-places-away/</a></p>
<h2 id="11월-17일-수요일">11월 17일 수요일</h2>
<p><a href="https://leetcode.com/problems/binary-number-with-alternating-bits/">https://leetcode.com/problems/binary-number-with-alternating-bits/</a>
<a href="https://leetcode.com/problems/reverse-only-letters/">https://leetcode.com/problems/reverse-only-letters/</a>
<a href="https://leetcode.com/problems/defuse-the-bomb/">https://leetcode.com/problems/defuse-the-bomb/</a>
<a href="https://leetcode.com/problems/add-digits/">https://leetcode.com/problems/add-digits/</a></p>
<h2 id="11월-24일">11월 24일</h2>
<p><a href="https://leetcode.com/problems/longer-contiguous-segments-of-ones-than-zeros/">https://leetcode.com/problems/longer-contiguous-segments-of-ones-than-zeros/</a>
<a href="https://leetcode.com/problems/minimum-changes-to-make-alternating-binary-string/">https://leetcode.com/problems/minimum-changes-to-make-alternating-binary-string/</a>
<a href="https://leetcode.com/problems/contains-duplicate/">https://leetcode.com/problems/contains-duplicate/</a>
<a href="https://leetcode.com/problems/find-the-difference/">https://leetcode.com/problems/find-the-difference/</a>
<a href="https://leetcode.com/problems/excel-sheet-column-number/">https://leetcode.com/problems/excel-sheet-column-number/</a>
<a href="https://leetcode.com/problems/rank-transform-of-an-array/">https://leetcode.com/problems/rank-transform-of-an-array/</a></p>
<h2 id="12월-1일-수요일예정">12월 1일 수요일(예정)</h2>
<p><a href="https://leetcode.com/problems/add-digits/">https://leetcode.com/problems/add-digits/</a>
<a href="https://leetcode.com/problems/find-the-difference/">https://leetcode.com/problems/find-the-difference/</a>
<a href="https://leetcode.com/problems/excel-sheet-column-number/">https://leetcode.com/problems/excel-sheet-column-number/</a>
<a href="https://leetcode.com/problems/monotonic-array/">https://leetcode.com/problems/monotonic-array/</a>
<a href="https://leetcode.com/problems/two-sum-iv-input-is-a-bst/">https://leetcode.com/problems/two-sum-iv-input-is-a-bst/</a>
<a href="https://leetcode.com/problems/maximum-population-year/">https://leetcode.com/problems/maximum-population-year/</a>
<a href="https://leetcode.com/problems/buddy-strings/">https://leetcode.com/problems/buddy-strings/</a>
<a href="https://leetcode.com/problems/can-place-flowers/">https://leetcode.com/problems/can-place-flowers/</a>
<a href="https://leetcode.com/problems/valid-mountain-array/">https://leetcode.com/problems/valid-mountain-array/</a>
<a href="https://leetcode.com/problems/excel-sheet-column-title/">https://leetcode.com/problems/excel-sheet-column-title/</a>
<a href="https://leetcode.com/problems/x-of-a-kind-in-a-deck-of-cards/">https://leetcode.com/problems/x-of-a-kind-in-a-deck-of-cards/</a>
<a href="https://leetcode.com/problems/length-of-last-word/">https://leetcode.com/problems/length-of-last-word/</a>
<a href="https://leetcode.com/problems/number-of-different-integers-in-a-string/">https://leetcode.com/problems/number-of-different-integers-in-a-string/</a>
<a href="https://leetcode.com/problems/check-if-n-and-its-double-exist/">https://leetcode.com/problems/check-if-n-and-its-double-exist/</a>
<a href="https://leetcode.com/problems/long-pressed-name/">https://leetcode.com/problems/long-pressed-name/</a></p>
<ul>
<li>자료구조 : <a href="https://leetcode.com/problems/deepest-leaves-sum/">https://leetcode.com/problems/deepest-leaves-sum/</a></li>
</ul>
<p><a href="https://leetcode.com/problems/merge-two-sorted-lists/">https://leetcode.com/problems/merge-two-sorted-lists/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[김영한님 Spring MVC 6장 - Logger 부분]]></title>
            <link>https://velog.io/@d-h-k/Spring-MVC-6%EC%9E%A5-Logger-%EB%B6%80%EB%B6%84</link>
            <guid>https://velog.io/@d-h-k/Spring-MVC-6%EC%9E%A5-Logger-%EB%B6%80%EB%B6%84</guid>
            <pubDate>Tue, 09 Nov 2021 11:29:25 GMT</pubDate>
            <description><![CDATA[<h2 id="logging">Logging</h2>
<ul>
<li>자바 로깅 관련 라이브러리가 많은데 우리는 slf4j 쓴다</li>
<li>여기링크(<a href="http://www.slf4j.org/manual.html">http://www.slf4j.org/manual.html</a>) 보면 </li>
</ul>
<h3 id="slf4j">slf4j</h3>
<ul>
<li>로거 구현체가 있는데, 수많은 로깅 구현체를 담을 수 있는 Logging 프레임워크<ul>
<li>추상 레이어를 제공 (=Facade 패턴)</li>
</ul>
</li>
<li>로거를 추상화된 레이어로 제공하는 이유는 : <strong><code>DIP</code></strong> 를 지키기 위해서<ul>
<li>나중에 더 좋은 라이브러리가 생겨 교체하더라도 우리가 짠 application의 코드를 변경할 필요가 없다</li>
<li>여러 로깅 라이브러리를 하나의 통일된 방식으로 사용</li>
</ul>
</li>
<li>참고 : 구현체 로깅 프레임워크 3가지<ul>
<li>log4j, JAVA.UTIL.LOGGING : 안씀 과거의 유물</li>
<li>logback-classic : 아무것도 설정을 안했을때 기본값, 지금 사용중인 프레임워크</li>
<li>log4j2 : logback에 비해 개선되었긴 한데..(왜안쓰지..?)</li>
</ul>
</li>
</ul>
<h3 id="facade-패턴">facade 패턴</h3>
<ul>
<li>소프트웨어의 일정 부분(=기능상의 모듈/라이브러리/프레임워크/표준) 에 대한 간략화된 인터페이스를 제공하는 패턴<ul>
<li>다시말해서 역할과 구현을 분리해내기 위해서 사용하고</li>
<li>좋은 객체지향 프로그래밍 원칙으로, 유연해지고 변경이 쉬워지는데<ul>
<li>클라이언트가 FrameWork의 역할(interface)만 알고, 프레임워크 내부 구조를 몰라도 되면서, 내부 구조가 변경되어도 클라이언트 소스는 단한줄도 바꾸지 않아도 된다</li>
</ul>
</li>
</ul>
</li>
<li>slf4j는 Java 진영에서 로깅을 하기 위한 표준, Logging 기능이 역할에 대해서만 정의만 있다면 (abstract)</li>
<li>logback은 slf4j라는 역할에 맞춰서 log 동작을 실제로 돌아가게 하는 구현체 (implementation)</li>
<li>의존관계 역전 원칙 <strong><code>DIP(Dependency inversion principle)</code></strong> 을 지켜낼수 있다</li>
</ul>
<h3 id="dipdependency-inversion-principle--의존관계-역전-원칙">DIP(Dependency inversion principle) : 의존관계 역전 원칙</h3>
<ul>
<li>DIP는 인터페이스와 클래스를 분리해라! 역활과 구현을 분리해서 내부구조 변경에 유연하게 대응하자라는 취지인데, OCP와 결을 같이한다!</li>
<li>비슷한 맥락으로 Stable Depencies Principle (안정된 의존성 원칙) : 의존은 안정적인 쪽을 향해야 한다. 쉽게 말해서 구체화된 클래스보다는 추상화된 인터페이스에 의존하라는 의미<ul>
<li>인터페이스들 간에 안정된 의존성 관계 원칙과 탄탄한 구조를 갖게 만들면 </li>
<li>인터페이스를 구체화하는 패키지는 변화하고 바뀌더라도 전체적인 구조는 그대로 가져가고 변경은 최소화되면서 인터페이스 단위로 개선이 가능합니다</li>
<li>이렇게 쉽게 바뀔 것이라고 예상되는 구체적인 패키지(클래스) 들은 바뀌기 어려운 패키지(인터페이스)의 의존 대상이 되어서는 안된다</li>
</ul>
</li>
<li>더 읽어보기 : <a href="https://techblog.woowahan.com/2561/">https://techblog.woowahan.com/2561/</a><ul>
<li>ISP 랑은 맥락이 다른게 ,이건 SRP랑 엮여서 하나의 클래스는 하나의 일만 담당하게 하자! 라는 의미다</li>
</ul>
</li>
</ul>
<h3 id="로깅에도-레벨이-있다">로깅에도 레벨이 있다</h3>
<ul>
<li>로그는 다섯 가지의 레벨로 나뉘고, Log Level 아래쪽으로 갈수록 심각한 오류를 의미하며 로그 레벨이 낮아도 출력이 된다</li>
</ul>
<table>
<thead>
<tr>
<th align="center">로깅레벨</th>
<th align="center">trace출력레벨</th>
<th align="center">debug 출력시</th>
<th align="center">info출력시(default)</th>
<th align="center">warn 출력시</th>
<th align="center">error출력시</th>
</tr>
</thead>
<tbody><tr>
<td align="center">log.trace 출력여부</td>
<td align="center">O</td>
<td align="center">X</td>
<td align="center">X</td>
<td align="center">X</td>
<td align="center">X</td>
</tr>
<tr>
<td align="center">log.debug 출력여부</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">X</td>
<td align="center">X</td>
<td align="center">X</td>
</tr>
<tr>
<td align="center">log.info 출력여부</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">X</td>
<td align="center">X</td>
</tr>
<tr>
<td align="center">log.warn 출력여부</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">X</td>
</tr>
<tr>
<td align="center">log.error 출력여부</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">O</td>
</tr>
</tbody></table>
<h3 id="spring-boot-에서-사용법">Spring boot 에서 사용법</h3>
<pre><code class="language-java">import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// ... 중략
private Logger logger = LoggerFactory.getLogger(this.getClass());</code></pre>
<ul>
<li>이거 참고로 처음 하시는분들이 많이 겪는 문제인데, 자꾸 컴파일 에러가 뜬다고 하면 import를 확인해보자 Logger 를 잘못 임포트하는경우가 많다</li>
<li>일반적인 import 순서로 Logger &gt;&gt; LoggerFactory 순서로 하는데, 이를 뒤집어서 LoggerFactory 먼저 임포트 하면 Logger 를 잘못 임포트 하는경우가 있다</li>
</ul>
<p><br><br></p>
<pre><code class="language-java">logger.trace(&quot;{} {} 출력&quot;, &quot;값1&quot;, &quot;값2&quot;);
logger.debug(&quot;{} {} 출력&quot;, &quot;값1&quot;, &quot;값2&quot;);
logger.info(&quot;{} {} 출력&quot;, &quot;값1&quot;, &quot;값2&quot;);
logger.warn(&quot;{} {} 출력&quot;, &quot;값1&quot;, &quot;값2&quot;);
logger.error(&quot;{} {} 출력&quot;, &quot;값1&quot;, &quot;값2&quot;);</code></pre>
<ul>
<li>로그를 찍을때는 위와같은 코드를 사용해 남기며면 된다!</li>
<li>로그를 콘솔출력 말고 파일로 저장할 수도 있다</li>
</ul>
<h2 id="로그에서-하지-말아야할-안티패턴1---표준입출력-사용">로그에서 하지 말아야할 안티패턴1 - 표준입출력 사용</h2>
<ul>
<li>System.out.println()으로 출력하는것과 Log로 출력하는것 사이에 어떤 차이가 있는지 생각해봤는데요</li>
<li>System.out.println()의 내부구조를 살펴보면(open-JDK11) 아래와 같은 코드로 이루어져 있습니다<pre><code class="language-java">public void println(String x) {
  synchronized (this) {
      print(x);
      newLine();
  }
}</code></pre>
</li>
<li>synchronized 키워드가 붙어있는데요, 이 키워드는 크리티컬 섹션에 대한 동시접근을 막기 위해 Block Unblock 동작을 하는 일종의 single-Semaphore(혹은 MUTEX)로 동작하게 해주는 키워드 입니다.</li>
<li>그럼 println 같은 Console 출력이 왜 크리티컬 섹션이고, 꼭 synchronized 키워드를 붙여야할만큼 중요한 작업인지는 &quot;그런일을 하지 않았을때 벌어지는 일&quot; 이 끔찍하기 때문입니다.</li>
<li>synchronized 키워드를 달아주지 않았을 경우 : 멀티스레드 상황에서 문자열이 뒤죽박죽 섞여서 나오는 현상이 발생할 수도 있는데요(랑데부 지점)</li>
<li>랑데부 지점에서 동시성 문제를 해결하기 위해 동시성 접근을 차단하기 위해 만들어놓은 장치가 synchronized 이며 Java라는 언어를 만든 사람들이 어느정도의 성능희생을 감수하고서라도 언어레벨에서 동시성 이슈가 책임지고 발생하지 않도록 print 함수를 만들어뒀기 때문입니다. </li>
<li>Database에서도 많이 언급했지만 성능과 동시성은 항상 Trade-off관계이기 때문이죠</li>
<li>Java언어에서의 println 함수와는 반대로, 동시성 이슈는 알아서 할테니, 최고의 IO 성능을 내고싶다면, 리눅스 시스템 프로그래밍에서배운 flock(2), fcntl(2) 등의 함수를 C언어로 호출해보면(아마 Java NIO로) 됩니다</li>
<li>여기서 한가지 얼렁뚱땅 넘어간 부분이 있는데요, 랑데부 상황에서 왜 문자열이 뒤죽박죽 섞여 나오냐면 리눅스는 IPC도 파일로 처리하고 공유될만한 대부분의 자원을 File로 처리하기 때문입니다. (근데 이건 설명하자면 한도끝도 없고 저도 잘 모르니까 Linux OS의 File과 표준입출력에 대해 공부하고 나중에 정리해보겠습니다)</li>
</ul>
<br>

<ul>
<li>너무 잡설이 길었는데... 그래서 이 println 함수롤 호출하는 순간 다른 쓰레드는 Block이 걸리게 되고, 다른 쓰레드에서는 println에서 Lock이 풀릴때까지 Blocking 상태가 되어 다음 일을 할 수가 없기 때문에 성능저하가 발생하게 됩니다.</li>
</ul>
<pre><code class="language-java">public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }</code></pre>
<h3 id="이외에-비슷한-기출변형도-안됩니다--exception-stack-trace--stringformat">이외에 비슷한 기출변형도 안됩니다 ( Exception stack trace / String.format)</h3>
<ul>
<li>Exception의 stack trace를 로깅할 때도 <code>e.printStackTrace()</code> 를 이용하면 안되는데 그 이유는 e.printStackTrace()은 내부적으로 <code>java.lang.System.err</code> 를 이용해서 로그를 남기게 됩니다. 즉 println과 같은 이유입니다<pre><code class="language-java"></code></pre>
</li>
<li>문자열 포멧팅(String.format)<ul>
<li>Formatter, StringBuilder 클래스를 생성하기 때문에 인스턴스 생성/소멸에 엄청난 비용이 들어가게 됩니다.</li>
</ul>
</li>
</ul>
<pre><code class="language-java">logger.debug(String.format(&quot;Hello %s(%s)&quot;, user.getName(), user.getEmail()));</code></pre>
<ul>
<li>결론 : 그래서 로그를 쓰면 됩니다.</li>
</ul>
<h3 id="로그에서-하지-말아야할-안티패턴2---문자열-직접연산-사용">로그에서 하지 말아야할 안티패턴2 - 문자열 직접연산 사용</h3>
<ul>
<li></li>
<li>문자열 연산을 한다는 차원의 문제뿐 아니라 </li>
<li>Logging 출력레벨보다 낮은(예를들어 trace) 레벨의 로거함수 호출에 문자열 더하기 연산이 존재하면, 로그가 찍히는지랑 상관없이 문자열을 다 더해주고난 다음 완성하고 함수를 호출하기 때문에 </li>
<li>logger출력 메소드 실행 이전에 어떠한 연산이라도 일어나게 되면 동일한 낭비됩니다</li>
<li>SLF4J의 치환문자({})를 사용하자 : 정식명칭은 Fluent Logging API : 링크참조(<a href="http://www.slf4j.org/manual.html#fluent">http://www.slf4j.org/manual.html#fluent</a>)</li>
</ul>
<h3 id="안티패턴별-벤치마크--httpsgithubcomd-h-kmvcblobmainspringmvcsrctestjavahellospringmvcbasiclogtestcontrollertestjava">안티패턴별 벤치마크 : <a href="https://github.com/d-h-k/mvc/blob/main/springmvc/src/test/java/hello/springmvc/basic/LogTestControllerTest.java">https://github.com/d-h-k/mvc/blob/main/springmvc/src/test/java/hello/springmvc/basic/LogTestControllerTest.java</a></h3>
<ul>
<li>코드 스니핏 짤라서 설명을 같이 적어줘야하지만 너무 졸려서 나중에 할래요..</li>
</ul>
<pre><code>20:12:56.792 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - String Plus 방식 : 15308129ns
20:12:56.798 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - Lazy Replace 방식: 993902ns
20:12:56.798 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - 결론 : Replace 가 14314227ns 빠름
20:12:56.799 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - 결론 : Replace 가 15.402050705200311배 빠름</code></pre><pre><code>20:12:57.024 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - String Plus 방식 : 134234613ns
20:12:57.024 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - Lazy Replace 방식: 77698955ns
20:12:57.024 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - 결론 : Replace 가 56535658ns 빠름
20:12:57.024 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - 결론 : Replace 가 1.727624432014562배 빠름
</code></pre><pre><code>20:17:12.765 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - print 방식 : 108410799ns
20:17:12.766 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - logger 방식: 80646422ns
20:17:12.766 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - 결론 : logger 가 27764377ns 빠름
20:17:12.766 [Test worker] INFO hello.springmvc.basic.LogTestControllerTest - 결론 : logger 가 1.3442728928507206배 빠름
</code></pre><ul>
<li>근데 이건 항상 성공하지는 않는다 프로세스 상태, 컴퓨터 메모리공간 등에 따라 다른듯</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[이펙티브 자바 아이템 36 ordinal 메서드 대신 인스턴스 필드를 사용하라]]></title>
            <link>https://velog.io/@d-h-k/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EC%95%84%EC%9D%B4%ED%85%9C-36-ordinal-%EB%A9%94%EC%84%9C%EB%93%9C-%EB%8C%80%EC%8B%A0-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%ED%95%84%EB%93%9C%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@d-h-k/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EC%95%84%EC%9D%B4%ED%85%9C-36-ordinal-%EB%A9%94%EC%84%9C%EB%93%9C-%EB%8C%80%EC%8B%A0-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%ED%95%84%EB%93%9C%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</guid>
            <pubDate>Sat, 06 Nov 2021 00:36:48 GMT</pubDate>
            <description><![CDATA[<h3 id="아이템-36-ordinal-메서드-대신-인스턴스-필드를-사용하라">아이템 36 ordinal 메서드 대신 인스턴스 필드를 사용하라</h3>
<h2 id="일반적인-경우-enum-의-ordinal-사용은-안티패턴">일반적인 경우 Enum 의 Ordinal 사용은 안티패턴</h2>
<pre><code class="language-java">public enum Ensemble {

  SOLO, DUET, TRIO, QUARTET, QUINTET,
  SEXTET, SEPTET, OCTET, NONET, DECTET;

  public int numberOfMusicians() { 
    return ordinal() + 1; 
  }
}</code></pre>
<ul>
<li>위 코드에서 SOLO=1 부터 접근하는, 열거타입을 int형 정수로 사용하게 된다</li>
<li>열거형을 정수로 사용하면 몇가지 문제가 발생하는데<ul>
<li>열거형의 장점을 모두 잃어버림(아이템 34) </li>
<li>더해서 이넘&gt;숫자는 변경이 쉬운데, 숫자&gt; 이넘 변경은 지저분할수 밖에 없다</li>
<li>중간에 비는값을 표현하지 못한다(3-&gt;7 못함), Dummy를 추가하면 되지만.. 코드도 지저분해지고, 실용성도 떨어진다</li>
</ul>
</li>
<li>해결책 : ordinal 사용하지 말고 인스턴스 필드에 저장하기</li>
</ul>
<h2 id="인스턴스-필드에-데이터-저장하기">인스턴스 필드에 데이터 저장하기</h2>
<pre><code class="language-java">public enum Ensemble {
  SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5),
  SEXTET(6), SEPTET(7), OCTET(8), DOUBLE_QUARTET(8),
  NONET(9), DECTET(10), TRIPLE_QUARTET(12);

  private final int numberOfMusicians;

  Ensemble(int size) { 
    this.numberOfMusicians = size; 
    }

  public int numberOfMusicians() { 
    return numberOfMusicians; 
    }
}</code></pre>
<ul>
<li>그러니까, ordinal을 사용하고 싶다는건 Enum 을 정수 숫자형으로 쵸현하고 싶은거기ㄴ까 이렇게 하면 된다</li>
</ul>
<h2 id="jpa에서-enum변수-저장시에도-ordinal-말고-string-전략으로">JPA에서 Enum변수 저장시에도 Ordinal 말고 String 전략으로</h2>
<ul>
<li>enum 타입을 엔티티 클래스의 필드변수로 사용할때 저장하는 방식이 두가지인데 @Enumerated 애노테이션에는 EnumType으로 지정이 가능<ul>
<li>EnumType.ORDINAL : enum 순서 값 숫자로 DB에 저장</li>
<li>EnumType.STRING : enum 이름을 문자열로 DB에 저장</li>
</ul>
</li>
<li>Enum필드를 저장할때 기본값이 EnumType.ORDINAL 이라서 숫자로 저장된다. <ul>
<li>오디널 쓰면 안되는 이유는 앞에서 많이 다뤘으므로 생략</li>
</ul>
</li>
</ul>
<pre><code class="language-java">@Column(name = &quot;item_sell_status&quot;, nullable = false)
@Enumerated(EnumType.STRING)
private SellStatus sellStatus;</code></pre>
<pre><code class="language-java">public enum SellStatus {
    ON_SALE(1),
    OUT_OF_STOCK(2),
    SUSPENDED(5),
    END_OF_LINE(9);

    private int sellStatus;

    SellStatus(int i) {
        sellStatus = i;
    }
}</code></pre>
<ul>
<li>이런식으로 쓰고 있습니다</li>
</ul>
<pre><code>@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Role role;</code></pre><pre><code>@Getter
@RequiredArgsConstructor
public enum Role {
    GUEST(&quot;ROLE_GUEST&quot;, &quot;손님&quot;),
    USER(&quot;ROLE_USER&quot;, &quot;일반사용자&quot;),
    ADMIN(&quot;ROLE_ADMIN&quot;,&quot;관리자&quot;);

    private final String Key; //스프링 시큐리티에서는 권한 코드에 항상 ROLE_가 앞에 붙어있어야 함
    private final String title;
}</code></pre>]]></description>
        </item>
    </channel>
</rss>