<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>rlackdals_98.log</title>
        <link>https://velog.io/</link>
        <description>일일 회고 : https://rlackdals981010.github.io/</description>
        <lastBuildDate>Thu, 29 Aug 2024 10:10:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>rlackdals_98.log</title>
            <url>https://velog.velcdn.com/images/rlackdals_98/profile/b4811ec2-60e1-4220-ae28-7ea494a4ac61/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. rlackdals_98.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/rlackdals_98" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[CS_데이터베이스.7 조인의 원리]]></title>
            <link>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.7-%EC%A1%B0%EC%9D%B8%EC%9D%98-%EC%9B%90%EB%A6%AC</link>
            <guid>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.7-%EC%A1%B0%EC%9D%B8%EC%9D%98-%EC%9B%90%EB%A6%AC</guid>
            <pubDate>Thu, 29 Aug 2024 10:10:54 GMT</pubDate>
            <description><![CDATA[<h1 id="중첩-루프-조인">중첩 루프 조인</h1>
<blockquote>
<p>NLJ, Nested Loop Join은 중첩 for문과 같은 원리로 조건에 맞는 조인을 하는 방법이다.
랜덤 접근에 대한 비용이 많이 증가하기 때문에 대용량 테이블에선 사용하지 앟는다.</p>
</blockquote>
<p>t1와 t2를 토인한다고 하면, t1에서 행을 하나 읽고, t2에서 for문을 통해 행을 하나씩 읽으면서 조건에 맞는 레코드를 찾는다.</p>
<p>NLJ에서 발전한 방법으로, 조인할 테이블을 작은 블록으로 나눠 블록 하나씩 조인하는 블록 중첩 루프 조인 BNL도 있다.</p>
<h1 id="정렬-병합-조인">정렬 병합 조인</h1>
<blockquote>
<p>각 테이블을 조인할 필드 기준으로 정렬하고 정렬이 끝난 이후 조인 작업을 수행</p>
</blockquote>
<p>조인할 때 쓸 적절한 인덱스가 없고 대용량의 테이블을 조인하고 조인 조건으로 &lt;,&gt;등 범위 비교 연산자가 있을 때 쓴다.</p>
<h1 id="해시-조인">해시 조인</h1>
<blockquote>
<p>해시 테이블을 기반으로 조인하는 방법. </p>
</blockquote>
<p>두 개의 테이블 조인시 하나의 테이블이 메모리에 온전히 들어가면 NLJ보다 효율이 좋다.</p>
<p>동등 조인에서만 사용할 수 있다.</p>
<h2 id="빌드-단계">빌드 단계</h2>
<p>입력 테이블 중 하나를 기반으로 메모리 내 해시 테이블을 빌드한다.</p>
<p>t1과 t2 테이블을 조인할 때 바이트가 더 작은 테이블을 기반으로 테이블을 빌드한다. t2가 더 작다고 가정한다.</p>
<p>조인에 사용되는 필드(t2_id)가 해시 테이블의 키로 사용된다.</p>
<h2 id="프로브-단계">프로브 단계</h2>
<p>빌드 단계 후 프로브 단계 동안 레코드 읽기를 시작하며, 각 레코드에서 t2_id에 일치하는 레코드를 찾아서 결과로 반환한다.</p>
<p>이를 통해 각 테이블은 한 번씩만 읽고, 중첩해서 두 개의 테이블을 읽는 중첩 루프 보다 성능이 보통은 더 좋다.</p>
<p>사용 가능한 메모리 양은 join_buffer_size라는 시스템 변수에 의해 제어된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS_데이터베이스.6 조인]]></title>
            <link>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.6-%EC%A1%B0%EC%9D%B8</link>
            <guid>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.6-%EC%A1%B0%EC%9D%B8</guid>
            <pubDate>Wed, 28 Aug 2024 11:05:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>하나의 테이블이 아닌 두 개 이상의 테이블을 묶어서 하나의 결과물을 만드는 것</p>
</blockquote>
<p>조인 연산은 NoSQL보단 관계형 데이터베이스에서 사용한다. </p>
<h1 id="inner-join">Inner Join</h1>
<p>두 테이블 간 교집합이다.</p>
<p>즉, <code>a A INNER JOIN b B on a.col=b.col</code>이라고 하면
a의 col이 1,2,3 이고 b의 col이 1,3,4면 Inner Join 결과 테이블은 1,3을 들고있는것</p>
<h1 id="rightleft-join">Right/Left Join</h1>
<p><code>a A RIGTH JOIN b B on a.col=b.col</code> 혹은 <code>a A LEFT JOIN b B on a.col=b.col</code>이렇게 작성하면 JOIN 기준으로 우측, 좌측에 있는 테이블의 전부 + 반대편 테이블에선 그에 해당하는 값만 합쳐진다. 대상 값이 없으면 NULL 취급</p>
<h1 id="full-outer-join">Full Outer Join</h1>
<p><code>a A FULL OUTTER JOIN b B on a.col=b.col</code> 이면 쉽게말해서 LEFT,RIGHT 모두 하는 것</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS_데이터베이스.5 인덱스]]></title>
            <link>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.5-%EC%9D%B8%EB%8D%B1%EC%8A%A4</link>
            <guid>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.5-%EC%9D%B8%EB%8D%B1%EC%8A%A4</guid>
            <pubDate>Tue, 27 Aug 2024 10:40:57 GMT</pubDate>
            <description><![CDATA[<p>인덱스를 통해 DB내부의 데이터에 빠르게 접근할 수 있다.</p>
<h1 id="b-트리">B-트리</h1>
<p>인덱스는 B-트리라는 자료 구조로 이뤄져있다.
B-트리는 <code>루트 노드``브랜치 노드``리프 노드</code>로 구성된다.
B-트리는 이진 트리와는 다르게 1개의 노드에 여러 정보를 갖고있을 수 있다.
최대 M개의 자식을 가질 수 있는 B트리를 M차 B트리라고 한다.</p>
<ul>
<li>M차 B트리의 노드는 M/2~M개의 자식을 가질 수 있다.</li>
<li>노드에는 (M/2)-1 ~ M-1개의 키가 포함될 수 있다.</li>
<li>노드의 키가 x개면 자식의 수는 x+1이다.</li>
<li>최소 차수는 자식수의 하한값이며, 최소 차수가 t면 M=2t-1이다.</li>
</ul>
<p>B트리에서 탐색은 &#39;있을법한 리프 노드를 탐색&#39;하는 방법이다.</p>
<p>루트노드와 리프노드로 구성되어있다고 가정하면
루트노드 : [A,D,L]
리프노드 : [A,B,C],[D,E,F],[L,M,N]
이 있고, E를 찾기 위해선 루트 노드 -&gt; 2번 리프 노드 . 총 2회만에 E를 찾을 수 있다.</p>
<p>위의 과정을 그대로 루트노드, 브랜치노드, 리프노드로 구성된 트리에 적용하면
루트노드 : [29,83,97]
브랜치 노드 : [1,20,22,29] ,[35,40,68,83] , [89,90,95,97]
리프 노드 : [36,37,40]...[50,57,68]...</p>
<p>57를 찾기 위해서 루트 - 2번 브랜치 - n번 리프로 총 3번을 거쳐 결과를 찾을 수 있다.</p>
<p>인덱스가 효율적인 이유는 효율적인 단계와, 모든 요소에 접근 가능한 균형 잡인 트리 구조와 트리 깊이의 대수확장성 때문이다.</p>
<blockquote>
<p>대수확장성 : 트리 깊이가 리프 노드 수에 비해 느리게 성장</p>
</blockquote>
<p>기본적으로 깊이 1레벨 증가시 최대 인텍스는 4배씩 증가한다. 이렇게 되면 트리 깊이 10 레벨로 100만개의 레코드 검색이 가능하다.</p>
<h1 id="인덱스-만들기">인덱스 만들기</h1>
<p>mysql의 경우 클러스터형 인덱스, 서컨더리 인덱스가 있다.
클러스터형의 경우 테이블당 하나 설정이 가능하고, primary key 옵션으로 기본키로 만들면 클러스터형 인덱스 생성이 가능하고, 기본키가 아닌 unique not null 옵션을 붙이면 클러스터형 인덱스로 만들 수 있다.</p>
<p>create index 명령어를 기반으로 한다면 세컨더리 인덱스 생성이 가능하다. 
세컨더리 인덱스는 보조 인덱스로 여러 개의 필드 값을 기반으로 쿼리를 많이 보낼 때 생성해야 한다.</p>
<p>단일 인덱스 기준으로 클러스터형 인덱스가 성능이 더 좋다. 만약 age라는 필드로 쿼리를 보낸다면 클러스터형 인덱스를, age,name,email등 여러 필드를 쿼리로 보낸다면 세컨더리 인덱스를 사용하면 된다.</p>
<h1 id="인덱스-최적화">인덱스 최적화</h1>
<p>인덱스 최적화는 DB마다 다르지만 기법은 대부분 유사하다.</p>
<h2 id="1-인덱스는-비용이다">1. 인덱스는 비용이다.</h2>
<p>인덱스는 인덱스 리스트 -&gt; 컬렉션 순으로 탐색하므로 두 번 탐색한다. 또한 컬렉션 수정시 인덱스도 수정되기 때문에 (논문 내용 수정시 앞 목차 수정하듯) B-트리 높이 조절 비용, 데이터 분산 비용도 든다.</p>
<p>그렇기 때문에 쿼리에 있는 필드에 인덱스를 전부 설정하면 리소스가 많이 사용되기 때문에 비효율일 수 있다.</p>
<h2 id="2-항상-테스팅하라">2. 항상 테스팅하라</h2>
<p>인덱스 최적화 기법은 서비스 특징에 따라 다르다. 서비스에서 사용하는 객체의 깊이, 테이블 양 등이 다르기 때문에 테스팅을 항상 해야한다.</p>
<p>explain()함수를 이용해 인덱스를 만들고 쿼리를 보낸 이후에 테스팅을 하며 걸리는 시간을 최소화 해야한다.</p>
<h2 id="3-복합-인덱스는-같음-정렬-다중-값-카디널리티-순이다">3. 복합 인덱스는 같음, 정렬, 다중 값, 카디널리티 순이다.</h2>
<p>보통 여러 필드를 기반으로 조회시 복합 인덱스를 생성한다. 이 인덱스를 생성할 때는 순서가 있고, 생성 순서에 따라 인덱스의 성능이 좌우된다.</p>
<p>그렇기 대문에 같음, 정렬, 다중 값, 카디널리티 순으로 생성하는 것이 성능이 가장 좋다.</p>
<ul>
<li>같음 : --, equal이라는 쿼리가 있다면 가장 먼저 인덱스로</li>
<li>정렬 : 정렬에 쓰는 필드라면 다음 인덱스로</li>
<li>다중 값 : 쿼리 자체가 &gt;,&lt; 등 범위 출력이라면 3번 인덱스로</li>
<li>카디널리티 : 유니크한 값의 정도를 카디널리티라고 하는데, 이게 높은 순서대로 인덱스를 생성해야한다. age, email을 예로 들면 email의 카디널리티가 더 높기 때문에 email이라는 필드에 대한 인덱스를 먼저 생성해야 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS_데이터베이스.4 데이터베이스의 종류]]></title>
            <link>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.4-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EC%A2%85%EB%A5%98</link>
            <guid>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.4-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EC%A2%85%EB%A5%98</guid>
            <pubDate>Tue, 27 Aug 2024 10:05:24 GMT</pubDate>
            <description><![CDATA[<h3 id="rdbms">RDBMS</h3>
<p>RDBMS는 데이터가 행과 열로 구성된 표 형태로 저장되는 데이터베이스 시스템입니다. 각 표는 데이터의 관계를 명확히 정의하여, 데이터 무결성을 유지하고 효율적으로 관리할 수 있습니다.</p>
<p>SQL(Structured Query Language)을 사용하여 데이터를 조작하고 쿼리합니다. SQL은 표준화된 언어이지만, 각 RDBMS 제품(예: MySQL, PostgreSQL, Oracle 등)은 특정 기능이나 확장을 위해 약간의 변형된 SQL 방식을 지원할 수 있습니다.</p>
<p>ACID 속성: 트랜잭션의 원자성, 일관성, 격리성, 지속성을 보장하여 데이터의 무결성을 유지합니다.
정규화: 데이터 중복을 최소화하고 데이터 무결성을 보장하기 위한 정규화 기법을 사용합니다.
스키마 기반: 데이터베이스 구조(스키마)가 사전에 정의되어야 하며, 스키마의 변경이 복잡할 수 있습니다.</p>
<h3 id="nosql-not-only-sql">NoSQL (Not Only SQL)</h3>
<p>NoSQL 데이터베이스는 SQL을 사용하지 않는 데이터베이스 시스템으로, 비정형 데이터나 대량의 데이터를 효율적으로 처리하도록 설계되었습니다.</p>
<p>MongoDB는 대표적인 NoSQL 데이터베이스 중 하나로, JSON 형태로 데이터를 저장하고 관리합니다. MongoDB는 BSON(Binary JSON) 형태로 데이터를 저장합니다.</p>
<p>확장성: 수평적 확장이 용이하여, 대량의 데이터를 처리할 때 높은 성능을 발휘합니다. 데이터 샤딩(분산 저장)을 통해 확장이 가능합니다.
스키마 유연성: 스키마가 유연하거나 없는 경우가 많아, 데이터 구조를 사전에 정의하지 않고 동적으로 변경할 수 있습니다. 이는 다양한 도메인에서의 데이터 저장과 분석에 유리합니다.
성능: 비정형 데이터나 대규모 데이터 처리 시 성능이 뛰어나며, 실시간 데이터 처리와 같은 특화된 작업에 적합합니다.
다양한 데이터 모델: 문서 기반, 키-값 기반, 열 기반, 그래프 기반 등 다양한 데이터 모델을 지원하여 특정 사용 사례에 맞춘 최적의 설계를 할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 29. 빌더 패턴]]></title>
            <link>https://velog.io/@rlackdals_98/Spring-29.-%EB%B9%8C%EB%8D%94-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@rlackdals_98/Spring-29.-%EB%B9%8C%EB%8D%94-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Fri, 23 Aug 2024 11:28:18 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 진행하다가 수많은 Response Dto 클래스의 양을 보고 뭔가 잘못됨을 느꼈고, Dto의 양을 줄이려고 한 Dto에 근 5개의 생성자를 쑤셔 넣다가 알게된 패턴이다.</p>
<p>일단 내가 이해한 빌더 패턴은 다음과 같다.
생성자로 필드에 값을 저장하던걸 Builder 클래스를 만들어서 대체하는 것.</p>
<p>이를 통해서 생성자 인자 순서를 파악하지 않아도 되고, 수많은 생성자 열거를 안해도 되고, 잘못된 값을 넣는 실수도 안할 수 있게 된다.</p>
<p>우선 Entity가 다음과 같다고 가정하자</p>
<pre><code>@Getter
@Setter
@NoArgsConstructor
public class Event  {

    private String title;

    private String content1;
    private String content2;
    private String content3;
    private String content4;
    private String content5;
    private String content6;
}</code></pre><p>Builder가 적용될 수 있는 Dto는 Response가 적합하므로 Response를 보면 다음과 같다.</p>
<pre><code>@Getter
@Setter
@AllArgsConstructor
public class EventResponseDto {

    private String title;

    private String content1;
    private String content2;
    private String content3;
    private String content4;
    private String content5;
    private String content6;
}</code></pre><p>여기도 사실 뭐 다른건 없는데, 지금까지 나는 응답을 할 때 {title, content1},{title, content3}, {title, content4, content6} 이런 경우 Dto를 3개를 만들어서 반환했다. 그러다가 뭔가 이상함을 느끼고 하나의 Dto로 통합한 후 생성자를 여러개 만들었는데, Builder를 사용하면 AllArgsConstructor 하나만 있으면 된다.</p>
<p>빌더는 다음과 같다.</p>
<pre><code>package com.example.demo;

public class EventBuilder {

    private String title;
    private String content1;
    private String content2;
    private String content3;
    private String content4;
    private String content5;
    private String content6;

    public EventBuilder title(String title){
        this.title = title;
        return this;
    }

    public EventBuilder content2(String content2){
        this.content2 = content2;
        return this;
    }

    public EventBuilder content3(String content3){
        this.content3 = content3;
        return this;
    }

    public EventBuilder content4(String content4){
        this.content4 = content4;
        return this;
    }

    public EventBuilder content5(String content5){
        this.content5 = content5;
        return this;
    }

    public EventBuilder content6(String content6){
        this.content6 = content6;
        return this;
    }


    public EventResponseDto build() {
        return new EventResponseDto(title, content1, content2, content3, content4, content5, content6);
    }
}
</code></pre><p>보면 Builder에서 각 필드 명을 생성자로 만들어서 단일 값만 받고, build메소드로 전체를 받은 객체를 넘겨주고 있다.</p>
<pre><code>    public EventResponseDto createEvent(EventRequestDto eventRequestDto) {
        EventResponseDto eventResponseDto = new EventBuilder()
                .title(eventRequestDto.getTitle())
                .content2(eventRequestDto.getContent2())
                .content6(eventRequestDto.getContent6())
                .build();

        return eventResponseDto;
    }
</code></pre><p>Service에선 이렇게 처리를 하는데, 각 대상 필드만 연속적으로 받고, Build를 딸깍해주면 된다. </p>
<p><img src="https://velog.velcdn.com/images/rlackdals_98/post/7a99e892-acc0-4a1e-b9dd-b4fa7903400d/image.png" alt="">
그러면 이렇게 반환이 된다.</p>
<p>이렇게 Builder를 쓰면 객체 생성 과정이 일관된 프로세스로 변하고, 필수 멤버와 선택적 멤버를 분리할 수 있다.</p>
<p>내가 빌더 패턴을 찾는 이유는 바로 필수 - 선택 멤버 분리에 있는데, 빌더 패턴도 단점은 좀 있다. 일단 생성자보다 낮은 성능이 있다. 하지만 이미 자바를 사용한다는것부터 성능을 포기한거긴 하지만, 그 중에서도 어느정도의 성능을 챙기기 위해선 생성자를 사용하는게 좋을 수 있다.</p>
<hr>
<p>빌더 패턴에는 2가지 종류가 있는데 이건 추후..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS_데이터베이스.3 트랜잭션과 무결성]]></title>
            <link>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.3-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EA%B3%BC-%EB%AC%B4%EA%B2%B0%EC%84%B1</link>
            <guid>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.3-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EA%B3%BC-%EB%AC%B4%EA%B2%B0%EC%84%B1</guid>
            <pubDate>Fri, 23 Aug 2024 10:24:38 GMT</pubDate>
            <description><![CDATA[<h1 id="트랜잭션">트랜잭션</h1>
<blockquote>
<p>DB에서 하나의 논리적 기능을 수행하기 위한 작업의 단위</p>
</blockquote>
<p>즉, 여러개의 쿼리의 묶음 단위이다.
트랜잭션은 원자성, 일관성, 독립성, 지속성이 있고 한번에 ACID 특징이라고 한다.</p>
<h2 id="원자성">원자성</h2>
<blockquote>
<p>all or nothing</p>
</blockquote>
<p>트랜잭션은 내부 쿼리가 모두 성공적으로 수행됬다면 성공, 하나라도 실패하면 실패다.</p>
<p>이걸 커밋과 롤백이라고 하는데, 컷밋은 여러 쿼리가 성공적으로 처리되었다고 확정하는 명령어다. 즉, &quot;커밋 수행&quot;은 &quot;하나의 트랜잭션이 성공했다.&quot;로 볼 수 있는 것이다.</p>
<p>롤백은 트랜잭션 내부 쿼리가 하나라도 실패하는 순간, 커밋 시작 전으로 상태를 되돌리는 것이다.</p>
<p>커밋과 롤백 던분에 데이터의 무결성이 보장되며, 데이터 변경 전 변경 사항을 쉽게 확인할 수 있고, 해당 작업을 그룹화할 수 있다.</p>
<h2 id="트랜잭션-전파">트랜잭션 전파</h2>
<p>그런데 트랜잭션은 커넥션(데이터베이스와 트랙잭션의 연결) 단위로 수행하기 때문에 커넥션 객체를 넘겨서 트랜잭션을 수행한다. </p>
<p>매번 넘기는게 번거로워서 여러 트랜잭션 관련 메소드의 호출을 하나의 트랜잭션에 묶이도록 하는것이 트랜잭션 전파다. </p>
<pre><code>@Transaction(readOnly = true)
public class MembetService{~~}</code></pre><p>Spring에서는 이렇게 어노테이션을 통해서 여러 쿼리 관련 코드를 하나의 트랜잭션으로 처리한다.</p>
<h2 id="일관성">일관성</h2>
<blockquote>
<p>허용된 방식으로만 데이터를 변경할 수 있따.</p>
</blockquote>
<p>DB내부 데이터는 여러 조건과 규칙에 따라 유효함을 가져야한다.
예를 들면 내가 소지한 돈이 만원인데 친구한테 10만원을 빌려주는것. 현실적으로 불가능하다. 즉, 이 방식은 수행되지 못한다.</p>
<h2 id="독립성">독립성</h2>
<blockquote>
<p>트랜잭션 수행 시 서로 끼어들지 못한다.</p>
</blockquote>
<p>여러 트랜잭션은 서로 격리되어 마치 순차적으로 실행되는 것처럼 작동되어야 한다.</p>
<p>SERIALIZABLE -&gt; REPEATABLE_READ -&gt; READ_COMMITTED -&gt; READ_UNCOMMITTED 순으로 동시성이 강해지면서 격리성이 낮아진다.</p>
<h3 id="serializable">SERIALIZABLE</h3>
<p>말 그대로 트랜잭션의 순차 진행이다. 여러 트랜잭션이 동시에 같은 행에 접근할 수 없다.</p>
<p>SERIALIZABLE에선 팬텀 리드 현상이 존재한다.
팬텀 리드는 한 트랜잭션 내부에서 동일한 쿼리를 보냈을 때 해당 조회 결과가 다른 경우를 말한다.</p>
<p>즉 A의 쿼리로 3개의 테이블이 조회 되고, B가 다른 레코드를 삽입, 그 후 A의 쿼리를 동일하게 입력했을때 4개의 테이블이 조회되는 현상이다.</p>
<h3 id="repeatable_read">REPEATABLE_READ</h3>
<p>하나의 트랜잭션이 수정한 행을 다른 트랜잭션이 수정할 수 없도록 막아주지만, 새로운 행을 추가하는건 허용한다.</p>
<p>REPEATABLE_READ에선 반복 불가능한 조회현상이 존재한다.
한 트랜잭션 내의 같은 행에 두 번 이상 조회가 발생했는데, 서로 다른 값이 조회되는 경우다.</p>
<p>팬텀 리드와의 차이점은 반복 불가능한 조회는 행 값이 달라질 수 있는데, 팬텀 리드는 다른 행이 선택되는 것이다.</p>
<h3 id="read_committed">READ_COMMITTED</h3>
<p>가장 많이 사용되는 격리 수준이며, 우리가 사용하는 DBMS의 기본값이다. 커밋이 완료된 데이터만 조회를 허용한다.</p>
<p>READ_COMMITTED에선 더티 리드 현상이 존재한다.
더티 리드는 반복 불가능한 조회와 유사하며, 한 트랜잭션이 실행 중일 때 다른 트랜잭션에 의해 수정되었지만 아직 커밋되지 않는 행의 데이터를 읽을 수 있을 때 발생한다.</p>
<p>즉, A가 어떤 값을 100 -&gt; 1로 수정하고 아직 커밋되지 않았는데, B가 조회를 하자 1로 출력되는 경우</p>
<h3 id="read_uncommitted">READ_UNCOMMITTED</h3>
<p>가장 낮은 격리 수준으로, 하나의 트랜잭션이 커밋되기 이전에 다른 트랜잭션에 노출되는 문제가 있다.</p>
<h2 id="지속성">지속성</h2>
<blockquote>
<p>성공적으로 수행된 트랜잭션은 영구 적용된다.</p>
</blockquote>
<p>즉, DB에 오류 발생시 원래 상태로 복구되는 기능이 있어야한다는 것이고,
체크섬, 저널링, 롤백등이 그 복구기능이다.</p>
<h1 id="무결성">무결성</h1>
<blockquote>
<p>데이터의 정확성, 일관성, 유효성을 유지한다.</p>
</blockquote>
<p>무결성이 유지되어야 DB에 저장된 데이터 값과 그 값에 해당하는 현실 세계의 실제 값이 일치하는 지에 대한 신뢰가 생긴다.</p>
<h2 id="개체-무결성">개체 무결성</h2>
<blockquote>
<p>기본키로 선택된 필드는 NULL을 허용하지 않는다.</p>
</blockquote>
<h2 id="참조-무결성">참조 무결성</h2>
<blockquote>
<p>서로 참조 관계에 있는 두 테이블의 데이터는 항상 일관된 값을 유지해야한다.</p>
</blockquote>
<h2 id="고유-무결성">고유 무결성</h2>
<blockquote>
<p>특정 속성에 대해 고유한 값을 갖도록 조건이 주어진 경우, 그 속성 값은 모두 고유한 값을 갖는다.</p>
</blockquote>
<h2 id="null-무결성">NULL 무결성</h2>
<blockquote>
<p>특정 속성 값에 NULL이 올 수 없다는 조건이 주어진 경우 그 속성 값은 NULL이 될 수 없다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS_데이터베이스.2 ERD와 정규화]]></title>
            <link>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.2-ERD%EC%99%80-%EC%A0%95%EA%B7%9C%ED%99%94</link>
            <guid>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.2-ERD%EC%99%80-%EC%A0%95%EA%B7%9C%ED%99%94</guid>
            <pubDate>Thu, 22 Aug 2024 11:19:59 GMT</pubDate>
            <description><![CDATA[<p>ERD : Entity Relationship Diagram은 DB 구축에 있어서 가장 기초적인 뼈대 역할을 하며, 릴레이션 간 관계를 정의한 것이다.</p>
<p>서비스 구축시 가장 먼저 신경써야 하며, 나도 프로젝트를 시작할때 첫날은 ERD 작성에 골머리를 썩힌다.</p>
<h1 id="erd의-중요성">ERD의 중요성</h1>
<blockquote>
<p>ERD는 시스템의 요구사항을 기반으로 작성되며, ERD를 기반으로 DB를 구축한다.</p>
</blockquote>
<p>DB 구축 이후에도 디버깅, 비즈니스 프로세스 재설계시 설계도 역할을 한다.
근데 비정형 데이터는 충분히 표현하지 못하는 단점이 있다.</p>
<h1 id="정규화-과정">정규화 과정</h1>
<blockquote>
<p>릴레이션간 잘못된 종속 관계로 인해 DB에 이상 현상이 일어나서 이를 해결하거나, 저장 공간을 효율적으로 사용하기 위해 릴레이션을 여러 개로 분리하는 과정</p>
</blockquote>
<p>예를 들면, 회원이 여러 등급을 갖고있거나, 삭제시 중요한 데이터가 같이 삭제되고, 데이터를 삽입해야 하는데, 하나의 필드 값이 NUll이 되면 안되서 삽입이 안되는 현상</p>
<p>정규화 과정은 정규형 원칙을 기반으로 정규형을 만들어 가는 과정이며, 정규화 레벨은 Normal Form인 NF로 표현한다.</p>
<p>제 1 정규형, 제 2 정규형, 제 3 정규형, 보이스/코드, 제 4 정규형, 제 5 정규형이 있고, 숫자가 커질수록 더 빢빡한 정규화다.</p>
<h2 id="정규형-원칙">정규형 원칙</h2>
<blockquote>
<p>같은 의미를 표현하는 릴레이션이지만, 더 좋은 구조, 중복성 감소, 독립적 관계는 별개의 릴레이션으로 표현, 각 릴레이션은 독립적 표현이 가능해야 하는 원칙</p>
</blockquote>
<h2 id="제1정규형">제1정규형</h2>
<blockquote>
<p>모든 도메인이 더 이상 분해될 수 없는 원자 값으로 구성된 릴레이션</p>
</blockquote>
<p>즉, 한 개의 기본키에 대해 두 개 이상의 값을 갖는 집합이 있어선 안된다. 한 row에 각 컬럼에 한 데이터만 들어있어야 한다는 것.</p>
<p><code>ID : 1, 수강과목 : CS, 알고리즘</code> 이걸 <code>ID : 1, 수강과목 : CS</code>,<code>ID : 1, 수강과목 : 알고리즘</code>
이렇게 분리 하라는 것.</p>
<h2 id="제2정규형">제2정규형</h2>
<blockquote>
<p>제 1정규형 + 부분 함수의 종속성 제거</p>
</blockquote>
<p>기본키가 아닌 모든 속성이 기본키에 완전 함수 종속적이라는 것이다.</p>
<table>
<thead>
<tr>
<th>유저정보</th>
<th>유저 이름</th>
<th>수강명</th>
<th>성적</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>범석</td>
<td>CS</td>
<td>A+</td>
</tr>
<tr>
<td>1</td>
<td>범석</td>
<td>OS</td>
<td>B+</td>
</tr>
<tr>
<td>2</td>
<td>창민</td>
<td>자료구조</td>
<td>B-</td>
</tr>
<tr>
<td>2</td>
<td>창민</td>
<td>CS</td>
<td>C+</td>
</tr>
<tr>
<td>이 사앹가 지금 1정규형인데,</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>유저정보</th>
<th>유저 이름</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>범석</td>
</tr>
<tr>
<td>2</td>
<td>창민</td>
</tr>
<tr>
<td>와</td>
<td></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>유저 이름</th>
<th>수강명</th>
<th>성적</th>
</tr>
</thead>
<tbody><tr>
<td>범석</td>
<td>CS</td>
<td>A+</td>
</tr>
<tr>
<td>범석</td>
<td>OS</td>
<td>B+</td>
</tr>
<tr>
<td>창민</td>
<td>자료구조</td>
<td>B-</td>
</tr>
<tr>
<td>창민</td>
<td>CS</td>
<td>C+</td>
</tr>
<tr>
<td>이렇게 나눈것이다.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h2 id="제3정규형">제3정규형</h2>
<blockquote>
<p>제 2정규형 + 기본키가 아닌 모든 속성이 이행적 함수 종속을 만족하지 않는 상태</p>
</blockquote>
<p>쉽게 말하면 이행적 함수 종속은 A -&gt; B -&gt; C 형태다. </p>
<table>
<thead>
<tr>
<th>유저 이름</th>
<th>수강명</th>
<th>성적</th>
</tr>
</thead>
<tbody><tr>
<td>범석</td>
<td>CS</td>
<td>A+</td>
</tr>
<tr>
<td>범석</td>
<td>OS</td>
<td>B+</td>
</tr>
<tr>
<td>창민</td>
<td>자료구조</td>
<td>B-</td>
</tr>
<tr>
<td>창민</td>
<td>CS</td>
<td>C+</td>
</tr>
<tr>
<td>이게 이행적 함수 종속인데, 이름 -&gt; 수강명 -&gt; 성적으로 이어져있다.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>이걸 `유저이름</td>
<td>수강명<code>,</code>수강명</td>
<td>성적` 으로 나누면 된다.</td>
</tr>
</tbody></table>
<h2 id="보이스코드">보이스/코드</h2>
<blockquote>
<p>제 3 정규형이면서, 결정자가 후보키가 아닌 함수 종속 관계를 제거해서 릴레이션의 함수 종속 관계에서 모든 결정자가 후보키인 상태</p>
</blockquote>
<p>결정자는 X-&gt;Y에서 X이다.</p>
<p>테이블에 학번, 수강명, 강사가 컬럼이면, [CS,창민]을 수강명, 강사에 넣고 싶어. 근데 이러면 학번이 없어서 학번이 null이 되는 문제가 발생한다. 또한, [학번,수강명]이 강사를 결정하고, 강사가 수강명을 결정하는 문제가 발생한다.</p>
<p>즉, 강사 속성이 결정자이지만 후보키가 아니라서 강사를 분리해야 한다.</p>
<p>[학번,강사],[수강명,강사]</p>
<hr>
<p>근데, 정규형이 딥해진다고 성능이 좋아지는건 아니다. 오히려 조인같은거 때문에 성능이 안좋아질 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS_데이터베이스.1 데이터베이스의 기본]]></title>
            <link>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.1-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EA%B8%B0%EB%B3%B8</link>
            <guid>https://velog.io/@rlackdals_98/CS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4.1-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EA%B8%B0%EB%B3%B8</guid>
            <pubDate>Wed, 21 Aug 2024 11:28:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>DB는 일정한 규칙을 통해 구조화되어 저장되는 데이터의 모음</p>
</blockquote>
<p>DBMS : MySql처럼 DB를 제어하는 통합 시스템
즉, 응용 프로그르램 아래에 DBMS가 있고, DBMS가 DB를 관리한다.</p>
<h1 id="entity">Entity</h1>
<blockquote>
<p>사람, 장소, 물건, 사건, 개념 등 여러 속성을 지닌 명사</p>
</blockquote>
<p>엔티티 : 회원
속성 : 이름, 아이디, 주소, 전화번호, 키..
속성은 엔티티를 구성하지만, 서비스에 요구 사항에 따라 속성은 사라질 수 있다.
예를 들어 서울사는 남자를 모아달라는 서비스인데 전화번호, 키는 쓸모없는 속성이기 때문에 해당 엔티티에선 사라진다.</p>
<p>엔티티는 강한엔티티, 약한엔티티로 구분된다.
강한 엔티티 : 건물
약한 엔티티 : 방
딱 보면 건물은 그 자체로 존재하지만, 방은 건물이 없으면 존재할 수 없는 거처럼 강한 엔티티는 스스로 존재유지가 가능하고, 약한 엔티니는 불가능하다.</p>
<h1 id="릴레이션">릴레이션</h1>
<blockquote>
<p>데이터베이스에서 정보를 구분하여 저장하는 기본 단위</p>
</blockquote>
<p>DB는 Entity에 관한 데이터를 1개의 릴레이션에 담아서 관리한다.
우리가 아는 테이블이 릴레이션이며 NoSql에서는 컬렉션이라고 불리는 그것이 바로 릴레이션이다.</p>
<p>즉, 엔티티가 DB에서 관리되기 위해서 릴레이션이 되는데, DBMS에 따라 그 이름이 다르다는 것.</p>
<p>컬럼이 이름, 전화번호면 김창민, 010-0000-0000이 레코드이고, 레코드들이 보여서 테이블이 되고, 테이블이 모여서 DB가 된다.</p>
<h1 id="속성">속성</h1>
<blockquote>
<p>릴레이션에서 관리하는 구체적이며 고유한 이름을 갖는 정보</p>
</blockquote>
<p>위에서 말했듯 엔티티의 속성이다.
엔티티 : 차
속성 : 차 번호, 바퀴 수, 차종, 색상..</p>
<h1 id="도메인">도메인</h1>
<blockquote>
<p>릴레이션에 포함된 각 속성들이 가질 수 있는 값의 집합</p>
</blockquote>
<p>뭔말이냐면,
속성 : 성별
도메인 : 남,여</p>
<h1 id="필드와-레코드">필드와 레코드</h1>
<blockquote>
<p>컬럼명 : 필드, 레코드 : 튜플 (인스턴스 라고 생각하셈)</p>
</blockquote>
<p>자세한건 SQL에서..</p>
<h1 id="관계">관계</h1>
<blockquote>
<p>여러 테이블 간 관계</p>
</blockquote>
<p>관계에는 3가지 종류가 있다</p>
<h2 id="11">1:1</h2>
<p>사람과 주민번호가 1:1 관계이다.
한 사람은 한개의 유효한 주민번호를 갖고, 한개의 유효한 주민번호는 딱 한사람만 가르킨다.</p>
<h2 id="1n">1:N</h2>
<p>포스트와 댓글이 1:N 관계이다
한개의 포스트에 여러 댓글(0개 이상)이 달리고, 여러 댓글은 한 포스트에 속해있기 때문</p>
<h2 id="nm">N:M</h2>
<p>사람과 온라인 게임이 N:M 관계이다.
사람은 여러 종류의 게임을 할 수 있고, 각 게임은 또 여러명의 유저를 감당할 수 있다. 
이 경우 N:M이다.</p>
<p>이때 1:1, 1:N은 모두 두 테이블만을 이용해서 관계를 표현할 수 있지만, N:M의 경우만 중간에 사람_게임 테이블을 생성해서 사람과 1:N, 게임과 1:M 관계를 맺고 간접적으로 연결해야 한다.</p>
<h1 id="키">키</h1>
<blockquote>
<p>테이블 간 관계를 보다 명확하게 하고, 테이블 자체의 인덱스가 된다.</p>
</blockquote>
<h1 id="슈퍼키">슈퍼키</h1>
<p>유일성만 있는 키다. </p>
<h1 id="후보키">후보키</h1>
<p>기본키가 될 수 있으며, 기본키처럼 유일성, 최소싱이 있다.</p>
<h1 id="대체키">대체키</h1>
<p>후보키가 2개 이상이면 하나는 기본키, 나머지는 대체키가 된다.
즉, 후보키 안에 기본키, 대체키가 있는 것.</p>
<h1 id="외래키">외래키</h1>
<p>FK, Foreign Key라고 불리는 이 키는 다른 테이블의 기본키를 참조하는 값이다.</p>
<p>이를 통해 다른 개체와의 관계를 식별 할 수 있다.</p>
<p>다만, 외래키는 중복될 수 있다. 
만약 학생1의 학번이 1111이면 해당 학생 테이블엔 학번이 1111가 기본키지만, 전공과목들 수강신청 테이블에 학생1이 4과목에 수강신청하면 1111가 4개가 존재하기 때문</p>
<h1 id="기본키">기본키</h1>
<p>PK, Primary Key로 불리는 기본키는 유일성과 최소성을 만족한다.
동일 테이블간 중복되는 값이 없고, 키를 구성하는 속성들 중 꼭 필요한 최소한의 속성만 지녀야 한다.</p>
<p>키를 {id, name}처럼 두 개의 속성을 이용할 수 있지만, 기본키는 그래선 안된다.</p>
<h2 id="자연키">자연키</h2>
<p>한국 사람들의 주민 번호처럼 동일 명, 동일 주소, 성별 처럼 중복 가능성이 있는 속성을 다 제외하고 중복 가능성이 없는 속성이 덩그러니 남는다면 그건 자연키다.</p>
<h2 id="인조키">인조키</h2>
<p>가장 많이 사용하는 기본키이다. auto increment등 그냥 1, 2, 3.. 처럼 index 부여하듯 생성하는 키다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS_운영체제.2 프로그래밍 패러다임]]></title>
            <link>https://velog.io/@rlackdals_98/CS%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C.2-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%8C%A8%EB%9F%AC%EB%8B%A4%EC%9E%84</link>
            <guid>https://velog.io/@rlackdals_98/CS%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C.2-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%8C%A8%EB%9F%AC%EB%8B%A4%EC%9E%84</guid>
            <pubDate>Tue, 20 Aug 2024 11:54:09 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>프로그래머에게 프로그래밍의 관점을 갖게 해주는 개발 방법론</p>
</blockquote>
<p>예를 들면 다음과 같다.
객체지향형 프로그래밍은 프로그램을 객체들의 집합으로 보게 하고
함수형 프로그래밍은 상태 값을 지니지 않는 함수 값들의 집합으로 보게 한다.</p>
<p>프로그래밍 패러다임은 크게 선언형과 명령형으로 나눠진다.</p>
<h1 id="선언형">선언형</h1>
<blockquote>
<p>선언형 프로그래밍은 &#39;무엇을&#39; 풀어내는가? 에 집중한다.</p>
</blockquote>
<p>또한 프로그램은 함수 집단이라는 명제가 담겨있기도 하다.</p>
<h2 id="함수형">함수형</h2>
<blockquote>
<p>함수형 프로그래밍은 선언형 패러다임의 일종이다.</p>
</blockquote>
<p>작은 순수 함수들을 블록처럼 쌓아 로직을 구현하고, 고차 함수를 통해 재사용성을 높인다.</p>
<h3 id="순수함수">순수함수</h3>
<p>출력이 입룍에만 의존하는 것</p>
<h3 id="고차-함수">고차 함수</h3>
<p>함수가 함수를 값처럼 매개변수로 받아 로직을 생성할 수 있는 것</p>
<p>이때, 해당 언어가 일급 객체라는 특징을 가져야 고차 함수를 쓸 수 있다.</p>
<blockquote>
<p>변수나 메서드에 함수를 할당할 수 있다.
함수 안에 함수를 매개변수로 담을 수 있다.
함수가 함수를 반환할 수 있다.</p>
</blockquote>
<p>이 외에도 커링, 불변성 등 많은 특징이 있다.</p>
<h1 id="명령형">명령형</h1>
<h2 id="객체지향형">객체지향형</h2>
<blockquote>
<p>Object-Oriented Programming은 객체들의 집합으로 프로그램의 상호 작용을 표현한다.</p>
</blockquote>
<p>데이터를 객체로 취급하며, 객체 내부에 선언된 메서드를 활용하는 방식이다.</p>
<p>설계에 많은 시간이 소요되고, 처리도 느리다.</p>
<h3 id="특징">특징</h3>
<ol>
<li>추상화 : 복잡한 시스템으로부터 핵심적 개념, 기능을 간추리는것</li>
<li>캡슐화 : 속성과 메소드를 하나로 묶고, 일부만 외부에 노출한다.</li>
<li>다형성 : 하나의 메서드나 클래스가 여러 방법으로 동작한다.</li>
<li>상속성 : 상위 클래스의 특성을 하위 클래스가 재사용한다.</li>
</ol>
<h3 id="설계-원칙">설계 원칙</h3>
<p>SOLID 원칙을 지켜야한다</p>
<h4 id="s">S</h4>
<p>Single Responsibility Principle, 단일 책임 원칙이다.
모든 클래스는 각각 하나의 책임만 가져야 한다.</p>
<h4 id="o">O</h4>
<p>Open Close Principle. 개방 폐쇄 원칙이다.
기존의 코드는 유지하면서 확장을 쉽게 할 수 있어야한다.</p>
<h4 id="l">L</h4>
<p>Liskov Substitution Principle. 리스코프 치환 원칙이다.
객체는 프로그램의 정확성을 유지하면서 하위 타입의 인스턴스로 바꿀 수 있는 원칙이다. 즉, 부모 객체에 자식 객체를 넣어도 시스템이 돌아가야한다.</p>
<h4 id="i">I</h4>
<p>Interface Segregation Principle. 인터페이스 분리 원칙이다.
하나의 일반적 인터페이스보다 구체적인 여러개 인터페이스를 만들어야 한다.</p>
<h4 id="d">D</h4>
<p>Dependency Iversion Principle. 의존 역전 원칙이다.
자신보다 변하기 쉬운것에 의존하던걸, 인터페이스나 상위 클래스에 의존하여 변하기 쉬운 것에 변화에 영향을 받지 않는것.
즉, 상위 계층은 하위 계층의 변화로부터 독립한다.</p>
<h2 id="절차지향형">절차지향형</h2>
<blockquote>
<p>로직이 수행되어야 할 연속적인 계산 과정으로 이뤄진다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 28.  N:M]]></title>
            <link>https://velog.io/@rlackdals_98/Spring-NM</link>
            <guid>https://velog.io/@rlackdals_98/Spring-NM</guid>
            <pubDate>Tue, 20 Aug 2024 11:16:05 GMT</pubDate>
            <description><![CDATA[<p>@ManyToMany 어노테이션은 N 대 M 관계를 맺어준다.</p>
<h2 id="단방향">단방향</h2>
<p>N:M 관계에서는 중간 테이블이 필요하다. 
중간 테이블의 이름을 orders, 조인할 컬럼을 food_id, user_id로 설정하면 다음과 같다</p>
<pre><code>@Entity
@Table(name = &quot;food&quot;)
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;

    @ManyToMany
    @JoinTable(name = &quot;orders&quot;, // create orders table
    joinColumns = @JoinColumn(name = &quot;food_id&quot;), // Food Entity&#39;s join column
    inverseJoinColumns = @JoinColumn(name = &quot;user_id&quot;)) // User Entity&#39;s join column
    private List&lt;User&gt; userList = new ArrayList&lt;&gt;();
}</code></pre><p>다만, 생성되는 중간 테이블을 컨트롤하기 어렵기 때문에 중간 테이블 변경시 문제 발생의 위험이 있다.</p>
<hr>
<h2 id="양방향">양방향</h2>
<pre><code>@Entity
@Table(name = &quot;food&quot;)
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;

    @ManyToMany
    @JoinTable(name = &quot;orders&quot;, // create orders table
    joinColumns = @JoinColumn(name = &quot;food_id&quot;), // Food Entity&#39;s join column
    inverseJoinColumns = @JoinColumn(name = &quot;user_id&quot;)) // User Entity&#39;s join column
    private List&lt;User&gt; userList = new ArrayList&lt;&gt;();
}</code></pre><pre><code>@Entity
@Table(name = &quot;users&quot;)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToMany(mappedBy = &quot;userList&quot;)
    private List&lt;Food&gt; foodList = new ArrayList&lt;&gt;();
}</code></pre><p>고객 Entity에 @ManyToMany로 연결하고, mappedBy 옵션으로 외래 키의 주인을 설정하면 양방향 관계가 가능하다.</p>
<hr>
<h2 id="중간-테이블">중간 테이블</h2>
<p>위의 경우 중간테이블을 간접적으로 생성했지만, 직접 생성하면 변경 발생시 컨트롤이 쉬워 확장성에 좋다.</p>
<pre><code>@Entity
@Table(name = &quot;food&quot;)
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;

    @OneToMany(mappedBy = &quot;food&quot;)
    private List&lt;Order&gt; orderList = new ArrayList&lt;&gt;();
}</code></pre><pre><code>@Entity
@Table(name = &quot;users&quot;)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = &quot;user&quot;)
    private List&lt;Order&gt; orderList = new ArrayList&lt;&gt;();
}</code></pre><pre><code>@Entity
@Table(name = &quot;orders&quot;)
@EntityListeners(AuditingEntityListener.class) //main에 EnableJpaAuditing
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = &quot;food_id&quot;)
    private Food food;

    @ManyToOne
    @JoinColumn(name = &quot;user_id&quot;)
    private User user;

    @CreatedDate
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime orderDate;
}</code></pre><p>이 경우 중간 테이블이 오래키를 모두 소지하고, 음식, 고객이 접근하는 형식으로 바뀐다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 27. 1:N]]></title>
            <link>https://velog.io/@rlackdals_98/Spring-1N</link>
            <guid>https://velog.io/@rlackdals_98/Spring-1N</guid>
            <pubDate>Tue, 20 Aug 2024 11:15:38 GMT</pubDate>
            <description><![CDATA[<p>@OneToMany어노테이션은 1:N 관계를 맺어준다.</p>
<h2 id="단방향">단방향</h2>
<p>1:N 관계에선 N 입장의 Entity가 외래 키의 주인이 된다고 했었다.
실제로 N:1 관계에선 그렇게 진행했다.</p>
<p>하지만 다음 경우는 1의 Entity가 외래 키의 주인이 되는 상황이다.
이 상황에선 N의 Entity가 외래 키 컬럼을 만들어 추가하고 관리는 1의 Entity가 한다.</p>
<pre><code>@Entity
@Table(name = &quot;food&quot;)
public class Food {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;

    @OneToMany
    @JoinColumn(name = &quot;food_id&quot;) // users 테이블에 food_id 컬럼
    private List&lt;User&gt; userList = new ArrayList&lt;&gt;();
}</code></pre><pre><code>@Entity
@Table(name = &quot;users&quot;)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}</code></pre><hr>
<h2 id="양방향">양방향</h2>
<p>위와 같은 1:N 관계에선 양방향 관계가 존재하지 않는다.
억지로 한다면</p>
<pre><code>@Entity
@Table(name = &quot;users&quot;)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToOne
    @JoinColumn(name = &quot;food_id&quot;, insertable = false, updatable = false)
    private Food food;
}</code></pre><p>를 통해 join으로 흉내는 가능하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 26. N:1]]></title>
            <link>https://velog.io/@rlackdals_98/Spring-N1</link>
            <guid>https://velog.io/@rlackdals_98/Spring-N1</guid>
            <pubDate>Tue, 20 Aug 2024 11:15:19 GMT</pubDate>
            <description><![CDATA[<p>@ManyToOne 어노테이션은 N:1 관계를 맺어준다.</p>
<h2 id="단방향">단방향</h2>
<p>@OneToOne을 @ManyToOne로 수정하면 1:1 관계와 동일한 형태로 사용한다.</p>
<hr>
<h2 id="양방향">양방향</h2>
<pre><code>@Entity
@Table(name = &quot;food&quot;)
public class Food {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;

    @ManyToOne
    @JoinColumn(name = &quot;user_id&quot;)
    private User user;
}</code></pre><pre><code>@Entity
@Table(name = &quot;users&quot;)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = &quot;user&quot;)
    private List&lt;Food&gt; foodList = new ArrayList&lt;&gt;();

    public void addFoodList(Food food) {
        this.foodList.add(food);
        food.setUser(this); // 외래 키(연관 관계) 설정
    }
}</code></pre><p>외래 키의 주인 Entity는 사실상 1:1과 동일하지만, 비주인 Entity는 private Food food;던 형태에서
private List<Food> foodList = new ArrayList&lt;&gt;();로 리스트 형태로 변했다.</p>
<p>이게 바로 N:1 관계이기 때문인데, 외래 키의 주인을 여러번 참조하기 때문에 리스트 형태로 받는것이다.
이후 이 List는 자바 내에서 사용되기 때문에 메소드로 기능을 구현해서 사용한다.</p>
<pre><code>User user = new User();
user.setName(&quot;Robbie&quot;);
user.addFoodList(food);
user.addFoodList(food2);</code></pre><p>이 코드를 통해 List에 저장하고, 추후 get메소드를 통해 해당 리스트를 받고 for문으로 출력을 진행하면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 25.  1:1]]></title>
            <link>https://velog.io/@rlackdals_98/Spring-11</link>
            <guid>https://velog.io/@rlackdals_98/Spring-11</guid>
            <pubDate>Tue, 20 Aug 2024 11:14:58 GMT</pubDate>
            <description><![CDATA[<p>@OneToOne 어노테이션은 1대1 관계를 맺어준다.</p>
<h2 id="단방향">단방향</h2>
<p><strong>외래 키의 주인 정하기</strong>
보통 1:N 관계에서는 N의 관계인 Entity가 외래 키의 주인이지만, 1:1 에서는 직접 지정해야한다.
주인이 외래 키를 등록, 수정, 삭제가 가능하며, 아닌쪽은 읽기만 가능하다.</p>
<p>외래 키의 주인 Entity는 @JoinColumn()을 통해 외래 키를 지정할 수 있다.
여기서 컬럼명, null 여부, unique등 옵션 설정이 가능하다.</p>
<pre><code>@Entity
@Table(name = &quot;food&quot;)
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;

    @OneToOne
    @JoinColumn(name = &quot;user_id&quot;)
    private User user;
}</code></pre><p>외래 키의 주인인 Entity는 위처럼 관계 - 외래 키 지정을 통해 다른 Entity 타입을 필드로 갖는다.</p>
<pre><code>@Entity
@Table(name = &quot;users&quot;)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}</code></pre><p>아닌 Entity는 이렇게 그냥 아무것도 없다.</p>
<p>만약 Food말고 User를 외래 키의 주인으로 하고 싶다면,</p>
<pre><code>@OneToOne
@JoinColumn(name = &quot;food_id&quot;)
private Food food;</code></pre><p>를 User에 추가하고, Food의 해당 부분은 삭제하면 된다. </p>
<hr>
<h2 id="양방향">양방향</h2>
<p>양방향 관계에서는 외래 키의 주인을 지정할 때 mappedBy 옵션을 사용한다.
mappedBy의 속성값은 외래 키의 주인 Entity의 필드명을 사용한다.</p>
<p>단 방향의 경우 위에서 말한대로 다른 Entity의 타입을 필드로 갖고 @JoinColumn()을 사용하고,
양 방향의 경우 주인은 단방향과 동일하되, 비주인 Entity는 주인 Entity 타입의 필드를 선언하고 mappedBy 옵션을 통해 
주인 Entity의 JoinColumn으로 설정된 필드명을 사용하면 된다.</p>
<pre><code>@Entity
@Table(name = &quot;food&quot;)
public class Food {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;

    @OneToOne
    @JoinColumn(name = &quot;user_id&quot;)
    private User user;
}</code></pre><pre><code>@Entity
@Table(name = &quot;users&quot;)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToOne(mappedBy = &quot;user&quot;)
    private Food food;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 24. Entity]]></title>
            <link>https://velog.io/@rlackdals_98/Spring-Entity</link>
            <guid>https://velog.io/@rlackdals_98/Spring-Entity</guid>
            <pubDate>Tue, 20 Aug 2024 11:14:30 GMT</pubDate>
            <description><![CDATA[<h2 id="db-table">DB table</h2>
<p><strong>테이블간 연관 관계</strong>
고객 테이블과 음식 테이블이 있을때,
고객이 음식을 주문하면, 주문 정보는 어느 테이블에 들어가는가?
정답은 &quot;중간 테이블이 필요하다&quot; 이다.</p>
<ul>
<li>고객 테이블의 경우</li>
</ul>
<p>한명의 고객이 여러 음식을 주문할 수 있다. -&gt; 고객:음식=1:N
Kim이 불고기 피자(1), 포테이토 피자(2)를 주문하면 고객 테이블에 다음과 같이 저장된다.</p>
<pre><code>INSERT INTO users (name, food_id) VALUES (&#39;Kim&#39;, 1);
INSERT INTO users (name, food_id) VALUES (&#39;Kim&#39;, 2);</code></pre><p>이 경우 Kim이라는 고객이 중복 생성되어 id를 1,2를 할당받는 문제가 생긴다.</p>
<ul>
<li>음식 테이블의 경우
하나의 음식은 여러 고객에게 주문될 수 있다. 음식:고객=1:N
후라이트 치킨이 1,2번 고객에게 주문되면 음식 테이블에 다음과 같이 저장된다.</li>
</ul>
<pre><code>INSERT INTO food (name, price, user_id) VALUES (&#39;후라이드 치킨&#39;, 15000, 1);
INSERT INTO food (name, price, user_id) VALUES (&#39;후라이드 치킨&#39;, 15000, 2);</code></pre><p>이 경우도 불필요하게 음식의 이름이 중복 생성되는 문제가 발생한다.</p>
<p>위 두 경우를 종합하면 고객:음식=1:N, 음식:고객=1:N 즉 고객:음식=N:M 이라고 볼 수 있다.
N:M 연관 관계의 테이블은 중간 테이블을 생성해서 연관 관계를 해결 할 수 있게 된다.</p>
<ul>
<li>중간 테이블의 경우<pre><code>INSERT INTO orders (user_id, food_id, order_date) VALUES (1, 1, SYSDATE());
INSERT INTO orders (user_id, food_id, order_date) VALUES (2, 1, SYSDATE());
INSERT INTO orders (user_id, food_id, order_date) VALUES (2, 2, SYSDATE());
INSERT INTO orders (user_id, food_id, order_date) VALUES (1, 4, SYSDATE());
INSERT INTO orders (user_id, food_id, order_date) VALUES (2, 3, SYSDATE());</code></pre>처럼 유저, 음식, 날짜를 이용해서 중복없이 처리할 수 있다.</li>
</ul>
<p>이렇게 3개의 테이블을 sql로 조회하기 위해선 join문을 사용할 수 있는데,
join문은 동일한 column이 있으면 가능하기 때문에 방향이라는 개념은 존재하지 않는다.</p>
<hr>
<h2 id="entity간-연간-관계">Entity간 연간 관계</h2>
<p>한명의 고객이 여러 음식을 주문할 수 있는 음식:고객=N:1 관계인 경우
고객 Entity에서 <code>List&lt;Food&gt; foodList = new ArrayList&lt;&gt;()</code>를 통해 여러번 접근이 가능함을 표현할 수 있다.
DB 테이블은 join을 통해서 바로바로 접근이 가능하지만, Entity는 필드에 해당 정보가 없으면 조회가 불가능하기 때문이다.
따라서 테이블에 실제 column으론 존재하지 않지만 Entity 상태에서 다른 Entity를 참조하기 위해 이러한 방법을 사용한다.</p>
<hr>
<p><strong>단방향</strong></p>
<pre><code>@Entity
@Table(name = &quot;food&quot;)
public class Food {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String name;
  private double price;

  @ManyToOne
  @JoinColumn(name = &quot;user_id&quot;)
  private User user;
}</code></pre><pre><code>@Entity
@Table(name = &quot;users&quot;)
public class User {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String name;
}</code></pre><p>음식 Entity: @ManyToOne라는 관계 어노테이션과 @JoinColumn라는 외래 키 column 지정 어노테이션을 사용
고객 Entity: 그냥 아무것도 없음. 즉, 고객에선 음식을 참조할 수 없다.
이런 관계를 단방향 관계라고 한다.</p>
<hr>
<p><strong>양방향</strong></p>
<pre><code>@Entity
@Table(name = &quot;food&quot;)
public class Food {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String name;
  private double price;

  @ManyToOne
  @JoinColumn(name = &quot;user_id&quot;)
  private User user;
}</code></pre><pre><code>@Entity
@Table(name = &quot;users&quot;)
public class User {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String name;

  @OneToMany(mappedBy = &quot;user&quot;)
  private List&lt;Food&gt; foodList = new ArrayList&lt;&gt;();
}</code></pre><p>음식 Entity: 단방향의 상태와 동일
고객 Entity: @OneToMany라는 관계 어노테이션이 생겼고, mappedBy = &quot;user&quot; 옵션을 통해 Food 엔터티의 user 필드에 의해 매핑됨을 알린다.
물론 Food Entity는 아래 List<Food>를 통해 알게되는 것이고, user는 Food의 private User user;를 말하는 것이다.</p>
<hr>
</div>
</details>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 23. 필터]]></title>
            <link>https://velog.io/@rlackdals_98/Spring-%ED%95%84%ED%84%B0</link>
            <guid>https://velog.io/@rlackdals_98/Spring-%ED%95%84%ED%84%B0</guid>
            <pubDate>Tue, 20 Aug 2024 11:14:01 GMT</pubDate>
            <description><![CDATA[<h2 id="filter">Filter?</h2>
<p>Web 애플리케이션에서 관리되는 영역.
Client로 부터 오는 요청과 응답에 대해 최초/최종 단계의 위치
이를 통해 요청과 응답의 정보를 변경하거나 부가적 기능을 추가함.</p>
<p>주로 로깅, 보안 처리등 범용적으로 적용해야하는 작업에 활용함
    또한 인증/인가 관련 로직도 처리 가능. 이 방식이면 비즈니스 로직과 인증/인가 분리가 가능함</p>
<hr>
<h2 id="filter-chain">Filter Chain</h2>
<p>필터는 한 개가 아닌 여러 필터가 Chain 형식으로 처리될 수 있음 </p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 22. 인증과 인가]]></title>
            <link>https://velog.io/@rlackdals_98/Spring-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@rlackdals_98/Spring-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Tue, 20 Aug 2024 11:13:34 GMT</pubDate>
            <description><![CDATA[<h1 id="인증과-인가">인증과 인가</h1>
<p>인증
    인증은 해당 유저가 실제 유저인지 인증하는 개념
    지문인식, 로그인등등 실제 유저가 맞는지 확인하는 절차</p>
<p>인가 
    해당 유저가 특정 리소스에 접근이 가능한지 확인하는 개념
    관리자 페이지에 접근시 관리자 권한이 있는지 확인하는 절차</p>
<hr>
<h1 id="웹-애플리케이션-인증">웹 애플리케이션 인증</h1>
<p>웹은 보통 서버- 클라이언트 구조로 되어있음
Http 프로토콜을 이용해서 통신하는데, Http는 비연결, 무상태임
근데 실제로 인터넷을 사용하면 모든 정보들이 연속적으로 느껴짐
이는 사실 url을 계층적으로 설계하고있기 때문</p>
<p>그렇다면 인증상태 유지는 어떻게 하는건가??</p>
<hr>
<p><strong>인증의 방식</strong></p>
<p>보통 웹 애플리케이션은 두 가지 방법을 통해서 인증을 처리함</p>
<p><strong>쿠키-세션 방식</strong>
특정 유저가 로그인 되었다는 상태를 저장하는 방식
인증과 관련된 약간의 정보(Sesstion Id)를 서버가 갖고, 유저도 어느정도의 인증 정보를 갖고 로그인을 유지함</p>
<p><strong>JWT 기반 방식</strong>
Json Web Token라는 인증에 필요한 정보를 암호화한 토큰을 사용함
JWT 기반 인증은 쿠키/세션 방식과 유사하게 JWT토큰을 HTTP 헤더에 실어 서버가 클라이언트를 식별하게 함</p>
<hr>
<h2 id="쿠키와-세션">쿠키와 세션</h2>
<p>쿠키와 세션 모두 HTTP에 상태 정보를 유지하기 위한 도구임
서버는 쿠키와 세션을 이용해서 클라마다의 인증/인가를 할 수 있게 됨</p>
<p><strong>쿠키</strong>
클라이언트에 저장될 목적의 작은 정보 파일
구성오소로는 다음 5개 요소가 있음.</p>
<blockquote>
<p>Name : 쿠키 PK
Value : 쿠키 값
Domain : 쿠키가 저장된 도메인
Path : 쿠키가 사용되는 경로
Expires : 쿠키 만료기한</p>
</blockquote>
<p><strong>세션</strong>
서버에서 일정시간동안 클라이언트 상태를 유지하기 위한 것
서버에서 클라이언트 별로 유일부이한 세션 ID를 부여하고, 클라이언트 별 필요한 정보를 서버에 저장함
세션 ID는 클라이언트의 쿠키값(세션 쿠키)로 저장되어 클라이언트 식별에 사용됨</p>
<p>즉, 클라이언트가 서버에 인증을 요청하면 서버는 쿠키에 세션 ID를 담아서 전달함.
클라이언트는 해당 세션 ID를 쿠키에 저장하고, 다음 요청시 세션 ID를 포함함
서버는 해당 세션 ID를 보고 동일한 클라이언트임을 파악함</p>
<hr>
<h2 id="쿠키-생성읽기">쿠키 생성/읽기</h2>
<p><strong>쿠키 생성</strong></p>
<pre><code>public static void addCookie(String cookieValue, HttpServletResponse res) {
    try {
        cookieValue = URLEncoder.encode(cookieValue, &quot;utf-8&quot;).replaceAll(&quot;\\+&quot;, &quot;%20&quot;); 

        Cookie cookie = new Cookie(AUTHORIZATION_HEADER, cookieValue); // Name-Value
        cookie.setPath(&quot;/&quot;);
        cookie.setMaxAge(30 * 60);

        res.addCookie(cookie);
    } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(e.getMessage());
    }
}</code></pre><p>new Cookie(AUTHORIZATION_HEADER, cookieValue);
    쿠키에 저장될 Name과 Value를 생성자로 받는 Cookie 객체를 생성</p>
<p>setPath(&quot;/&quot;), setMaxAge(30 * 60)
    Path와 만료시간 지정</p>
<p>res.addCookie(cookie);
    생성한 Cookie 객체를 HttpServletResponse 객체에 추가하여 브라우저로 반환함</p>
<hr>
<p><strong>쿠키 읽기</strong></p>
<pre><code>public static final String AUTHORIZATION_HEADER = &quot;Authorization&quot;;
~
@GetMapping(&quot;/get-cookie&quot;)
public String getCookie(@CookieValue(AUTHORIZATION_HEADER) String value) {
System.out.println(&quot;value = &quot; + value);

    return &quot;getCookie : &quot; + value;
}</code></pre><p>@CookieValue(&quot;Cookie의 Name&quot;)
    Cookie의 Name 정보를 전달하면 해당 정보를 토대로 Cookie의 Value를 가져옴</p>
<hr>
<h2 id="새션-생성읽기">새션 생성/읽기</h2>
<p><strong>세션 생성</strong></p>
<pre><code>@GetMapping(&quot;/create-session&quot;)
public String createSession(HttpServletRequest req) {

    HttpSession session = req.getSession(true);    
    session.setAttribute(AUTHORIZATION_HEADER, &quot;Robbie Auth&quot;);

    return &quot;createSession&quot;;
}</code></pre><p>HttpSession session = req.getSession(true);
    세션이 존재하면 해당 세션을 반환하고, 없으면 생성하고 반환</p>
<p>session.setAttribute(AUTHORIZATION_HEADER, &quot;Robbie Auth&quot;);
    세션에 저장될 정보 Name - Value를 추가함</p>
<hr>
<p><strong>세션 읽기</strong></p>
<pre><code>@GetMapping(&quot;/get-session&quot;)
public String getSession(HttpServletRequest req) {

    HttpSession session = req.getSession(false);

    String value = (String) session.getAttribute(AUTHORIZATION_HEADER); 
    System.out.println(&quot;value = &quot; + value);

    return &quot;getSession : &quot; + value;
}</code></pre><p>HttpSession session = req.getSession(false);
    세션이 존재하면 세션을 반환하고, 없으면 null을 반환함.</p>
<p>session.getAttribute(”세션에 저장된 정보 Name”)
    Name을 이용해서 세션에 저장된 Value를 가져옴</p>
<hr>
<h2 id="jwt">JWT</h2>
<p><strong>JWT</strong></p>
<blockquote>
<p>Json Web Token</p>
</blockquote>
<p>JSON 포맷을 이용해서 사용자에 대한 속성을 저장하는 Claim 기반 Web Token
일반적으로 쿠키 저장소를 사용해서 JWT를 저장함</p>
<hr>
<p><strong>JWT 사용 이유</strong>
서비스 운영을 위해서 여러대의 서버가 운영될 수 있음
이때 각 서버가 각 클라이언트의 정보를 갖게되면, 특정 클라이언트가 타 서버에 접속하면 문제가 발생함.
이를 위해서 세션 저장소를 생성하거나, JWT를 사용해서 클라이언트가 어떤 서버에 접속하던 API 요청을 처리할 수 있게 함</p>
<p>세션 저장소는 말 그대로 저장소고, JWT를 사용하는 방식은 다음과 같음
로그인 정보를 JWT로 암호화하여 Client에 저장하고, JWT를 통해서 인증/인가를 하는것임.
이때 모든 서버는 동일한 Secret Ket를 소유함</p>
<p>장점</p>
<blockquote>
<p>동시 접속자가 많을 시 서버의 부하를 낮춤
Client와 Server가 다른 도메인을 사용할때 좋음</p>
</blockquote>
<p>단점</p>
<blockquote>
<p>구현이 어려움
JWT에 담는 내용이 커질수록 네트워크 비용이 증가함
JWT의 일부 만료가 불가능함 
Secret key 유출시 JWT 조작이 가능함</p>
</blockquote>
<hr>
<p><strong>JWT 사용 흐름</strong>
Client가 username, password로 로그인 성공 시</p>
<blockquote>
<p>서버에서 로그인 정보를 JWT로 암호화함.
서버에서 직접 쿠키를 생성해 JWT를 담아 response에 전달. 이때 방식은 개발자 맘임
브라우저 쿠키 저장소에 JWT가 저장됨 자동으로</p>
</blockquote>
<p>Client에서 JWT 통해 인증방법</p>
<blockquote>
<p>서버에서 API 요청마다 쿠키에 포함된 JWT를 찾아서 사용함.
서버는 Client가 전달한 JWT의 위조 여부를 검증함.
서버는 JWT 유효 기간이 지나지 않았는지 검증함.
검증 성고시, JWT에서 사용자 정보를 가져와 확인함</p>
</blockquote>
<hr>
<p><strong>JWT 구조</strong>
<a href="https://jwt.io/">https://jwt.io/</a>여기를 보면 잘 나옴
Header - Payload - Signature로 구성됨
Header와 Signature에는 암호화 관련 정보 양식을, PayLoad네는 유저 정로를 담음</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 21. Bean]]></title>
            <link>https://velog.io/@rlackdals_98/Spring-Bean</link>
            <guid>https://velog.io/@rlackdals_98/Spring-Bean</guid>
            <pubDate>Tue, 20 Aug 2024 11:12:58 GMT</pubDate>
            <description><![CDATA[<h1 id="bean-수동-등록">Bean 수동 등록</h1>
<p>보통 @Component 사용시 @ComponentScan에 의해 해당 클래스는 자동으로 Bean으로 등록된다.
프로젝트의 규모가 커질 수록, 비즈니스 로직과 관련된 클래스가 많아질수록 이 방법이 개발 생산성에 유리해서 이게 일반적인 방법임</p>
<p>하지만, 기술적 문제나 공통적 관심사를 처리할 때는 수동으로 등록하는게 좋음
ex) 공통 로그처리와 같은 비즈니스 로직 지원 기능</p>
<p>비즈니스 Bean보다는 적어서 수동으로 할만하고, 수동 등록 Bean은 위치 파악에 유리하다는 장점이 있음</p>
<hr>
<p><strong>수동 등록 방법</strong></p>
<pre><code>@Configuration
public class PasswordConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}</code></pre><p>위와 같이 Bean으로 등록하고 싶은 메소드에 직접 @Bean 어노테이션을 등록할 수 있다.
구체적으로는 해당 메소드가 반환하는 BCryptPasswordEncoder() 객체가 passwordEncoder라는 이름의 빈으로 등록된것.
passwordEncoder가 Bean으로 등록되어서 다음과 같이 사용이 가능해졌다.</p>
<pre><code>@Autowired
PasswordEncoder passwordEncoder;
~~
String encodePassword = passwordEncoder.encode(password);</code></pre><p>그래서 BCryptPasswordEncoder의 내장 메소드인 encode를 위처럼 사용이 가능해진것이다.</p>
<hr>
<h1 id="동일-타입-bean이-2개">동일 타입 Bean이 2개</h1>
<hr>
<p>인터페이스를 사용하면 동일할 메소드 명을 갖는 2개의 클래스가 생성된다.</p>
<pre><code>public interface Food {
    void eat();
}

public class Chicken implements Food{~}
public class Pizza implements Food {~}</code></pre><p>Food의 구현체인 Chicken과 Pizza를 @Component를 이용해서 Bean으로 등록했다고 가정하면</p>
<pre><code>@Autowired
Food food;</code></pre><p>상황에서 어떤 Bean 객체를 주입해야할지 Spring에서 처리할 수 없다. </p>
<p>이 경우 개발자가 직접 주입할 객체를 알려줘야 하는데 다음과 같다.</p>
<p><strong>등록된 Bean 이름 명시하기</strong></p>
<pre><code>@Autowired
Food pizza;

@Autowired
Food chicken;</code></pre><p>그냥 이렇게 직접 Bean 이름을 명시하면 된다.</p>
<p><strong>@Primary 사용하기</strong></p>
<pre><code>@Component
@Primary
public class Chicken implements Food {
   @Override
   public void eat() {
      System.out.println(&quot;치킨을 먹습니다.&quot;);
      }
   }</code></pre><p>위 처럼 @Primary를 이용해서 해당 Bean을 우선으로 주입시킬 수 있다.</p>
<p><strong>@Qualifer 사용하기</strong></p>
<pre><code>@Component
@Qualifier(&quot;pizza&quot;)
public class Pizza implements Food {
   @Override
   public void eat() {
      System.out.println(&quot;피자를 먹습니다.&quot;); 
   } 
}
~~
@Autowired
@Qualifier(&quot;pizza&quot;)
Food food;
</code></pre><p>위처럼 @Qualifier(&quot;pizza&quot;)를 이용해서 주입시킬 수 있다.</p>
<p>이때 Primary와 Qualifer가 동시에 존재한다면, Qualifer가 우선순위가 더 높다.
Primary는 전역에서 유효하고, Qualifer는 명시한 곳에서만 효과가 있다. 즉, Qualifer의 범위가 더 좁다고 할 수 있는데,
Spring에서는 범위가 좁은것이 대부분 우선순위가 높다.</p>
<p>따라서 범용적으로 사용되는 객체에는 Primary를 설정해두고, 특정지역에서만 사용할 Bean에 Qualifier를 사용하면 되겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[디자인 패턴]]></title>
            <link>https://velog.io/@rlackdals_98/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@rlackdals_98/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Tue, 20 Aug 2024 10:59:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>프로그램 설계시 발생한 문제점들을 객체간 상호 관계를 이용해 해결할 수 있도록 하나의 규약으로 만들어 둔것.</p>
</blockquote>
<h1 id="싱글톤-패턴">싱글톤 패턴</h1>
<hr>
<blockquote>
<p>하나의 클래스에 오직 하나의 인스턴스만!</p>
</blockquote>
<p>원래 하나의 클래스로 무한대의 인스턴스를 만들 수 있다. (클래스는 설계도니까.)
하지만 그렇게 하지 않고, 하나의 클래스를 기반으론 하나의 인스턴스만 만들어 이를 기반으로 로직을 만드는 패턴이다.
보통 DB 연결 모듈에 많이 사용하고, Spring도 그렇다(고 알고있다. Prototype 제외하면..).</p>
<p>하나의 인스턴스를 만들고 해당 인스턴스를 여러 모듈이 공유하며 사용하게 된다. 
인스턴스 생성을 하나만 하니 비용은 줄지만, 의존성이 높아지는 단점이 있다.</p>
<p>또한 싱글톤 패턴은 TDD(Test Driven Development)시 단점이 된다.
TDD는 보통 단위 테스트를 하는데, 단위 테스트는 테스트 끼리 서로 독집적인 테스트이다.
하지만, 싱글톤은 하나의 인스턴스를 공유하니까 TDD가 쉽지 않다.</p>
<p>위에 서술한 여러 단점중 가장 핵심이 의존성이 높다는 것이다.
이를 해결하기 위해서 DI가 필수라고 볼 수 있다.</p>
<h1 id="팩토리-패턴">팩토리 패턴</h1>
<hr>
<blockquote>
<p>객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴</p>
</blockquote>
<p>상속 관계인 두 클래스에서 상위가 뼈대, 하위 애서 객체 생성 관련 내용을 구체화하는 패턴이다.</p>
<p>상위-하위 클래스가 분리되기 때문에 느슨한 결합을 갖게되며, 상위 클래스는 인스턴스 생성과 관련이 없기 때문에 매우매우 유연하다.
그리고 객체 생성 로직이 따로 분리되어있기 때문에 리팩토링을 진행해도 한 곳만 고치면 된다.</p>
<p>예를들어 상위 클래스는 커피공장이고 하위 클래스는 라떼 레시피, 아아 레시피 등등 구체적 레시피가 있다. 상위 클래스는 그냥 들어오는 레시피를 토대로 음료만 찍으면 된다.</p>
<h1 id="전략-패턴">전략 패턴</h1>
<hr>
<blockquote>
<p>객체의 행위를 바꾸고 싶은 경우 캡슐화한 알고리즘을 컨텍스트 안에서 바꿔주면서 상호 교체하는 패턴</p>
</blockquote>
<p>정책 패턴이라고도 불리는 이 패턴은 객체의 행위를 직접 수정하지 않는다. 그저 갈아 끼울뿐
결제라는 행위를 할때 네이버 페이, 카카오 페이, 카드 결제등 여러 방법으로 결제하듯 전략만 바꾸는거다.</p>
<h1 id="옵저버-패턴">옵저버 패턴</h1>
<hr>
<blockquote>
<p>주체가 어떤 객체(Subject)의 상태 변화를 관찰하다가, 변화 발생시 옵저버들에게 변화를 알리는 패턴</p>
</blockquote>
<p>여기서 주체는 객체의 상태 변화를 보는 관찰자이며,
옵저버는 객체의 상태 변화에 따라 전달되는 메서드 등을 기반으로 추가 변경 사항이 생기는 객체들 이다.</p>
<p>트위터가 옵저버 패턴을 활용한 서비스다.
내가 어떤 사람인 주체를 팔로우 했다면, 주체가 포스팅시 알림이 팔로워에게 간다.</p>
<p>또한 주로 이벤트 패턴에 사용되는 이 패턴은 MVC에서도 사용된다.
Model에서 변경 발생시 update 메서드로 옵저버(View)에게 알려주고, 이를 기반으로 Controller가 작동되는 방식이다.</p>
<h1 id="프록시-패턴과-프록시-서버">프록시 패턴과 프록시 서버</h1>
<hr>
<blockquote>
<p>대상 객체에 접근하기 전 그 접근에 대한 흐름을 가로채 대상 객체 앞단의 인터페이스 역할을 하는 패턴</p>
</blockquote>
<p>먼저 프록시 객체는 다음과 같다.
어떠한 대상의 기본적 동작(속성 접근, 할당, 순회, 열거, 함수 호출..)등 작업을 가로챌 수 있는 객체</p>
<p>프록시 서버는 다음과 같다.
서버와 클라이언트 사이에서 클라이언트가 자신ㄴ을 통해 다른 네트워크 서비스에 간접적으로 접속하게 해주는 시스템이나 프로그램</p>
<p>Q. 그렇다면, 로깅 및 보안 처리에 활용되고, 인증/인가를 처리하는 필터가 프록시 객체인가?
A. 이건 아니다. 프록시 패턴은 주로 객체 접근을 제어하는데 사용하고, 필터는 요청이나 응답을 변형하거나 전처리하는것에 목직이 있다.</p>
<h1 id="이터레이터-패턴">이터레이터 패턴</h1>
<hr>
<blockquote>
<p>Iterator를 사용해서 컬렉션의 요소들에 접근하는 패턴</p>
</blockquote>
<p>자료형의 구조와는 상관없이 이터레이터라는 인터페이스로 순회가 가능하다.</p>
<h1 id="노출모듈-패턴">노출모듈 패턴</h1>
<hr>
<blockquote>
<p>즉시 실행 함수를 통해 private, public 같은 접근 제어자를 만드는 패턴</p>
</blockquote>
<h1 id="mvc-패턴">MVC 패턴</h1>
<hr>
<blockquote>
<p>Model, View, Controller로 이뤄진 디자인 패턴</p>
</blockquote>
<p>Model
데이터베이스, 상수, 변수와 같은 데이터와 비즈니스 로직을 담당한다.
데이터를 저장하고 관리하며, 상태 변경에 대한 처리르 수행한다.</p>
<p>View
사용자에게 정보를 표시하는 UI를 구성한다.
모델에서 데이터를 가져와 사용자에게 보여준다.</p>
<p>Controller
사용자 입력을 처리하고, 모델과 뷰 사이의 상호작용을 조정한다.
사용자가 인터페이스와 상호작용시 Controller가 이를 처리하고, 적절한 모델을 업데이트 해서 View를 갱신한다.</p>
<h1 id="mvp-패턴">MVP 패턴</h1>
<hr>
<blockquote>
<p>MVC에서 Controller가 Presenter로 변경되었다.</p>
</blockquote>
<p>뷰와 프레젠터는 1:1 관계라서 MVC보다 더 강한 결합의 패턴이라고 보면 된다.</p>
<h1 id="mvvm-패턴">MVVM 패턴</h1>
<hr>
<blockquote>
<p>MVVM 패턴은 MVC의 C에 해당하는 컨트롤러가 View Model로 바뀐 패턴</p>
</blockquote>
<p>뷰 모델을 뷰를 더 추상화한 계층이다.
MVVM은 MVC와 다르게 커맨드와 데이터 바인딩을 갖는다.
뷰와 뷰모델 사이 양방향 데이터 바인딩을 지원하며, UI를 코드 수정 없이 재사용하고, 단위 테스팅이 쉬운 장점이 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[회고]]></title>
            <link>https://velog.io/@rlackdals_98/%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@rlackdals_98/%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 20 Aug 2024 10:59:03 GMT</pubDate>
            <description><![CDATA[<p><a href="https://rlackdals981010.github.io/daily">회고 블로그는 이동 되었습니다.</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 20. Query Methods]]></title>
            <link>https://velog.io/@rlackdals_98/Spring-20.-Query-Methods</link>
            <guid>https://velog.io/@rlackdals_98/Spring-20.-Query-Methods</guid>
            <pubDate>Fri, 16 Aug 2024 11:46:06 GMT</pubDate>
            <description><![CDATA[<h1 id="query-methods">Query Methods</h1>
<ul>
<li>Spring Data JPA에서는 메서드 이름으로 SQL을 생성하는 기능</li>
<li>Repository에서 사용하는 것.</li>
</ul>
<pre><code class="language-jsx">package com.sparta.memo.repository;

import com.sparta.memo.entity.Memo;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface MemoRepository extends JpaRepository&lt;Memo, Long&gt; {
    List&lt;Memo&gt; findAllByOrderByModifiedAtDesc();
}</code></pre>
<ul>
<li>이러면 즉, 수정 시간을 기준으로 전체 데이터를 내림차순으로 가져오는 SQL을 실행하는 메서드를 호출만으로 실행이 가능하다.</li>
</ul>
<h1 id="적용">적용</h1>
<pre><code class="language-jsx">public List&lt;MemoResponseDto&gt; getMemos() {
    // DB 조회
    return memoRepository.findAllByOrderByModifiedAtDesc().stream().map(MemoResponseDto::new).toList();
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>