<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>noh_hyuk.log</title>
        <link>https://velog.io/</link>
        <description>백엔드 개발자입니다.</description>
        <lastBuildDate>Mon, 25 Sep 2023 05:10:59 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>noh_hyuk.log</title>
            <url>https://velog.velcdn.com/images/noh_hyuk/profile/108830fe-bef4-4d0c-94bb-a61b6b24c3bc/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. noh_hyuk.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/noh_hyuk" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[SQL vs NoSQL]]></title>
            <link>https://velog.io/@noh_hyuk/SQL-vs-NoSQL</link>
            <guid>https://velog.io/@noh_hyuk/SQL-vs-NoSQL</guid>
            <pubDate>Mon, 25 Sep 2023 05:10:59 GMT</pubDate>
            <description><![CDATA[<h1 id="sql-관계형-db">SQL (관계형 DB)</h1>
<p>관계형 데이터베이스에는 핵심 특징 2가지 있습니다.</p>
<ul>
<li>데이터는 정해진 데이터 스키마에 따라 테이블에 저장된다.</li>
<li>데이터는 관계를 통해 여러 테이블에 분산된다.</li>
</ul>
<p>각 테이블마다 명확하게 정의된 구조가 있습니다. 해당 구조는 필드의 이름과 데이터 유형으로 정의 됩니다.</p>
<p>따라서 스키마를 준수하지 않은 레코드는 테이블에 추가할 수 없다. 즉, 스키마를 수정하지 않는 이상 정해진 구조에 맞는 레코드만 추가가 가능한 것이 관계형 데이터베이스의 특징 중 하나다.</p>
<p>또한, 데이터의 중복을 피하기 위해 ‘관계’를 이용한다.
<img src="https://velog.velcdn.com/images/noh_hyuk/post/be50d47f-192a-45e2-a680-790ad026da1d/image.png" alt=""></p>
<p>하나의 테이블에서 중복 없이 하나의 데이터만을 관리하기 때문에 다른 테이블에서 부정확한 데이터를 다룰 위험이 없어지는 장점이 있다.</p>
<h1 id="nosql-비관계형-db">NoSQL (비관계형 DB)</h1>
<p>한마디로 관계형 DB의 반대이다</p>
<p><strong>스키마도 관계도 존재하지 않는다</strong></p>
<p>SQL과 핵심적인 차이가 있는데, SQL은 정해진 스키마를 따르지 않으면 데이터 추가가 불가능하지만, NoSQL에서는 다른 구조의 데이터를 같은 컬렉션에 추가가 가능하다.</p>
<p>NoSQL에서는 레코드를 문서(documents)라고 부른다.</p>
<p>문서(documents)는 Json과 비슷한 형태로 구성되어 있다. 관계형 데이터베이스 처럼 여러 테이블에 나누어 담지 않고, 관련 데이터를 동일한 ‘컬렉션’에 넣는다.</p>
<p>따라서 여러 테이블에 조인할 필요없이 이미 필요한 모든 것을 갖춘 문서를 작성하는 것이 NoSQL이다.</p>
<h2 id="확장-개념">확장 개념</h2>
<p>두 데이터베이스를 비교할 때 중요한 Scaling 개념도 존재한다.</p>
<p>데이터베이스 서버의 확장성은 ‘수직성’ 확장과 ‘수평적’ 확장으로 나누어진다.</p>
<ul>
<li><p>*<em>수직적 확장 *</em>: 단순히 데이터베이스 서버의 성능을 향상시키는 것 (ex. CPU 업그레이드)</p>
</li>
<li><p>*<em>수평적 확장 *</em>: 더 많은 서버가 추가되고 데이터베이스가 전체적으로 분산됨을 의미 (하나의 데이터베이스에서 작동하지만 여러 호스트에서 작동)</p>
</li>
</ul>
<p>데이터 저장 방식으로 인해 SQL 데이터베이스는 일반적으로 수직적 확장만 지원함</p>
<p>수평적 확장은 NoSQL 데이터베이스에서만 가능</p>
<h2 id="그럼-어떤-걸-선택해야-할까">그럼 어떤 걸 선택해야 할까?</h2>
<p>둘다 훌륭한 솔루션이고 어떤 데이터를 다루느냐에 따라 선택을 고려해야한다.</p>
<h3 id="sql-장점">SQL 장점</h3>
<ul>
<li>명확하게 정의된 스키마, 데이터 무결성 보장</li>
<li>관계는 각 데이터를 중복없이 한 번만 저장</li>
</ul>
<h3 id="sql-단점">SQL 단점</h3>
<ul>
<li>유연하지 않음. 데이터 스키마를 사전에 계획하고 알려야 함</li>
<li>관계를 맺고 있어서 조인문이 많은 복잡한 쿼리가 만들어질 수 있음</li>
<li>대체로 수직적 확장만 가능함</li>
</ul>
<h3 id="nosql-장점">NoSQL 장점</h3>
<ul>
<li>스키마가 존재하지 않아 유연함.</li>
<li>데이터는 애플리케이션이 필요로 하는 형식으로 저장되어 데이터 읽어오는 속도가 빠름</li>
<li>수직 및 수평 확장이 가능함</li>
</ul>
<h3 id="nosql-단점">NoSQL 단점</h3>
<ul>
<li>데이터 중복을 계속 업데이트 해야 함</li>
<li>데이터가 여러 컬렉션에 중복되어 있기 때문에 수정 시 모든 컬렉션에서 수정해야 함</li>
</ul>
<h2 id="sql-데이터베이스-사용이-더-좋을-때">SQL 데이터베이스 사용이 더 좋을 때</h2>
<ul>
<li>관계를 맺고 있는 데이터가 자주 변경되는 애플리케이션의 경우</li>
<li>변경될 여지가 없고, 명확한 스키마가 사용자와 데이터에게 중요한 경우</li>
</ul>
<h2 id="nosql-데이터베이스-사용이-더-좋을-때">NoSQL 데이터베이스 사용이 더 좋을 때</h2>
<ul>
<li>정확한 데이터 구조를 알 수 없거나 변경/확장 될 수 있는 경우</li>
<li>데이터베이스를 수평으로 확장해야 하는 경우 (많은 양의 데이터를 다뤄야 하는 경우)</li>
</ul>
<p>위와 같은 장단점들을 고려하여 더 좋은 데이터베이스를 사용하길 바랍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[What is Redis?]]></title>
            <link>https://velog.io/@noh_hyuk/What-is-Redis</link>
            <guid>https://velog.io/@noh_hyuk/What-is-Redis</guid>
            <pubDate>Thu, 21 Sep 2023 03:03:00 GMT</pubDate>
            <description><![CDATA[<h1 id="nosql">NoSQL?</h1>
<p>NoSQL(원래 의미: non SQL 또는 non relational)</p>
<p>데이터베이스는 전통적인 관계형 데이터베이스 보다 덜 제한적인 일관성 모델을 이용하는 데이터의 저장 및 검색을 위한 매커니즘을 제공한다.</p>
<p>NoSQL 데이터베이스는 단순 검색 및 추가 작업을 위한 매우 최적화된 키 값 저장 공간이다.</p>
<h1 id="redis란">Redis란?</h1>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/9f0821c0-b4fd-4e2e-88ff-24445c02d6b1/image.png" alt=""></p>
<p>Redis의 풀네임에서 알 수 있듯이 dictionary 구조, 한마디로 key-value 형태로 데이터를 저장하고 관리하는 서버를 말합니다.</p>
<h2 id="cache">Cache</h2>
<p>Cache란 나중에 요청할 결과를 미리 저장해둔 후 빠르게 서비스해 주는 것을 의미한다. 즉, 미리 결과를 저장하고 나중에 요청이 오면 그 요청에 대해서 DB 또는 API를 참조하지 않고 Cache를 접근하여 요청을 처리하는 기법이다. 이러한 cache가 나온 배경에는 파레토 법칙이 있다.</p>
<p>파레토 법칙이란 80%의 결과는 20%의 원인으로 인해 발생한다는 뜻이다.</p>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/030d2b86-639d-4151-97d3-b5f837fd0104/image.png" alt=""></p>
<h3 id="cachce-사용-구조">Cachce 사용 구조</h3>
<p><strong><code>look aside cache</code></strong></p>
<p>look aside cache은 cache에 접근하여 데이터가 존재하는지 판단하고 cache에 존재하지 않는다면 db, api를 호출합니다. 대부분 cache를 사용한 개발이 이 프로세스를 따르고 있습니다.</p>
<ol>
<li>캐시에 데이터 존재 유무 확인</li>
<li>데이터가 있다면 캐시의 데이터 사용</li>
<li>데이터가 없다면 캐시의 실제 DB 데이터 사용</li>
<li>DB에서 가져 온 데이터를 캐시에 저장</li>
</ol>
<p><strong><code>write back</code></strong></p>
<ol>
<li>모든 데이터를 캐시에 저장</li>
<li>캐시의 데이터를 일정 주기마다 DB에 한꺼번에 저장 (배치)</li>
<li>DB에 저장한 데이터를 캐시에서 제거</li>
</ol>
<p>write back은 주로 쓰기 작업이 굉장히 많아서, INSERT 쿼리를 일일이 날리지 않고 한꺼번에 배치 처리를 하기 위해 사용한다.</p>
<p>DB에서 디스크를 접근하는 횟수가 줄어들기 때문에 성능 향상을 기대할 수 있지만, DB에 데이터를 저장하기 전에 캐시 서버가 죽으면 데이터가 유실된다는 문제점이 있다.</p>
<h2 id="인메모리">인메모리?</h2>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/b60155fa-b9a0-4525-a671-3fbbccb1a2c2/image.png" alt=""></p>
<p><strong>장점</strong></p>
<ul>
<li>컴퓨터의 주기억장치인 RAM에 데이터를 올려서 사용한다.</li>
<li>RAM에 데이터를 저장하여 메모리 내부에서 처리가 되므로 데이터를 저장하고 조회할 때 하드디스크를 오고 가는 과정을 거치지 않아도 된다.</li>
<li>그래서 속도가 매우 빠르다.</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li>용량으로 인해 데이터 유실이 발생할 수 있다!</li>
</ul>
<p>Redis는 인메모리 방식이므로 서버의 메모리 용량을 초과하는 데이터를 redis에서 처리할 경우, 서버 자체에도 문제가 생기며, RAM의 특성인 휘발성에 따라 RAM에 저장되었던 레디스의 데이터들은 유실될 수 있습니다. 때문에 메인 DB로 사용하기에는 무리가 있습니다.</p>
<h2 id="redis-특징">Redis 특징</h2>
<ul>
<li>List, Set, Sorted Set, Hash 등과 같은 Collection을 지원함.</li>
<li>영속성을 지원하는 인 메모리 데이터 저장소</li>
<li>다양한 자료 구조를 지원함.</li>
<li>싱글 스레드 방식으로 인해 연산을 원자적으로 수행이 가능함.</li>
<li>읽기 성능 증대를 위한 서버 측 리플리케이션을 지원</li>
<li>쓰기 성능 증대를 위한 클라이언트 측 샤딩 지원</li>
<li>다양한 서비스에서 사용되며 검증된 기술</li>
</ul>
<h2 id="redis의-영속성">Redis의 영속성</h2>
<p>Redis는 영속성을 보장하기 위해 데이터를 디스크에 저장할 수 있다. 서버가 내려가더라도 디스크에 저장된 데이터를 읽어서 메모리에 로딩한다. 데이터를 디스크에 저장하는 방식은 크게 두 가지가 있다.</p>
<ul>
<li>RDB(Snapshotting) 방식<ul>
<li>순간적으로 메모리에 있는 내용 전체를 디스크에 옮겨 담는 방식</li>
</ul>
</li>
<li>AOF(Append On File) 방식<ul>
<li>Redis의 모든 write/update 연산 자체를 모두 log 파일에 기록하는 형태</li>
</ul>
</li>
</ul>
<h2 id="싱글-스레드를-사용하는-redis">싱글 스레드를 사용하는 Redis</h2>
<p>Redis는 싱글 스레드를 사용하므로 연산을 원자적으로 처리하여 Race Condition이 거의 발생하지 않는다. 예를 들어, 친구 리스트의 친구를 추가하는 연산을 시도해 보자. 아래와 같이 정상적인 상황에서는 유저 각각의 트랜잭션이 순서대로 잘 행해지고 있으므로 문제가 없다.
<img src="https://velog.velcdn.com/images/noh_hyuk/post/398d3688-d330-4ad0-9d50-9cf721777349/image.png" alt=""></p>
<p>그러나 동시에 친구 리스트에 B,C를 추가하면 어떨까?</p>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/a8da16a0-6a53-4601-ae4e-6ba667076ecc/image.png" alt=""></p>
<p>두 트랜잭션이 동일한 최종 상태인 A를 자신의 메모리로 읽어 들이고, 그 상태에서 각자 B 또는 C를 추가하게되면 최종 상태가 (A, B) 혹은 (A, C)가 된다. (A, B) 혹은 (A, C)라고 한 이유는 컨텍스트 스위칭에 따라 두 트랜잭션 중 누가 먼저 끝날 지 예측할 수 없기 때문이다.</p>
<p>이러한 Race Condition을 해결하기 위해 여러 가지 기법이 있지만, Redis는 싱글 스레드를 사용하므로 하나의 트랜잭션은 하나의 명령만 실행할 수 있으므로 다수의 Race Condiotion을 해결할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쓰레기 Collection에 대해 알아보자(1)]]></title>
            <link>https://velog.io/@noh_hyuk/%EC%93%B0%EB%A0%88%EA%B8%B0-Collection%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%901</link>
            <guid>https://velog.io/@noh_hyuk/%EC%93%B0%EB%A0%88%EA%B8%B0-Collection%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%901</guid>
            <pubDate>Wed, 19 Jul 2023 08:31:55 GMT</pubDate>
            <description><![CDATA[<h2 id="garbage-collection-이란">Garbage Collection 이란?</h2>
<p>개발 하다 보면 <strong>유효하지 않은 메모리</strong>인 가비지(Garbage)가 발생하게 된다. Java나 Kotlin을 이용해 개발을 하다 보면 개발자가 메로리를 직접 해제해주는 일이 없다. 그 이유는 J<strong>VM의 가비지 컬렉터가 불필요한 메모리를 알아서 정리해주기</strong> 때문이다. 대신 명시적으로 불필요한 데이터를 표현하기 위해서 일반적으로 null을 선언해준다.</p>
<pre><code class="language-java">Student student = new Student();
student.setName(&quot;nohhyuk&quot;);

Student student = new Student();
student.setName(&quot;ParkNokhhyuk&quot;);</code></pre>
<p>기존의 nohhuk으로 생성된 student 객체는 더이상 사용이 되지 않아서 Garbage가 되었다. 이러한 메모리 누수를 방지하기 위해 Garbage Collector가 <strong>주기적으로 검사하여 메모리를 청소</strong>해준다.</p>
<h2 id="minor-gc--major-gc">Minor GC &amp; Major GC</h2>
<p>JVM의 Heap영역은 처음 설계될 때 2가지를 전제(Weak Generational Hypothesis)로 설계되었다.</p>
<ul>
<li><strong>대부분의 객체는 금방 접근 불가능한 상태(Unreachable)가 된다.</strong></li>
<li><strong>오래된 객체에서 새로운 객체로의 참조는 아주 적게 존재한다.</strong></li>
</ul>
<p>정리하자면, <strong>객체는 대부분 일회성되며, 메모리에 오랫동안 남아있는 경우는 드물다</strong>는 것이다. 그렇기 때문에 객체의 생존 기간에 따라 물리적인 Heap 영역을 나누게 되었고 Young, Old 총 2가지 영역으로 설계되었다.
<img src="https://velog.velcdn.com/images/noh_hyuk/post/117bb45c-889c-4c58-a10f-888310e710c2/image.png" alt=""></p>
<h3 id="young-영역">Young 영역</h3>
<ul>
<li>새롭게 생성된 객체가 할당(Allocation)되는 영역</li>
<li>대부분의 객체가 금방 Unreachable 상태가 되기 때문에, 많은 객체가 Young 영역에 생성되었다가 사라진다.</li>
<li>Young 영역에 대한 가비지 컬렉션(Garbage Collection)을 Minor GC라고 부른다.</li>
</ul>
<h3 id="old-영역">Old 영역</h3>
<ul>
<li>Young 영역에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역</li>
<li>Young 영역보다 크게 할당되며, 영역의 크기가 큰 만큼 가비지는 적게 발생한다.</li>
<li>Old 영역에 대한 가비지 컬렉션(Garbage Collection)을 Major GC라고 부른다.</li>
</ul>
<p>Old 영역이 Young 영여보다 크게 할당되는 이유는 <strong>Young 영역의 수명이 짧은 객체들은 큰 공간을 필요로 하지 않으며 큰 객체들은 바로 Old 영역에 할당</strong>되기 때문이다.</p>
<p>예외적인 상황으로 Old 영역에 있는 객체가 Young 영역의 객체를 참조하는 경우를 대비하여 Old 영역에는 512 bytes의 덩어리(Chunk)로 되어있는 카드 테이블(Card Table)이 존재한다.
<img src="https://velog.velcdn.com/images/noh_hyuk/post/cc6f54d3-e944-4574-b2cf-169a17dac970/image.png" alt=""></p>
<p>카드 테이블에는 <strong>Old 영역에 있는 객체가 Young 영역의 참조할 때 마다 그에 대한 정보가 표시</strong>된다. 카드 테이블이 도입된 이유는 <strong>Young 영역에서 가비지 컬렉션이 실행될 때 모든 Old 영역에 존재하는 객체를 검사하여 참조되지 않는 Young 영역의 객체를 식별하는 것이 비효율</strong>적이기 때문이다. 이러한 이유로 Y<strong>oung 영역에서 가비지 컬렉션이 진행될 때 카드 테이블만 조회하여 GC의 대상인지 식별</strong>할 수 있다.</p>
<h2 id="garbage-collection-동작-방식">Garbage Collection 동작 방식</h2>
<h3 id="stop-the-world">Stop The World</h3>
<p>Stop The World는 가비지 컬렉션을 실행하기 위해 JVM이 <strong>애플리케이션의 실행을 멈추는 작업</strong>이다. GC가 실행될 때는 <strong>GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업이 중단되고, GC가 완료되면 작업이 재개</strong>된다.</p>
<h3 id="mark-and-sweep">Mark and Sweep</h3>
<ul>
<li>Mark: <strong>사용되는 메모리와 사용되지 않는 메모리를 식별</strong>하는 작업</li>
<li>Sweep: <strong>Mark 단계에서 사용되지 않음으로 식별된 메모리를 해제</strong>하는 작업</li>
</ul>
<p>Stop The World를 통해 모든 작업을 중단시키면, GC는 스택의 모든 변수 또는 Reachable 객체를 스캔하면서 각각이 어떤 객체를 참고하고 있는지를 탐색하게 된다. 그리고 <strong>사용되고 있는 메모리를 식별하는데, 이러한 과정을 Mark라고 한다. 이후에 Mark가 되지 않은 객체들을 메모리에서 제거하는데, 이러한 과정을 Swee</strong>p라고 한다.</p>
<p>이번 편에는 여기까지 알아보고 다음편에는 <strong>Minor GC의 동작 방식과 Major GC의 동작 방식</strong>을 알아보겠습니다.</p>
<p>다음시간에 봐요~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체 지향 4가지 특성]]></title>
            <link>https://velog.io/@noh_hyuk/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-4%EA%B0%80%EC%A7%80-%ED%8A%B9%EC%84%B1</link>
            <guid>https://velog.io/@noh_hyuk/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-4%EA%B0%80%EC%A7%80-%ED%8A%B9%EC%84%B1</guid>
            <pubDate>Fri, 14 Jul 2023 12:49:42 GMT</pubDate>
            <description><![CDATA[<h2 id="what-is-oop">What is OOP?</h2>
<p>OOP는 프로그래밍 패러다임 중 하나로, <strong>컴퓨터 프로그램을 객체들의 모임으로 바라보고 객체들 간의 상호작용으로 서술하는 방식</strong>입니다. 객체들의 유기적인 협력과 결합을 통해 좀 더 유지보수와 확장성이 용이한 프로그램을 만들 수 있습니다.</p>
<h2 id="4가지-특성">4가지 특성</h2>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/d36b2bd1-33fd-4f8a-a654-831e03016716/image.webp" alt=""></p>
<h2 id="객체obejct란">객체(Obejct)란?</h2>
<p><strong>“모든 실제하는 대상”을 객체 지향 프로그래밍 언어에서는 객체</strong>라고 부릅니다.
예를 들어 지금 여러분 옆에 있는 휴대폰, 물컵 등 여러 사물이나 사람들 모두를 하나의 객체라 볼 수 있습니다.</p>
<h3 id="1-캡슐화-encapsulation">1. 캡슐화 (Encapsulation)</h3>
<ul>
<li>특정 객체가 독립적으로 역할을 제대로 수행하기 위해 필요한 데이터와 기능을 하나로 묶은 것(모듈화의 의미)</li>
<li>데이터를 은닉하고 데이터를 접근하는 기능을 노출시키지 않는다는 의미이다.</li>
</ul>
<p><strong>접근 제어자</strong></p>
<pre><code>public - 모두가 접근 가능
protected - 클래스, 패키지, 상속
private - 클래스(본인만 접근 가능)</code></pre><h3 id="2-추상화-abstraction">2. 추상화 (Abstraction)</h3>
<ul>
<li>추상화란 객체들의 공통적인 특성(속성과 기능)을 뽑아내는 것이다.</li>
</ul>
<p>→ 객체들이 가진 공통적인 데이터와 기능을 도출해 내는 것을 의미한다.</p>
<h3 id="3-상속-inheritance">3. 상속 (Inheritance)</h3>
<ul>
<li>상위 개념의 특징을 하위 개념이 물려받는 것</li>
<li>같은 기능을 또 구현 할 필요가 없어서 코드의 재사용성을 늘릴 수 있다.</li>
<li>좀 더 폭넓게 사용 가능하다. 상속 받은 함수를 추가적으로 데이터와 함수 내용을 변경할 수 있다.</li>
</ul>
<h3 id="4-다형성-polymorphism">4. 다형성 (Polymorphism)</h3>
<ul>
<li>다형성이란 다양한 형태로 표현이 가능한 것을 의미한다.</li>
<li>극대화 하기 위해 추상클래스나 인터페이스를 이용한다.</li>
</ul>
<blockquote>
<p>‼️ <strong>오버라이딩</strong>(Overriding)</p>
</blockquote>
<ul>
<li>같은 메서드 이름 / 같은 인자 목록 / 상위 클래스의 메서드를 재정의 한다.</li>
<li>상위 클래스 타입의 객체 참조 변수에서 자동으로 하위 클래스가 오버라이딩한 메소드를 호출해 준다.</li>
</ul>
<blockquote>
</blockquote>
<p>‼️ <strong>오버로딩</strong>(Overroding)</p>
<ul>
<li>같은 메서드 이름 / 다른 인자 목록 / 다수의 메서드 중복 정의 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA? 제가 알려드릴게요]]></title>
            <link>https://velog.io/@noh_hyuk/JPA-%EC%A0%9C%EA%B0%80-%EC%95%8C%EB%A0%A4%EB%93%9C%EB%A6%B4%EA%B2%8C%EC%9A%94</link>
            <guid>https://velog.io/@noh_hyuk/JPA-%EC%A0%9C%EA%B0%80-%EC%95%8C%EB%A0%A4%EB%93%9C%EB%A6%B4%EA%B2%8C%EC%9A%94</guid>
            <pubDate>Thu, 13 Jul 2023 01:44:38 GMT</pubDate>
            <description><![CDATA[<h3 id="jpa-영속성-컨텍스트">JPA 영속성 컨텍스트</h3>
<hr>
<p><strong>Entity를 영구 저장하는 환경이라는</strong> 뜻으로, <strong>가상의 데이터베이스</strong> 같은 역할을 한다.</p>
<p>아래의 코드는 Entity를 영속성 컨텍스트에 저장하는 코드이며, 해당 코드는 DB에 저장이 안된 상태이다.(트랜잭션이 끝나야 DB에 반영을 한다)</p>
<pre><code class="language-java">entityManager.persist(member);</code></pre>
<h3 id="영속성-컨텍스트를-사용하는-이유">영속성 컨텍스트를 사용하는 이유</h3>
<hr>
<ul>
<li><strong>1차 캐시</strong></li>
<li><strong>동일성 보장</strong></li>
<li><strong>트랜잭션을 지원하는 쓰기 지연</strong></li>
<li><strong>변경 감지</strong></li>
</ul>
<h3 id="1차-캐시">1차 캐시</h3>
<hr>
<p>영속성 컨텍스트 내부에는 <strong>1차 캐시</strong>라는 공간이 있다. 캐시는 <strong>Map</strong>의 형태로 만들어지며 key는 id값, value로 이루어져 있고 해당 entity값이 들어 있다.
<img src="https://velog.velcdn.com/images/noh_hyuk/post/00ecb5b7-039a-40de-a70f-e39fc8fbe0f7/image.png" alt=""></p>
<pre><code class="language-java"> Member member = em.find(Member.class, &quot;member&quot;);</code></pre>
<ul>
<li><strong>1차 캐시에서 엔티티를 첫 번째</strong>로 찾는다. 해당 엔티티가 있을 경우 <strong>바로 반환</strong>한다.</li>
<li>1차 캐시에 <strong>없을 경우 데이터베이스</strong>에서 조회합니다. 조회한 데이터로 <strong>엔티티를 생성해 1차 캐시에 저장</strong>한다. 이후 엔티티를 반환한다.
<img src="https://velog.velcdn.com/images/noh_hyuk/post/bdf83a49-5105-4214-9136-fb583037866a/image.png" alt=""></li>
</ul>
<h3 id="동일성-보장">동일성 보장</h3>
<hr>
<p>엔티티의 동일성을 보장한다.</p>
<pre><code class="language-java">Member a = em.find(Member.class, &quot;member1&quot;);
Member b = em.find(Member.class, &quot;member2&quot;);
System.out.print(a==b) // true</code></pre>
<h3 id="쓰기-지연">쓰기 지연</h3>
<hr>
<p>Entity들을 1차 캐시에 저장할 때, <strong>저장할 뿐만 아니라 쓰기 지연 SQL 저장소</strong>라는 곳에 해당 SQL문도 함께 저장한다.</p>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/7f48ac6e-9610-4676-af3b-f248fc4b8a49/image.png" alt=""></p>
<ul>
<li>트랜잭션 커밋을 실행할 때, <strong>미리 저장해두었던 SQL문들을 한 번에 실행</strong>한다.</li>
</ul>
<h3 id="변경-감지">변경 감지</h3>
<hr>
<p>jpa에서 update를 할 때, 따로 update()나 modify()가 없다.</p>
<pre><code class="language-java">Member member = em.find(Memeber.class, &quot;member1&quot;);
member.setName(&quot;updateName&quot;);</code></pre>
<ul>
<li>find()로 member를 조회한 다음 data를 변경하기만 하면 이를 감지해, commit()할 때 update문이 알아서 생성되어 날아간다.</li>
</ul>
<p>이는 아래와 같은 흐름덕분에 가능하다.</p>
<ul>
<li>1차 캐시에는 <strong>스냅샷이라는 공간</strong>이 있다.<ul>
<li>snapshot: 1차(최초)로 객체가 들어왔을 시점을 저장</li>
<li>Entity: 실제 저장된 Entity</li>
</ul>
</li>
<li>flush()가 실행될 때, <strong>먼저 Entity와 스냅샷을 비교</strong>한다.</li>
<li>위의 코드에서는 Name을 변경했기에, Entity의 Name과 스냅샷의 Name이 다른것을 감지해 자동으로 Update문을 생성하는 것이다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/57261718-ae9b-40e1-b9af-19ffa198a309/image.png" alt=""></p>
<h2 id="엔티티의-생명주기">엔티티의 생명주기</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/3ec5f430-de26-479b-aab1-b836f1e4380e/image.png" alt=""></p>
<h3 id="1-비영속-newtransient">1) 비영속 (new/transient)</h3>
<ul>
<li>영속성 컨텍스트와 <strong>전혀 관계가 없는 새로운 상태</strong></li>
</ul>
<p>엔티티 객체를 생성했다. 지금은 순수한 객체 상태이며 아직 저장하지 않았다.</p>
<pre><code class="language-java">Member member = new Member();
member.setName(&quot;aa&quot;); // new/transient</code></pre>
<h3 id="2-영속-managed">2) 영속 (managed)</h3>
<ul>
<li>영속성 컨텍스트에 <strong>관리되는 상태</strong></li>
</ul>
<p>엔티티 매니저를 통해서 엔티티를 영속성 컨텍스트에 저장했다. 이렇게 영속성 컨텍스트가 관리하는 엔티티를 영속 상태라 한다.</p>
<pre><code class="language-java">em.persist(member); // 영속성 상태</code></pre>
<p><strong>객체가 영속 상태가 된다고 해서, 해당 시점에 DB 쿼리가 수행되지 않는다</strong>. 트랜잭션이 <strong>commit()</strong>되는 시점에 쿼리가 수행된다.</p>
<h3 id="3-준영속-detached">3) 준영속 (detached)</h3>
<ul>
<li>영속성 컨텍스트에 <strong>저장되었다가 분리된 상태</strong></li>
</ul>
<p>영속성 컨텍스트가 관리하던 영속 상태의 엔티티를 영속성 컨텍스트가 관리하지 않으면 준영속 상태가 된다.</p>
<pre><code class="language-java">em.detach(member); // 영속성을 지운다.
em.close(); // 영속성 컨텍스트를 닫는다.
em.clear(); // 영속성 컨텍스트를 초기화한다.</code></pre>
<h3 id="4-삭제-removed">4) 삭제 (removed)</h3>
<ul>
<li>영속성 컨텍스트에서 <strong>삭제된 상태</strong></li>
</ul>
<p>엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제한다.</p>
<pre><code class="language-java">em.remove(member); // 객체를 삭제한 상태</code></pre>
<h2 id="🥕-플러시flush">🥕 플러시(flush)</h2>
<hr>
<p>플러시는 <strong>영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.</strong></p>
<h3 id="flush-호출-시-일어나는-일">flush() 호출 시 일어나는 일</h3>
<ul>
<li>변경 감지가 동작해서 <strong>영속성 컨텍스트에 있는 모든 엔티티를 스냅샷과 비교해서 수정된 엔티티를 찾는다</strong>. 수정된 엔티티는 수정 쿼리를 만들어서 쓰기 지연 SQL 저장소에 등록된다.</li>
<li>저장된 쿼리를 데이터베이스에 전송한다.</li>
</ul>
<h2 id="🥕-병합-merge">🥕 병합: merge()</h2>
<hr>
<p><strong>준영속 상태의 엔티티를 다시 영속 상태로 변경하려면 병합을 사용하면 된다.</strong></p>
<pre><code class="language-java">Member mergeMember = em.merge(member);</code></pre>
<p>merge()는 파라미터로 넘어온 준영속 상태의 엔티티를 사용해서 새롭게 병합된 영속 상태의 엔티티를 반환한다.</p>
<p>식별자 값으로 엔티티를 조회할 수 있으면 불러서 병합하고 조회할 수 없으면 새로 생성해서 병합한다. 따라서 병합은 save or update 기능을 수행한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스크럼 방식을 채택하였습니다.]]></title>
            <link>https://velog.io/@noh_hyuk/%EC%8A%A4%ED%81%AC%EB%9F%BC-%EB%B0%A9%EC%8B%9D-%EC%B1%84%ED%83%9D%ED%95%98%EC%98%80%EC%8A%B5%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@noh_hyuk/%EC%8A%A4%ED%81%AC%EB%9F%BC-%EB%B0%A9%EC%8B%9D-%EC%B1%84%ED%83%9D%ED%95%98%EC%98%80%EC%8A%B5%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Wed, 12 Jul 2023 01:59:09 GMT</pubDate>
            <description><![CDATA[<p>먼저 스크럼을 알아보기 전에 애자일(Agile)이 무엇인지 알아보자</p>
<h2 id="애자일agile">애자일(Agile)</h2>
<blockquote>
<p>변화에 기민하면서 효율적으로 대응하응 <strong>다양한 개발방법론</strong>을 의미하는 것으로 주로 소프트웨어 기업에서 사용하는 <strong>프로그래밍 기법</strong>
<img src="https://velog.velcdn.com/images/noh_hyuk/post/1b7298db-3517-4590-a7cf-8c5ae7890dae/image.png" alt=""></p>
</blockquote>
<p>애자일의 다양한 개발방법론 중 가장 대표적인게 뭘까요?</p>
<p>바로 <strong>스크럼(Scrum)</strong>입니다.</p>
<h2 id="스크럼scrum">스크럼(Scrum)</h2>
<blockquote>
<p>비지니스 요구를 충족시키는데 초점을 맞추기 위해 <strong>작은 목푶를 짧은 주기로 점진적이며 경험적으로 제품을 지속적으로 개발</strong>하는 관리 프레임워크이다.
<img src="https://velog.velcdn.com/images/noh_hyuk/post/1c7c18bb-db34-4cae-b068-96c35fb18dea/image.png" alt=""></p>
</blockquote>
<p>스크럼은 <strong>스프린트로 개발 및 검토를 하며 빠르고 효율적인 협업 방법</strong>이다.</p>
<h2 id="스프린트sprint">스프린트(Sprint)</h2>
<blockquote>
<p><strong>한 달 또는 그보다 짧은 기간 팀이 목표롤 정해놓은 일</strong>을 하는 것을 말한다.
<img src="https://velog.velcdn.com/images/noh_hyuk/post/b20f9ff9-625a-4a72-9069-4fb5d3393683/image.png" alt=""></p>
</blockquote>
<p>여기까지 각각의 개념을 알아보았다. </p>
<p>이제 스크럼 방식을 채택하기 전과 후의 느낀 점을 알려주겠다.</p>
<h2 id="🔕-before">🔕 Before</h2>
<p>스크럼 방식을 사용하기 전에는 </p>
<p>팀원들끼리의 회의가 뜸하다 보니 <strong>커뮤니케이션이 많이 부족</strong>했고</p>
<p><strong>목표가 뚜렷하지 않아서</strong> 프로젝트 진행에 있어 진도가 빨리 나가지 않는 느낌이었다.</p>
<h2 id="🔔-after">🔔 After</h2>
<p>스크럼 방식을 사용하니</p>
<p>1주일마다 회의를 하며 각자 해야 할 목표를 정했다.</p>
<p>정해진 주기마다 회의하니 <strong>많은 커뮤니케이션을 할 수 있는 기회를 얻어 서로 피드백하는 시간</strong>을 가질 수 있었다. </p>
<p>그리고 정해진 기간 안에 해야 하는 <strong>목표가 뚜렷하니 프로젝트 진행에 있어 더 빨라</strong>진 느낌이 들었다.</p>
<h2 id="🥱-마치며">🥱 마치며</h2>
<p>처음으로 스크럼 방식을 프로젝트에 채택하여 프로젝트를 진행해 보았는데</p>
<p><strong>많은 이점</strong>들이 있으니 한 번쯤은 프로젝트를 할 때 스크럼 방식을 채택하여 프로젝트를 진행해 보는 걸 추천한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[OSI 7계층, TCP/IP 4계층]]></title>
            <link>https://velog.io/@noh_hyuk/OSI-7%EA%B3%84%EC%B8%B5-TCPIP-4%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@noh_hyuk/OSI-7%EA%B3%84%EC%B8%B5-TCPIP-4%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Tue, 11 Jul 2023 06:03:37 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>💡 OSI 7계층은 네트워크에서 <strong>통신이 일어나는 과정</strong>을 7단계로 나눈 것을 말한다.</p>
</blockquote>
<h2 id="왜-7계층으로-나눈걸까">왜 7계층으로 나눈걸까?</h2>
<ul>
<li>통신이 일어나는 과정을 단계별로 파악할 수 있기 때문이다.</li>
<li>특정한 곳에 이상이 생기면 다른 단계의 장비 및 소프트웨어를 건들이지 않고도 이상이 생긴 단계만 고칠 수 있기 때문이다.</li>
</ul>
<h3 id="예시">예시</h3>
<pre><code>명직(사람 이름)이가 PC방에서 롤을 하고 있는데 연결이 끊겼다.

먼저 어디에 문제가 있는지 확인하다.

모든 PC가 문제가 있다면
라우터의 문제(3계층 네트워크 계층)이거나 회선 문제(1계층 물리 계층)

만약 한 PC만 문제가 있다면
롤의 소프트웨어에 문제(7계층 어플리케이션 계층)</code></pre><h2 id="osi-7계층-단계">OSI 7계층 단계</h2>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/d9e037dc-2ddd-4ff5-b4da-22fce7019dff/image.jpg" alt=""></p>
<h3 id="1계층---물리계층-physical-layer">1계층 - 물리계층 (Physical Layer)</h3>
<ul>
<li>전기적, 기계적, 기능적인 특성을 이용해서 통신 케이블로 데이터를 전송하는 <strong>물리적인 장비</strong></li>
<li>통신 단위는 비트</li>
<li>단지 데이터를 전달만 할뿐 전송 하려는(또는 받으려는) <strong>데이터가 무엇인지, 어떤 에러가 있는지 등에는 전혀 신경 쓰지 않는다.</strong></li>
<li>대표적인 장비는 통신 케이블, 리피터 허브등이 있다.</li>
</ul>
<hr>
<h3 id="2계층---데이터-링크계층-datalink-layer">2계층 - 데이터 링크계층 (DataLink Layer)</h3>
<ul>
<li>1계층(물리계층)을 통해 송수신 되는 정보의 오류와 흐름을 관리하여 안전한 정보의 전달을 수행할 수 있도록 도와주는 역할을 한다.</li>
<li>프레임에 물리적 <strong>주소(MAC address)</strong>를 부여하고 에러검출, 재전송, 흐름제어를 수행</li>
<li>전송 단위는 프레임(Frame)</li>
<li>대표적인 장비는 브리지, 스위치, 이더넷 등(여기서 MAC주소를 사용)</li>
</ul>
<p><strong>→ 브릿지나 스위치를 통해 맥주소를 가지고 물리계층에서 받은 정보를 전달함</strong></p>
<p>데이터 링크 계층은 포인트 투 포인트(Point to Point) 간 신뢰성있는 전송을 보장하기 위한 계층으로 CRC 기반의 오류 제어와 흐름 제어가 필요하다.
네트워크 위의 개체들 간 데이터를 전달하고, 물리 계층에서 발생할 수 있는 오류를 찾아 내고, 수정하는 데 필요한 기능적, 절차적 수단을 제공한다. </p>
<p>-&gt; <strong>프레임에 주소부여(MAC - 물리적주소), 에러검출/재전송/흐름제어</strong></p>
<hr>
<h3 id="3계층---네트워크-계층-network-layer">3계층 - 네트워크 계층 (Network Layer)</h3>
<ul>
<li><strong>데이터를 목적까지 가장 안전하고 빠르게 전달하는 기능(라우팅)</strong>이다.</li>
<li>라우터(Router)를 통해 경로를 선택하고 주소를 정하고(IP) 경로(Route)에 따라 패킷을 전달 → IP 헤더 붙음</li>
<li>전송 단위는 패킷(Packet)</li>
<li>대표적인 장비는 라우터(Router)</li>
</ul>
<p>네트워크 계층은 라우팅, 흐름 제어, 세그멘테이션(segmentation/desegmentation), 오류 제어, 인터네트워킹(Internetworking) 등을 수행한다.
데이터를 연결하는 다른 네트워크를 통해 전달함으로써 인터넷이 가능하게 만드는 계층이다.</p>
<p><strong>→ 주소부여(IP), 경로설정(Route)</strong></p>
<hr>
<h3 id="4계층---전송-계층-transport-layer">4계층 - 전송 계층 (Transport Layer)</h3>
<ul>
<li>port 번호, 전송방식(TCP/UDP) 결정 → TCP 헤더 붙음<ul>
<li>TCP: 신뢰성, 연결지향적</li>
<li>UDP: 비신뢰성, 비연결성, 실시간</li>
</ul>
</li>
<li>두 지점간의 <strong>신뢰성</strong> 있는 데이터를 주고 받게 해주는 역할</li>
<li>신호를 분산하고 다시 합치는 과정을 통해서 에러와 경로를 제어</li>
</ul>
<p><strong>연결 기반(connection oriented)</strong>이다. 이는 <strong>전송 계층이 패킷들의 전송이 유효한지 확인하고 전송 실패한 패킷들을 다시 전송한다는 것</strong>을 뜻한다.</p>
<p>→ <strong>패킷 생성(Assembly/Sequencing/Deassembly/Error detection/Request repeat/Flow control) 및 전송</strong></p>
<hr>
<h3 id="5계층---세션-계층-session-layer">5계층 - 세션 계층 (Session Layer)</h3>
<ul>
<li>데이터가 통신하기 위한 논리적인 연결을 말한다.</li>
<li>세션 설정, 유지, 종료, 전송 중단시 복구 등의 기능이 있다.</li>
<li>동시 송수신 방식(duplex), 반이중 방식(half-duplex), 전이중 방식(Full Duplex)의 통신과 함께, 체크 포인팅과 유휴, 종료, 다시 시작 과정 등을 수행한다.</li>
<li><strong>TCP/IP 세션을 만들고 없애는 책임</strong>을 진다.</li>
</ul>
<p>→ 통신하는 사용자들을 동기화하고 오류복구 명령들을 일괄적으로 다룬다. 통신을 하기 위한 세션을 확립/유지/중단 (운영체제가 해줌)</p>
<hr>
<h3 id="6계층---표현-계층-presentation-layer">6계층 - 표현 계층 (Presentation Layer)</h3>
<ul>
<li>전송하는 데이터의 표현방식을 결정(ex. 데이터변환, 압축, 암호화 등)</li>
<li>파일인코딩, 명령어를 포장, 압축, 암호화</li>
</ul>
<p>표현 계층(Presentation layer)은 코드 간의 번역을 담당하여 사용자 시스템에서 데이터의 형식상 차이를 다루는 부담을 응용 계층으로부터 덜어 준다.</p>
<p>→ 사용자의 명령어를 완성 및 결과 표현</p>
<hr>
<h3 id="7계층---응용-계층-application-layer">7계층 - 응용 계층 (Application Layer)</h3>
<ul>
<li>최종 목적지로, 응용 프로세스와 직접 관계하여 일반적인 응용 서비스를 수행한다.(ex. chorome 등)</li>
<li>HTTP, FTP, SMTP, POP3, IMAP 등과 같은 프로토콜이 있다.</li>
</ul>
<p>→ 네트워크 소프트웨어 UI 부분, 사용자의 입축력(I/O) 부분</p>
<hr>
<h1 id="tcpip-4계층이란">TCP/IP 4계층이란?</h1>
<blockquote>
</blockquote>
<p>💡 <strong>OSI 7계층 보다 먼저 나온 규격이나 현재 더 많이 활용된다.</strong></p>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/3172b4d9-384f-4748-b521-8822a4c0e0b4/image.webp" alt=""></p>
<h3 id="1계층---네트워크-액세스-계층-network-access-layer">1계층 - 네트워크 액세스 계층 (Network Access Layer)</h3>
<ul>
<li>OSI 7계층의 <strong>물리 계층과 데이터 링크 계층</strong>에 해당한다</li>
<li>TCP/IP 패킷을 네트워크 매체로 전달하는 것과 네트워크 매체에서 TCP/IP 패킷을 받아들이는 과정을 담당한다</li>
<li>물리적인 주소로 MAC을 사용한다</li>
<li>LAN, 패킷망 등에 사용한다</li>
</ul>
<hr>
<h3 id="2계층---인터넷-계층-internet-layer">2계층 - 인터넷 계층 (Internet Layer)</h3>
<ul>
<li>OSI 7계층의 <strong>네트워크 계층</strong>에 해당한다</li>
<li>어드레싱(addressing), 패키징(packaging), 라우팅(routing) 기능을 제공한다</li>
<li>네트워크상 최종 목적지까지 정확하게 연결되도록 연결성을 제공하게 된다</li>
<li>프로토콜 종류 - IP, ARP, RARP</li>
</ul>
<hr>
<h3 id="3계층---전송-계층-transport-layer">3계층 - 전송 계층 (Transport Layer)</h3>
<ul>
<li>OSI 7계층의 <strong>전송 계층</strong>에 해당한다</li>
<li>IP와 Port를 이용하여 프로세스와 통신한다</li>
<li>통신 노드 간의 연결을 제어하고, <strong>신뢰성</strong> 있는 데이터 전송을 담당한다.</li>
<li>프로토콜 종류 – TCP, UDP</li>
</ul>
<hr>
<h3 id="4계층---응용-계층-application-layer">4계층 - 응용 계층 (Application Layer)</h3>
<ul>
<li>OSI 7계층의 <strong>세션 계층, 표현 계층, 응용 계층</strong>에 해당한다.</li>
<li>다른 계층의 서비스에 접근할 수 있게 하는 애플리케이션을 제공</li>
<li>애플리케이션들이 데이터를 교환하기 위해 사용하는 프로토콜을 정의</li>
<li>HTTP, SMTP 등의 프로토콜이 있다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA 기본 메서드 vs JPQL]]></title>
            <link>https://velog.io/@noh_hyuk/%EB%A7%9E%EC%A7%B1-%EB%9C%A8%EC%9E%90-%EB%8B%A4-%EB%93%A4%EC%96%B4%EC%99%80</link>
            <guid>https://velog.io/@noh_hyuk/%EB%A7%9E%EC%A7%B1-%EB%9C%A8%EC%9E%90-%EB%8B%A4-%EB%93%A4%EC%96%B4%EC%99%80</guid>
            <pubDate>Mon, 10 Jul 2023 09:23:54 GMT</pubDate>
            <description><![CDATA[<h1 id="👊-jpa-기본-메서드-vs-jpql">👊 JPA 기본 메서드 vs JPQL</h1>
<p>이번 글에서는 JPA에서 기본으로 제공하는 메서드와 JPQL 둘 중 <strong>어떤 상황에서 무엇이 더 효율</strong>적인지 알아보겠습니다.</p>
<blockquote>
<p>먼저 JPA의 기본 메서드들을 알아보겠습니다.</p>
</blockquote>
<pre><code class="language-java">findAll()
findById(id)
save(member)
saveAll(memberList)
delete(member)
deleteAll(memberList)
count()
exists(id)
flush()</code></pre>
<p>JPA는 위와 같이 기본적으로 제공하는 메서드들이 있습니다. </p>
<blockquote>
<p>다음으로 Native Query를 사용한 코드를 보겠습니다.
<img src="https://velog.velcdn.com/images/noh_hyuk/post/64db493a-817a-41a0-a9da-71fa09aa57ee/image.png" alt=""></p>
</blockquote>
<p>JPQL은 직접 쿼리 문을 작성하여 조금 더 복잡한 쿼리 문을 수행할 수 있습니다. 위의 코드는 실제 사용헀던 코드로 post를 투표 수 역순으로 정렬해주는 메서드입니다.</p>
<h2 id="💪-장단점">💪 장단점</h2>
<h3 id="jpa-기본-메서드들의-장단점">JPA 기본 메서드들의 장단점</h3>
<p><strong>장점</strong></p>
<ul>
<li>사용하기 <strong>쉽고 간편</strong>하며, <strong>반복적인 CRUD</strong> 작업을 간단히 처리할 수 있습니다.</li>
<li>데이터베이스 종속성을 줄여주고, 개발자는 데이터베이스에 <strong>직접 접근하지 않</strong>고도 데이터 조작이 가능합니다.</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li>단순한 CRUD 작업 이상의 <strong>복잡한 쿼리를 처리하기 어렵</strong>습니다.</li>
<li>유연한 쿼리 작성이 어렵기 때문에 성능 최적화나 고급 기능을 구현하기에는 한계가 있습니다.</li>
</ul>
<h3 id="jpql의-장단점">JPQL의 장단점</h3>
<p><strong>장점</strong></p>
<ul>
<li>객체 지향적인 쿼리 언어로, 엔티티와 관련된 <strong>복잡한 쿼리</strong>를 작성할 수 있습니다.</li>
<li>엔티티 간의 관계를 쉽게 다룰 수 있으며, 객체 지향적인 특징을 활용하여 쿼리를 작성할 수 있습니다.</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li>JPQL은 SQL을 추상화한 언어이기 때문에 일부 기능이 SQL에 비해 <strong>제한적</strong>일 수 있습니다.</li>
<li>복잡한 쿼리를 작성할 경우 <strong>성능 이슈</strong>가 발생할 수 있으며, 적절한 인덱스나 최적화 기법을 적용해야 합니다.</li>
</ul>
<h2 id="👇-어떤-상황에-무엇을-사용하는게-더-좋을까">👇 어떤 상황에 무엇을 사용하는게 더 좋을까??</h2>
<p><strong>먼저 JPA는</strong></p>
<ul>
<li>간단한 CRUD</li>
</ul>
<p>이게 JPA 기본 메서드의 핵심이자 꽃이죠.</p>
<p><strong>JPQL은</strong></p>
<ul>
<li>복잡한 쿼리 문이 필요할 때</li>
<li>객체지향적인 특징을 활요해야 할 때</li>
<li>집계 함수나 그룹화 작업을 수행해야 할 때</li>
</ul>
<p>위와 같은 상황에서 이점을 얻을 수 있는게 바로 JPQL입니다.</p>
<p>이렇게 보면 JPQL이 정말 좋아보이지만 <strong>JPQL을 자주 사용하지 않는 개발자들은 쿼리 문을 이해하지 못할 수 있고 가독성 부분에서 떨어질 수</strong> 있습니다. 이러한 점들도 있으니 주의하고 잘 선택하여 사용해주시길 바랍니다.</p>
<h2 id="끝내며">끝내며</h2>
<p>JPA와 JPQL을 비교해가며 어떤 상황에 무엇을 사용하는게 더 좋을지 설명해보았는데
신중히 선택하여 좋은 코드를 작성하시길 바랍니다.
끝까지 읽어주셔서 감사합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[초이스 출시 회고록 (많관부)]]></title>
            <link>https://velog.io/@noh_hyuk/%EC%B4%88%EC%9D%B4%EC%8A%A4-%EC%B6%9C%EC%8B%9C-%ED%9A%8C%EA%B3%A0%EB%A1%9D-%EB%A7%8E%EA%B4%80%EB%B6%80</link>
            <guid>https://velog.io/@noh_hyuk/%EC%B4%88%EC%9D%B4%EC%8A%A4-%EC%B6%9C%EC%8B%9C-%ED%9A%8C%EA%B3%A0%EB%A1%9D-%EB%A7%8E%EA%B4%80%EB%B6%80</guid>
            <pubDate>Mon, 10 Jul 2023 01:48:37 GMT</pubDate>
            <description><![CDATA[<p>나는 현재 광주소프트웨어마이스터고에 재학중인 학생이다.
친구들끼리 프로젝트를 진행하다가 어찌저찌 하다보니 출시까지 하게 되었다.
나의 역할은 백엔드 개발이다. 백엔드 개발 인원은 나 혼자였다.
혼자 개발하다보니 이슈가 발생할때 마다 모든걸 혼자 처리해야하니깐 정말 힘들었다 ㅠㅠ</p>
<p>일단 초이스라는 앱은 👇
<img src="https://velog.velcdn.com/images/noh_hyuk/post/251761dd-3acb-4d34-82f9-157b8b19800d/image.png" alt=""></p>
<blockquote>
<p>💡 ‘Choice’ 는 일상 속 선택의 순간을 더욱 즐겁게 보낼 수 있도록 개발한 서비스입니다.</p>
</blockquote>
<p>처음에 App Store에 검사를 맡았을 때 당연히 빠꾸를 먹었다. sns서비스인데 <strong>게시물 신고기능과 유저 차단 기능</strong>이 없어서 빠꾸를 먹은것이였다. 여러분들은 출시까지 하고 싶으면 위와같은 기능을 추가하여 개발하면 더 좋을것이다. 기능을 추가한 뒤 재검사를 맡았는데 드디어 통과하였다!!
<img src="https://velog.velcdn.com/images/noh_hyuk/post/6548da3e-274f-4b6c-b59d-5785f6d34f96/image.png" alt=""></p>
<p>출시하기전까진 느끼지 못했지만 출시하고나니 정말 후련했다. 그리고 AppStore에 ‘<strong>초이스</strong>’라고 치니 바로 나오니깐 정말 뿌듯했다 ㅎㅎ</p>
<p>처음 앱 출시 후 학교에 홍보하게 되었는데 많은 학생들이 초이스 앱을 사용해주었다. 홍보 전 당시 사용자가 30명밖에 되지 않았는데 어느새 사용자가 100명 가까이 되었다.
<img src="https://velog.velcdn.com/images/noh_hyuk/post/9ab0b2b3-e597-40b4-a210-da1f5cd8ecd2/image.png" alt=""></p>
<p>출시하고 나니깐 드는 생각인데 <strong>초반 설계와 기획하는 단계에서 절대 대충하지 말고 제대로</strong>하고 넘어가길 바란다.</p>
<p>그리고 뭐든 <strong>혼자하는 것보다 여러명이서</strong> 하길 바란다.</p>
<p>초이스의 아쉬운 점과 보완할 점이 아직 많다고 생각이 든다.</p>
<p>초이스라는 앱은 정말 좋은 아이디어라고 생각이 든다. 하지만 UI적으로 아직 개선할 점이 많다고 생각이 든다. 프로젝트를 진행할 때 디자이너가 디자인한게 아니라 IOS 개발자가 디자인을 하여 많은 아쉬움이 있다. 하지만 그때 상황이 어쩔 수 없는 상황이라 마음 한 구석에 아쉬움만 남긴채로 넘어가기로 했다.</p>
<p><strong>많은 관심 부탁드립니다 ㅎㅎ</strong>
<a href="https://choice-time.com/">WebSite주소</a>
<a href="https://apps.apple.com/app/%EC%B4%88%EC%9D%B4%EC%8A%A4/id6449449610">AppStore주소</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[@Scheduled 사용해 보았다.]]></title>
            <link>https://velog.io/@noh_hyuk/Scheduled-%EC%82%AC%EC%9A%A9%ED%95%B4-%EB%B3%B4%EC%95%98%EB%8B%A4</link>
            <guid>https://velog.io/@noh_hyuk/Scheduled-%EC%82%AC%EC%9A%A9%ED%95%B4-%EB%B3%B4%EC%95%98%EB%8B%A4</guid>
            <pubDate>Fri, 07 Jul 2023 01:41:28 GMT</pubDate>
            <description><![CDATA[<h2 id="스케줄러란">스케줄러란?</h2>
<blockquote>
<p>일정한 시간간격 또는 일정한 시각에 특정 로직을 돌리기 위해 사용하는 것을 Scheduler라고 한다.</p>
</blockquote>
<p>프로젝트를 하는 도중 매일 자정이 될때마다 초기화 해야되는 기능이 있었다. ‘이것을 어떻게 해결하지?’ 라고 생각하던 와중에 Scheduler라는 것을 발견하게 되었다.</p>
<h2 id="scheduled-기본-사용법">@Scheduled 기본 사용법</h2>
<p>아래와 같이 Application 클래스에서 EnableScheduling 어노테이션을 사용하여 스케줄링 기능을 사용할 수 있는 상태로 만들어 준다.</p>
<p><strong>실제 프로젝트에서 사용한 코드</strong></p>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/eb760ff9-ba73-408d-be13-e4f5fd42c096/image.jpeg" alt=""></p>
<p>다음으로 아래와 같이 수행해야할 메서드위에 @Scheduled 어노테이션으로 스케줄 시간을 설정해주면 된다.</p>
<p><strong>실제 프로젝트에서 사용한 코드</strong>
<img src="https://velog.velcdn.com/images/noh_hyuk/post/09a26c40-23ac-4ab0-a65b-794c2a198413/image.png" alt=""></p>
<p>위 코드는 00시가 되면 user의 diaryCount가 초기화되는 코드이다.</p>
<h2 id="scheduled-설정-옵션">@Scheduled 설정 옵션</h2>
<ul>
<li>cron: 크론 표현식을 이용하여 스케줄링한다.</li>
</ul>
<pre><code class="language-java">@Scheduled(cron = &quot;0 0 0 * * *&quot;) // 순서대로 분, 시, 일, 월, 요일, 년을 나타낸다.</code></pre>
<ul>
<li>fixedDelay / fixedDelayString: 이전 작업이 종료되고 다시 시작되는 시간을 설정한다.</li>
</ul>
<pre><code class="language-java">@Scheduled(fixedDelay = 5000 또는 fixedDelayString = &quot;5s&quot;) // 이전 스케줄 작업 완료 후 1초 뒤에 다시 시작</code></pre>
<ul>
<li>fixedRate / fixedRateString: 이전 작업의 종료 여부와 상관없이 설정된 시간 간격으로 반복한다.</li>
</ul>
<pre><code class="language-java">@Scheduled(fixedRate = 5000 또는 fixedRateString = &quot;5s&quot;) // 매 5초마다 수행</code></pre>
<ul>
<li>initialDelay / initialDelayString: Job을 처음 실행까지 초기 딜레이(대기) 시간 설정</li>
</ul>
<pre><code class="language-java">@Scheduled(initialDelay = 5000 또는 initialDelayString = &quot;5s&quot;) // App 실행 완료 후 5초 후에 실행</code></pre>
<h2 id="scheduled-적용-필수-조건">@Scheduled 적용 필수 조건</h2>
<ul>
<li>cron 표현식, fixedDelay, fixedRate 와 같은 <strong>실행 주기를 설정하는 옵션을 필수로 한 가지</strong>는 적용해야한다.</li>
<li>@Scheduled를 통해 수행되는 job 메소드는 <strong>return type이 void 여야하고, parmeter를 줄 수 없다</strong>는 제약이 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[AOP(Aspect Oriented Programming)]]></title>
            <link>https://velog.io/@noh_hyuk/AOPAspect-Oriented-Programming</link>
            <guid>https://velog.io/@noh_hyuk/AOPAspect-Oriented-Programming</guid>
            <pubDate>Thu, 06 Jul 2023 11:45:57 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>💡 AOP(Aspect-Oriented Programming)는 <strong>어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 모듈화하여 재사용할 수 있도록 지원하는 것</strong>입니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/988e8597-f30e-4b1c-b419-629e805b30e2/image.png" alt=""></p>
<p>각각의 Service를 핵심적인 관점으로 보았을 때는 User와 Order의 공통된 요소가 없지만 부가적인 관점으로 보았을 때는 공통된 요소를 볼 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/e3c5e611-8883-4a1a-9a15-1b61f6d9cda9/image.png" alt=""></p>
<p>부가적인 관점에서 바라보면 공통된 메서드를 확인할 수 있습니다.
기존에 OOP에서 바라보는 관점을 다르게 하여 부가기능적인 측면에서 보았을 때 공통된 요소를 추출 하자는 것입니다. 이때 공통된 부분을 잘라내는 것을 AOP를 <strong>크로스 컷팅(Cross-Cutting)</strong>이라고 부르기도 합니다.</p>
<p><strong>OOP: 비지니스 로직의 모듈화</strong></p>
<ul>
<li>모둘화의 핵심 단위는 <strong>비지니스 로직</strong></li>
</ul>
<p><strong>AOP: 인프라 혹은 부가기능의 모듈화</strong></p>
<ul>
<li>대표적인 예: 모니터링 및 로깅, 오류 검사 및 처리, 동기화, 성능 최적화(캐싱) 등</li>
<li>모듈화된 모듈들의 주 목적 외에 필요한 <strong>부가적인 기능들</strong>을 모듈화 하는 것</li>
</ul>
<p>간단하게 한줄로 AOP를 정리해보자면 AOP는 <strong>공통된 기능을 모듈화하여 재사용하는 기법</strong>입니다.
OOP에선 공통된 기능을 재사용하는 방법으로 상속이나 위임을 사용합니다.
하지만 전체 애플리케이션에서 부가기능들을 상속이나 위임을 처리하기에는 깔끔한 모듈화가 어렵습니다.
그래서 등장한 것이 바로 AOP입니다.</p>
<h2 id="aop-장점">AOP 장점</h2>
<ul>
<li>애플리케이션 전체에 흩어진 공통 기능을 하나의 장소에서 관리할 수 있어서 유지보수가 좋습니다.</li>
<li>핵심 로직과 부가 기능 로직은 분리하기 때문에 핵심 로직은 자신의 목적만 신경 쓸 수 있습니다.</li>
</ul>
<h2 id="aop-주요-용어">AOP 주요 용어</h2>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/c6a8478d-f888-41d2-95d5-99aea6b30122/image.png" alt=""></p>
<ul>
<li><strong>Join point</strong><ul>
<li>Advice가 적용될 위치, 끼어들 수 있는 지점.</li>
<li>메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능</li>
</ul>
</li>
<li><strong>PointCut</strong><ul>
<li>Join point 중에서 advice가 적용될 위치를 선별하는 기능</li>
<li>Spring AOP는 proxy 기반이기 때문에 Join point와 PointCut은 메서드 실행 시점에서만 가능</li>
</ul>
</li>
<li><strong>Target</strong><ul>
<li>advice의 대상이 되는 객체</li>
<li>PointCut으로 결정</li>
</ul>
</li>
<li><strong>advice</strong><ul>
<li>실질적인 부가 기능 로직을 정의하는 곳</li>
<li>특정 Join point에서 Aspect에 의해 취해지는 조치</li>
</ul>
</li>
<li><strong>Aspect</strong><ul>
<li>advice + PointCut를 모듈화 한 것</li>
<li>@Aspect와 같은 의미</li>
</ul>
</li>
</ul>
<h2 id="aop-적용-방식">AOP 적용 방식</h2>
<p>AOP 적용 방식에는 3가지 방법이 있습니다.</p>
<ul>
<li>컴파일 시점<ul>
<li>.java 파일을 컴파일러를 통해 .class를 만드는 시점에 부가 기능 로직을 추가하는 방식</li>
<li>모든 지점에 적용 가능</li>
<li>AspectJ가 제공하는 특별한 컴파일러를 사용해야 하기 때문에 특별한 컴파일러가 필요한 점과 복잡하다는 단점</li>
</ul>
</li>
</ul>
<ul>
<li>클래스 로딩 시점<ul>
<li>.class 파일을 JVM 내부의 클래스 로더에 보관하기 전에 조작하여 부가 기능 로직 추가하는 방식</li>
<li>모든 지점에 적용 가능</li>
<li>특별한 옵션과 클래스 로더 조작기를 지정해야하므로 운영하므로 어려움</li>
</ul>
</li>
</ul>
<ul>
<li><strong>런타임 시점</strong><ul>
<li><strong>스프링이 사용</strong>하는 방식</li>
<li>컴파일이 다 끝나고 자바가 실행된 다음에 동작하는 런타임 방식</li>
<li>실제 대상 코드는 그대로 유지되고 프록시를 통해 부가 기능이 적용</li>
<li><strong>프록시는 메서드 오버라이딩 개념으로 동작하기 때문에 메서드에만 적용 가능</strong> → <strong>스프링 빈에만 AOP를 적용 가능</strong></li>
<li>스프링만 있으면 AOP를 적용할 수 있기 때문에 스프링 AOP는 런타임 방식을 사용</li>
</ul>
</li>
</ul>
<h2 id="spring-aop">Spring AOP</h2>
<ul>
<li>Spring에서 제공하는 Spring AOP는 proxy AOP 기반의 AOP 구현체이다.</li>
<li>proxy 객체를 사용하는 것은 접근 제어 및 부가 기능을 추가하기 위해서이다.</li>
<li>위에서 말한듯 Spring AOP는 Spring Bean에만 적용할 수 있습니다.</li>
<li>Spring AOP는 모든 AOP의 기능을 제공하는 것이 목적이 아닌, 중복 코드나 proxy 클래스 작성의 번거로움 등 흔한 문제를 해결하기 위한 솔루션을 제공하는 것이 목적이다.</li>
</ul>
<h3 id="프록시-패턴">프록시 패턴</h3>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/6d8b7387-674a-4ed3-aec6-aa6072ea4f84/image.webp" alt=""></p>
<p>proxy 패턴에서는 interface가 존재하고 Client는 이 interface 타입으로 Proxy 객체를 사용한다. Proxy 객체는 기존의 타겟 객체(Real Subject)를 참조한다. Proxy 객체와 기존의 타겟 객체의 타입은 같고, Proxy는 원래 할 일을 가지고 있는 Real Subject를 감싸서 Client의 요청을 처리한다.</p>
<h2 id="예시">예시</h2>
<p>이해하기 쉽게 Spring AOP를 적용하기 전과 적용한 후의 코드 예시를 보여드리겠습니다.</p>
<h3 id="적용하기-전-코드">적용하기 전 코드:</h3>
<pre><code class="language-java">public class UserService {
    public void addUser(User user) {
        // 사용자 추가 로직
    }

    public void deleteUser(int userId) {
        // 사용자 삭제 로직
    }
}</code></pre>
<h3 id="적용한-후-코드">적용한 후 코드:</h3>
<pre><code class="language-java">public interface UserService {
    void addUser(User user);
    void deleteUser(int userId);
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(User user) {
        // 사용자 추가 로직
    }

    @Override
    public void deleteUser(int userId) {
        // 사용자 삭제 로직
    }
}

@Aspect
@Component
public class LoggingAspect {
    @Before(&quot;execution(* com.example.UserService.*(..))&quot;)
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println(&quot;메서드 실행 전 로깅 작업&quot;);
    }
}</code></pre>
<p>위의 예시에서 UserService 인터페이스와 그를 구현한 UserServiceImpl 클래스를 정의하였다. 그리고 AOP를 적용하기 위해 LoggingAspect 클래스를 작성하였다. LoggingAspect는 @Aspect 어노테이션을 사용하여 AOP 관련 설정을 하고, @Before 어노테이션을 사용하여 메서드 실행 전에 로깅 작업을 수행한다.</p>
<p>이렇게 Spring AOP를 적용하면 UserService의 메서드를 호출할 때마다 로깅 작업이 수행된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring boot] 생성자 어노테이션 정리]]></title>
            <link>https://velog.io/@noh_hyuk/Spring-boot-%EC%83%9D%EC%84%B1-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@noh_hyuk/Spring-boot-%EC%83%9D%EC%84%B1-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 05 Jul 2023 13:38:27 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/dcdbf039-5185-4362-8a2b-5ca98f70325d/image.png" alt="">
Spring Boot 로 개발하다보면 Lombok 어노테이션을 이용하여 편리하게 개발을 할수 있어서 자주 사용한다.</p>
<p>생성자 어노테이션의 대해 알아보자.</p>
<h1 id="생성자-어노테이션-종류">생성자 어노테이션 종류</h1>
<blockquote>
<p>@<strong>NoArgsConstructor</strong>
@<strong>AllArgsConstructor</strong>
@<strong>RequiredArgsConstructor</strong></p>
</blockquote>
<pre><code class="language-java">import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
public class Person {
    private String name;
    private int age;
    private final String address;
}

// 위 코드를 작성한 후에 컴파일러가 자동으로 생성한 코드는 아래와 같습니다.

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private String name;
    private int age;
    private String address;

    // @NoArgsConstructor가 생성하는 기본 생성자
    public Person() {
    }

    // @AllArgsConstructor가 생성하는 모든 매개변수를 갖는 생성자
    public Person(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // @RequiredArgsConstructor가 생성하는 final 멤버변수를 매개변수로 갖는 생성자
    public Person(String address) {
        this.address = address;
    }
}</code></pre>
<p><strong><code>@NoArgsConstructor</code></strong></p>
<ul>
<li>매개변수가 없는 기본 생성자를 자동으로 생성합니다. 주로 entity 클래스에서 많이 사용되며, 기본 생성자가 필요한 경우 사용됩니다.</li>
</ul>
<p><strong><code>@AllArgsConstructor</code></strong></p>
<ul>
<li>클래스의 모든 필드를 매개변수로 받는 생성자를 자동으로 생성합니다. 이 어노테이션은 특별한 경우가 아니면 거의 사용되지 않습니다.</li>
</ul>
<p><strong><code>@RequiredArgsConstructor</code></strong></p>
<ul>
<li>클래스에서 <strong><code>final</code></strong>로 선언된 필드를 매개변수로 받는 생성자를 자동으로 생성합니다. 이 어노테이션은 주로 의존성 주입(Dependency Injection)을 사용하는 클래스에서 사용됩니다. <strong><code>final</code></strong>로 선언된 필드에는 반드시 값이 설정되어야 하므로, 생성자에서 매개변수로 받아 초기화하는 것이 안전하다고 판단되는 경우 사용됩니다.</li>
</ul>
<p>참고
<a href="https://chaezzing-fly-dev.tistory.com/33">https://chaezzing-fly-dev.tistory.com/33</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Reflaction? 그게 뭔데?]]></title>
            <link>https://velog.io/@noh_hyuk/Reflaction-%EA%B7%B8%EA%B2%8C-%EB%AD%94%EB%8D%B0</link>
            <guid>https://velog.io/@noh_hyuk/Reflaction-%EA%B7%B8%EA%B2%8C-%EB%AD%94%EB%8D%B0</guid>
            <pubDate>Mon, 03 Jul 2023 09:14:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
</blockquote>
<p>💡 구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API</p>
<h1 id="reflection-이란">Reflection 이란?</h1>
<p>리플렉션은 힙 영역에 로드된 Class 타입의 객체를 통해, 원하는 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있도록 지원하는 API이다.</p>
<h1 id="사용-방법">사용 방법</h1>
<p>리플렉션을 사용하기에 앞서, 힙 영역에 로드된 클래스 타입의 객체를 가져와야 한다. 가져오는 방법으로는</p>
<ul>
<li>클래스.class로 가져오기</li>
<li>인스턴스.getClass()로 가져오기</li>
<li>Class.forName(”클래스명”)으로 가져오기</li>
</ul>
<p>가 있습니다.</p>
<pre><code class="language-java">public class Member {

    private String name;

    protected int age;

    public Member() {
    }

    public Member(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void speak(String message) {
        System.out.println(message);
    }

    private void secret() {
        System.out.println(&quot;비밀번호는 1234입니다.&quot;);
    }

    @Override
    public String toString() {
        return &quot;Member{&quot; +
            &quot;name=&#39;&quot; + name + &#39;\&#39;&#39; +
            &quot;, age=&quot; + age + &#39;\&#39;&#39; 
            &#39;}&#39;;
    }
}

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        Class&lt;Member&gt; memberClass = Member.class;
        System.out.println(System.identityHashCode(memberClass));

        Member member = new Member(&quot;노혁&quot;, 19);
        Class&lt;? extends Member&gt; memberClass2 = member.getClass();
        System.out.println(System.identityHashCode(memberClass2));

        Class&lt;?&gt; memberClass3 = Class.forName(&quot;{패키지명}.Member&quot;);
        System.out.println(System.identityHashCode(memberClass3));
    }
}

// 실행 결과
1740000325
1740000325
1740000325</code></pre>
<p>3가지 방법으로 가져온 인스턴스는 모두 같은 해시 값인것을 확인 할 수 있다.
가져온 Class 타입을 통해 해당 클래스의 인스턴스를 생성할 수도 있고, 인스턴스의 필드와 메서드를 접근 제어자와 상관 없이 사용할 수 있게 되었다. 먼저 해당 클래스의 인스턴스를 생성해 보자.</p>
<blockquote>
<p>해시값이란 객체를 구분할 수 있는 고유값을 나타낸다.</p>
</blockquote>
<pre><code class="language-java">public class Main {

    public static void main(String[] args) throws Exception {
        // Member의 모든 생성자 출력
        Member member = new Member();
        Class&lt;? extends Member&gt; memberClass = member.getClass();
        Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);

        // Member의 기본 생성자를 통한 인스턴스 생성
        Constructor&lt;? extends Member&gt; constructor = memberClass.getConstructor();
        Member member2 = constructor.newInstance();
        System.out.println(&quot;member2 = &quot; + member2);

        // Member의 다른 생성자를 통한 인스턴스 생성
        Constructor&lt;? extends Member&gt; fullConstructor =
            memberClass.getConstructor(String.class, int.class);
        Member member3 = fullConstructor.newInstance(&quot;노혁&quot;, 19);
        System.out.println(&quot;member3 = &quot; + member3);
    }
}

// 실행 결과
public Member()
public Member(java.lang.String,int)
member2 = Member{name=&#39;null&#39;, age=0}
member3 = Member{name=&#39;노혁&#39;, age=19}</code></pre>
<p><code>getConstructor()</code>를 통해서 생성자를 가져오고, <code>newInstance()</code>를 통해서 Member 인스턴스를 동적으로 생성해 줄 수 있다.</p>
<p>마지막으로 인스턴스의 필드와 메소드를 접근 제어자와 상관없이 접근하여 사용해 보자.</p>
<pre><code class="language-java">public class Main {

    public static void main(String[] args) throws Exception {
        Member member = new Member(&quot;노혁&quot;, 19);
        Class&lt;? extends Member&gt; memberClass = member.getClass();

        // 필드 접근
        Field[] fields = memberClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            System.out.println(field.get(member));
        }
        fields[0].set(member, &quot;노혁2&quot;);
        System.out.println(member);

        // 메소드 접근
        Method speakMethod = memberClass.getDeclaredMethod(&quot;speak&quot;, String.class);
        speakMethod.invoke(member, &quot;리플렉션 테스트&quot;);

        Method secretMethod = memberClass.getDeclaredMethod(&quot;secret&quot;);
        secretMethod.setAccessible(true);
        secretMethod.invoke(member);
    }
}

// 실행 결과
노혁
19
Member{name=&#39;노혁2&#39;, age=19}
리플렉션 테스트
비밀번호는 1234입니다.</code></pre>
<p><code>getDeclaredFileds()</code> 를 통해 클래스의 인스턴스 변수를 모두 가져올 수 있고, <code>get()</code> 을 통해 필드 값을 반환받을 수 있고, <code>set()</code> 을 통해 필드 값을 수정할 수 있는 것을 알 수 있다. 이때 주의할 점은 private 접근 제어자가 있는 필드에 접근할 때는 <code>setAccessible()</code> 의 인자를 true로 넘겨주어야 한다.</p>
<p>메소드도 <code>getDeclaredMethod()</code> 를 통해 메소드를 가져올 수 있다. 이때 메소드의 이름과 파라미터의 타입을 같이 인자로 넘겨줘야 한다. 마찬가지로 private 접근 제어자가 있는 메소드에 접근할 때는 <code>setAccessible()</code> 의 인자를 true로 설정해야 한다. 마지막으로 <code>invoke()</code> 메소드를 통해 리플렉션 API로 얻어 온 메소드를 호출할 수 있다.</p>
<h1 id="장단점">장단점</h1>
<p><strong>장점</strong></p>
<ul>
<li>접근 제어자를 무시할 수 있습니다. private 필드나 메소드에 접근이 가능합니다.</li>
<li>런타임에 객체의 타입 정보를 알 수 있기 때문에 유연한 프로그래밍이 가능합니다.</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li>캡슐화를 저해합니다.</li>
<li>접근 제한자를 무시하면서 코드가 복잡해지는 경우가 있습니다.</li>
<li>리플렉션을 사용하면 런타임에 객체 정보를 검색하므로 성능이 떨어질 수 있습니다.</li>
</ul>
<h1 id="왜-사용하는-걸까">왜 사용하는 걸까?</h1>
<p>Spring의 Bean Factory를 보면, <code>@Controller, @Service, @Repository</code> 등의 어노테이션만 붙이면 Bean Factory에서 알아서 해당 어노테이션이 붙은 클래스를 생성하고 관리해 주는 것을 알 수 있다. 개발자는 Bean Factory에 해당 클래스를 알려준 적이 없는데 가능한 이유는 바로 리플렉션 덕분이다. 런타임에 해당 어노테이션이 붙은 클래스를 탐색하고 발견한다면, 리플렉션을 통해 해당 클래스의 인스턴스를 생성하고 필요한 필드를 주입하여 Bean Factory에 저장하는 식으로 사용이 된다.</p>
<p>물론, 위에 단점에서 말했듯이 캡슐화를 저해하기 때문에 꼭 필요한 상황에서만 사용하는 것이 좋습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Data JPA를 사용하여 Paging & Sort를 동시에 구현해보자]]></title>
            <link>https://velog.io/@noh_hyuk/Spring-Data-JPA%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-Paging-Sort%EB%A5%BC-%EB%8F%99%EC%8B%9C%EC%97%90-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@noh_hyuk/Spring-Data-JPA%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-Paging-Sort%EB%A5%BC-%EB%8F%99%EC%8B%9C%EC%97%90-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Fri, 30 Jun 2023 04:53:55 GMT</pubDate>
            <description><![CDATA[<p>먼저 Paging이 무엇인지 JPA를 사용하여 어떻게 구현 하는지 알아보자!!</p>
<h2 id="what-is-paging">What is Paging?</h2>
<hr>
<p>Paging은 말 그대로 Page화 하는 것이다. 조회 결과가 너무 많을 경우 <strong>일정한 크기를 갖는 페이지로 조회 목록을 쪼개는 것</strong>이다. 이를 이용하여 검색 결과나 게시물의 메인페이지를 조회하는 부분에서 유용하게 사용될 수 있습니다.</p>
<p>페이지는 Size, Page, Sort 3가지 값을 필요로 합니다.</p>
<h3 id="size">Size</h3>
<ul>
<li>Size는 Page의 개수를 의미한다. 10개의 조회 목록이 있을 경우 Size가 3이라면 4개의 페이지가 생성되고 마지막 페이지에는 1개의 목록만 있게 됩니다.</li>
</ul>
<h3 id="page">Page</h3>
<ul>
<li>Page는 생성된 페이지를 접근할 때 사용하는 인덱스입니다.</li>
</ul>
<h3 id="sort">Sort</h3>
<ul>
<li>Sort는 페이지를 자르기 전 목록을 정렬한다. 이 때 정렬할 기준값을 설정할 수 있습니다.<ul>
<li>Sort.By(”기준 컬럼 프로퍼티명”).desending() 이나 .asending()을 하여 오름차순, 내림차순 정렬을 정해 줄 수 있습니다.</li>
<li>기준 값으로 하고자하는 값은 객체에 존재하는 컬럼의 프로퍼티명을 적어주면 됩니다.(예: idx)</li>
</ul>
</li>
</ul>
<h2 id="paging--sort를-동시에">Paging &amp; Sort를 동시에</h2>
<hr>
<p>처음 해보시는 분들을 위하여 실제 구현한 Controller부터 Service, Repository까지의 예시를 보면서 설명하겠습니다.</p>
<h3 id="controller">Controller</h3>
<pre><code class="language-kotlin">@RestController
@RequestMapping(&quot;/post&quot;)
class PostController(
    private final postConverter: PostConverter
    private final getLatestPostsService: GetLatestPostsService
) {
    // 최신순으로 게시물을 정렬하는 Controller 입니다.
    @GetMapping
    fun getAllPostList(pageable: Pageable): ResponseEntity&lt;PostListResponse&gt; = 
                getLatestPostsService.getLatestPost(pageable)
                        .let { postConverter.toResponse(it) }
                        .let { ResponseEntity.ok(it) }
    }
} </code></pre>
<p>실제 프로젝트에서 <a href="http://localhost:8080/post?page=0&amp;size=2">http://localhost:8080/post?page=0&amp;size=2</a> 쿼리 스트링을 사용하여 page, size 값을 받았습니다. 이때 Reuqest 값으로는 Pageable 타입으로 받으면 됩니다. 위의 코드를 자세히 볼 필요는 없고 그냥 Request를 어떤 타입으로 받는지만 보시면 될꺼같습니다.</p>
<h3 id="service">Service</h3>
<pre><code class="language-kotlin">override fun getLatestPosts(pageable: Pageable): List&lt;PostDto&gt; = 
            postRepository.findAll(
                PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(&quot;idx&quot;).descending())
                .toList()
                .let { postConverter.toDto(it) }</code></pre>
<p>함수의 내용을 자세히 살펴보면, postRepository에서 findAll() 메소드를 사용하여 모든 게시물을 가져온 뒤, Pageable 인터페이스를 구현한 PageRequest.of()를 사용하여 페이지 번호와 페이지 크기, 그리고 정렬 방식을 지정합니다. 최신순으로 정렬을 하는 API이기 때문에 idx 값을 기준으로 내림차순 정렬을 하도록 설정되어 있습니다.</p>
<p>이처럼 PageRequest.of를 사용하여 Pageable과 Sort를 동시에 진행할 수 있습니다.</p>
<p>PageRequest.of에 대해 설명하진 않았지만 구글링하면 금방 나오니깐 이해가 가질 않으시다면 찾아보는걸 추천해드립니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CORS ERROR와의 만남]]></title>
            <link>https://velog.io/@noh_hyuk/CORS-ERROR%EC%99%80%EC%9D%98-%EB%A7%8C%EB%82%A8</link>
            <guid>https://velog.io/@noh_hyuk/CORS-ERROR%EC%99%80%EC%9D%98-%EB%A7%8C%EB%82%A8</guid>
            <pubDate>Thu, 22 Jun 2023 14:51:05 GMT</pubDate>
            <description><![CDATA[<p>front-end와의 첫 api 통신중에 CORS Error를 만났습니다. 이 게시물을 통해 CORS가 무엇인지, 해결방법에는 무엇이 있는지 알아보겠습니다.</p>
<h2 id="먼저-짚고-넘어가자">먼저 짚고 넘어가자</h2>
<p>처음으로 짚고 넘어가야 하는것이 <strong>SOP(Same Origin Policy)</strong>라는 것이다.
<strong>SOP</strong>는 다른 출처의 리소스를 사용하는것에 제한하는 보안 방식</p>
<p><strong>출처란?</strong>
<img src="https://velog.velcdn.com/images/noh_hyuk/post/a87659ad-fbb6-4913-b09f-3b0248b98828/image.png" alt=""></p>
<p><strong>Protocol + Host + Port</strong> 3가지를 통하여 같은 출처인지 다른 출처인지 확인이 가능하다.</p>
<h3 id="동일-출처-예시">동일 출처 예시</h3>
<table>
<thead>
<tr>
<th>예시</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><a href="http://www.choice-time.com:80">http://www.choice-time.com:80</a> <a href="http://www.choice-time.com">http://www.choice-time.com</a></td>
<td>HTTP 기본 Port인 80번이 생략되어있으므로 동일 출처입니다</td>
</tr>
<tr>
<td><a href="http://choice-time.com/post/1">http://choice-time.com/post/1</a> <a href="http://choice-time.com/post/2">http://choice-time.com/post/2</a></td>
<td>Protocol, Host, Port(생략)이 같으며, Path부터 다르므로 동일 출처입니다</td>
</tr>
</tbody></table>
<h3 id="다른-출처-예시">다른 출처 예시</h3>
<table>
<thead>
<tr>
<th>예시</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><a href="http://choice-time.com/post/1">http://choice-time.com/post/1</a> <a href="https://choice-time.com/post/2">https://choice-time.com/post/2</a></td>
<td>Protocol이 다릅니다</td>
</tr>
<tr>
<td><a href="http://oasis-time.com">http://oasis-time.com</a> <a href="http://www.choice-time.com">http://www.choice-time.com</a></td>
<td>Host가 다릅니다</td>
</tr>
<tr>
<td><a href="http://www.choice-time.com">http://www.choice-time.com</a> <a href="http://ww.choice-time.com:8080">http://ww.choice-time.com:8080</a></td>
<td>80, 8080으로 포트가 다릅니다</td>
</tr>
<tr>
<td>- 같은 오리진 길이만 데이터를 송수신 하고자 한다 라는 것이 Same Origin Policy를 지키는 것이다.</td>
<td></td>
</tr>
</tbody></table>
<h2 id="💫-cors란">💫 CORS란?</h2>
<p>CORS(Cross-Origin Resource Sharing)는 한 출처에 있는 자원에서 다른 출처에 있는 자원에 접근하도록 하는 개념입니다. 직여하게 되면, <strong>교차되는 출처 자원들의 공유</strong>입니다. </p>
<blockquote>
<p>교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 다른 출처의 자원에 접근할 수 있는 권한을 브라우저에 알려 줍니다. 웹 애플리케이션은 <strong>리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행</strong>합니다.</p>
</blockquote>
<h2 id="왜-필요하지">왜 필요하지?</h2>
<p>CORS 제한이 없다면 다른 사이트에서 원래 사이트를 모방하여 사용자 정보를 탈취하거나 해킹할 수 있습니다. 이를 방지하기 위해 브라우저는 보호 기능을 제공하며, 필요한 경우에만 서버와 협의하여 요청할 수 있도록 제한을 두어야 합니다.</p>
<h2 id="어떻게-동작-하는지">어떻게 동작 하는지</h2>
<h3 id="단순-요청simple-request인-경우"><strong>단순 요청</strong>(<strong>Simple Request)인 경우</strong></h3>
<ul>
<li>GET, HEAD, POST 요청만 가능합니다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/490d1dd2-935f-427d-8308-2c3136525200/image.png" alt=""></p>
<p>브라우저는 자신의 주소 <a href="https://www.site.com%EC%9D%84">https://www.site.com</a>를 origin에 담아서 요청을 보냅니다.
서버는 요청을 확인하고 다른 출처 주소 <a href="https://www.site.com%EC%97%90">https://www.site.com에</a> 접근이 가능하다는 access-control-allow-origin에 해당 주소를 담아서 결과를 리턴합니다.</p>
<p>특히, access-control-allow-origin는 CORS 헤더 요소 중 하나로 어떤 요청을 허용할지  결정합니다.
이 헤더 값은 하나의 출처가 될 수 있고, “*”를 사용해 어떤 출처도 허용하도록 할 수 있습니다.</p>
<p>서버가 CORS 헤더를 응답하지 않으면 Same-Origin Policy가 적용되어 요청이 차단됩니다.
서버가 &quot;Access-Control-Allow-Origin&quot; 헤더를 응답해야 하며, 이 헤더에 요청한 출처가 포함되어 있지 않으면 브라우저는 해당 요청을 차단합니다.</p>
<h3 id="preflight-요청일-경우">Preflight 요청일 경우</h3>
<ul>
<li>Preflight는 OPTIONS 메서드로 HTTP 요청을 미리 보내 실제 요청이 전송하기에 안전한지 확인합니다.</li>
</ul>
<p>요청 헤더에는 다음 값이 존재합니다.</p>
<p><strong>origin</strong> : 어디서 요청을 했는지 서버에 알려주는 주소</p>
<p><strong>access-control-request-method</strong> : 실제 요청이 보낼 HTTP 메서드</p>
<p><strong>access-control-request-headers</strong> : 실제 요청에 포함된 header</p>
<p>응답 헤더에는 다음 값이 존재합니다.</p>
<p><strong>access-control-allow-origin</strong> : 서버가 허용하는 출처</p>
<p><strong>access-control-allow-methods</strong> : 서버가 허용하는 HTTP 메서드 리스트</p>
<p><strong>access-control-allow-headers</strong> : 서버가 허용하는 header 리스트</p>
<p><strong>access-control-max-age</strong> : 프리 플라이트 요청의 응답을 캐시에 저장하는 시간</p>
<p><img src="https://velog.velcdn.com/images/noh_hyuk/post/64d9dd48-b521-4001-a880-417072fb0fb8/image.png" alt=""></p>
<p>Origin헤더에 현재 요청하는 origin과, Access-control-request-method헤더에 요청하는 HTTP method와 Access-Control-Request-Headers 요청 시 사용할 헤더를 OPTIONS 메서드로 서버에 요청합니다. 이때 내용물은 없이 헤더만 전송합니다.</p>
<p>Browser가 서버에서 응답한 헤더를 보고 유요한 요청인지 확인합니다. 만약 유효하지 않은 요청이라면 요청은 중단되고 에러가 발생합니다. 유효한 요청이라면 원래 요청으로 보내려던 요청을 다시 요청하여 리소스를 응답받습니다.</p>
<blockquote>
<aside>
💡 Preflight 요청을 사용하는게 좋습니다. 실제 요청이 실행되기 이전에 검사를 하여 허용할지 않할지를 결정 할 수 있기 때문입니다.
</blockquote>
</aside>

<h1 id="spring-boot에서-cors-설정하기">Spring Boot에서 CORS 설정하기</h1>
<p>spring boot에서 CORS설정하는 방법에는 메서드 설정, 컨트롤러 설정, 전역 설정이 있습니다. 저는 이 중에서 전역 설정을 하는 방법을 소개하겠습니다.</p>
<h2 id="전역-설정하기">전역 설정하기</h2>
<p>CORS 정책의 설정은 WevMvcConfigurer를 구현하여 설정할 수 있습니다.
(아래 예시는 실제 프로젝트에서 사용한 CORS 설정입니다)
<img src="https://velog.velcdn.com/images/noh_hyuk/post/9b668d2e-4ce2-4cb5-9160-2571537f6588/image.png" alt=""></p>
<h3 id="참고-자료">참고 자료</h3>
<p><a href="https://wonit.tistory.com/572">https://wonit.tistory.com/572</a>
<a href="https://escapefromcoding.tistory.com/724">https://escapefromcoding.tistory.com/724</a></p>
]]></description>
        </item>
    </channel>
</rss>