<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Sunwoo’s log</title>
        <link>https://velog.io/</link>
        <description>백엔드 개발자 준비생</description>
        <lastBuildDate>Sat, 09 Nov 2024 09:39:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Sunwoo’s log</title>
            <url>https://velog.velcdn.com/images/i-migi0104/profile/af56dc5e-9cad-4f4c-b2dd-ddde665337ec/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Sunwoo’s log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/i-migi0104" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[네트워크 면접 키워드 - 1]]></title>
            <link>https://velog.io/@i-migi0104/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%A9%B4%EC%A0%91-%ED%82%A4%EC%9B%8C%EB%93%9C-1</link>
            <guid>https://velog.io/@i-migi0104/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%A9%B4%EC%A0%91-%ED%82%A4%EC%9B%8C%EB%93%9C-1</guid>
            <pubDate>Sat, 09 Nov 2024 09:39:38 GMT</pubDate>
            <description><![CDATA[<h2 id="1-네트워크-기본-개념">1. 네트워크 기본 개념</h2>
<ul>
<li><p><code>컴퓨터 네트워크</code></p>
<p>  컴퓨터 네트워크란 두 대 이상의 컴퓨터와 기타 장치들이 서로 연결되어 데이터를 주고받을 수 있는 시스템을 말한다. 컴퓨터 네트워크는 자원 공유, 데이터 전송, 원경 통신을 가능하게 하여 현대 정보 통신 시스템의 기반이 된다. 네트워크의 규모는 소규모 LAN 부터 전 세계를 연결하는 WAN 까지 다양하다.</p>
<p>  주요 구성 요소</p>
<ol>
<li>노드: 네트워크에 연결된 컴퓨터, 서버, 라우터와 같은 개별 장치를 의미</li>
<li>전송 매체: 데이터를 전달하기 위해 사용하는 물리적 또는 무선 매체로 이더넷 케이블이나 와이파이 같은 형태가 있다</li>
<li>프로토콜: 네트워크 통신을 위해 사용하는 규칙과 표준으로, 대표적으로 TCP/IP 가 있다</li>
<li>라우터: 서로 다른 네트워크를 연결하고, 패킷이 목적지로 갈 수 있도록 경로를 지정</li>
<li>스위치: 같은 네트워크 내에서 장치들을 연결하고, 데이터를 특정 기기로 전달하는 장치</li>
</ol>
</li>
<li><p><code>인터넷</code></p>
<p>  인터넷은 전 세계의 다양한 컴퓨터 네트워크가 하나로 연결된 거대한 네트워크 시스템이다. 인터넷은 TCP/IP 라는 통신 규약을 기반으로 작동하며, 네트워크 간 데이터 교환과 커뮤니케이션을 가능하게 한다. 오늘날 인터넷은 정보의 공유, 원격 통신, 다양한 서비스 제공의 중심에 있으며, 거의 모든 디지털 활동의 기반이 된다.</p>
<p>  인터넷의 주요 요소</p>
<ol>
<li>IP 주소: 네트워크 상에서 기기를 식별하기 위해 사용하는 고유한 숫자 주소이다. IPv4 와 IPv6 두 가지 형식이이 존재한다</li>
<li>도메인 이름: IP 주소 대신 사람이 쉽게 기억할 수 있는 이름으로, DNS 를 통해 IP 주소로 변환된다</li>
<li>프로토콜: 인터넷에서 데이터 통신이 이루어지는 규칙으로 HTTP, FTP, SMTP 등이 있다</li>
<li>ISP: Internet Service Provider 로 인터넷 연결 서비스를 제공하는 사업자이다</li>
</ol>
</li>
<li><p><code>네트워크 구조</code></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/278cc3d3-a8f4-42b2-9e10-e8bf2734dd65/image.png" alt=""></p>
<pre><code>네트워크 구조는 컴퓨터와 장치들이 어떻게 서로 연결되고 통신하는지를 나타내며, 네트워크의 효율성, 확장성, 보안성 등에 영향을 미친다. 네트워크 구조는 물리적 연결 방식과 논리적 통신 방식에 따라 다양하게 구성되며, 대표적인 구조로 버스형, 스타형, 링형, 메시형, 트리형이 있다.

1. 버스형 구조
    1. 모든 컴퓨터와 장치가 버스라고 하는 하나의 중앙 케이블에 연결되는 구조이다
    2. 장점은 설치가 간단하고 케이블 비용이 낮다
    3. 단점은 중앙 케이블이 손상되면 전체 네트워크가 중단되고, 대역폭이 제한적이기 때문에 장치가 많아지면 성능이 저하될 수 있다
    4. 소규모 네트워크에 적합하다

2. 스타형 구조
    1. 중앙에 스위치나 허브가 있고, 모든 장치가 이 중앙 장치에 각각 연결되는 구조이다
    2. 장점은 중앙 장치의 유지 보수 만으로 관리가 용이하고, 한 장치가 고장 나도 네트워크 전체에는 영향을 미치지 않는다
    3. 단점은 중앙 장치가 고장 나면 전체 네트워크가 중단될 수 있고, 중앙 장치에 부하가 많이 걸릴 수 있다
    4. 가정이나 소규모에서 대규모 네트워크에 널리 사용된다

3. 링형 구조
    1. 각 장치가 양 옆의 장치와 연결되어 원형 형태를 이루는 구조이고, 데이터는 한 방향으로 순환하며 목적지에 도달한다
    2. 장점은 데이터 충돌이 적고, 네트워크의 흐름을 제어 하기가 용이하다
    3. 단점은 하나의 장치나 링크가 고장 나면 전체 네트워크에 문제가 생긴다
    4. 트래픽 관리가 중요한 네트워크에 사용된다

4. 메시형 구조
    1. 모든 장치가 서로 직접 연결되는 구조로, 모든 장치 간에 다수의 경로가 존재한다
    2. 장점은 고장 시 다른 경로로 데이터 전송이 가능해 매우 안정적이고, 보안성과 신뢰성이 높다
    3. 단점은 설치 비용과 복잡도가 높고, 많은 케이블과 포트가 필요해 확장성이 떨어진다
    4. 안정성과 보안이 중요한 대규모 네트워크에 적합하다

5. 트리형 구조
    1. 계층 구조를 이루며, 여러 스타형 네트워크가 버스 형태로 연결된 구조이고, 각 계층의 장치가 계층적으로 연결된다
    2. 장점은 네트워크의 확장과 관리를 쉽게 할 수 있으며, 네트워크 분할에 용이하다
    3. 단점은 버스 케이블에 장애가 발생하면 전체 네트워크가 영향을 받을 수 있다
    4. 대규모 네트워크 환경에서 계층적으로 연결하여 관리가 필요한 경우 사용된다

네트워크의 구조는 확장성, 유지 보수 용이성, 안정성 및 복구 가능성, 비용 등을 고려하고 선택해야 한다</code></pre><ul>
<li><p><code>LAN</code></p>
<p>  LAN은 Local Area Network 로 동일한 물리적 위치 내에서 제한된 거리 안의 컴퓨터와 장치들을 연결한 네트워크이다. 일반적으로 사무실, 학교, 가정 등 좁은 공간에서 사용되며, 고속 데이터 전송이 가능하고 외부 인터넷 연결 없이도 네트워크 내에서 자원 공유와 통신이 가능하다</p>
<p>  주요 특징</p>
<ol>
<li><p>제한된 범위: LAN은 비교적 좁은 공간 내에서 운영된다</p>
</li>
<li><p>고속 데이터 전송: 근거리 내의 네트워크로 빠른 전송 속도가 가능하다</p>
</li>
<li><p>자원 공유: 네트워크에 연결된 장치들이 프린터, 스캐너, 파일 서버 등의 자원을 공유한다</p>
</li>
<li><p>보안 및 제어: 네트워크 관리자에 의해 제어되어, 외부 접근을 제한하고 보안을 강화할 수 있다</p>
</li>
<li><p>유선 및 무선 연결: 이더넷 케이블을 사용하는 유선 LAN  과 WIFI 를 이용하는 무선 LAN 으로 구성될 수 있다</p>
<p>구성 요소</p>
</li>
<li><p>NIC: 네트워크 인터페이스 카드로 컴퓨터가 네트워크에 연결되도록 하는 장치이고 유선 또는 무선 연결을 지원한다</p>
</li>
<li><p>스위치: 네트워크 내의 장치들이 서로 연결되어 데이터를 주고받도록 하는 장치이다</p>
</li>
<li><p>라우터: LAN 과 외부 네트워크를 연결해주며, 네트워크 간 데이터 경로를 설정한다</p>
</li>
<li><p>케이블 및 무선 액세스 포인트: 유선 LAN 에서는 이더넷 케이블이 사용되고, 무선 LAN 에서는 무선 액세스 포인트가 사용된다</p>
</li>
</ol>
</li>
</ul>
<p>장점</p>
<ol>
<li>고속 데이터 전송: 근거리 환경에서 높은 전송 속도를 제공해 데이터 교환이 빠르다</li>
<li>비용 효율성: 좁은 범위에서 이루어지기 때문에 초기 설치 비용이 낮다</li>
<li>자원 및 파일 공유: 같은 네트워크에 연결된 여러 사용자가 쉽게 자원을 공유할 수 있다</li>
<li>보안성: 외부 네트워크와 격리되어 있어 보안성을 높일 수 있다</li>
</ol>
<p>단점</p>
<ol>
<li>거리 제한: 한정된 범위 내에서만 구축 가능하고 다른 지점과 연결하려면 WAN 을 사용해야 한다</li>
<li>트래픽 부하: 많은 장치가 연결되면 대역폭이 소모되어 네트워크 속도가 저하 될 수 있다</li>
</ol>
<ul>
<li><p><code>WAN</code></p>
<p>  WAN 은 Wide Area Network 로 지리적으로 떨어져 있는 넓은 지역에 걸쳐 여러 LAN 과 다른 네트워크를 연결한 네트워크이다. 도시, 국가, 대륙 단위로 네트워크를 구성하며, 가장 대표적인 예로 인터넷이 있다.</p>
</li>
</ul>
<p>주요 특징</p>
<ol>
<li>광범위한 범위: WAN 은 국가, 대륙 간의 넓은 범위에서 네트워크를 연결할 수 있다</li>
<li>낮은 데이터 전송 속도: 넓은 지역을 커버하기 때문에 LAN 에 비해 전송 속도가 낮다</li>
<li>공공 네트워크 인프라 활용: WAN 은 통신사를 통한 ISP, 전용선과 같은 네트워크 서비스를 사용하여 연결되는 경우가 많다</li>
<li>고비용: 광범위한 연결과 유지 관리에 비용이 많이 든다</li>
<li>인터넷 연결: 인터넷과 같은 광범위한 네트워크와 연결되어 세계적인 데이터 교환을 가능하게 한다</li>
</ol>
<p>주요 구성 요소</p>
<ol>
<li>라우터: 서로 다른 네트워크를 연결하고, 데이터가 목적지까지 올바르게 전달되도록 경로를 설정</li>
<li>전용선: 두 지역을 연결하기 위해 통신사에서 임대한 전용 회선으로, 고속 데이터 전송이 가능하지만 비용이 높다</li>
<li>위성 링크: 멀리 떨어진 지역 간의 연결을 위해 위성을 통한 통신을 사용하며, 특히 해양이나 외딴 지역에서 유용하다</li>
<li>모뎀: 전화선을 이용한 네트워크 연결을 위해 사용되는 장치로, 데이터를 아날로그 신호로 변환하여 전송</li>
</ol>
<p>장점</p>
<ol>
<li>광범위한 연결: 다양한 지역, 국가, 대륙 간 네트워크를 연결할 수 있어 글로벌 통신 가능</li>
<li>효율적인 자원 공유: 원격지 간 자원과 데이터를 공유할 수 있어 대규모 조직이나 지점 간 협업에 용이</li>
<li>강화된 보안: WAN 전용선을 사용하거나, VPN 을 통해 보안이 강화된 연결 제공</li>
</ol>
<p>단점</p>
<ol>
<li>높은 비용</li>
<li>낮은 전송 속도</li>
<li>복잡한 관리</li>
</ol>
<ul>
<li><code>회선 교환 네트워크</code></li>
</ul>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/713e2a64-8337-45f7-90c8-9948a16905fd/image.png" alt=""></p>
<p>회선 교환 네트워크는 통신을 위해 물리적인 경로를 고정적으로 설정하여 데이터를 전송하는 네트워크 방식이다.</p>
<p>회선 교환 방식은 주로 전화망에서 사용되며, 통신이 시작되면 두 장치 간에 하나의 전용 회선이 설정되고, 통신이 끝날 때 까지 해당 회선이 독점적으로 사용된다</p>
<p>특징</p>
<ol>
<li>고정 경로 설정: 통신이 시작되기 전에 경로가 설정되며, 경로는 통신이 끝날 때까지 고정</li>
<li>연속 데이터 전송: 데이터가 연속적으로 전달되며, 전송 중에 경로 변경 X</li>
<li>낮은 지연: 경로가 설정되면 데이터가 연속적으로 전송되어 지연이 거의 없다</li>
<li>전용 회선 사용: 통신 중에는 다른 장치가 해당 회선을 사용할 수 없어, 안정적이고 독점적인 데이터 전송 가능</li>
</ol>
<p>주요 단계</p>
<ol>
<li>경로 설정 (Call Setup): 두 장치 간 통신 경로가 설정되는 단계, 모든 네트워크 장비가 데이터를 전송할 고정 경로 확보</li>
<li>데이터 전송 (Data Transfer): 경로가 설정된 후 데이터가 연속적으로 전송, 이 동안 회선은 통신하는 두 장치가 독점적으로 사용</li>
<li>경로 해제 (Call Teardown): 통신이 끝나면 경로가 해제되어 다른 통신이 해당 경로 사용할 수 있게 된다</li>
</ol>
<p>장점</p>
<ol>
<li>지속적이고 안정적인 전송: 경로가 설정되면 데이터가 끊김 없이 전송되어, 음성 통신과 같은 실시간 통신에 적합</li>
<li>낮은 지연 시간: 경로가 고정되어 있어 데이터 전송 시 지연이 거의 없다</li>
<li>일관된 대역폭 제공: 설정된 경로의 대역폭을 두 장치가 독점적으로 사용해 일정한 전송 속도를 유지할 수 있다</li>
</ol>
<p>단점</p>
<ol>
<li>낮은 자원 효율성: 통신 중에는 회선이 두 장치에 고정되어 실제 데이터가 전송되지 않을 때도 회선이 비어 있게 되어 자원이 낭비될 수 있다</li>
<li>고비용: 전용 회선을 사용하는 방식이기 때문에, 특히 대규모 네트워크에서는 비용이 많이 들 수 있다</li>
<li>설정 시간 필요: 통신 전에 경로 설정이 필요하여, 데이터 전송 전에 약간의 지연이 발생할 수 있다</li>
</ol>
<p>패킷 교환 네트워크와의 비교</p>
<ul>
<li><p>회선 교환: 통신을 위해 고정된 경로가 설정되고, 경로를 통신하는 두 장치가 독점적으로 사용해 실시간 음성 통신에 적합</p>
</li>
<li><p>패킷 교환: 데이터를 작은 패킷 단위로 나눠 각 패킷이 독립적으로 최적의 경로로 전송되어 자원 효율성이 높고, 인터넷과 같은 데이터 통신에 적합</p>
</li>
<li><p><code>패킷 교환 네트워크</code></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/a87ebca3-e149-41fd-b3a5-5ea0e2833193/image.png" alt=""></p>
<p>패킷 교환 네트워크는 데이터를 작은 단위인 패킷으로 나누어 전송하는 방식의 네트워크이다.</p>
<p>각 패킷은 독립적으로 목적지로 전송되며, 경로는 고정되지 않고 네트워크 상황에 따라 동적으로 설정된다. 이 방식은 회선 교환 네트워크와 달리 여러 사용자가 네트워크 자원을 효율적으로 공유할 수 있게 해준다</p>
<p>작동방식</p>
<ol>
<li>데이터 분할: 보낼 데이터를 일정한 크기의 패킷으로 나눈다</li>
<li>패킷 헤더 추가: 각 패킷에는 송신자, 수신자 주소, 순서 정보 등이 포함된 헤더가 존재</li>
<li>독립적 전송: 각 패킷은 독립적으로 최적의 경로를 찾아 전송되며, 경로는 전송 중에 변경할 수 없다</li>
<li>재조립: 목적지에 도착한 패킷들은 순서에 따라 다시 원래 데이터로 조립</li>
</ol>
<p>주요 특징</p>
<ol>
<li>동적 경로 설정: 각 패킷이 최적의 경로를 찾아 목적지로 이동해 네트워크 상태에 따라 경로가 달라질 수 있다</li>
<li>높은 자원 효율성: 네트워크 자원을 모든 사용자가 공유하기 때문에 회선 교환에 비해 자원 효율이 높다</li>
<li>데이터 손실 가능성: 네트워크 혼잡이나 장애로 인해 일부 패킷이 손실될 수 있지만, 패킷 재전송으로 보완된다</li>
<li>지연 발생 가능성: 패킷이 서로 다른 경로로 이동하고, 혼잡 시 지연이 발생할 수 있다</li>
</ol>
<p>장점</p>
<ol>
<li>효율적인 자원 사용: 회선을 독점하지 않고 필요한 만큼의 자원을 사용하기 때문에 네트워크 효율성이 높다</li>
<li>유연성: 네트워크 상태에 따라 경로를 동적으로 조정할 수 있어, 장애에 유연하게 대응할 수 있다</li>
<li>비용 절감: 여러 사용자가 네트워크 자원을 공유해 대규모 네트워크 구축 비용 절감</li>
</ol>
<p>단점</p>
<ol>
<li>가변 지연: 네트워크 상태에 따라 경로와 전송 시간이 달라져, 지연이 발생할 수 있다</li>
<li>복잡한 관리: 패킷 재전송, 경로 최적화 등 복잡한 네트워크 관리가 필요</li>
<li>데이터 손실 가능성: 패킷 손실 시 재전송 매커니즘이 필요해 추가적인 대역폭 소모가 발생할 수 있다</li>
</ol>
<ul>
<li><p><code>프로토콜</code></p>
<p>  프로토콜은 컴퓨터 네트워크에서 데이터 통신을 위한 일련의 규칙과 규약을 의미한다. 서로 다른 장치가 원활하게 데이터를 주고받기 위해 따르는 일종의 언어와 약속이다. 각 프로토콜은 특정한 기능과 구조를 가지고 있으며, 네트워크의 여러 계층에서 다양한 역할을 수행한다</p>
<p>  주요 프로토콜의 종류와 역할</p>
<ol>
<li>TCP/IP (Transmission Control Protocol / Internet Protocol)<ul>
<li>TCP: 데이터를 신뢰성 있게 전송하기 위한 프로토콜로, 데이터 전송 중 손실된 패킷을 재전송하는 기능을 제공하고 연결 지향적이다</li>
<li>IP: 패킷을 목적지 주소로 전달하는 역할을 하며, 라우팅을 담당한다. 주소 지정 및 패킷 전달을 수행하며 비연결형이다</li>
</ul>
</li>
<li>HTTP/HTTPS (Hypertext Transer Protocol / Secure)<ul>
<li>HTTP: 웹 브라우저와 서버 간에 통신에 사용하는 프로토콜로, 주로 HTML 문서를 전송하는 데 사용된다</li>
<li>HTTPS: HTTP에 보안 계층(SSL/TLS)을 추가한 것으로, 데이터 암호화를 통해 사용자 정보를 보호한다</li>
</ul>
</li>
<li>FTP (File Transfer Protocol)<ul>
<li>파일을 서버와 클라이언트 간에 전송하기 위한 프로토콜로 대용량 파일 전송에 적합하며, 업로드와 다운로드 모두 가능하다</li>
</ul>
</li>
<li>SMTP/POP3/IMAP (Email Protocols)<ul>
<li>SMTP (Simple Mail Transfer Protocol): 이메일을 보내는 데 사용되는 프로토콜</li>
<li>POP3 (Post Office Protocol 3): 서버에서 이메일을 가져오고 로컬에 저장하는 프로토콜</li>
<li>IMAP (Internet Message Access Protocol): 서버에서 이메일을 관리하고 동기화하는 데 사용되며, 여러 기기에서 이메일을 확인할 때 사용</li>
</ul>
</li>
<li>DNS (Domain Name System)<ul>
<li>사용자가 입력한 도메인 이름을 IP 주소로 변환하기 위한 프로토콜로 웹사이트에 접근할 때 사람이 이해하기 쉬운 도메인 이름을 컴퓨터가 이해할 수 있는 IP 주소로 변환해주는 프로토콜</li>
</ul>
</li>
<li>DHCP (Dynamic Host Configuration Protocol)<ul>
<li>네트워크에 연결되는 장치에 IP 주소를 자동으로 할당하는 프로토콜</li>
</ul>
</li>
<li>SSH (Secure Shell)<ul>
<li>네트워크를 통해 원격으로 컴퓨터에 안전하게 접근하기 위한 프로토콜로 보안이 강화된 통신을 제공하고, 원격 서버나 장치에 접속할 때 흔히 사용된다</li>
</ul>
</li>
<li>SNMP (Simple Network Management Protocol)<ul>
<li>네트워크 장비를 모니터링하고 관리하기 위한 프로토콜</li>
</ul>
</li>
<li>ARP (Address Resolution Protocol)<ul>
<li>네트워크 내에서 IP 주소를 MAC 주소로 변환하는 프로토콜로 네트워크 계층의 IP 주소와 데이터 링크 계층의 물리 주소 간 매핑을 제공</li>
</ul>
</li>
<li>ICMP (Internet Control Message Protocol)<ul>
<li>네트워크 진단 및 오류 보고를 위한 프로토콜로 주로 ping 명령어로 목적지와의 연결 상태를 확인 할 때 사용</li>
</ul>
</li>
</ol>
</li>
<li><p><code>네트워크 참조 모델</code></p>
</li>
</ul>
<pre><code>네트워크 참조 모델은 네트워크 통신이 이루어지는 과정과 각 계층의 역할을 정의하는 표준화된 모델이다. 네트워크의 다양한 요소가 서로 상호작용하고 데이터를 전달하기 위해 필요한 규칙과 절차를 계층별로 나누어 설명하고 대표적인 네트워크 참조 모델로는 OSI (Open Systems Interconnection) 모델이나 TCP/IP 모델이 있다</code></pre><ol>
<li>OSI 모델 (7계층 모델)</li>
</ol>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/2d2ce4d2-56a2-4b8e-91b3-629204004843/image.png" alt=""></p>
<pre><code>OSI 모델은 ISO가 제안한 7계층 모델로, 네트워크 통신을 7개의 계층으로 나누어 각 계층이 독립적으로 기능할 수 있도록 한 모델이다

1. 물리 계층
    - 전기적, 물리적 신호의 전송을 담당하며, 비트 단위로 데이터를 전송
    - 케이블, 무선과 관련된 전송매체들의 표준을 정의
2. 데이터 링크 계층
    - 에러 감지 및 수정, 프레임을 통한 데이터 전송을 담당
    - MAC 주소를 기반으로 네트워크 내에서 장치 간의 통신을 관리
    - 스위치와 같은 장치가 작동하는 계층
3. 네트워크 계층
    - 데이터를 목적지 IP 주소로 전달하고, 패킷의 라우팅을 담당
    - IP 주소를 사용해 데이터가 네트워크를 통해 전송되는 경로를 결정
    - 라우터가 작동하는 계층
4. 전송 계층
    - TCP 와 UDP 가 위치하며, 데이터 전송의 신뢰성과 속도 조정
    - 패킷을 분할하고 재조립
5. 세션 계층
    - 통신 세션을 설정하고 유지, 종료하는 기능을 담당
6. 표현 계층
    - 데이터의 인코딩, 압축, 암호화를 수행
7. 응용 계층
    - 사용자와 직접 상호 작용하는 프로토콜이 위치하며, HTTP, FTP, SMTP 등이 포함</code></pre><ol>
<li>TCP/IP 모델 (4계층 모델)</li>
</ol>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/47afcb12-8241-4d55-85d4-407e86fce5ba/image.png" alt=""></p>
<pre><code>TCP/IP 모델은 인터넷에서 사용되는 기본적인 네트워크 모델로, OSI 모델과 달리 4계층으로 구성된다. OSI 모델보다 단순하며, 실제 인터넷에서 널리 사용되는 모델이다

1. 네트워크 접근 계층
    - OSI 모델의 물리 계층과 데이터 링크 계층을 결합한 계층으로, 데이터가 네트워크 매체를 통해 전달되는 과정을 담당
    - 이더넷, 와이파이와 같은 물리적 연결 기술을 포함
2. 인터넷 계층
    - OSI 모델의 네트워크 계층에 해당하며, IP 주소를 통해 데이터 패킷이 목적지에 도달할 수 있도록 라우팅
3. 전송 계층
    - 데이터의 신뢰성과 속도를 제어하며, 애플리케이션 간 데이터 전송을 관리
4. 응용 계층
    - OSI 모델의 상위 3개 계층을 결합해 사용자가 직접 접속하는 부분</code></pre><p>네트워크 참조 모델의 중요성</p>
<ul>
<li><p>표준화된 통신: 네트워크 통신의 표준을 제시해 서로 다른 기기나 시스템이 원활하게 통신 가능</p>
</li>
<li><p>계층화된 구조: 각 계층이 독립적으로 기능을 수행할 수 있어 네트워크 설계와 관리가 용이</p>
</li>
<li><p>문제 해결 및 유지보수 용이: 각 계층별로 문제를 구분하고 해결할 수 있어 네트워크 유지보수가 효율적</p>
</li>
<li><p>다양한 프로토콜 통합: 각 계층에서 특정한 프로토콜을 사용해 다양한 통신 요구를 충족 가능</p>
</li>
<li><p><code>캡슐화</code></p>
<p>  캡슐화는 네트워크 통신 과정에서 데이터를 송신자에서 수신자로 보내기 위해 각 계층별로 헤더나 트레일러를 추가하는 과정이다. 이 과정을 통해 데이터가 효율적이고 안전하게 전달되며, 계층 간의 독립성이 보장된다. 캡슐화는 데이터가 네트워크를 통해 이동하는 동안 각 계층에서 필요한 정보를 추가하는 방식으로 이루어진다.</p>
<p>  캡슐화 과정</p>
<ol>
<li><p>응용 계층</p>
<ul>
<li>사용자가 입력한 데이터를 전달받고, 응용 계층 프로토콜(HTTP, FTP..)이 해당 데이터를 처리</li>
<li>응용 계층에서 제공된 데이터는 전송 계층으로 전달</li>
</ul>
</li>
<li><p>전송 계층</p>
<ul>
<li>전송 계층에서는 데이터에 TCP/UDP 헤더를 추가해 데이터 전송의 신뢰성과 속도를 관리</li>
<li>이 계층에서 추가된 헤더에는 송신 포트와 수신 포트 정보, 오류 검출을 위한 정보가 포함</li>
<li>이 과정으로 만들어진 데이터를 세그먼트(TCP) 나 데이터그램(UDP) 로 부른다</li>
</ul>
</li>
<li><p>네트워크 계층</p>
<ul>
<li>네트워크 계층에서는 세그먼트에 IP 헤더를 추가해 패킷을 목적지로 전달하는 데 필요한 정보를 포함한다</li>
<li>이 계층에서 만들어진 데이터를 패킷 이라고 한다</li>
</ul>
</li>
<li><p>데이터 링크 계층</p>
<ul>
<li>데이터 링크 계층에서는 패킷에 MAC 주소와 같은 물리적인 주소 정보를 포함한 프레임 헤더와 오류 검출을 위한 트레일러를 추가</li>
<li>이 과정으로 만들어진 데이터를 프레임 이라고 한다</li>
</ul>
</li>
<li><p>물리 계층</p>
<ul>
<li>물리 계층에서는 프레임을 전기 신호 또는 무선 신호로 변환하여 실제 네트워크 매체를 통해 전송</li>
<li>비트 단위로 데이터를 전송</li>
</ul>
<p>캡슐화의 장점</p>
</li>
</ol>
<ul>
<li>계층 간 독립성: 각 계층이 독립적으로 기능을 수행할 수 있어 네트워크 설계와 관리가 용이</li>
<li>확장성: 새로운 프로토콜이나 기능을 추가할 때, 다른 계층에 영향을 최소화 할 수 있다</li>
<li>데이터 보안과 무결성: 각 계층에서 필요한 정보를 추가하므로, 데이터 전송 중 오류 검출과 보안 기능이 강화된다</li>
</ul>
</li>
<li><p><code>역캡슐화</code></p>
<p>  송신자가 보낸 데이터를 수신자가 받는 과정에서 각 계층별로 추가된 헤더와 트레일러를 제거하여 원래의 데이터로 추출하는 과정이다. 이는 캡슐화의 반대 과정으로, 네트워크를 통해 전달된 데이터가 목적지에서 다시 해석되고 원래 형태로 변환되는 데 사용된다</p>
<p>  역캡슐화 과정</p>
<ol>
<li><p>물리 계층</p>
<ul>
<li>데이터를 비트 단위의 전기 신호 또는 무선 신호로 받아들인다</li>
<li>받은 비트 신호를 디지털 데이터로 변환하여 상위 계층인 데이터 링크 계층으로 전달</li>
</ul>
</li>
<li><p>데이터 링크 계층</p>
<ul>
<li>물리 계층에서 받은 프레임을 처리하며, 이 과정에서 프레임 헤더와 트레일러를 제거하고 남은 데이터를 네트워크 계층으로 전달</li>
<li>프레임 헤더에는 송신자와 수신자의 MAC 주소와 같은 물리적 주소 정보가 포홤되어 있다</li>
</ul>
</li>
<li><p>네트워크 계층</p>
<ul>
<li>데이터 링크 계층에서 받은 패킷을 처리하고, 패킷의 IP 헤더를 제거한다</li>
<li>이 과정에서 송신 IP 주소와 수신 IP 주소 등의 정보가 사용되며, 데이터가 최종 목적지에 도달하도록 관리</li>
<li>네트워크 계층의 처리가 끝나면 전송 계층으로 전달</li>
</ul>
</li>
<li><p>전송 계층</p>
<ul>
<li>네트워크 계층에서 받은 세그먼트를 처리하여 TCP 또는 UDP 헤더를 제거</li>
<li>이 헤더에는 포트 번호와 오류 검출 정보 등이 포함되어 있으며, 데이터가 올바르게 수신되었는지 확인</li>
<li>전송 계층의 처리 후 데이터는 응용 계층으로 전달</li>
</ul>
</li>
<li><p>응용 계층</p>
<ul>
<li>전송 계층에서 받은 데이터에서 응용 계층 프로토콜을 통해 원래의 데이터를 추출</li>
<li>최종적으로 사용자가 이해할 수 있는 형태로 데이터가 제공</li>
</ul>
<p>역캡슐화의 중요성</p>
</li>
</ol>
<ul>
<li>데이터 복원: 송신자가 보낸 원본 데이터를 수신자가 올바르게 해석할 수 있게 한다</li>
<li>네트어크 계층별 기능 분리: 각 계층이 캡슐화/역캡슐화 과정을 통해 독립적으로 기능 수행 가능</li>
<li>데이터 무결성 및 오류 검출: 각 계층에서 헤더 정보를 통해 데이터가 올바르게 전송되었는지 확인</li>
</ul>
</li>
<li><p><code>PDU</code></p>
<p>  PDU(Protocol Data Unit)는 프로토콜 데이터 단위로 네트워크의 각 계층에서 데이터를 전송하거나 처리할 때 사용하는 데이터의 단위를 의미한다. 네트워크의 각 계층별로 다르게 정의되며, 계층별 데이터 전송 규약에 따라 특정 이름으로 불린다.</p>
<p>  OSI 모델에서의 PDU 종류</p>
<ol>
<li>응용 계층, 표현 계층, 세션 계층 - 데이터</li>
<li>전송 계층 - 세그먼트</li>
<li>네트워크 계층 - 패킷</li>
<li>데이터 링크 계층 - 프레임</li>
<li>물리 계층 - 비트</li>
</ol>
</li>
</ul>
<p>PDU의 중요성</p>
<ul>
<li>데이터 관리와 계층별 처리: 각 계층의 PDU는 데이터를 계층별로 독립적으로 처리할 수 있도록 하며, 이를 통해 네트워크 통신이 구조적으로 이루어진다</li>
<li>데이터 전송 오류 방지: 각 계층에서 데이터 오류를 감지하고 수정할 수 있는 정보가 추가되어, 데이터 전송의 신뢰성을 보장</li>
<li>프로토콜 간 상호 운용성: 다양한 네트워크 프로토콜이 계층별 PDU를 사용해 상호 운용되며, 다양한 네트워크 환경에서의 통신을 가능하게 한다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[세션과 JWT]]></title>
            <link>https://velog.io/@i-migi0104/%EC%84%B8%EC%85%98%EA%B3%BC-JWT</link>
            <guid>https://velog.io/@i-migi0104/%EC%84%B8%EC%85%98%EA%B3%BC-JWT</guid>
            <pubDate>Thu, 31 Oct 2024 13:36:22 GMT</pubDate>
            <description><![CDATA[<h2 id="인증과-인가란">인증과 인가란?</h2>
<p>세션과 JWT에 대해서 설명드리기 전에 인증과 인가에 대해서 말씀드리겠습니다.
인증이란 <code>Authentication</code> 으로 사용자 또는 장치의 신원을 확인하는 과정입니다. 
예를 들어 어떤 서비스를 이용하기 전에 아이디와 패스워드를 입력해서 서비스가 사용자의 신원을 확인하는 과정을 인증이라고 합니다.
인가는 <code>Authorization</code> 으로 사용자가 어떤 리소스에 접근할 수 있는지 또는 어떤 동작을 수행할 수 있는지 검증을 하는 것을 의미합니다.
예를 들어 무신사에서 상품을 삭제하는 기능을 사용자가 사용할 수 있는지 권한을 확인하는 과정을 의미합니다.</p>
<p>저희가 서비스를 개발할 때 인증과 인가를 사용해서 현재 로그인되어 있는 사용자가 어떤 기능을 사용할 수 있는지 확인하는 과정을 거쳐야합니다.
그런데 저희가 사용하는 <code>HTTP</code> 는 클라이언트가 서버에 요청을 보내고, 서버가 클라이언트에게 데이터를 응답하는 방식으로 동작하는 프로토콜인데, <code>HTTP</code> 의 대표적인 특징으로는 <code>Stateless</code>가 있습니다. <code>Stateless</code>란 클라이언트와 첫번째 통신에서 데이터를 주고받았을지라도, 두번째 통신에서는 이전 데이터를 유지하지 않는다는 것입니다. 그렇기 때문에 로그인 상태를 유지하기 위해서는 서버에 요청을 보낼 때마다 내가 누구인지를 알려줘야 합니다. </p>
<p><em><strong>그런데 모든 요청마다 사용자가 로그인을 할 수는 없으니 로그인 상태를 유지해야하는데 이때 사용되는 기술이 세션과 JWT입니다.</strong></em></p>
<hr>
<h2 id="세션">세션</h2>
<p>세션 방식 로그인은 기존에 전통적으로 많이 사용되어온 방식입니다.</p>
<p>세션 방식의 로그인 과정은 아래와 같습니다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/d1f0efd7-534d-4e1a-a1a7-a5b1cf50d994/image.png" alt=""></p>
<blockquote>
</blockquote>
<ol>
<li>유저가 사이트에 접속을 한 후 로그인을 진행합니다.</li>
<li>유저가 로그인을 완료하면 서버에서 세션 ID를 생성하고 세션 ID, 사용자 정보, 세션 유효기간 등의 정보가 담긴 새로운 세션을 생성하고 서버의 메모리나 세션 스토어등에 저장합니다</li>
<li>생성한 세션 ID를 클라이언트에 Header 의 Set-Cookie로 전달해줍니다.</li>
<li>이후 클라이언트에서는 서버로부터 전달받은 세션 ID를 클라이언트의 쿠키에 저장합니다.</li>
<li>해당 사이트에 대한 모든 요청에 세션 ID를 담은 쿠키를 함께 전송합니다.</li>
<li>서버는 클라이언트가 보낸 세션 ID와 서버에서 관리하고 있는 세션 ID를 비교해서 일치한다면 인가를 수행해줍니다.</li>
</ol>
<p>이 과정에서의 중요한 점은 유저의 정보들을 서버 쪽에서 관리한다는 것입니다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/5591b90b-1b76-480c-aa00-e2003b5050b0/image.png" alt=""></p>
<p>세션 방식의 로그인의 <code>장점</code>은 위와 같습니다.</p>
<ol>
<li>사용자 정보가 서버에 저장되기 때문에 클라이언트가 정보를 직접 보관하지 않아 보안성이 높습니다.</li>
<li>클라이언트는 세션 ID만 저장하면 되기 때문에, 클라이언트의 저장 공간을 절약할 수 있습니다.</li>
<li>서버에서 세션 정보를 관리하므로, 사용자의 추가 정보나 상태를 쉽게 업데이트하고 관리할 수 있습니다.</li>
<li>사용자가 여러 장치에서 로그인할 경우, 서버는 이를 관리해서 사용자가 각 장치에서 동일한 세션을 사용할 수 있게 하여 편리함을 제공해줍니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/2ec04df6-fc49-4f63-8e4b-5be744a3868e/image.png" alt="">
<code>단점</code>은 위와 같습니다.</p>
<ol>
<li>서버는 모든 사용자 세션 정보를 메모리나 데이터베이스에 저장해야 하므로, 많은 사용자 트래픽을 처리할 경우 서버의 리소스가 많이 소모됩니다.</li>
<li>중앙 세션 스토어가 아닌, 서버 컴퓨터의 메모리나 하드디스크에 저장할 경우 확장성이 좋지 않습니다. 그 이유는 서버 컴퓨터를 추가할 경우 각 서버 컴퓨터마다 세션 정보를 공유해야하기 때문입니다.</li>
<li>사용자의 아이디와 패스워드를 몰라도 공격 대상이 이미 시스템에 접속되어 세션이 연결되어 있는 상태를 가로채기 하는 공격으로 공격자가 인증 작업 등이 완료된 정상적으로 통신하고 있는 다른 사용자의 세션을 가로채서 별도의 인증 작업을 거치지 않고 가로챈 세션으로 통신을 계속하는 <code>세션 하이재킹</code> 공격을 당할 수 있습니다. 이를 방지하기 위해 HTTPS를 사용해 요청 자체를 탈취해도 정보를 읽기 힘들게 하고, 세션에 유효기간을 넣어, 정보를 탈취하더라도 유효기간이 끝나면 더이상 해커가 이용할 수 없도록 해야합니다.</li>
</ol>
<hr>
<h2 id="jwt">JWT</h2>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/ebe9f1fd-768f-4547-bf87-3bb5e7133b16/image.png" alt=""></p>
<p>JWT는 JSON Web Token의 약자로 특수한 형태의 문자열입니다</p>
<p><code>.</code> 을 기준으로 구분되어 <code>header</code>, <code>payload</code>, <code>signature</code> 로 구성되어 있습니다.
JWT는 암호화되어 있지 않기때문에 누구나 확인할 수 있습니다. 그렇기 때문에 중요한 정보를 JWT에 담으면 안됩니다.</p>
<p><code>Header</code>에는 <code>typ</code> 와 <code>alg</code> 라는 정보가 존재하는데 typ 에는 토큰의 타입이 작성되어 있고,  alg에는 Signature 에서 사용하는 알고리즘이 무엇인지가 작성되어 있습니다.</p>
<p><code>Payload</code>에는 토큰의 실제 내용을 담고 있습니다. 여기에는 클레임 이라고 불리는 정보 조각이 포함됩니다. </p>
<p><code>Signature</code>에는 헤더와 페이로드를 조합하여 특정 비밀키로 서명되어 생성됩니다. Signature를 사용해 해당 토큰이 변조되지 않았음을 검증하는 데 사용됩니다.</p>
<p>JWT 토큰을 사용한 로그인 과정에 대해서 설명해보겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/314b5c5e-d5f0-454e-9e4f-779657cd9a1b/image.png" alt=""></p>
<blockquote>
</blockquote>
<ol>
<li>사용자가 사이트에 접속을 한 후 로그인을 진행합니다. </li>
<li>로그인이 완료되면 서버에서 JWT 토큰을 생성하고 클라이언트에 전송합니다. </li>
<li>클라이언트는 JWT 토큰을 쿠키나 로컬 스토리지에 저장을 하고 모든 요청에 Authorization 헤더에 JWT 토큰을 포함하여 서버에 전송합니다. </li>
<li>서버에서는 JWT에 포함된 Signature를 확인하여 토큰의 무결성을 검사하고 만료 시간을 체크한 후에 요청을 처리합니다. <blockquote>
</blockquote>
앞의 세션과의 가장 큰 차이점은 세션은 사용자의 정보를 서버에 저장하고 SESSIONID만 클라이언트에 전송하는 것과 사용자의 정보를 JWT 토큰에 저장하고 토큰 자체를 클라이언트에 전송하는 것입니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/96aaad26-db64-4c4e-8119-f72871716344/image.png" alt=""></p>
<p>JWT 방식의 로그인의 <code>장점</code>에 대해서 말씀드리겠습니다.</p>
<ol>
<li>JWT는 클라이언트에 저장되므로 서버는 사용자 세션 정보를 유지할 필요가 없어 서버의 메모리 사용량을 줄이고 여러 대의 서버 사용을 용이하게 만듭니다.</li>
<li>JWT는 사용자 정보의 메타데이터를 토큰 자체에 포함하기 때문에 서버는 JWT를 검증함으로써 사용자의 정보를 즉시 조회할 수 있어 별도의 데이터베이스 호출이 필요하지 않습니다.</li>
<li>http 헤더나 url 파라미터를 통해 간단하게 전송되기 때문에 네트워크 부하가 적습니다</li>
</ol>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/cab6b2c1-8b37-4b4c-9e42-73ca776c77a8/image.png" alt=""></p>
<p>다음으로 JWT 방식의 로그인의 <code>단점</code>에 대해서 말씀드리겠습니다.</p>
<ol>
<li><code>XSS 공격</code>에 대한 취약성입니다. XSS 공격은 악성 스크립트를 웹 페이지에 삽입해 사용자의 브라우저에서 실행되도록 하는 공격 방식입니다. JWT 토큰이 클라이언트 측의 로컬 스토리지나 세션 스토리지에 저장되면, 악의적인 스크립트가 삽입될 경우 XSS 공격에 의해 JWT가 탈취될 수 있으며, 이렇게 탈취된 토큰을 공격자가 사용할 수 있습니다.</li>
<li><code>CSRF 공격</code>에 대한 취약성입니다. CSRF 공격은 신뢰할 수 있는 사이트에서 사용자의 인증이 되어있을 때 사용자의 웹 브라우저가 악의적인 웹사이트나 이메일, 메시지 등에 의해 원치않는 action을 수행하는 공격입니다. 이러한 공격은 웹 브라우저의 request가 자동적으로 세션 쿠키를 포함한 모든 쿠키를 자동적으로 포함하기 때문에 발생합니다. JWT를 쿠키에 저장할 경우, 요청 시 자동으로 포함되기 때문에 CSRF 공격에 취약할 수 있습니다.</li>
<li><code>토큰의 크기</code> 문제입니다. JWT는 헤더, 페이로드, 서명을 포함하기 때문에 상대적으로 크기가 커 요청 시 매번 전송해야 할 데이터량이 많아질 수 있습니다. 이는 네트워크 대역폭을 낭비하게 됩니다.</li>
<li><code>정보를 수정할 수 없습니다.</code> JWT는 서명된 토큰으로 발급 후에 내용을 수정할 수 없습니다. 사용자 정보가 변경되면 새로운 JWT를 발급받아야 하며, 추가적인 서버 자원 소모를 유발할 수 있습니다</li>
<li><code>탈취 시 접근 허용</code>
JWT가 탈취당하면 해당 토큰을 사용하여 공격자가 인증된 사용자로 서버에 접근할 수 있다는 문제가 생깁니다. 특히 JWT는 서버에서 상태를 유지하지 않기 때문에, 토큰 자체를 검증하는 방식으로 작동하며, 탈취된 토큰이 만료되기 전까지는 이를 무효화하기 어렵습니다. 따라서, 탈취된 JWT는 만료 시점까지 악용될 수 있으며, 이를 방지하려면 짧은 만료 시간을 설정하고, 추가로 리프레시 토큰이나 IP 확인 등 보안 조치를 도입하는 것이 필요합니다.</li>
</ol>
<hr>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/22feacb0-3a61-4c0d-aa46-5ce28d416f16/image.png" alt=""></p>
<h4 id="결론적으로-세션-방식의-로그인과-jwt-방식의-로그인-중-하나를-선택할-때에는-사용-환경과-요구-사항에-따라-결정을-해야합니다"><strong><em>결론적으로 세션 방식의 로그인과 JWT 방식의 로그인 중 하나를 선택할 때에는 사용 환경과 요구 사항에 따라 결정을 해야합니다.</em></strong></h4>
<p>세션 방식은 서버에서 사용자의 세션을 관리하기 때문에, 보다 강력한 보안을 요구하는 상황에 적합하고, 특히 사용자 정보를 서버에 안전하게 저장하고 싶거나, 실시간으로 사용자 세션을 업데이트해야 하는 경우에 유리합니다. 예를 들어 금융 서비스와 같이 사용자의 데이터 보호가 중요한 시스템에서 세션 방식을 사용합니다.
JWT 로그인 방식은 서버의 상태를 유지하지 않기 때문에 확장성이 뛰어나고 서버의 부하를 줄일 수 있습니다. MSA 와 같이 분산된 시스템에서는 JWT 방식이 유용할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 예외란?]]></title>
            <link>https://velog.io/@i-migi0104/%EC%9E%90%EB%B0%94-%EC%98%88%EC%99%B8%EB%9E%80</link>
            <guid>https://velog.io/@i-migi0104/%EC%9E%90%EB%B0%94-%EC%98%88%EC%99%B8%EB%9E%80</guid>
            <pubDate>Fri, 04 Oct 2024 07:39:09 GMT</pubDate>
            <description><![CDATA[<h2 id="예외-계층-그림">예외 계층 그림</h2>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/d655c01d-0ed3-4da6-b017-d5491b27d55a/image.png" alt=""></p>
<ul>
<li><p><code>Object</code> 
모든 객체의 최상의 부모는 <code>Object</code>이다. 예외또한 객체이기 때문에 예외의 최상의 부모도 <code>Object</code>이다.</p>
</li>
<li><p><code>Throwable</code>
최상위 예외이다. 하위에 <code>Exception</code>과 <code>Error</code>가 있다</p>
</li>
<li><p><code>Error</code> (언체크 예외)
메모리 부족이나 심각한 시스템 오류와 같이 애플리케이션에서 복구 불가능한 시스템 예외이다.
애플리케이션 개발자는 이 예외를 잡으려고 해서는 안된다
그렇기 때문에 상위 예외를 <code>catch</code>로 잡으면 그 하위 예외까지 함께 잡기 때문에 <code>Throwable</code> 예외를 <code>catch</code>하면 안되고 <code>Exception</code> 부터 필요한 예외로 생각하고 잡아야한다</p>
</li>
<li><p><code>Exception</code> (체크 예외)
애플리케이션 로직에서 사용할 수 있는 실질적인 최상위 예외이다. <code>RuntimeException</code>을 제외하고 <code>Exception</code>과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다</p>
</li>
<li><p><code>RuntimeException</code> (언체크 예외)
컴파일러가 체크 하지 않는 언체크 예외이다</p>
</li>
</ul>
<h2 id="예외-기본-규칙">예외 기본 규칙</h2>
<p>예외는 폭탄 돌리기로 이해하면 된다. 잡아서 처리하거나, 처리할 수 없으면 밖으로 던져야한다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/d01a67ef-1ca7-4bee-b46a-9295f81698af/image.png" alt=""></p>
<p>위의 그림과 같이 예외를 처리하지 못하면 호출한 곳으로 예외를 계속 던지게 된다</p>
<p>추가로 예외를 잡거나 던질 때 지정한 예외뿐만 아니라 그 예외의 자식들도 함께 처리된다</p>
<p>그러면 여기서 예외를 처리하지 못하면 밖으로 던지면 된다고 했는데 계속해서 처리하지 못하고 던지게 된다면 자바 <code>main()</code> 쓰레드의 경우 예외 로그를 출력하면서 시스템이 종료된다</p>
<p>웹 애플리케이션의 경우 여러 사용자의 요청을 처리하기 때문에 하나의 예외 때문에 시스템이 종료되면 안된다. 이럴때 <code>WAS</code>가 해당 예외를 받아서 사용자에게 개발자가 지정한 오류 페이지를 보여주는 식의 처리를 한다</p>
<h2 id="체크-예외">체크 예외</h2>
<p><code>Exception</code>과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다. 물론 <code>RuntimeException</code>은 언체크 예외이다.</p>
<p>체크 예외는 잡아서 처리하거나, 또는 밖으로 던지도록 선언해야한다</p>
<pre><code class="language-java">/**
* 예외를 잡아서 처리하는 코드 */
         public void callCatch() {
             try {
                 repository.call();
             } catch (MyCheckedException e) {
//예외 처리 로직
log.info(&quot;예외 처리, message={}&quot;, e.getMessage(), e); }
}</code></pre>
<pre><code class="language-java">/**
* 체크 예외를 밖으로 던지는 코드
* 체크 예외는 예외를 잡지 않고 밖으로 던지려면 throws 예외를 메서드에 필수로 선언해야한다. */
         public void callThrow() throws MyCheckedException {
             repository.call();
} }
     static class Repository {
         public void call() throws MyCheckedException {
             throw new MyCheckedException(&quot;ex&quot;);
         }
}</code></pre>
<h3 id="체크-예외의-장단점">체크 예외의 장단점</h3>
<p>체크 예외는 예외를 잡아서 처리할 수 없을 때, 예외를 밖으로 던지는 <code>throws 예외</code>를 필수로 선언해야 한다. 그렇지 않으면 컴파일 오류가 발생한다.</p>
<p>장점</p>
<blockquote>
<p>개발자가 실수로 예외를 누락하지 않도록 컴파일러를 통해 문제를 잡아주는 안전 장치이다</p>
</blockquote>
<p>단점</p>
<blockquote>
<p>실제로는 개발자가 모든 체크 예외를 잡거나 던지도록 처리해야 하기 때문에, 너무 번거로운 일이 되며 크게 신경쓰고 싶지 않은 예외까지 모두 챙겨야 한다.</p>
</blockquote>
<h2 id="언체크-예외">언체크 예외</h2>
<p><code>RuntimeException</code>과 그 하위 예외는 언체크 예외로 분류된다
언체크 예외는 말 그대로 컴파일러가 예외를 체크하지 않는다. 체크 예외와의 차이점은 예외를 던지는 <code>throws</code>를 선언하지 않고 생략할 수 있다. 이 경우에는 자동으로 예외를 던진다.</p>
<pre><code class="language-java"> try {
     repository.call();
} catch (MyUncheckedException e) { //예외 처리 로직
     log.info(&quot;error&quot;, e);
 }</code></pre>
<p> 언체크 예외도 필요한 경우에는 <code>catch</code>를 사용해서 처리할 수 있다</p>
<pre><code class="language-java">  public void callThrow() {
     repository.call();
}</code></pre>
<p>체크 예외와는 다르게 <code>throws 예외</code>를 선언하지 않아도 된다</p>
<h3 id="언체크-예외의-장단점">언체크 예외의 장단점</h3>
<p>언체크 예외는 예외를 잡아서 처리할 수 없을 때, 예외를 밖으로 던지는 <code>throws 예외</code>를 생략할 수 있다.</p>
<p>장점</p>
<blockquote>
<p>신경쓰고 싶지 않은 예외를 무시할 수 있다. 체크 예외의 경우 처리할 수 없는 예외를 밖으로 던지려면 항상 <code>throws 예외</code>를 선언해야 하지만, 언체크 예외는 이 부분을 생략할 수 있다</p>
</blockquote>
<p>단점</p>
<blockquote>
<p>개발자가 실수로 예외를 누락할 수 있다.</p>
</blockquote>
<h2 id="그렇다면-언제-체크-예외를-사용하고-언제-언체크-예외를-사용하면-될까">그렇다면 언제 체크 예외를 사용하고 언제 언체크 예외를 사용하면 될까??</h2>
<p>그 답은 2가지의 원칙을 확인해보면된다.</p>
<ul>
<li>기본적으로 언체크 예외를 사용하자!!</li>
<li>체크 예외는 비즈니스 로직상 의도적으로 던지는 예외에만 사용하자!!<ul>
<li>이 경우 해당 예외를 잡아서 반드시 처리해야 하는 문제일 때만 체크 예외를 사용해야한다</li>
</ul>
</li>
</ul>
<p>이 원칙에 의문이 들 수가 있다. 물론 체크 예외는 필수로 <code>throws 예외</code>를 선언해야 하기 때문에 개발자 입장에서 귀찮을 수 있지만 언체크 예외에 비해 더 안전해보이는데 왜 기본적으로 언체크 예외를 사용해야하는지 의문이 드는 것이 당연하다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/e34ffd06-72b2-415d-bc91-0ca1afac3b1b/image.png" alt=""></p>
<p>위의 그림을 바탕으로 그 이유를 설명해보도록 하겠다.</p>
<ul>
<li>리포지토리는 DB에 접근해서 데이터를 저장하고 관리한다. 여기서는 <code>SQLException</code> 체크 예외를 던진다</li>
<li><code>NetworkClient</code>는 외부 네트워크에 접속해서 어떤 기능을 처리하는 객체이다. 여기서는 <code>ConnectionException</code> 체크 예외를 던진다</li>
<li>서비스는 리포지토리와 <code>NetworkClient</code> 둘 다 호출한다<ul>
<li>따라서 두 곳에서 올라오는 체크 예외 두개를 처리해야 한다. 그러나 서비스는 <code>ConnectException</code>처럼 연결이 실패하거나, <code>SQLException</code>처럼 데잍머베이스에서 발생하는 문제들과 같이 심각한 문제들은 대부분 애플리케이션 로직에서 처리할 방법이 없다</li>
</ul>
</li>
<li>서비스는 <code>SQLException</code>과 <code>ConnectException</code>을 처리할 수 없으므로 둘 다 밖으로 던진다 둘 다 체크 예외이기 때문에  <code>method() throws SQLException, ConnectException</code>과 같이 선언해야한다. </li>
<li>서비스와 마찬가지로 컨트롤러도 두 예외를 처리할 수 없기 때문에 <code>method() throws SQLException, ConnectException</code>과 과 같이 선언해야한다.</li>
<li>웹 애플리케이션이라면 서블릿의 오류 페이지나 또는 스프링 MVC가 제공하는 <code>ControllerAdvice</code>에서 이런 예외를 공통으로 처리한다.</li>
<li>추가로 이런 Exception을 처리할 때에는 사용자에게 어떤 문제가 발생했는지 자세히 설명하기 어렵기 때문에 &quot;서비스에 문제가 있습니다&quot;와 같이 일반적인 메시지를 보여준다</li>
</ul>
<p>위에서 설명한 내용을 보면 체크 예외의 2가지 문제점을 알 수 있다</p>
<ol>
<li>복구 불가능한 예외</li>
<li>의존 관계에 대한 문제</li>
</ol>
<h4 id="1-복구-불가능한-예외">1. 복구 불가능한 예외</h4>
<p>대부분의 예외는 복구가 불가능하다. 일부 복구가 가능한 예외도 있지만 아주 적다
<code>SQLException</code>을 예로 들면 데이터베이스에 문제가 있어서 발생하는 예외이다. SQL 문법에 문제가 있을 수도 있고, 데이터베이스 자체에 문제가 발생했을 수도 있다. 대부분의 서비스나 컨트롤러는 이러한 문제들을 해결할 수 없다. 따라서 이런 문제들은 일관성 있게 공통으로 처리해야 한다. 오류 로그를 남기고 개발자가 해당 오류를 빠르게 인지하는 것이 필요하다.
<code>서블릿 필터</code>, <code>스프링 인터셉터</code>, 스프링의 <code>ControllerAdvice</code>를 사용하면 이런 부분을 깔끔하게 공통으로 해결할 수 있다</p>
<h4 id="2-의존-관계에-대한-문제">2. 의존 관계에 대한 문제</h4>
<p>체크 예외의 또 다른 문제는 예외에 대한 의존 관계 문제이다
앞서 대부분의 예외는 복구 불가능한 예외라고 했다. 그런데 체크 예외이기 때문에 컨트롤러나 서비스 입장에서는 본인이 처리할 수 없어도 어쩔 수 없이 <code>throws</code>를 통해 던지는 예외를 선언해야 한다</p>
<p>그런데 여기서 예외를 던지는 것이 문제인 이유는 바로 서비스, 컨트롤러에서 <code>java.sql.SQLException</code>을 의존하기 때문에 문제가 된다</p>
<p>그로인해 OCP, DI를 통해 클라이언트 코드의 변경 없이 대상 구현체를 변경할 수 있다는 장점이 체크 예외 때문에 발목을 잡히게 된다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/3ee3e56c-239a-4b1d-bb27-d362dbe820f7/image.png" alt=""></p>
<h3 id="언체크-예외-활용">언체크 예외 활용</h3>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/9f4ad90f-9639-4bc9-a3c0-8cbd197ae7c2/image.png" alt=""></p>
<p><code>SQLException</code>을 런타임 예외인 <code>RuntimeSQLException</code>으로 변환하고
<code>ConnectException</code> 대신에 <code>RuntimeConnectException</code>을 사용하도록 바꿀 수 있다</p>
<pre><code class="language-java">static class Repository {
    public void call() {
        try {
            runSQL();
        } catch (SQLException e) {
            throw new RuntimeSQLException(e);
} }</code></pre>
<p>리포지토리에서 체크 예외인 <code>SQLException</code>이 발생하면 런타임 예외인 <code>RuntimeSQLException</code>으로 전환해서 예외를 던진다 이런식으로 체크 예외 대신에 언체크 예외를 던지게 해주면 서비스나 컨트롤러가 이런 복구 불가능한 예외를 신경쓰지 않아도 된다.
<img src="https://velog.velcdn.com/images/i-migi0104/post/82dc3cbb-d62b-41f0-8228-306ec94ecc05/image.png" alt=""></p>
<p>런타임 예외를 사용하면 중간에 기술이 변경되어도 해당 예외를 사용하지 않는 컨트롤러, 서비스에서는 코드를 변경하지 않아도 된다. 구현 기술이 변경되는 경우 예외를 공통으로 처리하는 곳에서는 예외에 따른 처리가 필요할 수 있다. 하지만 공통 처리하는 한곳만 변경하면 되기 때문에 변경의 영향 범위는 최소화된다</p>
<pre><code class="language-java"> public void call() {
     try {
         runSQL();
     } catch (SQLException e) {
throw new RuntimeSQLException(e); //기존 예외(e) 포함 }
}</code></pre>
<p>추가로 이런식으로 언체크 예외로 throw를 할 떄에는 기존 예외를 포함해줘야한다.</p>
<h2 id="체크-예외와-인터페이스">체크 예외와 인터페이스</h2>
<p>서비스 계층은 가급적 특정 구현 기술에 의존하지 않고, 순수하게 유지하는 것이 좋다. 이렇게 하려면 예외에 대한 의존도 함께 해결해야한다. 예를 들어서 <code>SQLException</code>에 대한 의존을 제거하려면 런타임 예외로 전환해서 언체크 예외로 전환해 해당 예외를 무시하면서 특정 구현 기술에 의존하는 부분을 제거하고 서비스 계층을 순수하게 유지하면된다</p>
<pre><code class="language-java">catch (SQLException e) {
     throw new MyDbException(e);
}</code></pre>
<p>위와 같이 체크 예외를 런타임 예외로 변환하면 인터페이스와 서비스 계층의 순수성을 유지할 수 있다 덕분에 향후 JDBC나 JPA로 변경하더라도 서비스 계층의 코드를 변경하지 않고 유지할 수 있다</p>
<p>이럴때 특정 상황에서는 예외를 잡아서 복구를 하고싶을 때가 있다 그럴때는 데이터 접근 예외를 직접 만들면된다.</p>
<p>예를 들어서 회원 가입시 DB에 이미 같은 ID가 있으면 ID 뒤에 숫자를 붙여서 새로운 ID를 만들어야하는 상황이다. 이런 상황에서 JDBC 드라이버는 <code>SQLException</code>을 던지고 이 예외에는 데이터베이스가 제공하는 <code>errorCode</code>가 존재한다</p>
<pre><code class="language-java">e.getErrorCode() == 23505</code></pre>
<p>서비스 계층에서는 예외 복구를 위해 키 중복 오류 코드를 확인할 수 있어야한다. 하지만 <code>SQLException</code>을 그대로 서비스 계층으로 던지면 서비스 계층이 JDBC 기술에 의존하게 된다. 그렇게 때문에 위에 작성한 것과 같이 리포지토리에서 예외를 변환해서 던져야 한다.</p>
<pre><code class="language-java">public class MyDuplicateKeyException extends MyDbException {
    public MyDuplicateKeyException() {
}
    public MyDuplicateKeyException(String message) {
        super(message);
}
    public MyDuplicateKeyException(String message, Throwable cause) {
        super(message, cause);
}
    public MyDuplicateKeyException(Throwable cause) {
        super(cause);
} }</code></pre>
<pre><code class="language-java">} catch (SQLException e) {
     //h2 db
     if (e.getErrorCode() == 23505) {
         throw new MyDuplicateKeyException(e);
}
     throw new MyDbException(e);
 }</code></pre>
<p> 위와 같이 오류 코드를 확인해서 오류 코드가 키 중복 오류(23505)인 경우에 <code>MyDuplicateKeyException</code>을 새로 만들어서 서비스 계층에 던지면 된다</p>
<pre><code class="language-java"> try {
     repository.save(new Member(memberId, 0));
     log.info(&quot;saveId={}&quot;, memberId);
} catch (MyDuplicateKeyException e) { log.info(&quot;키 중복, 복구 시도&quot;);
String retryId = generateNewId(memberId); log.info(&quot;retryId={}&quot;, retryId); repository.save(new Member(retryId, 0));
} catch (MyDbException e) { log.info(&quot;데이터 접근 계층 예외&quot;, e); throw e;
}</code></pre>
<p>그렇게 하면 <code>MyDuplicateKeyException</code>을 <code>catch</code>해서 <code>memberId</code>에 추가적인 숫자를 더하는 로직을 실행할 수 있다</p>
<p>그런데 여기서 추가로 발생하는 문제점은 <code>SQL ErrorCode</code>는 각각의 데이터베이스마다 다르다. 결과적으로 데이터베이스가 변경될 때마다 <code>ErrorCode</code>를 모두 변경해야 한다.</p>
<p>이 문제점을 해결하기 위해서는 데이터 접근과 관련된 예외를 추상화해서 제공해야한다.</p>
<h2 id="스프링-데이터-접근-예외-계층">스프링 데이터 접근 예외 계층</h2>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/b766d497-7ab7-4bcd-a419-f10b1ecdb239/image.png" alt=""></p>
<p>스프링은 데이터 접근 계층에 대한 수십 가지 예외를 정리해서 일관된 예외 계층을 제공한다
각각의 예외는 특정 기술에 종속적이지 않게 설계되어 있다. 따라서 서비스 계층에서도 스프링이 제공하는 예외를 사용하면 된다.</p>
<p>JDBC나 JPA를 사용할 때 발생하는 예외를 스프링이 제공하는 예외로 변환해주는 역할도 스프링이 제공한다</p>
<h3 id="스프링이-제공하는-예외-변환기">스프링이 제공하는 예외 변환기</h3>
<pre><code class="language-java"> @Test
 void exceptionTranslator() {
     String sql = &quot;select bad grammar&quot;;
     try {
         Connection con = dataSource.getConnection();
         PreparedStatement stmt = con.prepareStatement(sql);
         stmt.executeQuery();
     } catch (SQLException e) {
         assertThat(e.getErrorCode()).isEqualTo(42122);
         //org.springframework.jdbc.support.sql-error-codes.xml
         SQLExceptionTranslator exTranslator = new
 SQLErrorCodeSQLExceptionTranslator(dataSource);
         //org.springframework.jdbc.BadSqlGrammarException
         DataAccessException resultEx = exTranslator.translate(&quot;select&quot;, sql, e);
         log.info(&quot;resultEx&quot;, resultEx);
         assertThat(resultEx.getClass()).isEqualTo(BadSqlGrammarException.class);
} }</code></pre>
<pre><code class="language-java">SQLExceptionTranslator exTranslator = new
 SQLErrorCodeSQLExceptionTranslator(dataSource);
DataAccessException resultEx = exTranslator.translate(&quot;select&quot;, sql, e);</code></pre>
<p><code>translate()</code> 메서드의 첫번째 파라미터는 읽을 수 있는 설명이고, 두번째는 실행한 sql, 마지막은 발생된 발생한 <code>SQLException</code>을 전달하면 된다.</p>
<p>이번 글에서 스프링에서 예외처리를 어떤식으로 해야하는지, 왜 해야 하는지, 체크 예외와, 언체크 예외가 무엇이지 등을 작성해봤다.</p>
<p>작성을 하면서도 아직 모르는 개념들이 많다는 생각이 들었고 헷갈리기까지 한다. 프로젝트를 진행하면서 체크 예외와 언체크 예외를 구분해서 생각을 해본 적이 없는데 앞으로는 아 이 예외는 체크 예외이기때문에 언체크 예외로 전환을 해줘야 되겠구나, 이 예외는 체크 예외로 해야겠구나 등을 생각하면서 진행해야 익숙해질 것 같다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DB SIDU와 인덱스와 VIEW]]></title>
            <link>https://velog.io/@i-migi0104/DB-SIDU%EC%99%80-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EC%99%80-VIEW</link>
            <guid>https://velog.io/@i-migi0104/DB-SIDU%EC%99%80-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EC%99%80-VIEW</guid>
            <pubDate>Thu, 03 Oct 2024 09:01:07 GMT</pubDate>
            <description><![CDATA[<h3 id="데이터-입력---insert">데이터 입력 - INSERT</h3>
<p>CREATE TABLE 직후에는 아무런 행도 없는 상태이다. 그 후에 INSERT를 사용해서 DB를 삽입한다</p>
<p><code>INSERT INTO 테이블이름 VALUES (값1, 값2, 값3, ...);</code></p>
<p>INSERT를 실행할 떄에는 <code>NOT NULL</code>이 걸린 필드나 외래키 참조 상황인 필드를 고려해서 <code>INSERT</code>를 실행해야 한다.</p>
<h3 id="데이터-조회---select">데이터 조회 - SELECT</h3>
<p><code>SELECT</code>의 순서</p>
<blockquote>
<p>SELECT 컬럼
    FROM 테이블
    WHERE 조건
    GROUP BY 그룹화할_컬럼
    HAVING 필터_조건
    ORDER BY 정렬할_컬럼
    ;</p>
</blockquote>
<h4 id="연산-및-집계함수">연산 및 집계함수</h4>
<ul>
<li>COUNT: <code>SELECT COUNT(*) FROM products;</code></li>
<li>SUM: <code>SELECT SUM(price) FROM products;</code></li>
<li>AVG: <code>SELECT AVG(price) FROM products;</code></li>
<li>MAX: <code>SELECT MAX(price) FROM products;</code></li>
<li>MIN: <code>SELECT MIN(price) FROM products;</code></li>
</ul>
<h4 id="like를-이용한-패턴-검색">LIKE를 이용한 패턴 검색</h4>
<p><code>WHERE</code>를 이용해서 조건 검색을 하기 위해서는 문자열이 완전하게 일치해야 한다.</p>
<p><code>SELECT name FROM products WHERE name = &#39;monitor&#39;;</code></p>
<p>그러나 <code>LIKE</code>를 사용하면 부분적 일치로도 검색을 할 수 있다.
사용 가능한 와일드 카드는 <code>%</code>, <code>_</code> 두 가지이다.
<code>%</code>는 임의의 문자열을 뜻하고 <code>_</code>는 하나의 임의의 문자를 뜻한다</p>
<p><code>SELECT name FROM products WHERE name LIKE &#39;_onitor&#39;;</code>
<code>SELECT name FROM products WHERE name LIKE &#39;%monitor&#39;;</code></p>
<h4 id="group-by--having">GROUP BY &amp; HAVING</h4>
<blockquote>
<p>SELECT 컬럼
    FROM 테이블
    WHERE 조건
    GROUP BY 그룹화할_컬럼
    HAVING 필터_조건
    ORDER BY 정렬할_컬럼
    ;</p>
</blockquote>
<p>테이블 내 데이터를 그룹으로 묶어 조회하고 싶을 때, 주로 집계 함수와 함께 사용한다
<code>HAVING</code>을 상요해서 그룹화한 결과를 필터링 할 수 있다.</p>
<h4 id="서브-쿼리">서브 쿼리</h4>
<p>서브 쿼리란 퀴리 속의 <code>select</code>이다.</p>
<h3 id="데디터-수정---update">데디터 수정 - UPDATE</h3>
<p><code>UPDATE</code>를 사용해서 데이터의 수정을 할 수 있다.</p>
<p><code>UPDATE 테이블 SET 열=값, 열=값 [WHERE 조건식];</code></p>
<p><code>where</code>를 이용해서 특정 행일 식별해 조건에 일치하는 모든 행을 대상으로 업데이트한다.
<code>where</code>가 생략된 경우 모든 행이 갱신을 하며 <code>set</code>에 사용된 <code>=</code>는 대입 연산자이다.</p>
<h4 id="참조-시-updateon-update">참조 시 UPDATE(on update)</h4>
<ul>
<li><code>cascade</code>: 참조 데이터 업데이트 시 상대방 데이터도 함께 업데이트</li>
<li><code>set null</code>: 참조 데이터 업데이트 시 상대방 테이블의 참조 컬럼을 <code>NULL</code>로 업데이트</li>
<li><code>set default</code>: 참조 데이터 업데이트 시 상대방 테이블의 참조 컬럼을 <code>Default</code> 값으로 업데이트</li>
<li><code>restrict</code>: 참조하고 있을 경우, 업데이트 불가</li>
<li><code>no Action</code>: <code>Restrict</code>와 동일, 옵션을 지정하지 않았을 경우에 자동으로 선택</li>
</ul>
<h3 id="데이터-삭제--delet">데이터 삭제 -DELET</h3>
<h4 id="조건에-맞는-데이터-삭제">조건에 맞는 데이터 삭제</h4>
<p><code>DELETE FROM 테이블명 WHERE 조건식;</code></p>
<h4 id="테이블의-모든-데이터-삭제">테이블의 모든 데이터 삭제</h4>
<p><code>DELETE FROM 테이블명;</code></p>
<h4 id="참조-시-deleteon-delete">참조 시 DELETE(on delete)</h4>
<ul>
<li><code>cascade</code>: 참조 데이터 업데이트 시 상대방 데이터도 함께 삭제</li>
<li><code>set null</code>: 참조 데이터 업데이트 시 상대방 테이블의 참조 컬럼을 <code>NULL</code>로 업데이트</li>
<li><code>set default</code>: 참조 데이터 업데이트 시 상대방 테이블의 참조 컬럼을 <code>Default</code> 값으로 업데이트</li>
<li><code>restrict</code>: 참조하고 있을 경우, 삭제 불가</li>
<li><code>no Action</code>: <code>Restrict</code>와 동일, 옵션을 지정하지 않았을 경우에 자동으로 선택</li>
</ul>
<h2 id="인덱스">인덱스</h2>
<p>인덱스란 테이블을 더 빠르게 조회하기 위한 참조 수단으로 실무에서 검색 속도를 향상시키기 위해서 빈번히 사용한다.
<img src="https://velog.velcdn.com/images/i-migi0104/post/392a88a2-1a5c-47d8-af15-293b2cd59be2/image.png" alt=""></p>
<p>이미지와 같이 인덱스는 <code>열</code> 단위로 사용한다</p>
<p>인덱스가 없다면 데이터를 찾기 위해서 모든 테이블의 데이터를 스캔해야한다.</p>
<h3 id="인덱스의-부작용">인덱스의 부작용</h3>
<ul>
<li>인덱스를 저장할 추가적인 저장 공간이 필요하다</li>
<li>인덱스를 작성할 때 시간이 사용된다. </li>
<li>SELECT가 아닌 INSERT, UPDATE, DELETE 시 성능이 악화된다</li>
</ul>
<h3 id="인덱스의-종류">인덱스의 종류</h3>
<ul>
<li>클러스터형 인덱스</li>
<li>보조 인덱스</li>
</ul>
<h4 id="클러스터형-인덱스-clustered-index">클러스터형 인덱스 (Clustered index)</h4>
<p><code>PRIMARY KEY</code> (우선 지정) / <code>UNIQUE</code> + <code>NOT NULL</code> 제약 조건 지정 시 자동으로 생성된다.
테이블 당 하나이며 행 데이터가 인덱스로 지정된 데이터에 대해 자동 정렬된다.</p>
<blockquote>
<p>select 했을 때 primary key에 대해 자동 정렬되는 이유이다</p>
</blockquote>
<h4 id="보조-인덱스-secondary-index">보조 인덱스 (Secondary index)</h4>
<p>테이블 당 여러 개가 사용 가능하며 중복이 가능하다
고유키로 지정된 컬럼은 자동으로 보조 인덱스가 생성되며 행 데이터가 인덱스로 지정된 데이터에 대해 자동으로 정렬되지 않는다</p>
<p>보조 인덱스 생성
<code>CREEATE INDEX 인덱스명 ON 테이블명;</code></p>
<p>인덱스 조회
<code>SHOW INDEX FROM 테이블명;</code></p>
<p>인덱스 삭제
<code>DROP INDEX 인덱스명 FROM 테이블명;</code></p>
<h3 id="view">VIEW</h3>
<p>뷰란 가상의 테이블로 <code>SELECT</code>의 결과를 마치 테이블과 같이 간주한 것이다. <code>VIEW</code>로 인해 쿼리를 단순화할 수 있고 모든 데이터를 보여줄 필요가 없어진다 </p>
<p>뷰 생성
<code>USE VIEW 뷰이름 AS SELECT;</code></p>
<p>뷰 삭제
<code>DROP VIEW 뷰이름;</code></p>
<p>뷰를 조회하는 것은 제한이 없지만 삽입, 수정, 삭제 등은 제약이 있을 수도 있다</p>
<h3 id="join">JOIN</h3>
<p><code>JOIN</code>이란 관계형 데이터베이스의 핵심으로 여러 개의 테이블을 하나로 묶는 방법이다.</p>
<p><code>JOIN</code>의 종류에는 <code>INNER JOIN</code>, <code>(LEFT, RIGHT, FULL) OUTER JOIN</code> 이 있다.</p>
<h4 id="inner-join">INNER JOIN</h4>
<p><code>SELECT 열명
FROM 테이블명1
    INNER JOIN 테이블명2
    ON 결합조건</code>
테이블명 1과 테이블명 2 결합 조건을 모두 만족하는 데이터만을 선택해 결합(교집합)</p>
<h4 id="left-outer-join">LEFT OUTER JOIN</h4>
<p>테이블 1을 모두 선택하고 테이블2를 합치되 매칭되지 않으면 NULL</p>
<h4 id="right-outer-join">RIGHT OUTER JOIN</h4>
<p>테이블 2를 모두 선택하고, 테이블1를 합치되 매칭되지 않으면 NULL</p>
<h4 id="full-outer-join">FULL OUTER JOIN</h4>
<p>두 테이블 모두 선택하고 매칭되지 않으면 NULL</p>
<h1 id="관련-면접-질문-리스트">관련 면접 질문 리스트</h1>
<h3 id="1-where-과-having의-차이점은">1. WHERE 과 HAVING의 차이점은?</h3>
<blockquote>
<p><code>WHERE</code>은 그룹화되기 전에 데이터를 필터링하는데 사용되고, <code>HAVING</code>은 <code>GROUP BY</code>로 그룹화된 결과에 대해 조건을 걸 때 사용된다</p>
</blockquote>
<h3 id="2-인덱스의-장단점은">2. 인덱스의 장단점은?</h3>
<blockquote>
<p>인덱스는 테이블의 특정 열에 대해 데이터를 빠르게 검색할 수 있도록 도와줍니다. <code>SELECT</code> 쿼리의 속도를 크게 향상시킬 수도 있지만 반대로 <code>INSERT</code>, <code>UPDATE</code>, <code>DELETE</code> 작업의 성능은 저하됩니다. 이는 데이터 변경 시 인덱스도 함께 업데이트 되어야 하기 때문입니다.</p>
</blockquote>
<h3 id="3-delete와-truncate의-차이점은">3. DELETE와 TRUNCATE의 차이점은?</h3>
<blockquote>
<p><code>DELETE</code>는 <code>WHERE</code> 조건을 사용해 특정 행을 사용할 수 있으며, 트랜잭션 로그를 남겨서 복구를 할 수 있습니다. 반면에 <code>TRUNCATE</code>는 테이블의 모든 행을 빠르게 삭제하지만 트랜잭션 로그를 남기지 않아서 복구가 불가능합니다. 또한 <code>TRUNCATE</code>는 자동으로 모든 행을 삭제하므로, 테이블 구조는 그대로 유지됩니다</p>
</blockquote>
<h3 id="4-on-delete-cascade-란">4. ON DELETE CASCADE 란?</h3>
<blockquote>
<p><code>ON DELETE CASCADE</code>는 외래 키 제약 조건을 설정할 때 사용하는 옵션입니다. 부모 테이블의 데이터가 삭제될 때, 해당 데이터를 참조하는 자식 테이블의 데이터도 자동으로 삭제됩니다. 이를 통해서 데이터의 무결성을 유지할 수 있습니다</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[oAuth 2.0 jwt]]></title>
            <link>https://velog.io/@i-migi0104/oAuth-2.0-jwt</link>
            <guid>https://velog.io/@i-migi0104/oAuth-2.0-jwt</guid>
            <pubDate>Tue, 01 Oct 2024 08:40:58 GMT</pubDate>
            <description><![CDATA[<p>이번 게시글은 개발을 하는 도중에 Spring OAuth 2.0 을 jwt 방식으로 사용하면서 겪은 시행착오에 대해서 작성하려고 한다. </p>
<p>우선 사용자의 인증과 인가를 처리할 때 쿠키 방식, 세션 방식, JWT 토큰 방식의 차이점에 대해서 말해보겠다</p>
<h2 id="쿠키-vs-세션-vs-jwt">쿠키 VS 세션 VS JWT</h2>
<h3 id="쿠키-인증-방식">쿠키 인증 방식</h3>
<p>쿠키 인증 방식은 쿠키에 사용자의 인증 정보를 담아서 인증하는 방식으로 클라이언트가 인증 정보를 관리한다</p>
<p>대략적인 흐름은</p>
<ol>
<li>클라이언트가 서버에 첫 로그인 인증 요청을 보내면, 서버에서 응답으로 쿠키에 사용자 인증 정보를 담아서 보낸다</li>
<li>서버에서 응답한 쿠키를 클라이언트에서 저장하고, 인증/인가 요청 시마다 서버로 요청한다</li>
</ol>
<p>흐름이 간단해보이는 것처럼 인증/인가 작업이 가장 쉽고 간단하다. 사용자의 인증 정보를 클라이언트가 관리하기 때문에 서버 부하가 적고, 인증 상태를 서버가 관리하지 않고 매번 클라이언트의 인증 정보를 담은 쿠키의 요청을 받을 때 처리하므로 Stateless 하다.</p>
<p>그러나 인증/인가 작업이 간단한 만큼 가장 치명적인 단점이 존재한다</p>
<ol>
<li>클라이언트 즉 사용자가 쉽게 쿠키에 담긴 인증 정보를 위조할 수 있다</li>
<li>쿠키의 데이터 크기가 제한적이고, 또 크키가 커진다면 네트워크 부하가 심해진다</li>
</ol>
<p><code>CSRF</code>, <code>XSS</code> 공격등에 매우 취약하기 때문에 3가지의 인증/인가 방법 중에서 가장 기초적이고 사용하지 않는 방식이다</p>
<h3 id="세션-인증-방식">세션 인증 방식</h3>
<p>다음으로는 세션 인증 방식이다. 세션 인증 방식은 세션으로 사용자의 인증 정보를 관리하는 방식이다</p>
<p>대략적인 인증/인가 방식은 아래와 같다</p>
<ol>
<li>클라이언트가 서버에 첫 로그인 요청을 보내면, 서버에서 인증 정보를 저장하고 <code>SessionID</code>를 클라이언트에게 제공한다</li>
<li>서버에서 받은 <code>SessionId</code>로 서버에게 인증, 인가 요청을 해서 서버에서 <code>SessionID</code>에 해당하는 인증 정보로 인증/인가를 처리한다</li>
</ol>
<p>쿠키 인증 방식과는 다르게 사용자의 인증 정보를 클라이언트가 아닌 서버가 관리한다. 그렇기 때문에 보안상 훨씬 안전하다 추가로 세션의 장점은</p>
<ol>
<li>한 사용자의 디바이스별 인증을 관리할 수 있다</li>
<li>하나의 계정 공유를 관리할 수 있다</li>
<li>비정상적인 접근 신고가 들어오면, 서버에서 판단하여 해당 세션을 삭제해서 바로 로그아웃시킬 수 있다</li>
</ol>
<p>여기서 서버가 사용자 인증 정보를 관리하는 곳에 따라서도 장단점이 나뉜다.
크게 메모리, 하드디스크, DB에서 사용자의 인증 정보를 관리한다.</p>
<p>메모리 영역에서 사용자의 인증 정보를 관리하게 되면, 속도가 하드디스크나 DB에서 관리하는 것에 비해 빠르지만 사용자가 동시에 <code>SessionId</code>로 인증 요청을 많이 보내게 되면 서버 메모리가 부족해지고, 서버를 껐다 켰을 때 Session 정보가 휘발된다는 치명적인 단점이 존재한다</p>
<p>하드디스크에서 관리하게되면 메모리에서보다는 속도가 느리지만 DB보다는 빠르다 그리고 발생하는 문제점은 메모리도 마찬가지지만</p>
<p><code>Scale-out</code>을 사용해서 서버가 여러 대 존재하여 로드 밸런싱을 사용할 때 한 사용자가 로그인 후 다음 인가 요청 시 로드 밸런싱으로 인해  사용자가 로그인한 서버가 아닌 다른 서버로 요청이 보내지면 다시 재로그인 필요하다.</p>
<p>한마디로 사용하는 서버가 여러개일때에는 인증 요청과 인가 요청 대상 서버가 달라지면 재로그인을 해야 한다는 것이다.</p>
<p>마지막으로 서버DB에서 사용자 인증 정보를 관리하는 방식이 있다 속도는 가장 느리지만 위에서 말한 단점들을 모두 해결할 수 있다 그렇기 때문에 세션으로 인증/인가를 진행을 해야한다면 서버 DB에 사용자 인증 정보를 관리하는 것이 가장 효율적이다.</p>
<p>단점은 로그인한 모든 유저의 인증 정보를 DB에 관리해야하므로 무겁고, 매 인증마다 DB를 거쳐 인증 정보를 Select 해야하므로 연결 비용이 클 수 있다.</p>
<h3 id="jwt-토큰-방식">JWT 토큰 방식</h3>
<p>마지막으로 JWT 토큰 방식에 대해서 설명하겠다. 인증/인가 흐름은 아래와 같다</p>
<ol>
<li>클라이언트가 서버에 첫 로그인 인증 요청을 보내면, 서버에서 응답으로 Token을 담아서 보낸다</li>
<li>서버에서 응답한 Token을 클라이언트에서 저장하고, 인증/인가 요청 시마다 서버로 요청한다</li>
</ol>
<p>이것만 들으면 기본적으로 클라이언트가 인증 정보를 관리하기 때문에 쿠키 인증 방식과 뭐가 다르지?라는 생각이 나는 처음에 들었다. 하지만 좀 더 알아보니 차이점은 쿠키가 아닌 JWT 토큰을 매개체로 인증한다는 것과 서버에서 <code>토큰 검증</code>이 이루어지는 것이 쿠키에 비해서 보안적으로 안전하게 관리될 수 있는 차이점이다.</p>
<p>JWT는 사용자의 인증 정보와 서버의 <code>SecretKey</code>로 이루어져 서버가 생성한 Token이기 때문에 요청으로 온 Token이 서버에서 발급한 Token인지 검증할 때 Token의 SecretKey가 다르다면 위조, 변조된 Token이라고 판단할 수 있다</p>
<p>세션 방식과 비교해서는 기본적으로 클라이언트가 Token을 관리하기 때문에 서버 부하가 비교적 적다. 단점은 보안이 세션 방식 보다는 취약하고 세션 방식의 여러 기능들을 사용할 수 없다는 단점이 존재했다.</p>
<p>나는 현재 프로젝트에서 <code>JWT</code> 토큰 방식을 사용하고 있다</p>
<p>그래서 내가 JWT 토큰을 사용하면서 겪었던 고민을 말해보겠다
그러기 전에 우선 <code>Access Token</code>과 <code>Refresh Token</code>이 뭔지 알아야한다.</p>
<h2 id="access-refresh-token">Access, Refresh Token</h2>
<p><code>Jwt</code> 토큰은 유저의 신원이나 권한을 결정하는 정보를 담고 있는 데이터 조각이기 때문에 탈취를 당했을 때 Jwt 토큰을 탈취한 사람은 마치 신뢰할만한 사람인 것처럼 인증을 통과할 수 있고, 클라이언트와 탈취한 사람을 서버는 구분할 수 없기 때문에 최소한의 대비책으로 <code>유효기간</code>을 필수로 두어야한다.</p>
<p>그런데 유효기간을 짧게 두면 사용자가 로그인을 자주 해야하기 때문에 사용자 경험적으로 좋지 않고, 유효기간을 길게 두면 보안상 탈취 위험에서 벗어날 수 없다.</p>
<p>그래서 나온 개념이 <code>Access Token</code>과 <code>Refresh Token</code>이다.</p>
<p>보통 <code>Access Token</code>의 유효기간은 1시간에서 60일 정도로 정해진 기간은 없지만 비교적 짧게 설정이 되고,
<code>Refresh Token</code>의 유효기간은 몇 십일에서 1년 정도를 길게 설정한다. 평소에 API와 통신할 때에는 Access Token을 사용하고, Refresh Token은 Access Token이 만료되었을 때 갱신을 하기 위해서 사용한다.</p>
<h3 id="서버-클라이언트-통신-과정">서버-클라이언트 통신 과정</h3>
<p>좀 더 구체적으로 서버와 클라이언트가 JWT토큰을 이용해서 통신하는 과정을 말해주겠다</p>
<ol>
<li>로그인 인증에 성공한 클라이언트는 <code>Refresh Token</code>과 <code>Access Token</code>을 서버로부터 받는다</li>
<li>클라이언트는 <code>Refresh Token</code>과 <code>Access Token</code>을 로컬에 저장해놓는다.</li>
<li>클라이언트는 <code>헤더</code>에 <code>Access Token</code>을 넣고 API 통신을 한다 (Authorization)</li>
<li>일정 기간이 지나 <code>Access Token</code>의 유효기간이 만료가된다.
 4.1 <code>Access Token</code>은 이제 유효하지 않기 때문에 권한이 없는 사용자가 된다
 4.2 클라이언트로부터 유효기간이 지난 <code>Access Token</code>을 받은 서버는 <code>401 Unauthorized</code> 에러 코드로 응답한다
 4.3 401 에러코드를 통해 클라이언트는 토큰의 유효기간이 만료되었음을 알 수 있다</li>
<li>헤더에 <code>Access Token</code> 대신 <code>Refresh Token</code>을 넣어서 API를 재요청한다</li>
<li><code>Refresh Token</code>으로 사용자의 권한을 확인한 서버는 응답쿼리 헤더에 새로운 <code>Access Token</code>을 넣어서 응답한다</li>
<li>만약 <code>Refresh Token</code>도 만료되었다면 서버는 동일하게 <code>401 errorCode</code>를 보내고 클라이언트는 재로그인해야한다.</li>
</ol>
<p>그렇기 때문에 탈취자가 통신이 빈번히 일어나는 <code>Access Token</code>을 탈취를 하더라도 유효기간이 짧기 때문에 보안을 높일 수 있는 대안이 되는것이다</p>
<p>내가 여기서 고민했던 점는 <code>Access Token</code>과 <code>Refresh Token</code>에 어떤 정보를 담아서 만들어야 하고, 어디에 저장을 어떻게 해야하는지였다.</p>
<p>우선 첫번째 후보는 로컬 스토리지 &amp; 세션 스토리지 였다. 그러나 이렇게 저장한다면 자바스크립트로 토큰 값을 꺼내서 보내는 방식이기 때문에 <code>XSS</code> 공격에 취약하기 때문에 적절치 않다고 판단했다. 두번 째 후보는 쿠키이다. 쿠키 또한 자바스크립트로 접근이 가능하지만 HTTP Only 옵션과 secure 옵션을 설정한다면 보안을 높일 수 있지만 <code>CSRF</code> 공격에는 취약할 수 있다. 처음에는 그래도 다른 곳에 저장하는 방식에 비해서는 안전하다고 생각해 <code>Access Token</code>과 <code>Refresh Token</code>을 둘 다 쿠키에 저장을 하려고 했지만 <code>Refresh Token</code>은 사용자의 <code>Access Token</code>을 재발급 하는 용도이기 때문에 괜찮을 수 있지만 <code>Access Token</code> 또한 쿠키에 저장한다면 <code>CSRF</code> 공격을 통해서 인증 인가 과정으로 보호된 동작을 실행해버릴 수 있기 때문에 <code>Refresh Token</code>만 쿠키에 저장하기로 결정했다</p>
<p>그리고 <code>Access Token</code>은 <code>Authorization Header</code>를 통해서 클라이언트에게 전달하기로 했다. 물론 완벽한 보안은 아닐 수 있지만 최소한의 위험만 감수하는 방법이라고 생각한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[영속성이란?]]></title>
            <link>https://velog.io/@i-migi0104/%EC%98%81%EC%86%8D%EC%84%B1</link>
            <guid>https://velog.io/@i-migi0104/%EC%98%81%EC%86%8D%EC%84%B1</guid>
            <pubDate>Sat, 28 Sep 2024 14:10:54 GMT</pubDate>
            <description><![CDATA[<p>영속성이란 JPA를 공부하면서 많이 접하게 되는 용어이다.
영속성은 JPA가 제공하는 핵심 개념 중 하나로, 데이터베이스와 자바 애플리케이션 간의 데이터 저장과 관리를 의미한다. 이를 통해서 데이터베이스의 레코드를 자바 객체로 다루면서, 객체 지향적인 방법으로 데이터베이스 조작을 할 수 있게 된다.</p>
<p>JPA에서의 영속성에 대해 주요 개념을 추가로 설명해보겠다.</p>
<h3 id="1-영속성-컨텍스트">1. 영속성 컨텍스트</h3>
<p>영속성 컨텍스트는 엔티티 객체를 관리하는 일종의 메모리 공간이다. 엔티티 매니저가 이 영속성 컨텍스트를 통해서 엔티티의 상태를 관리한다. 영속성 컨텍스트는 JPA에서 데이터의 일관성을 보장하는 중요한 역할을 하며, 여기서 관리되는 엔티티들은 영속 관계에 놓인다.</p>
<h4 id="영속성-컨텍스트의-장점">영속성 컨텍스트의 장점</h4>
<ul>
<li>1차 캐시: 영속성 컨텍스트는 엔티티를 메모리에 캐시해서 중복 조회를 방지하고 성능을 최적화시켜준다.</li>
<li>변경 감지: 엔티티의 상태가 변경되면 자동으로 감지되어, 트랜잭션이 커밋될 때 자동으로 데이터베이스에 반영된다.</li>
<li>지연 로딩: 필요한 시점에만 데이터베이스에서 데이터를 조회하는 방식으로 성능을 최적화할 수 있다.</li>
</ul>
<h3 id="2-엔티티와-영속성">2. 엔티티와 영속성</h3>
<ul>
<li>엔티티는 데이터베이스의 테이블과 매핑된 자바 객체로, JPA를 사용하면 이 객체를 통해 테이블의 데이터를 조작한다. JPA는 엔티티의 생명주기를 기반으로 엔티티의 상태를 관리하는데, 이를 통해 영속성과 관련된 여러 상태 변화를 관리할 수 있다</li>
</ul>
<h3 id="3-엔티티의-생명주기">3. 엔티티의 생명주기</h3>
<p><code>JPA</code>에서 엔티티 객체는 네 가지의 생명주기를 가진다.</p>
<ol>
<li><p>비영속 상태 <code>Transient</code>
엔티티 객체가 영속성 컨텍스트와 전혀 연과되어 있지 않은 상태이다. 자바 객체로는 존재하지만, 데이터베이스와 연관되어 있지 않은 상태를 말한다. 예를 들어서 <code>new</code> 키워드를 사용해서 새로 생성한 엔티티는 비영속 상태이다.</p>
</li>
<li><p>영속 상태 <code>Persistent</code>
엔티티가 영속성 컨텍스트에 저장된 상태로, 데이터베이스에 반영될 준비가 된 상태이다. 이 상태에서는 영속성 컨텍스트가 엔티티를 관리하며, 자동으로 변경 사항을 데이터베이스에 반영한다.
예를 들어서 <code>entityManager.persist(entity)</code>를 호출하면 엔티티가 영속 상태로 전환된다</p>
</li>
<li><p>준영속 상태 <code>Detached</code>
영속성 컨텍스트에 의해 관리되던 엔티티가 더 이상 관리되지 않는 상태이다. 즉 데이터베이스와의 동기화가 끊긴 상태이다.
예를 들어서 <code>entityManger.detach(entity)</code>를 호출하거나 <code>entityManger.clear()</code>로 전체 컨텍스트를 비울 때 엔티티가 준영속 상태가 된다.</p>
</li>
<li><p>삭제 상태 <code>Removed</code>
마지막 상태로 엔티티가 영속성 컨텍스트에서 삭제되어 데이터베이스에서도 삭제될 준비가 된 상태이다. <code>entityManager.remove(entity)</code> 를 호출하면 엔티티가 상태 상태가 된다.</p>
</li>
</ol>
<p>아마 엔티티의 생명주기를 읽으면서 나는 <code>EntityManager</code>를 직접 사용한적이 없는데 생명주기를 이용안한건가?? 라는 생각을 할 수도 있지만 <code>JPA</code>를 사용한다면 <code>EntityManager</code>를 내부적으로 사용하면서 개발자가 직접 <code>EntityManger</code>를 다루지 않고도 쉽게 JPA의 기능을 활용할 수 있도록 도와준다고 한다.</p>
<pre><code class="language-java">public interface MemberRepository extends JpaRepository&lt;MemberEntity, Long&gt; {
    // 기본 CRUD 메서드가 자동으로 제공됨
    Optional&lt;MemberEntity&gt; findByUsername(String username);
}</code></pre>
<p>예를 들어서 위의 코드에서는 <code>JpaRepository</code> 인터페이스를 상속받기만 하면 자동으로 <code>EntityManager</code>를 사용해서 데이터베이스와 상호작용하는 메서드를 제공하기 때문에 <code>EntityManager</code>를 직접 다룰 필요가 없다</p>
<h3 id="4-트랜잭션과-영속성">4. 트랜잭션과 영속성</h3>
<ul>
<li>JPA는 트랜잭션을 기반으로 엔티티의 상태를 데이터베이스에 반영한다 트랜잭션이 커밋될 때 영속성 컨텍스트는 모든 변경 사항을 데이터베이스에 적용한다 </li>
<li>추가로 트랜잭션 내에서만 엔티티가 영속성 컨텍스트에 의해 관리되므로, 트랜잭션이 끝나면 영속성 컨텍스트도 더 이상 존재하지 않는다</li>
</ul>
<h3 id="5-플러시-flush">5. 플러시 Flush</h3>
<ul>
<li><code>Flush</code>는 영속성 컨텍스트에 있는 변경 사항을 데이터베이스에 반영하는 작업을 의미한다. 기본적으로 <code>JPA</code>는 트랜잭션이 커밋되기 전에 플러시를 수행해서 엔티티의 변경사항을 자동으로 데이터베이스에 동기화한다. 개발자가 필요에 따라 <code>flush()</code> 메서드를 호출해서 수동으로 플러시를 할 수도 있다</li>
<li>주의 해야 할점은 플러시는 영속성 컨텍스트의 변경 사항을 데이터베이스에 반영하지만, 트랜잭션을 커밋하지 않으면 그 데이터는 데이터베이스에 반영되지 않고 롤백될 수 있다.</li>
</ul>
<h3 id="6-변경-감지-dirty-checking">6. 변경 감지 Dirty Checking</h3>
<p>변경 감지 즉 <code>Dirty Checking</code> 이란 용어도 개발을 진행하면서 자주 들었지만 어떤 용어인지는 잘 몰랐던 용어인데, JPA가 영속 상태의 엔티티 객체가 변경되었을 때 자동으로 이를 감지하여, 트랜잭션 커밋 시점에 데이터베이스에 업데이트한다. 이러한 로직을 <code>Dirty Checking</code>이라고 한다. 이 과정에서 <code>@Entity</code> 객체의 필드 값이 변경되었는지를 추적한다.
<code>Dirty Checking</code>은 성능을 최적화하고 데이터 일관성을 보장하기 위해 매우 중요한 개념이라고 한다</p>
<h3 id="7-lazy-loading--eager-loading">7. Lazy Loading &amp; Eager Loading</h3>
<p><code>Lazy Loading</code> 즉 지연 로딩은 위에서도 언급한 용어인데, 연관된 엔티티나 데이터를 실제로 사용할 때 데이터베이스에서 조회하는 방식이다. 필요하지 않은 데이터를 미리 로드하지 않기 때문에 성능 상의 이점이 있다</p>
<p><code>Eager Loading</code> 은 즉시 로딩이라고 하는데 연관된 엔티티나 데이터를 즉시 데이터베이스에서 가져오는 방식이다. 기본적으로 모든 관련 엔티티가 함께 로드되기 때문에 성능 저하가 발생할 수 있다</p>
<p>두 개의 용어를 언제 사용하냐면 보통 엔티티 관계에서 <code>@OneToMany</code> 와 <code>@ManyToOne</code> 등의 관계를 설정할 때 많이 사용했다.</p>
<p>그래서 만약에 누군가가 너 JPA에서의 <code>영속성</code> 과 <code>영속성 컨텍스트</code>가 뭐야? 라고 묻는다면</p>
<blockquote>
<p>영속성은 데이터를 데이터베이스에 영구적으로 저장되어서 애플리케이션이 종료되더라도 유지되는 것이고, 영속성 컨텍스트란 엔티티 객체를 관리하고 트랜잭션이 진행되는 동안 데이터베이스와의 동기화를 처리하는 메모리 공간이야 정리하자면 영속성 컨텍스트는 애플리케이션이 실행되는 동안만 메모리에서 엔티티 객체를 관리하는 일종의 임시 저장소라 애플리케이션이 종료되면 영속성과 다르게 사라져!!</p>
</blockquote>
<p>라고 말할 수 있어야 할 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 배치]]></title>
            <link>https://velog.io/@i-migi0104/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B0%B0%EC%B9%98</link>
            <guid>https://velog.io/@i-migi0104/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B0%B0%EC%B9%98</guid>
            <pubDate>Thu, 26 Sep 2024 09:16:23 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 진행하면서 일정 시간마다 특정 로직을 실행시켜줘야 하는 경우가 여러 번 있었고 그때마다 나는 <code>@Scheduled(cron = &quot;0 0 12 * * ?&quot;)</code> 어노테이션을 사용해서 특정 로직을 일정 시간마다 실행시켜줬다. 그런데 대량의 데이터를 처리하거나 더욱 더 복잡한 비즈니스 로직을 포함한 작업을 효율적으로 실행시키기 위해서는 스프링의 <code>Batch</code>를 사용한다는 글을 봐서 이번 기회에 스케줄러 대신에 배치를 사용해 보기로 했다.</p>
<p>내가 스프링 배치를 사용해보기 전에 우선 스케줄러와 배치의 개념에 대해서 알아보기로 했다.</p>
<h2 id="1-스케줄러-scheduler">1. 스케줄러 (Scheduler)</h2>
<p>스케줄러의 주된 목적은 특정 시간 간격 또는 일정에 따라 단순 작업을 반복적으로 실행하는 것이다.
사용하는 방법은 위에서 말했듯이 <code>@Scheduled</code> 어노테이션을 사용하면된다.</p>
<h3 id="스케줄러의-특징은">스케줄러의 특징은</h3>
<ul>
<li>어노테이션 하나만 붙이면 되는만큼 간단하고 빠르게 설정할 수 있다.</li>
<li>주로 짧은 기간 동안 실행되는 작업 즉 짧은 시간 안에 완료되는 작업에 적합하다.</li>
<li>기본적으로 스프링의 <code>TaskScheduler</code>를 사용하며, 멀티스레드로 동작 가능하다</li>
<li>상태 관리가 복잡하지 않고, 트랜잭션 관리나 대규모 데이터 처리에는 부적합하다.</li>
</ul>
<p>여기서 스프링의 <code>TaskScheduler</code>란 스프링에서 제공하는 스케줄링 인터페이스로, 주기적으로 반복 실행되거나 특정 시간에 실행되는 작업을 관리하는 데 사용된다.</p>
<h2 id="2-배치-batch">2. 배치 (Batch)</h2>
<p>배치의 주된 목적은 위에서 말했듯이 대량의 데이터를 처리하거나 복잡한 비즈니스 로직을 포함한 작업을 효율적으로 실행하는 것이다.
배치의 사용 방법은 <code>Spring Batch</code>를 사용해서 배치 잡(Job), 스텝(Step), 리더(Reader), 프로세서(Processor), 라이터(Writer) 등을 정의해서 작업을 수행한다.</p>
<h3 id="배치의-특징은">배치의 특징은</h3>
<ul>
<li>대규모 데이터 처리, 트랜잭션 처리, 실패 관리, 체크포인트 등의 기능을 제공한다</li>
<li>병렬 처리나 대량의 데이터 작업, 복잡한 로직을 실행하는 데 적합하다</li>
<li>배치 작업의 흐름(Flow)를 관리하고, 여러 단계(Step)으로 구성할 수 있다</li>
<li>데이터베이스 또는 외부 시스템과의 연동을 통해 데이터를 읽고 쓰는 작업을 쉽게 구현할 수 있다</li>
<li>스케줄러와 결합해서 특정 시간에 배치 작업을 실행하는 것도 가능하다</li>
</ul>
<pre><code class="language-java">@Bean
public Job sampleJob() {
    return jobBuilderFactory.get(&quot;sampleJob&quot;)
        .start(step1())
        .next(step2())
        .build();
}

@Bean
public Step step1() {
    return stepBuilderFactory.get(&quot;step1&quot;)
        .&lt;InputType, OutputType&gt;chunk(10)
        .reader(reader())
        .processor(processor())
        .writer(writer())
        .build();
}
</code></pre>
<p>언뜻 봐도 스케줄러에 비해 배치의 난이도가 훨씬 높아 보이긴 하는 것 같다.</p>
<h2 id="스케줄러-vs-배치">스케줄러 VS 배치</h2>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/abd40042-bf24-4986-8950-35d437bf49d1/image.png" alt=""></p>
<blockquote>
<p>결론적으로 스케줄러는 단순하고 주기적으로 수행할 작업을 실행하는 데 적합하며, 배치는 대규모 데이터 처리를 수행하고 복잡한 로직이 필요한 경우에 적합하다. 스케줄러를 사용해 배치 작업을 주기적으로 실행할 수도 있으며, 두 가지 기능을 서로 상호 보완적으로 사용할 수 있다</p>
</blockquote>
<p>그럼 내가 작성한 스케줄러를 사용한 코드를 배치와 함께 사용할 수 있도록 리팩토링 해보겠다.</p>
<pre><code class="language-java">@Slf4j
@RequiredArgsConstructor
@Service
@EnableScheduling
public class MemberScheduled {

    private final MemberRepository memberRepository;

    @Scheduled(cron = &quot;*/10 * * * * *&quot;)
    public void deleteMembersFromDb() {
        log.info(&quot;Deleting members&quot;);
        log.info(&quot;삭제 처리 중&quot;);

        List&lt;MemberEntity&gt; deleteMembers = memberRepository.findAllByIsDeletedTrue();

        for (MemberEntity memberEntity : deleteMembers) {
            LocalDateTime lastConnectionAt = memberEntity.getLastConnectionAt();
            if (lastConnectionAt != null) {
                if (ChronoUnit.DAYS.between(lastConnectionAt, LocalDateTime.now()) &gt; 7) {
                    memberRepository.delete(memberEntity);
                    log.info(&quot;Delete member: {}&quot;, memberEntity.getProviderId());
                }
            }
        }
        log.info(&quot;Deleting members&quot;);
    }

}</code></pre>
<p>일단 위의 코드는 매일 자정마다 memberRepository에서 isdeleted가 true인 member들 중에 lastConnectionAt이 일주일이 지난 member를 memberRepository에서 삭제하는 스케줄러를 사용한 로직이다. 그래서 이 로직을 배치와 함께 사용하는 것으로 바꿔보기로 했다.</p>
<p>첫번쨰로 전체적인 배치를 설정하는 <code>BatchConfig</code> 클래스를 만들어야 한다</p>
<pre><code class="language-java">@Configuration
@EnableBatchProcessing
public class BatchConfig {

    @Bean
    public Job deleteMemberJob(Step deleteMemberStep, JobRepository jobRepository) {
        return new JobBuilder(&quot;deleteMemberJob&quot;, jobRepository)
            .start(deleteMemberStep)
            .build();
    }

    @Bean
    public Step deleteMemberStep(ItemReader&lt;MemberEntity&gt; reader, ItemProcessor&lt;MemberEntity, MemberEntity&gt; processor,
        ItemWriter&lt;MemberEntity&gt; writer, JobRepository jobRepository, PlatformTransactionManager transactionManager) {
        return new StepBuilder(&quot;deleteMemberStep&quot;, jobRepository)
            .&lt;MemberEntity, MemberEntity&gt;chunk(10, transactionManager)
            .reader(reader)
            .processor(processor)
            .writer(writer)
            .build();
    }

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}</code></pre>
<p>우선 <code>@EnableBatchProcessing</code> 어노테이션으로 Spring Batch 기능을 활성화 시켜줘야 한다.</p>
<p>그리고 Job을 설정해야 한다. <code>JobBuilder</code>를 사용해서 배치 작업(Job)을 구성한다. Job은 하나 이상의 <code>Step</code>으로 구성된 전체 배치 작업의 흐름을 정의하는 객체이다.</p>
<p><code>start(deleteMemberStep)</code> 현재 메소드의 Job이 deleteMemberStep 을 첫 번째 단계로 설정한 것이다.</p>
<blockquote>
<p>이 <code>Job</code>은 <code>deleteMemberStep</code>이라는 단일 <code>Step</code>을 실행하며, 회원 삭제 작업을 처리한다.</p>
</blockquote>
<p>다음으로 회원 삭제 작업을 처리하는 Step을 설정한다.
<code>chunk(10)</code> 이 부분에서 CHunk 기반 처리를 설정한다 즉 ItemReader는 한 번에 10개의 데이터를 읽고, ItemProcessor는 이 10개의 데이터를 처리하며, ItemWriter는 처리된 데이터를 한 번에 10개씩 DB에 반영한다.</p>
<p>그 후 이 Step에서 사용할 <code>itemReader</code>, <code>writer</code>, <code>ItemReader</code>를 지정한다
이 메소드의 <code>Step</code>의 역할은 ItemReader를 사용해서 회원 데이터를 DB에서 읽어오고, ItemProcessor를 사용해서 읽어온 데이터 중에서 조건에 맞는 데이터를 필터링 또는 처리하고 ItemWriter를 사용해 처리된 데이터를 DB에 반영하거나 삭제한다.</p>
<p>다음은 Spring Batch의 ItemReader를 구현한 <code>MemberItemReader</code> 클래스이다. 이 클래스는 삭제 대상 회원의 목록을 읽어 오는 역할을 한다. <code>ItemReader</code>가 배치 작업에서 데이터를 읽는 단계를 담당하며, 그 후에 <code>ItemProcessor</code>와 <code>ItemWriter</code>로 데이터를 전달한다.</p>
<p>즉 <code>MemberRepository</code>를 사용해서 <code>isDeleted</code>가 <code>true</code>로 설정된, 삭제 대상인 회원 목록을 DB에서 가져온 후에 하나씩 반환하며, 더 이상 반환할 데이터가 없으면 null 을 반환하여 읽기 작업을 종료한다.</p>
<pre><code class="language-java">@Component
public class MemberItemReader implements ItemReader&lt;MemberEntity&gt; {

    private final MemberRepository memberRepository;
    private List&lt;MemberEntity&gt; membersToDelete;

    public MemberItemReader(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    @Override
public MemberEntity read() {
    if (membersToDelete == null || membersToDelete.isEmpty()) {
        // 삭제 대상 회원 목록을 DB에서 읽어옴
        membersToDelete = memberRepository.findAllByIsDeletedTrue();
    }
    // 목록이 비어있으면 null을 반환하여 읽기 종료
    return membersToDelete.isEmpty() ? null : membersToDelete.remove(0);
}</code></pre>
<p>다음은 <code>ItemProcessor</code>를 구현한 <code>MemberItemProcessor</code>이다. 회원의 마지막 접속 시간을 확인하고, 7일 이상 접속하지 않은 회원만 다음 단계로 넘기도록 처리하는 역할을 한다</p>
<pre><code class="language-java">@Component
public class MemberItemProcessor implements ItemProcessor&lt;MemberEntity, MemberEntity&gt; {

    @Override
    public MemberEntity process(MemberEntity memberEntity) throws Exception {
        LocalDateTime lastConnectionAt = memberEntity.getLastConnectionAt();
        if (lastConnectionAt != null &amp;&amp; ChronoUnit.DAYS.between(lastConnectionAt, LocalDateTime.now()) &gt; 7) {
            return memberEntity;  // 삭제 대상으로 처리
        }
        return null;  // 처리할 필요 없는 경우 null 리턴
    }
}</code></pre>
<p><code>ItemProcessor&lt;T,R&gt;</code> 은 Spring Batch의 핵심 인터페이스 중 하나로, 데이터를 처리하는 역할을 담당한다.</p>
<ul>
<li>T는 입력 타입으로 MemberEntity 객체가 입력으로 들어온다</li>
<li>R은 출력 타입으로 처리된 후 반환되는 타입이다. 여기서는 다시 <code>MemberEntity</code> 객체를 반환한다. 만약 처리가 필요하지 않은면 <code>null</code>을 반환해서 해당 데이터를 무시할 수 있다</li>
</ul>
<p>그 후 마지막 처리 작업인 MemberItemWriter 코드이다.</p>
<pre><code class="language-java">@Component
public class MemberItemWriter implements ItemWriter&lt;MemberEntity&gt; {

    private final MemberRepository memberRepository;

    public MemberItemWriter(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Override
    public void write(Chunk&lt;? extends MemberEntity&gt; members) throws Exception {
        for (MemberEntity member : members) {
            if (member != null) {
                memberRepository.delete(member);
            }
        }
    }
}</code></pre>
<p>위의 클래스는 회원 삭제 작업을 수행하는 <code>Writer</code> 역할을 한다.
<code>ItemWriter</code> 인터페이스를 구현해서 처리할 회원 데이터를 삭제하는 로직을 정의하고, <code>writer</code> 메서드는 <code>Chunk</code> 단위로 데이터를 받아오며, 여기서 <code>memberRepository.delete()</code>를 통해서 회원 정보를 삭제한다.</p>
<p>여기서 <code>Chunk</code> 단위란 Spring Batch에서 데이터를 읽고, 처리하고, 쓰는 작업을 묶음 단위로 처리하는 방식이다. 즉 데이터를 한 번에 하나씩 처리하는 것이 아니라, 일정한 개수의 데이터 묶음(Chunk)을 만들어서 일괄적으로 처리하는 개념이다.</p>
<p>Spring Batch에서는 Chunk 기반 프로세싱을 통해서 대용량 데이터를 효과적으로 처리한다. 예를 들어서 1000개의 데이터를 처리한다고 할 때, 한 번에 1000개를 처리하는 것이 아니라, 10개씩 묶음으로 나누어 처리하는 방식이다.</p>
<p>마지막으로 현재까지 작성한 <code>MemberItemReader</code> -&gt; <code>MemberItemProcessor</code> -&gt; <code>MemberItemWriter</code> 을 지나 최종 종착역인 <code>BatchScheduler</code>(스케줄러) 코드이다.!</p>
<pre><code class="language-java">@Configuration
@EnableScheduling
@Slf4j
public class BatchScheduler {

    private final JobLauncher jobLauncher;
    private final Job deleteMemberJob;

    public BatchScheduler(JobLauncher jobLauncher, Job deleteMemberJob) {
        this.jobLauncher = jobLauncher;
        this.deleteMemberJob = deleteMemberJob;
    }

    @Scheduled(cron = &quot;0 0 0 * * ?&quot;)
    public void runDeleteMemberJob() {
        log.info(&quot;Deleting members&quot;);
        try {
            jobLauncher.run(deleteMemberJob, new JobParametersBuilder()
                .addLong(&quot;time&quot;, System.currentTimeMillis())
                .toJobParameters());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}</code></pre>
<p>이 클래스는 스케줄러로 배치 작업을 일정한 주기로 실행한다.
<code>JobLauncher</code>는 Spring Batch에서 배치 작업을 실행하는 역할을 담당하는 인터페이스로, <code>JobLauncher</code>는 정의된 배치 작업(Job)을 실행하고, 실행 시 <code>JobParameters</code>를 전달할 수 있다.\</p>
<p>또한 <code>Job deleteMemberJob</code>은 회원 삭제 작업을 정의한 Spring Batch의 <code>Job</code>이다. 이 작업이 스케줄러에 의해 매일 자정에 실행되며 삭제 작업을 수행한다.</p>
<p>여기서 <code>JobParametersBuilder</code>를 사용해서 현재 시간을 파라미터로 전달한다. 파라미터로 전달하는 이유는 매번 배치 작업을 실행할 때 새로운 실행 인스턴스로 관리하기 위함이다.</p>
<blockquote>
<p>Spring Batch는 동일한 파라미터로는 새로운 인스턴스를 실행하지 않는다</p>
</blockquote>
<p>위의 이유를 더 풀어서 설명하면 <code>Spring Batch</code>는 배치 작업이 중복해서 실행되는 것을 방지하기 위한 메커니즘을 가지고 있다. 예를 들어, 동일한 데이터에 대해 배치 작업을 중복으로 실행하는 경우, 같은 데이터를 여러 번 처리하게 되면 데이터 오류가 발생할 수 있기 떄문이다. 이를 방지하기 위해 Spring Batch는 동일한 <code>JobParameters</code>로 동일한 배치 작업을 여러 번 실행 하지 않도록 설계되었다.</p>
<pre><code class="language-java">@Configuration
@EnableBatchProcessing
public class BatchConfig {

    @Bean
    public Job deleteMemberJob(Step deleteMemberStep, JobRepository jobRepository) {
        return new JobBuilder(&quot;deleteMemberJob&quot;, jobRepository)
            .start(deleteMemberStep)
            .build();
    }

    @Bean
    public Step deleteMemberStep(ItemReader&lt;MemberEntity&gt; reader, ItemProcessor&lt;MemberEntity, MemberEntity&gt; processor,
        ItemWriter&lt;MemberEntity&gt; writer, JobRepository jobRepository, PlatformTransactionManager transactionManager) {
        return new StepBuilder(&quot;deleteMemberStep&quot;, jobRepository)
            .&lt;MemberEntity, MemberEntity&gt;chunk(10, transactionManager)
            .reader(reader)
            .processor(processor)
            .writer(writer)
            .build();
    }

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}</code></pre>
<p>지금 여기서 config 파일을 다시 확인해보면 deleteMemberStep에서 reader, processor, writer를 builder 하는 것으로 보아 reader -&gt; processor -&gt; writer 로 데이터를 전송하는 것을 build를 함으로써 해주는 것으로 예상하고 있고,</p>
<p>여러개의 step을 jopRepository에 넣어서 .start(deleteMemberStep)으로 자동화 시키는 것이다.</p>
<p>이렇게 배치를 사용해봤는데 뭔가 스케줄러보다 체계적인 것 같고 동시에 여러 개의 스케줄러를 사용할 수 있을 것 같다. 좋은 시간이었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[정적 팩토리 메서드란?]]></title>
            <link>https://velog.io/@i-migi0104/%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%9E%80</link>
            <guid>https://velog.io/@i-migi0104/%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%9E%80</guid>
            <pubDate>Wed, 25 Sep 2024 10:23:08 GMT</pubDate>
            <description><![CDATA[<p>Kernel 360에서 파이널 프로젝트를 하던 중 <code>정적 팩토리 메서드</code>라는 단어가 많이 나와서 한 번 정리를 해보도록 했다.</p>
<p>정적 팩토리 메서드는 Static Factory Method로 개발자가 생성자를 호출해서 객체를 생성하는 것 대신에 static 을 사용해서 간접적으로 생성자를 호출해서 생성하는 디자인 패턴이라고 한다.</p>
<pre><code class="language-java">
class Book {
    private String title;

    // 생성자를 private화 하여 외부에서 생성자 호출 차단
    private Book(String title) { this.title = title; }

    // 정적 팩토리 메서드
    public static Book titleOf(String title) {
        return new Book(title); // 메서드에서 생성자를 호출하고 리턴함
    }
}
</code></pre>
<pre><code class="language-java">public static void main(String[] args) {
    // 정적 메서드 호출을 통해 인스턴스화된 객체를 얻음
    Book book1 = Book.titleOf(&quot;어린왕자&quot;); 
}</code></pre>
<p>이렇게 생성자 대신에 정적 팩토리 메서드를 통해서 객체를 생성하면 단순히 생성자의 역할을 대신 이행해주는 것 뿐만이 아니라 개발자가 좀 더 가독성 좋은 코드를 작성하고 객체 지향적으로 프로그래밍 할 수 있도록 도와준다.</p>
<h3 id="정적-팩토리-메서드의-특징">정적 팩토리 메서드의 특징</h3>
<ol>
<li>생성 목적에 대한 이름 표현이 가능하다</li>
</ol>
<p>클래스를 설계할 때 다양한 타입의 객체를 생성하기 위해서 생성 목적에 따라 생성자를 오버로딩하여 사용한다. 이렇게 객체를 new 키워드를 통해서 생성하려면 개발자가 해당 생성자의 인자 순서와 내부 구조를 알고 있어야 목적에 맞게 객체를 생성할 수 있다.</p>
<p>예를 들어 코드를 보여주겠다.</p>
<pre><code class="language-java">class Car {
    private String brand;
    private String color = &quot;black&quot;;

    public Car(String brand, String color) {
        this.brand = brand;
        this.color = color;
    }

    public Car(String brand) {
        this.brand = brand;
    }
}</code></pre>
<pre><code class="language-java">public static void main(String[] args) {
    // 검정색 테슬라 자동차 
    Car teslaCar = new Car(&quot;Tesla&quot;);

    // 빨간색 BMW 자동차
    Car bmwRedCar = new Car(&quot;BMW&quot;, &quot;Red&quot;);
}</code></pre>
<pre><code class="language-java">class Car {
    private String brand;
    private String color;

    // private 생성자
    private Car(String brand, String color) {
        this.brand = brand;
        this.color = color;
    }

    // 정적 팩토리 메서드 (매개변수 하나는 from 네이밍)
    public static Car brandBlackFrom(String brand) {
        return new Car(brand, &quot;black&quot;);
    }

    // 정적 팩토리 메서드 (매개변수 여러개는 of 네이밍)
    public static Car brandColorOf(String brand, String color) {
        return new Car(brand, color);
    }
}</code></pre>
<pre><code class="language-java">public static void main(String[] args) {
    // 검정색 테슬라 자동차 
    Car teslaCar = Car.brandBlackFrom(&quot;Tesla&quot;);

    // 빨간색 BMW 자동차
    Car bmwRedCar = Car.brandColorOf(&quot;BMW&quot;, &quot;Red&quot;);
}</code></pre>
<p>위의 예시와 같이 생성자 대신에 정적 팩토리 메서드를 호출함으로써 생성될 객체의 특성에 대해 쉽게 묘사할 수 있어 가독성을 높여준다.
이 이유가 정적 팩토리 메서드를 사용하는 가장 중요한 이유이며 이외에도 몇 가지 이유가 있다.</p>
<ol start="2">
<li><p>인스턴스에 대한 통제 및 관리가 가능하다
메서드를 통해 한단계 거쳐 간접적으로 객체를 생성하기 때문에, 기본적으로 전반적인 객체 생성 및 통제 관리를 할 수 있게 된다. 즉, 필요에 따라 항상 새로운 객체를 생성해서 반환할 수도 있고, 아니면 객체 하나만 만들어두고 이를 공유하여 재사용하게 하여 불필요한 객체를 생성하는 것을 방지 할 수 있다</p>
</li>
<li><p>하위 자료형 객체를 반환할 수 있다.</p>
<pre><code class="language-java">interface SmarPhone {}
</code></pre>
</li>
</ol>
<p>class Galaxy implements SmarPhone {}
class IPhone implements SmarPhone {}
class Huawei implements SmarPhone {}</p>
<p>class SmartPhones {
    public static SmarPhone getSamsungPhone() {
        return new Galaxy();
    }</p>
<pre><code>public static SmarPhone getApplePhone() {
    return new IPhone();
}

public static SmarPhone getChinesePhone() {
    return new Huawei();
}</code></pre><p>}</p>
<p>```</p>
<p>위의 코드처럼 클래스의 다형성의 특징을 응용해서 메서드 호출을 통해 얻을 객체의 인스턴스를 자유롭게 선택할 수 있는 유연성을 갖는다.</p>
<ol start="4">
<li>객체 생성을 캡슐화 할 수 있다.
생성자를 사용하는 경우 외부에 내부 구현을 드러내야 하는데, 정적 팩토리 메서드는 구현부를 외부로부터 숨길 수 있어 캡슐화 및 정보 은닉을 할 수 있다. 또한 노출하지 않는다는 특징은 정보 은닉성을 가지기도 하지만, 동시에 사용하고 있는 구현체를 숨겨 의존성을 제거해주는 장점을 가지고 있다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[API 예외 처리]]></title>
            <link>https://velog.io/@i-migi0104/API-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@i-migi0104/API-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 22 Sep 2024 10:55:13 GMT</pubDate>
            <description><![CDATA[<p>HTML 페이지의 경우 지금까지 설명했던 것 처럼 4xx, 5xx와 같은 오류 페이지만 있으면 대부분의 문제를 해결할 수 있지만 API의 경우에는 각 오류 상황에 맞는 오류 응답 스펙을 정하고, JSON으로 데이터를 내려주어야 한다. 전 작성글의 서블릿 오류 페이지 방식을 사용하면 오류가 발생했을 때 만들어둔 오류 페이지 HTML이 반환된다. 하지만 API의 경우에는 정상 요청이든 오류 요청이든 JSON을 반환해주어야 한다.</p>
<pre><code class="language-java"> @RequestMapping(value = &quot;/error-page/500&quot;, produces =
 MediaType.APPLICATION_JSON_VALUE)
 public ResponseEntity&lt;Map&lt;String, Object&gt;&gt; errorPage500Api(HttpServletRequest
 request, HttpServletResponse response) {
     log.info(&quot;API errorPage 500&quot;);
     Map&lt;String, Object&gt; result = new HashMap&lt;&gt;();
     Exception ex = (Exception) request.getAttribute(ERROR_EXCEPTION);
     result.put(&quot;status&quot;, request.getAttribute(ERROR_STATUS_CODE));
     result.put(&quot;message&quot;, ex.getMessage());
     Integer statusCode = (Integer)
 request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
     return new ResponseEntity(result, HttpStatus.valueOf(statusCode));
 }</code></pre>
<p> <code>produces = MediaType.APPLICATION_JSON_VALUE</code>의 뜻은 클라이언트가 요청하는 HTTP Header의 Accept의 값이 application/json일 때 해당 메서드가 호출된다는 것이다. </p>
<p> 응답 데이터를 위해서 Map을 만들고 status, message키에 값을 할당했다. Jackson 라이브러리는 Map을 JSON 구조로 변환할 . 수있다.
 <code>ResponseEntity</code>를 사용해서 응답하기 때문에 메시지 컨버터가 동작하면서 클라이언트에 JSON이 반환된다.</p>
<h3 id="스프링-기본-오류-처리">스프링 기본 오류 처리</h3>
<pre><code class="language-java"> @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
 public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse
 response) {}
 @RequestMapping
 public ResponseEntity&lt;Map&lt;String, Object&gt;&gt; error(HttpServletRequest request) {}</code></pre>
<p> /error 동일한 경로를 처리하는 errorHtml(), error() 두 메서드를 확인할 수 있다.</p>
<p> <img src="https://velog.velcdn.com/images/i-migi0104/post/4d9f97b4-2744-40a2-b691-f32c6da8184f/image.png" alt=""></p>
<h4 id="html-페이지-vs-api-오류">HTML 페이지 vs API 오류</h4>
<p>스프링 부트가 제공하는 <code>BasicErrorController</code>는 HTML 페이지를 제공하는 경우에는 매우 편리하다.
그런데 API 오류 처리는 다른 차원의 이야기이다. API마다 각각의 컨트롤러나 예외마다 서로 다른 응답 결과를 출력해줘야 한다. 따라서 API 오류 처리는 <code>@ExceptionHandler</code>를 사용해서 처리해야 한다.</p>
<h3 id="handlerexceptionresolver">HandlerExceptionResolver</h3>
<p>예외가 발생해서 서블릿을 넘어 WAS까지 예외가 전달되면 HTTP 상태코드가 500으로 처리된다. 발생하는 예외에 따라 400, 404 등등 다른 상태코드로 처리할 수 있다.</p>
<p>예를 들어서 <code>IllegalArgumentException</code>을 처리하지 못해서 컨트로럴 밖으로 넘어가는 일이 발생하면 HTTP 상태코드를 400으로 처리하고 싶다.</p>
<pre><code class="language-java">@GetMapping(&quot;/api/members/{id}&quot;)
 public MemberDto getMember(@PathVariable(&quot;id&quot;) String id) {
if (id.equals(&quot;ex&quot;)) {
throw new RuntimeException(&quot;잘못된 사용자&quot;);
     }
     if (id.equals(&quot;bad&quot;)) {
throw new IllegalArgumentException(&quot;잘못된 입력 값&quot;); }
     return new MemberDto(id, &quot;hello &quot; + id);
 }</code></pre>
<p> 코드를 실행해보면 상태 코드가 500인 것을 확인할 수 있다.
 WAS입장에서는 서버 내부에서 Exception이 터진 것이기 때문에 상태코드 500이 나오는 것이 맞다.</p>
<h4 id="handlerexceptionresolver-1">HandlerExceptionResolver</h4>
<p> 스프링 MVC는 컨트롤러(핸들러) 밖으로 예외가 던져진 경우 예외를 해결하고, 동작을 새로 정의할 수 있는 방법을 제공한다.</p>
<p> <img src="https://velog.velcdn.com/images/i-migi0104/post/1cf44698-9bb8-4526-a832-b0d0268d552c/image.png" alt=""></p>
<p>여기서 postHandle()이란 스프링 프레임워크의 인터셉터에서 사용하는 메서드 중 하나로, 클라이언트의 요청을 처리한 후에 실행되는 메서드이다.</p>
<ol>
<li>클라이언트 요청이 컨트롤러로 전달되고</li>
<li>컨트롤러가 그 요청을 처리한 뒤</li>
<li>뷰에 결과를 보내기 전에 postHandle() 메서드가 호출된다.</li>
</ol>
<p>이 메서드는 주로 요청이 처리된 이후에 추가적인 작업을 수행할 때 사용된다.
<code>postHandle()</code>은 컨트롤러가 정상적으로 요청을 처리한 이후에 실행되는 메서드이기 때문에, 요청 처리 중에 예외가 발생하면 해당 메서드는 호출되지 않는다.</p>
<pre><code class="language-java">@Slf4j
 public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
     public ModelAndView resolveException(HttpServletRequest request,
 HttpServletResponse response, Object handler, Exception ex) {
         try {
             if (ex instanceof IllegalArgumentException) {
                 log.info(&quot;IllegalArgumentException resolver to 400&quot;);
                 response.sendError(HttpServletResponse.SC_BAD_REQUEST,
 ex.getMessage());
                 return new ModelAndView();
             }
         } catch (IOException e) {
             log.error(&quot;resolver ex&quot;, e);
}
         return null;
     }
}</code></pre>
<p>위의 코드에서는 <code>IllegalArgumentException</code>이 발생하면 response.sendError(400)을 호출해서 HTTP 상태 코드를 400으로 지정하고, 빈 ModelAndView를 반환한다.</p>
<p>여기서 ModelAndView는 스프링 프레임워크에서 컨트롤러가 클라이언트의 요청을 처리한 후에 반환하는 객체로 모델과 뷰 정보를 함께 담고 있다. 즉 <code>ModelAndView</code>는 컨트롤러에서 어떤 데이터를 보여줄지(Model)과 어떤 화면(View)를 보여줄지를 . 한번에 처리하는 객체이다.</p>
<h4 id="반환-값에-따른-동작-방식">반환 값에 따른 동작 방식</h4>
<ul>
<li>빈 ModelAndView: <code>new ModelAndView()</code> 처럼 빈 ModelANdView를 반환하면 뷰를 렌더링 하지 않고, 정상 흐름으로 서블릿이 리턴된다.</li>
<li>ModelAndView 지정: ModelAndView에 View, Model 등의 정보를 지정해서 반환하면 뷰를 렌더링 한다.</li>
<li>null: null를 반환하면, 다음 <code>ExceptionResolver</code>를 찾아서 실행한다. 만약 처리할 수 있는 ExceptionResolver가 없으면 예외 처리가 안되고, 기존에 발생한 예외를 서블릿 밖으로 던진다</li>
</ul>
<h4 id="exceptionresolver-활용">ExceptionResolver 활용</h4>
<ul>
<li>예외 상태 코드 변환<ul>
<li>예외를 <code>response.sendError(xxx)</code> 호출로 변경해서 서블릿에서 상태 코드에 따른 오류를 처리하도 록 위임</li>
<li>이후 WAS는 서블릿 오류 페이지를 찾아서 내부 호출, 예를 들어서 스프링 부트가 기본으로 설정한 <code>/ error</code> 가 호출됨</li>
</ul>
</li>
<li>뷰 템플릿 처리<ul>
<li><code>ModelAndView</code> 에 값을 채워서 예외에 따른 새로운 오류 화면 뷰 렌더링 해서 고객에게 제공</li>
</ul>
</li>
<li>API 응답 처리<ul>
<li><code>response.getWriter().println(&quot;hello&quot;);</code> 처럼 HTTP 응답 바디에 직접 데이터를 넣어주는 것도 가능하다. 여기에 JSON 으로 응답하면 API 응답 처리를 할 수 있다.</li>
</ul>
</li>
</ul>
<h3 id="handlerexceptionresolver-활용">HandlerExceptionResolver 활용</h3>
<p>예외가 발생하면 WAS까지 예외가 던져지고, WAS에서 오류 페이지 정보를 찾아서 다시 /error를 호출하는 과정은 복잡하다. <code>ExceptionResolver</code>를 활용하몀ㄴ 예외가 발생했을 때 이런 복잡한 과정없이 여기에서 문제를 해결할 . 수있다.</p>
<pre><code class="language-java">public class UserException extends RuntimeException {
     public UserException() {
         super();
}
     public UserException(String message) {
         super(message);
}
     public UserException(String message, Throwable cause) {
         super(message, cause);
}
     public UserException(Throwable cause) {
         super(cause);
}
protected UserException(String message, Throwable cause, boolean
 enableSuppression, boolean writableStackTrace) {
         super(message, cause, enableSuppression, writableStackTrace);
     }
}</code></pre>
<pre><code class="language-java">@Slf4j
 @RestController
 public class ApiExceptionController {
     @GetMapping(&quot;/api/members/{id}&quot;)
     public MemberDto getMember(@PathVariable(&quot;id&quot;) String id) {
if (id.equals(&quot;ex&quot;)) {
throw new RuntimeException(&quot;잘못된 사용자&quot;);
         }
         if (id.equals(&quot;bad&quot;)) {
throw new IllegalArgumentException(&quot;잘못된 입력 값&quot;); }
if (id.equals(&quot;user-ex&quot;)) {
throw new UserException(&quot;사용자 오류&quot;);
}
         return new MemberDto(id, &quot;hello &quot; + id);
     }
     @Data
     @AllArgsConstructor
     static class MemberDto {
private String memberId;
private String name;
     }
}</code></pre>
<p>/user-ex 호출 시 UserException이 발생하도록 해두었다.</p>
<pre><code class="language-java"> @Slf4j
 public class UserHandlerExceptionResolver implements HandlerExceptionResolver {
     private final ObjectMapper objectMapper = new ObjectMapper();
@Override
     public ModelAndView resolveException(HttpServletRequest request,
 HttpServletResponse response, Object handler, Exception ex) {
         try {
             if (ex instanceof UserException) {
                 log.info(&quot;UserException resolver to 400&quot;);
                 String acceptHeader = request.getHeader(&quot;accept&quot;);
                 response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                 if (&quot;application/json&quot;.equals(acceptHeader)) {
                     Map&lt;String, Object&gt; errorResult = new HashMap&lt;&gt;();
                     errorResult.put(&quot;ex&quot;, ex.getClass());
                     errorResult.put(&quot;message&quot;, ex.getMessage());
                     String result =
 objectMapper.writeValueAsString(errorResult);
                     response.setContentType(&quot;application/json&quot;);
                     response.setCharacterEncoding(&quot;utf-8&quot;);
                     response.getWriter().write(result);
                     return new ModelAndView();
                 } else {
                     //TEXT/HTML
                     return new ModelAndView(&quot;error/400&quot;);
                 }
             }
         } catch (IOException e) {
             log.error(&quot;resolver ex&quot;, e);
         }
         return null;
     }
}</code></pre>
<h3 id="스프링이-제공하는-exceptionresolver">스프링이 제공하는 ExceptionResolver</h3>
<p>스프링 부트가 기본으로 제공하는 <code>ExceptionResolver</code>는 다음과 같다.
HandlerExceptionResolverComposite에 다음 순서로 등록한다.</p>
<ol>
<li>ExceptionHandlerExceptionResolver
<code>@ExceptionHandler</code>을 처리한다. API 예외 처리는 대부분 . 이기능으로 해결한다.</li>
<li>ResponseStatusExceptionResolver
HTTP 상태코드를 지정해준다.</li>
<li>DefaultHandlerExceptionResolver -&gt; 우선 순위가 가장 낮다</li>
</ol>
<h3 id="responsestatusexceptionresolver">ResponseStatusExceptionResolver</h3>
<p>예외에 따라서 HTTP 상태 코드를 지정해준다.</p>
<pre><code class="language-java"> package hello.exception.exception;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = &quot;잘못된 요청 오류&quot;) public class BadRequestException extends RuntimeException {
}</code></pre>
<p><code>@ResponseStatus</code> 어노테이션을 적용하면 HTTP 상태코드를 변경해준다.</p>
<h3 id="responsestatusexception">ResponseStatusException</h3>
<p><code>@ResponseStatus</code>는 개발자가 직접 변경할 . 수없는 예외에는 적용할 수 없다. 이때에는 <code>ResponseStatusException</code> 예외를 사용하면 된다.</p>
<pre><code class="language-java">@GetMapping(&quot;/api/response-status-ex2&quot;)
 public String responseStatusEx2() {
     throw new ResponseStatusException(HttpStatus.NOT_FOUND, &quot;error.bad&quot;, new
 IllegalArgumentException());
}</code></pre>
<h3 id="defaulthandlerexceptionresolver">DefaultHandlerExceptionResolver</h3>
<p>스프링 내부에서 발생하는 스프링 예외를 해결한다.
대표적으로 파라미터 바인딩 시점에 타입이 맞지 않으면 내부에서 <code>TypeMismatchException</code> 이 발생하는데, 이 경 우 예외가 발생했기 때문에 그냥 두면 서블릿 컨테이너까지 오류가 올라가고, 결과적으로 500 오류가 발생한다. 그런데 파라미터 바인딩은 대부분 클라이언트가 HTTP 요청 정보를 잘못 호출해서 발생하는 문제이다. HTTP 에서는 이런 경우 HTTP 상태 코드 400을 사용하도록 되어 있다.
<code>DefaultHandlerExceptionResolver</code> 는 이것을 500 오류가 아니라 HTTP 상태 코드 400 오류로 변경한다. 스프링 내부 오류를 어떻게 처리할지 수 많은 내용이 정의되어 있다.</p>
<h2 id="exceptionhandler">@ExceptionHandler</h2>
<p>API 예외처리의 어려운 점</p>
<ul>
<li>HandlerExceptionResolver를 떠올려 보면 ModelAndView를 반환해야 했다. 이것은 API 응답에는 필요하지 않다.</li>
<li>API 응답을 위해서 HttpServletResponse에 직접 응답 데이터를 넣어주었다. 이것은 비효율적이다.</li>
<li>특정 컨트롤러에서만 발생하는 예외를 별도로 처리하기 어렵다.</li>
</ul>
<pre><code class="language-java"> package hello.exception.exhandler;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 @Data
 @AllArgsConstructor
 public class ErrorResult {
     private String code;
     private String message;
 }</code></pre>
<pre><code class="language-java"> @Slf4j
 @RestController
 public class ApiExceptionV2Controller {
     @ResponseStatus(HttpStatus.BAD_REQUEST)
     @ExceptionHandler(IllegalArgumentException.class)
     public ErrorResult illegalExHandle(IllegalArgumentException e) {
         log.error(&quot;[exceptionHandle] ex&quot;, e);
         return new ErrorResult(&quot;BAD&quot;, e.getMessage());
     }
     @ExceptionHandler
     public ResponseEntity&lt;ErrorResult&gt; userExHandle(UserException e) {
         log.error(&quot;[exceptionHandle] ex&quot;, e);
         ErrorResult errorResult = new ErrorResult(&quot;USER-EX&quot;, e.getMessage());
         return new ResponseEntity&lt;&gt;(errorResult, HttpStatus.BAD_REQUEST);
         }
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public ErrorResult exHandle(Exception e) {
    log.error(&quot;[exceptionHandle] ex&quot;, e);
return new ErrorResult(&quot;EX&quot;, &quot;내부 오류&quot;); }
@GetMapping(&quot;/api2/members/{id}&quot;)
public MemberDto getMember(@PathVariable(&quot;id&quot;) String id) {
if (id.equals(&quot;ex&quot;)) {
throw new RuntimeException(&quot;잘못된 사용자&quot;);
    }
    if (id.equals(&quot;bad&quot;)) {
throw new IllegalArgumentException(&quot;잘못된 입력 값&quot;); }
if (id.equals(&quot;user-ex&quot;)) {
throw new UserException(&quot;사용자 오류&quot;);
}
    return new MemberDto(id, &quot;hello &quot; + id);
}
@Data
@AllArgsConstructor
static class MemberDto {
    private String memberId;
    private String name;
}
}</code></pre>
<p><code>@ExceptionHandler</code> 어노테이션을 선언하고 해당 컨트롤러에서 처리하고 싶은 예외를 지정해주면 된다. 해당 컨트롤러에서 예외가 발생하면 이 메서드가 호출된다. 참고로 지정한 예외 또는 그 예외의 자식 클래스는 모두 잡을 수 있다.</p>
<p>스프링의 우선순위는 항상 자세한 것이 우선권을 가진다. 예를 들어서 부모 자식 클래스가 있고 다음과 같이 예외가 처리된다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/b9092377-73a7-42b4-9b29-2614c3e668ac/image.png" alt=""></p>
<p><code>@ExceptionHandler</code> 에 지정한 부모 클래스는 자식 클래스까지 처리할 수 있다. 따라서 <code>자식예외</code> 가 발생하면 <code>부모 예외처리()</code> , <code>자식예외처리()</code> 둘다 호출 대상이 된다. 그런데 둘 중 더 자세한 것이 우선권을 가지므로 <code>자식예외처리()</code> 가 호출된다. 물론 <code>부모예외</code> 가 호출되면 <code>부모예외처리()</code> 만 호출 대상이 되므로 <code>부모예외처리()</code> 가 호출된다.</p>
<h4 id="실행-흐름">실행 흐름</h4>
<ul>
<li>컨트롤러를 호출한 결과 IllegalArgumentException 예외가 컨트롤러 밖으로 던져진다.</li>
<li>예외가 발생했으므로 ExceptionResolver 가 작동한다. 가장 우선순위가 높은 ExceptionHandlerExceptionResolver가 실행된다.</li>
<li>해당 컨트롤러에 IllegalArgumentException을 처리 할 수 있는 <code>@ExceptionHandler</code>가 있는지 확인한다.</li>
<li>illegalExHandle()가 적용된다. 따라서 HTTP 컨버터가 사용되고, 응답이 다음과 같은 JSON으로 반환된다.</li>
<li>@ResponseStatus(HttpStatus.BAD_REQUEST)를 지정했으므로 HTTP 상태코드 400으로 응답한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/29f312c2-b8fa-4c0e-99ff-5043d431b153/image.png" alt=""></p>
<h3 id="controlleradvice">@ControllerAdvice</h3>
<p>@ExceptionHandler를 사용해서 예외를 깔끔하게 처리할 수 있지만 정상 코드와 예외 처리 코드가 하나의 컨트롤러에 섞여 있다. @ControllerAdvice, @RestControllerAdvice를 사용하면 둘을 분리할 수 있따.</p>
<pre><code class="language-java"> @Slf4j
 @RestControllerAdvice
 public class ExControllerAdvice {
     @ResponseStatus(HttpStatus.BAD_REQUEST)
     @ExceptionHandler(IllegalArgumentException.class)
     public ErrorResult illegalExHandle(IllegalArgumentException e) {
         log.error(&quot;[exceptionHandle] ex&quot;, e);
         return new ErrorResult(&quot;BAD&quot;, e.getMessage());
     }
     @ExceptionHandler
     public ResponseEntity&lt;ErrorResult&gt; userExHandle(UserException e) {
         log.error(&quot;[exceptionHandle] ex&quot;, e);
         ErrorResult errorResult = new ErrorResult(&quot;USER-EX&quot;, e.getMessage());
         return new ResponseEntity&lt;&gt;(errorResult, HttpStatus.BAD_REQUEST);
}
     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
     @ExceptionHandler
     public ErrorResult exHandle(Exception e) {
         log.error(&quot;[exceptionHandle] ex&quot;, e);
return new ErrorResult(&quot;EX&quot;, &quot;내부 오류&quot;); }
}</code></pre>
<h3 id="controlleradvice-1">@ControllerAdvice</h3>
<p><code>@ControllerAdvice</code> 는 대상으로 지정한 여러 컨트롤러에 <code>@ExceptionHandler</code> , <code>@InitBinder</code> 기능
을 부여해주는 역할을 한다.
<code>@ControllerAdvice</code> 에 대상을 지정하지 않으면 모든 컨트롤러에 적용된다. (글로벌 적용) <code>@RestControllerAdvice</code> 는 <code>@ControllerAdvice</code> 와 같고, <code>@ResponseBody</code> 가 추가되어 있다. <code>@Controller</code> , <code>@RestController</code> 의 차이와 같다.</p>
<p> @ControllerAdvice(annotations = RestController.class)
위와 같이 작성하면 컨트롤러 클래스를 지정해줄 수 있다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[서블릿 예외 처리]]></title>
            <link>https://velog.io/@i-migi0104/%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@i-migi0104/%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 22 Sep 2024 05:59:48 GMT</pubDate>
            <description><![CDATA[<p>서블릿은 2가지 방식으로 예외 처리를 지원한다</p>
<ul>
<li>Exception</li>
<li>response.sendError(HTTP 상태 코드, 오류 메시지)</li>
</ul>
<h3 id="exception예외">Exception(예외)</h3>
<p>자바의 메인 메서드를 실행 -&gt;<code>main</code>이라는 이름의 쓰레드가 실행
실행 도중에 예외를 잡지 못하면 -&gt; 예외 정보를 남기고, 해당 쓰레드는 종료된다.</p>
<h4 id="웹-애플리케이션">웹 애플리케이션</h4>
<p>웹 애플리케이션은 사용자 요청별로 별도의 쓰레드가 할당되고, 서블릿 컨테이너 안에서 실행된다.
애플리케이션에서 예외가 발생했을 때, 어디선가 try ~ catch로 예외를 잡아서 처리하면 아무런 문제가 없다. 그러나 예외를 잡지 못하고, 서블릿 밖으로 까지 예외가 전달하면 아래와 같이 동작한다.</p>
<h1 id="서블릿">서블릿</h1>
<p>여기서 서블릿이란 간단하게 말하면 Java를 기반으로 한 웹 애플리케이션을 개발할 때 서버 측에서 클라이언트의 요청을 처리하고 그에 대한 응답을 생성하는 자바 클래스이다. 웹 서버에서 실행되며, 클라이언트로부터의 HTTP 요청을 받아 적절한 비즈니스 로직을 수행하고, 결과를 HTTP 응답으로 반환하는 역할을 한다.</p>
<blockquote>
<p>WAS(여기까지 전파) &lt;- 필터 &lt;- 서블릿 &lt;- 인터셉터 &lt;- 컨트롤러(예외발생)</p>
</blockquote>
<p>결국 톰캣 같은 WAS까지 예외가 전달된다.</p>
<pre><code class="language-java"> @Controller
 public class ServletExController {
     @GetMapping(&quot;/error-ex&quot;)
     public void errorEx() {
throw new RuntimeException(&quot;예외 발생!&quot;); }
}</code></pre>
<p>위 url을 실행하면 HTTP 상태 코드 500이 반환된다.
-&gt; <code>Exception</code>의 경우 서버 내부에서 처리할 수 없는 오류가 발생한 것으로 생각되어 상태 코드 500이 반환된다.</p>
<h3 id="responsesenderrorhttp-상태코드-오류-메시지">response.sendError(HTTP 상태코드, 오류 메시지)</h3>
<p>이것은 호출한다고 당장 예외가 발생하는 것은 아니지만, 서블릿 컨테이너에게 오류가 발생했다는 점을 전달할 수 있다.</p>
<pre><code class="language-java">@GetMapping(&quot;/error-500&quot;)
 public void error500(HttpServletResponse response) throws IOException {
     response.sendError(500);
 }</code></pre>
<blockquote>
<p>WAS(sendError 호출 기록 확인) &lt;- 필터 &lt;- 서블릿 &lt;- 인터셉터 &lt;- 컨트롤러 (response.sendError())</p>
</blockquote>
<p><code>response</code> 내부에서 오류가 발생했다는 상태를 저장해둔다.
그리고 서블릿 컨테이너는 고객에게 응답 전에 response
sendError()가 호출되었는지 확인한다. 그리고 호출 되었다면 설정한 오류 코드에 맞추어 기본 오류 페이지를 보여준다.</p>
<h1 id="서블릿-컨테이너">서블릿 컨테이너</h1>
<p>여기서 서블릿 컨테이너가 무엇인지 간단히 말하자면</p>
<p>서블릿 컨테이너는 서블릿이라는 프로그램을 관리하고 실행하는 역할을 하는 곳으로, 서블릿을 실행하고, 클라이언트의 요청을 받아서 서블릿에게 전달하고, 서블릿이 처리한 결과를 다시 클라이언트에게 보내주는 일을 한다.</p>
<h3 id="오류-화면-제공">오류 화면 제공</h3>
<p>서블릿 컨테이너가 제공하는 기본 예외 처리 화면은 고객 친화적이지 않다.</p>
<p>과거에는 <code>web.xml</code>이라는 파일에 오류화면을 등록했다.</p>
<pre><code class="language-java"> &lt;web-app&gt;
     &lt;error-page&gt;
     &lt;error-code&gt;404&lt;/error-code&gt;
     &lt;location&gt;/error-page/404.html&lt;/location&gt;
     &lt;/error-page&gt;
     &lt;error-page&gt;
     &lt;error-code&gt;500&lt;/error-code&gt;
     &lt;location&gt;/error-page/500.html&lt;/location&gt;
     &lt;/error-page&gt;
     &lt;error-page&gt;
     &lt;exception-type&gt;java.lang.RuntimeException&lt;/exception-type&gt;
     &lt;location&gt;/error-page/500.html&lt;/location&gt;
     &lt;/error-page&gt;
 &lt;/web-app&gt;</code></pre>
<p>지금은 스프링 부트를 통해서 서블릿 컨테이너를 실행하기 때문에 스프링 부트의 기능을 사용할 수 있다.</p>
<pre><code class="language-java">package hello.exception;
 import org.springframework.boot.web.server.ConfigurableWebServerFactory;
 import org.springframework.boot.web.server.ErrorPage;
 import org.springframework.boot.web.server.WebServerFactoryCustomizer;
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Component;
@Component
 public class WebServerCustomizer implements
 WebServerFactoryCustomizer&lt;ConfigurableWebServerFactory&gt; {
     @Override
     public void customize(ConfigurableWebServerFactory factory) {
         ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, &quot;/error-
 page/404&quot;);
         ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,
 &quot;/error-page/500&quot;);
ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, &quot;/error-
page/500&quot;);
         factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
} }</code></pre>
<p>response.sendError(404): <code>errorPage404</code> 호출
response.sendError(500): <code>errorPage500</code> 호출
<code>RuntimeException</code> 또는 그 자식 타입의 예외: <code>errorPageEx</code> 호출</p>
<pre><code class="language-java">@Slf4j
 @Controller
 public class ErrorPageController {
     @RequestMapping(&quot;/error-page/404&quot;)
     public String errorPage404(HttpServletRequest request, HttpServletResponse
 response) {
         log.info(&quot;errorPage 404&quot;);
         return &quot;error-page/404&quot;;
     }</code></pre>
<p>오류를 처리할 컨트롤러이다.</p>
<h3 id="오류-페이지-작동-원리">오류 페이지 작동 원리</h3>
<p>서블릿은 <code>Exception</code>이 발생해서 서블릿 밖으로 전달되거나 또는 <code>response.sendError()</code>가 호출 되었을 때 설정된 오류 페이지를 찾는다.</p>
<p>WAS는 해당 예외를 처리하는 오류 페이지 정보를 확인한다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/386cf7a5-19cc-4162-9a61-cf5a434d5ddf/image.png" alt=""></p>
<p>중요한 점은 웹 브라우저(클라이언트)는 서버 내부에서 이런 일이 일어나는지 전혀 모른다. 오직 서버 내부에서 오류 페이지를 찾기 위해 추가적인 호출을 한다.</p>
<ol>
<li>예외가 발생해서 WAS까지 전파된다.</li>
<li>WAS는 오류 페이지 경로를 찾아서 내부에서 오류 페이지를 호출한다. 이때 오류 페이지 경로로 필터, 서블릿, 인터셉터, 컨트롤러가 모두 다시 호출된다.</li>
</ol>
<h4 id="오류-정보-추가">오류 정보 추가</h4>
<p>WAS는 오류 페이지를 단순히 다시 요청만 하는 것이 아니라, 오류 정보를 <code>request</code>의 <code>attribute</code>에 추가해서 넘겨준다.</p>
<pre><code class="language-java">public static final String ERROR_EXCEPTION =
&quot;javax.servlet.error.exception&quot;;
    public static final String ERROR_EXCEPTION_TYPE =
&quot;javax.servlet.error.exception_type&quot;;
    public static final String ERROR_MESSAGE = &quot;javax.servlet.error.message&quot;;
    public static final String ERROR_REQUEST_URI =
&quot;javax.servlet.error.request_uri&quot;;
    public static final String ERROR_SERVLET_NAME =
&quot;javax.servlet.error.servlet_name&quot;;
    public static final String ERROR_STATUS_CODE =
&quot;javax.servlet.error.status_code&quot;;
    @RequestMapping(&quot;/error-page/404&quot;)
    public String errorPage404(HttpServletRequest request, HttpServletResponse
response) {
        log.info(&quot;errorPage 404&quot;);
        printErrorInfo(request);
        return &quot;error-page/404&quot;;
}

private void printErrorInfo(HttpServletRequest request) {
        log.info(&quot;ERROR_EXCEPTION: ex=&quot;, request.getAttribute(ERROR_EXCEPTION));
        log.info(&quot;ERROR_EXCEPTION_TYPE: {}&quot;,
request.getAttribute(ERROR_EXCEPTION_TYPE));
        log.info(&quot;ERROR_MESSAGE: {}&quot;, request.getAttribute(ERROR_MESSAGE)); //ex
        의 경우 NestedServletException 스프링이 한번 감싸서 반환 log.info(&quot;ERROR_REQUEST_URI: {}&quot;, request.getAttribute(ERROR_REQUEST_URI));
         log.info(&quot;ERROR_SERVLET_NAME: {}&quot;,
 request.getAttribute(ERROR_SERVLET_NAME));
         log.info(&quot;ERROR_STATUS_CODE: {}&quot;,
 request.getAttribute(ERROR_STATUS_CODE));
         log.info(&quot;dispatchType={}&quot;, request.getDispatcherType());
     }
}
</code></pre>
<h3 id="필터">필터</h3>
<p>로그인 인증 체크 같은 경우를 생각해보면, 이미 한번 필터나, 인터셉터에서 로그인 체크를 완료했다. 따라서 서버 내부에서 오류 페이지를 호출한다고 해서 해당 필터나 인터셉트가 한번 더 호출되는 것은 매우 비효율적이다.
결국 클라이언트로 부터 발생한 정상 요청인지, 아니면 오류 페이지를 출력하기 위한 내부 요청인지 구분할 수 있어야 한다. 서블릿은 이런 문제를 해결하기 위해 <code>DispatcherType</code>이라는 추가 정보를 제공한다.</p>
<p>필터에 이런 경우를 위해서 <code>dispatcherType</code> 라는 옵션을 제공한다.</p>
<p>고객이 처음 요청하면 <code>dispatcherType=REQUEST</code>이다. 
이렇듯 서블릿 스펙은 실제 고객이 요청한 것인지, 서버가 내부에서 오류 페이지를 요청하는 것인지 <code>DispatcherType</code>으로 구분할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/530c2205-4cd6-424a-8a94-e5c4eee7eeb1/image.png" alt=""></p>
<pre><code class="language-java">@Configuration
 public class WebConfig implements WebMvcConfigurer {
     @Bean
     public FilterRegistrationBean logFilter() {
         FilterRegistrationBean&lt;Filter&gt; filterRegistrationBean = new
 FilterRegistrationBean&lt;&gt;();
         filterRegistrationBean.setFilter(new LogFilter());
         filterRegistrationBean.setOrder(1);
         filterRegistrationBean.addUrlPatterns(&quot;/*&quot;);
         filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST,
 DispatcherType.ERROR);
         return filterRegistrationBean;
} }</code></pre>
<h3 id="인터셉터">인터셉터</h3>
<p>앞서 필터의 경우에는 필터를 등록할 때 어떤 <code>DispatcherType</code>인 경우에 필터를 적용할 지 선택할 수 있었다. 그런데 인터셉터는 서블릿이 제공하는 기능이 아니라 스프링이 제공하는 기능이다. 따라서 DispatcherType과 무관하게 항상 호출된다.</p>
<p>대신에 인터셉터는 다음과 같이 요청 경로에 따라서 추가하거나 제외하기 쉽게 되어 있기 때문에, 이러한 설정을 사용해서 오류 페이지 경로를 <code>excludePathPattern</code> 를 사용해서 빼주면 된다.</p>
<pre><code class="language-java">@Override
     public void addInterceptors(InterceptorRegistry registry) {
     registry.addInterceptor(new LogInterceptor())
        .order(1)
        .addPathPatterns(&quot;/**&quot;)
        .excludePathPatterns(
); }</code></pre>
<h3 id="스프링-부트---오류-페이지">스프링 부트 - 오류 페이지</h3>
<p>스프링 부트는 모든 과정을 기본으로 제공한다</p>
<ul>
<li><p><code>ErrorPage</code>를 자동으로 등록한다. 이때 <code>/error</code>라는 경로로 기본 오류 페이지를 설정한다.</p>
<ul>
<li><code>new ErrorPage(&quot;/error&quot;)</code>, 상태코드와 예외를 설정하지 않으면 기본 오류 페이지로 사용된다.</li>
<li>서블릿 밖으로 예외가 발생하거나, <code>response.sendError()</code>가 호출되면 모든 오류는 <code>/error</code>를 호출하게 된다.</li>
</ul>
</li>
<li><p><code>BasicErrorController</code>라는 스프링 컨트롤러를 자동으로 등록한다.</p>
<ul>
<li><code>ErrorPage</code>에서 등록한 <code>/error</code>를 매핑해서 처리하는 컨트롤러이다.</li>
</ul>
</li>
</ul>
<p>cf) <code>ErrorMvcAutoConfiguration</code>이라는 클래스가 오류 페이지를 자동으로 등록하는 역할을 한다.</p>
<blockquote>
<p><code>BasicErrorController</code>는 기본적인 로직이 모두 개발되어 있다. 개발자는 오류 페이지 화면만 <code>BasicErrorController</code>가 제공하는 룰과 우선순위에 따라서 등록하면 된다. 정적 HTML이면 정적 리소스, 뷰 템플릿을 사용해서 동적으로 오류 화면을 만들고 싶으면 뷰 템플릿 경로에 오류 페이지 파일을 만들어서 넣어두기만 하면 된다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/e623257b-a39d-4b88-ab42-b2eb02ac9ffe/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[기본 키와 외래 키, 데이터베이스와 테이블 생성하기]]></title>
            <link>https://velog.io/@i-migi0104/%EA%B8%B0%EB%B3%B8-%ED%82%A4%EC%99%80-%EC%99%B8%EB%9E%98-%ED%82%A4-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%99%80-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@i-migi0104/%EA%B8%B0%EB%B3%B8-%ED%82%A4%EC%99%80-%EC%99%B8%EB%9E%98-%ED%82%A4-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%99%80-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 17 Sep 2024 07:15:05 GMT</pubDate>
            <description><![CDATA[<h2 id="키key">키(key)</h2>
<ul>
<li>조건에 맞는 데이터를 찾기 위한 식별자</li>
<li>기본 키, 외래 키, 고유 키 ...</li>
</ul>
<p>관계형 데이터베이스란 테이블처럼 표의 형태로 데이터를 관리한다</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/c9f1a015-401a-4c26-8e61-fd69141b1185/image.png" alt=""></p>
<p>위의 테이블에서 각각의 행을 지칭할 수 있는 가장 중요한 것 -&gt; <code>ID</code> -&gt; <code>기본키</code>(Primary Key, PK)</p>
<h3 id="기본-키primary-key">기본 키(Primary Key)</h3>
<ul>
<li>중복되어서는 안되고, 고유해야 하며, NULL 이어서는 안된다.</li>
<li>때로는 여러 열을 묶어 하나의 기본 키로 삼기도 한다.</li>
</ul>
<h3 id="고유-키unique-key">고유 키(Unique Key)</h3>
<ul>
<li>기본 키와 유사하나 널(NULL)로 지정 가능하다</li>
</ul>
<h3 id="외래-키foreign-key-fk">외래 키(Foreign Key, FK)</h3>
<ul>
<li>두 개의 테이블 간의 관계를 표현하기 위한 키</li>
<li>다른 테이블을 연결(참조)하기 위한 키</li>
</ul>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/03149f8c-1201-4bb0-bc03-9188cce4ba86/image.png" alt=""></p>
<p>외래키를 사용할 떄에는 주의해야 할 점이 있다.
예를 들어서 
참조한 테이블이 삭제된다면??(on delete)
참조한 테이블이 변경된다면??(on update)
-&gt; 해결 방법</p>
<ul>
<li>SET NULL: 전부 NULL 값으로 채워라</li>
<li>CASCADE: 함께 변경해라 (함게 삭제, 함께 업데이트)</li>
<li>RESTRICT: 삭제, 업데이트 자체를 제한</li>
</ul>
<h3 id="mysql이-관리하는-것">MySQL이 관리하는 것</h3>
<ul>
<li>데이터베이스</li>
<li>테이블</li>
<li>데이터를 의미하는 행과 열</li>
<li>데이터</li>
</ul>
<p>MySQL에서는 데이터베이스를 스키마로 표현한다.</p>
<p>데이터베이스(스키마) 생성
<code>CREATE DATABASE DBNAME;</code></p>
<p>데이터베이스(스키마) 조회
<code>SHOW DATABASES;</code></p>
<p>데이터베이스(스키마) 사용
<code>USE DBNAME;</code></p>
<p>데이터베이스(스키마) 삭제
<code>DROP DATABSE DBNAME;</code></p>
<p>테이블 생성
<code>CREATE TABLE 테이블이름 (
    열이름1 자료형, [DEFAULT 기본값] [NULL | NOT NULL]
    ....
 )</code>
 괄호로 자료형의 최대 길이 명시 가능, 다양한 제약 조건을 추가할 수 있다.</p>
<p> 자료형</p>
<ul>
<li><p>숫자형(INT, BIGINT, FLOAT, DOUBLE, DECIMAL, ....)</p>
</li>
<li><p>문자형(CHAR, VARCHAR, ...)</p>
</li>
<li><p>날짜/시간형(DATE, DATETIME, TIMESTAMP, ..)</p>
</li>
<li><p>ENUM, SET, JSON, XML, ...</p>
<p>테이블 조회
<code>SHOW TABLES;</code>
<code>SHOW TABLES FROM DBNAME;</code></p>
<p>테이블 변경
<code>ALTER TABLE tbl_name alter_option</code></p>
<p>테이블 삭제1
<code>DROP TABLE TABLENAME;</code></p>
<p>테이블 삭제2 - 테이블 구조는 유지, 행만 삭제한다
<code>TRUNCATE TABLE TABLENAME</code></p>
</li>
</ul>
<hr>
<h1 id="관련-면접-질문">관련 면접 질문</h1>
<h2 id="기본-키primary-key란-무엇인가요">기본 키(Primary Key)란 무엇인가요?</h2>
<p>답변: 기본 키는 테이블의 각 행을 고유하게 식별할 수 있는 열입니다. 중복될 수 없으며, NULL 값을 가질 수 없습니다. 하나의 테이블에는 하나의 기본 키만 존재합니다.</p>
<h2 id="고유-키unique-key와-기본-키의-차이점은-무엇인가요">고유 키(Unique Key)와 기본 키의 차이점은 무엇인가요?</h2>
<p>답변: 고유 키는 중복되지 않는 값을 가지지만, NULL 값을 허용할 수 있다는 점에서 기본 키와 다릅니다. 기본 키는 반드시 값이 존재해야 하며 NULL을 허용하지 않습니다. 또한 기본 키는 테이블당 하나만 설정할 수 있지만, 고유 키는 여러 개 존재할 수 있습니다</p>
<h2 id="외래-키foreign-key란-무엇인가요">외래 키(Foreign Key)란 무엇인가요?</h2>
<p>답변: 외래 키는 다른 테이블의 기본 키를 참조하는 키입니다. 두 테이블 간의 관계를 설정할 때 사용됩니다. 외래 키를 통해 참조 무결성을 유지할 수 있습니다.</p>
<h2 id="테이블에서-기본-키로-여러-열column을-사용할-수-있나요">테이블에서 기본 키로 여러 열(Column)을 사용할 수 있나요?</h2>
<p>답변: 네, 가능합니다. 여러 열을 묶어 복합 기본 키(Composite Primary Key)를 설정할 수 있습니다. 이 경우 각 열의 조합이 고유하면 됩니다.</p>
<h2 id="truncate와-drop의-차이점은-무엇인가요">TRUNCATE와 DROP의 차이점은 무엇인가요?</h2>
<p>답변: TRUNCATE는 테이블의 모든 데이터를 삭제하지만 테이블 구조는 유지합니다. DROP은 테이블 자체를 삭제하여 데이터뿐만 아니라 테이블 구조도 사라집니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MVC 패턴 강의 정리]]></title>
            <link>https://velog.io/@i-migi0104/MVC-%ED%8C%A8%ED%84%B4-%EA%B0%95%EC%9D%98-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@i-migi0104/MVC-%ED%8C%A8%ED%84%B4-%EA%B0%95%EC%9D%98-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 09 Sep 2024 01:46:29 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="객체지향-프로그래밍-특징">객체지향 프로그래밍 특징</h1>
<ol>
<li>캡슐화(Encapsulation)
캡슐화는 객체의 상태(속성, 필드)와 행동(메서드)을 하나의 단위로 묶는 개념
객체의 내부 데이터는 외부에서 직접 접근할 수 없도록 보호되며, 필요한 경우 공개된 메서드를 통해서만 접근 가능하게 한다 </li>
</ol>
<p>-&gt; 객체의 무결성을 유지하고, 데이터의 직접적인 변경을 방지할 수 있다</p>
<pre><code class="language-java">
public class Car {
    private String model;  // private로 선언된 속성 (캡슐화)
    private int speed;

    // public 메서드를 통해서만 접근 가능
    public void setModel(String model) {
        this.model = model;
    }

    public String getModel() {
        return model;
    }
}
</code></pre>
<ol start="2">
<li>상속(Inheritance)
상속은 기존 클래스(부모 클래스, 슈퍼 클래스)의 속성과 메서드를 새로운 클래스(자식 클래스, 서브 클래스)가 물려받아 사용할 수 있게 하는 개념</li>
</ol>
<p>-&gt; 코드 재사용성을 높이고, 공통된 기능을 여러 클래스에서 쉽게 구현할 수 있게 한다.</p>
<pre><code class="language-java">
public class Vehicle {
    protected int speed;

    public void move() {
        System.out.println(&quot;The vehicle is moving.&quot;);
    }
}

public class Car extends Vehicle {
    private String model;

    public void showModel() {
        System.out.println(&quot;The model is &quot; + model);
    }
}
</code></pre>
<ol start="3">
<li>다형성(Polymorphism)
다형성은 동일한 인터페이스나 부모 클래스를 상속받은 객체들이 각기 다른 방식으로 행동 할 수 있는 능력이다. 메서드 오버라이딩과 메서드 오버로딩이 있다.</li>
</ol>
<pre><code class="language-java">
public class Animal {
    public void makeSound() {
        System.out.println(&quot;Some generic animal sound&quot;);
    }
}

public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println(&quot;Bark&quot;);
    }
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println(&quot;Meow&quot;);
    }
}

public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        Animal myCat = new Cat();
        myDog.makeSound();  // Bark
        myCat.makeSound();  // Meow
    }
}
</code></pre>
<ol start="4">
<li>추상화(Abstraction)
추상화는 불필요한 세부사항을 숨기고 중요한 정보만 드러내는 것이다.
객체가 가진 속성과 메서드를 공통적인 인터페이스나 추상 클래스로 정의하고, 구현은 하위 클래스에서 책임진다.</li>
</ol>
<pre><code class="language-java">
public abstract class Shape {
    // 추상 메서드 (구현은 하위 클래스에서 제공)
    public abstract double getArea();
}

public class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle(5);
        System.out.println(&quot;Area of circle: &quot; + circle.getArea());
    }
}
</code></pre>
<hr>
<h1 id="solid">SOLID</h1>
<p>SOLID는 객체지향 설계 원칙 중에서 가장 중요한 다섯 가지 원칙을 나타내는 약어이다. 이 원칙들은 유지보수성, 확장성, 가독성을 높이기 위해 설계된 원칙들로, 객체지향 프로그래밍에서 견고하고 유연한 소프트웨어를 개발하는 데 도움을 준다.</p>
<ol>
<li>단일 책임 원칙(SRP: Single Responsibility Principle)
원칙: 하나의 클래스는 오직 하나의 책임만 가져야 한다.</li>
</ol>
<p>-&gt; 클래스는 하나의 기능만 수행하고, 그 기능에 대한 변경 사유는 하나뿐이어야 한다는 의미이다. 
-&gt; 책임이 많을수록 클래스를 변경해야 할 이유가 많아져, 유지보수가 어려워진다.</p>
<pre><code class="language-java">
public class User {
    private String name;
    private String email;

    // 사용자 정보 저장 관련 로직
    public void saveUserToDatabase() {
        // DB에 사용자 저장하는 로직
    }

    // 이메일 보내기 관련 로직 (단일 책임 원칙 위반)
    public void sendEmail(String message) {
        // 이메일 전송 로직
    }
}
</code></pre>
<p>이 경우, 사용자 정보 저장과 이메일 전송이라는 두 가지 책임이 있어 단일 책임 원칙을 위반한다. 이를 분리한 코드가 아래의 코드이다.</p>
<pre><code class="language-java">
public class User {
    private String name;
    private String email;
    // 사용자 정보만 다루는 클래스
}

public class EmailService {
    public void sendEmail(String email, String message) {
        // 이메일 전송 로직
    }
}
</code></pre>
<ol start="2">
<li>개방-폐쇄 원칙(OCP: Open/Closed Principle)
원칙: 클래스는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다</li>
</ol>
<p>-&gt; 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있도록 설계해야 한다
-&gt; 변경으로 인한 리스크를 줄이고, 시스템의 유연성을 높일 수 있다.</p>
<pre><code class="language-java">// 잘못된 예시: 할인 정책 변경 시 기존 코드 수정 필요
public class DiscountService {
    public double applyDiscount(double price, String discountType) {
        if (discountType.equals(&quot;fixed&quot;)) {
            return price - 10;
        } else if (discountType.equals(&quot;rate&quot;)) {
            return price * 0.9;
        }
        return price;
    }
}
</code></pre>
<pre><code class="language-java">public interface DiscountPolicy {
    double applyDiscount(double price);
}

public class FixedDiscountPolicy implements DiscountPolicy {
    @Override
    public double applyDiscount(double price) {
        return price - 10;
    }
}

public class RateDiscountPolicy implements DiscountPolicy {
    @Override
    public double applyDiscount(double price) {
        return price * 0.9;
    }
}
</code></pre>
<ol start="3">
<li>리스코프 치환 원칙(LSP: Liskov Substitution Principle)
원칙: 서브 클래스는 언제나 자신의 부모 클래스를 대체할 수 있어야 한다</li>
</ol>
<p>-&gt; 자식 클래스는 부모 클래스의 행위를 일관되게 유지해야 하며, 부모 클래스로 선언된 객체를 자식 클래스로 치환해도 프로그램의 행동이 변하지 않아야 한다</p>
<pre><code class="language-Java">
public class Bird {
    public void fly() {
        System.out.println(&quot;Bird is flying&quot;);
    }
}

public class Ostrich extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException(&quot;Ostrich can&#39;t fly&quot;);
    }
}
</code></pre>
<p>타조는 날 수 없기 때문에 부모 클래스인 <code>Bird</code>의 행위를 위반하게 된다 이는 LSP를 위반한다.</p>
<ol start="4">
<li>인터페이스 분리 원칙(ISP: Interface Segregation Principle)
원칙: 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하지 않아야 한다.</li>
</ol>
<p>-&gt; 특정 클래스가 사용하지 않는 메서드를 강제로 구현하게 하지 말아야 한다.
-&gt; 인터페이스를 세분화하여 필요한 부분만 구현하도록 해야 한다.</p>
<pre><code class="language-java">public interface Worker {
    void work();
    void eat();
}

public class HumanWorker implements Worker {
    public void work() {
        // 일하는 로직
    }
    public void eat() {
        // 밥 먹는 로직
    }
}

public class RobotWorker implements Worker {
    public void work() {
        // 일하는 로직
    }
    public void eat() {
        // 로봇은 밥을 먹지 않으므로 문제 발생
    }
}
</code></pre>
<p>위의 코드철머 큰 인터페이스를 구현하면 모든 메서드를 구현해야 하므로 문제가 될 수 있다.</p>
<pre><code class="language-java">
public interface Workable {
    void work();
}

public interface Eatable {
    void eat();
}

public class HumanWorker implements Workable, Eatable {
    public void work() {
        // 일하는 로직
    }

    public void eat() {
        // 밥 먹는 로직
    }
}

public class RobotWorker implements Workable {
    public void work() {
        // 일하는 로직
    }
}

</code></pre>
<ol start="5">
<li>의존 역전 원칙(DIP: Dependency Inversion Principle)
원칙: 고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 둘 다 추상화에 의존해야 한다.</li>
</ol>
<p>-&gt; 구체적인 구현체에 의존하지 말고, 추상화된 인터페이스나 부모 클래스에 의존해야 한다.</p>
<hr>
<h1 id="servlet">Servlet</h1>
<p>서블릿은 Java를 기반으로 한 웹 애플리케이션을 개발하기 위한 서버 측 컴포넌트이다. 주로 HTTP 요청을 처리하고, 그에 대한 응답을 생성하는 역할을 한다. 서블릿은 자바 인터페이스로, 웹 서버에서 실행되며 동적인 웹 페이지를 생성하는 데 사용된다.</p>
<p>서블릿의 주요 기능</p>
<ul>
<li>HTTP 요청 처리: 클라이언트로부터 들어온 요청을 받아 처리하고, 적절한 응답을 생성한다.</li>
<li>동적 콘텐츠 생성: 요청에 맞는 동적인 웹 페이지나 데이터를 생성해 클라이언트로 전송한다</li>
<li>상태 관리: 세션, 쿠키 등을 이용해서 클라이언트의 상태를 관리할 수 있다.</li>
</ul>
<pre><code class="language-java">@WebServlet(&quot;/hello&quot;)
public class HelloServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType(&quot;text/html&quot;);
        PrintWriter out = response.getWriter();
        out.println(&quot;&lt;h1&gt;Hello, World!&lt;/h1&gt;&quot;);
    }
}
</code></pre>
<h2 id="servletcontainer">ServletContainer</h2>
<p>서블릿 컨테이너는 서블릿의 실행 환경을 제공하는 소프트웨어이다. 일반적으로 웹 애플리케이션의 서버(WAS)의 일부로 존재하며, 서블릿의 생명주기 관리, 요청 및 응답 처리, 세션 관리등의 기능을 수행한다.</p>
<p>서블릿 컨테이너의 역할</p>
<ul>
<li>서블릿 관리: 서블릿의 생성, 초기화, 실행, 종료를 포함한 생명주기 관리</li>
<li>HTTP 요청 및 응답 처리: 클라이언트로부터 HTTP 요청을 받아 적절한 서블릿에 전달하고, 서블릿의 응답을 클라이언트로 전달</li>
<li>멀티스레드 지원: 하나의 서블릿이 여러 클라이언트 요청을 동시에 처리할 수 있도록 스레드를 관리</li>
<li>세션 관리: 클라이언트의 상태를 유지하기 위해 세션 및 쿠키를 관리</li>
</ul>
<p>서블릿 컨테이너로는 <code>Apache Tomcat</code>, <code>Jetty</code>, <code>Undertow</code> 등이 있다.</p>
<h2 id="was-vs-서블릿-컨테이너">WAS VS 서블릿 컨테이너</h2>
<p>WAS는 웹 서버 기능뿐만 아니라 비즈니스 로직을 처리하는 애플리케이션 서버이다. 즉, WAS는 서블릿 컨테이너뿐만 아니라 EJB 컨테이너와 같은 더 복잡한 애플리케이션 로직을 처리할 수 있는 다양한 모듈을 퐇마한다. WAS는 데이터베이스와 연동하거나, 트랜잭션 관리, 분산 컴퓨팅, 보안 등을 처리할 수 있는 기능을 제공한다.</p>
<p>WAS의 주요기능</p>
<ul>
<li>서블릿, JSP 실행: 서블릿 컨테이너 역할</li>
<li>비즈니스 로직 처리: EJB(Enterprise JavaBeans) 등 복잡한 비즈니스 로직을 처리</li>
<li>트랜잭션 관리: 여러 데이터베이스 작업이나 비즈니스 로직 실행 간의 트랜잭션 관리</li>
<li>자원 관리: 연결 풀링, 데이터베이스 연결, 메시징 시스템 등을 관리</li>
<li>보안: 인증 및 인가, SSL 지원 등을 통해 보안을 강화</li>
</ul>
<p>서블릿 컨테이너는 서블릿과 JSP를 실행하는 환경을 제공하는 서버이고, 주로 HTTP 요청과 응답을 처리한다.
WAS는 서블릿 컨테이너의 기능을 포함하면서 더 복잡한 비즈니스 로직 처리, 트랜잭션 관리 등 다양한 기능을 제공하는 웹 애플리케이션 서버이다.</p>
<h1 id="dispatcher-servlet">Dispatcher Servlet</h1>
<p>디스패처 서블릿이란 Servlet의 일종이다.</p>
<pre><code class="language-java">
public class DispatcherServlet extends FrameworkServlet {
}

public abstract class FrameworkServlet extends HttpServletBean {
}

public abstract class HttpServletBean extends HttpServlet {
}
</code></pre>
<p>가장 먼저 요청을 받고, 적절하게 처리할 함수(컨트롤러)를 찾아서 정해주는 역할을 한다.</p>
<p>과거에는 모든 서블릿을 URL 매핑을 위해 <code>web.xml</code>에 등록해야 했지만, 디스패처 서블릿을 통해 해결할 수 있다. 
디스패처 서블릿은 스프링 MVC의 중앙 서블릿이며 어플리케이션으로 오는 모든 요청을 핸들링하고 공통작업을 처리해준다. -&gt; 이처럼 서블릿 컨테이너 맨 앞에서 모든 요청을 받아 처리해주는 컨트롤러로 <code>프론트 컨트롤러</code> 패턴이라고 한다.</p>
<p>서블릿은 스스로 동작하지 않는다. 보통 서블릿을 관리할 톰캣 등과 같은 서블릿 컨테이너를 사용한다.</p>
<p>서블릿 컨테이너는 싱글톤인 서블릿 객체의 생명주기를 관리한다. 또한 웹서버와 소켓으로 통신하며, 클라이언트의 요청을 받고 응답을 보내준다.</p>
<p>MVC란</p>
<p>MVC 는 Model, View, Controller의 약자 입니다. 하나의 애플리케이션, 프로젝트를 구성할 때 그 구성요소를 세가지의 역할로 구분한 패턴입니다. </p>
<p>모델은 컨트롤러에 컨트롤러는 뷰에 뷰는 다시 유저 유저는 다시 컨트롤러를 향해서 갑니다.</p>
<p>모델, Model</p>
<p>애플리케이션의 정보, 데이타를 나타냅니다. 데이타베이스, 처음의 정의하는 상수, 초기화값, 변수 등을 뜻합니다. 또한 이러한 DATA, 정보들의 가공을 책임지는 컴포넌트를 말합니다.</p>
<p>이 모델은 다음과 같은 규칙을 가지고 있습니다. </p>
<ol>
<li>사용자가 편집하길 원하는 모든 데이터를 가지고 있어야 한다.</li>
</ol>
<p>즉, 화면안의 네모박스에 글자가 표현된다면, 네모박스의 화면 위치 정보, 네모박스의 크기정보, 글자내용, 글자의 위치, 글자의 포맷 정보 등을 가지고 있어야 한다는 것입니다.</p>
<ol start="2">
<li>뷰나 컨트롤러에 대해서 어떤 정보도 알지 말아야 한다.</li>
</ol>
<p>데이터 변경이 일어났을 때 모델에서 화면 UI를 직접 조정해서 수정할 수 있도록 뷰를 참조하는 내부 속성값을 가지면 안 된다는 말입니다.</p>
<ol start="3">
<li>변경이 일어나면, 변경 통지에 대한 처리방법을 구현해야만 한다.</li>
</ol>
<p>모델의 속성 중 텍스트 정보가 변경이 된다면, 이벤트를 발생시켜 누군가에게 전달해야 하며, 누군가 모델을 변경하도록 요청하는 이벤트를 보냈을 때 이를 수신할 수 있는 처리 방법을 구현해야 합니다. 또한 모델은 재사용가능해야 하며 다른 인터페이스에서도 변하지 않아야 합니다. </p>
<p>뷰, View</p>
<p>input 텍스트, 체크박스 항목 등과 같은 사용자 인터페이스 요소를 나타냅니다. 다시 말해 데이터 및 객체의 입력, 그리고 보여주는 출력을 담당합니다. 데이타를 기반으로 사용자들이 볼 수 있는 화면입니다.  </p>
<p>뷰에서는 다음과 같은 규칙들이 있습니다.   </p>
<ol>
<li>모델이 가지고 있는 정보를 따로 저장해서는 안된다.</li>
</ol>
<p>화면에 글자를 표시 하기 위해, 모델이 가지고 있는 정보를 전달받게 될텐데, 그 정보를 유지하기 위해서 임의의 뷰 내뷰에 저장하면 안됩니다. 단순히 네모 박스를 그리라는 명령을 받으면, 화면에 표시하기만 하고 그 화면을 그릴 때 필요한 정보들은 저장하지 않아야 합니다.</p>
<ol start="2">
<li>모델이나 컨트롤러와 같이 다른 구성요소들을 몰라야 된다.</li>
</ol>
<p>모델과 같은 자기 자신의 빼고는 다른 요소는 참조하거나 어떻게 동작하는지 알아서는 안됩니다. 그냥 뷰는 데이터를 받으면 화면에 표시해주는 역할만 가진다고 보면 됩니다.</p>
<ol start="3">
<li>변경이 일어나면 변경통지에 대한 처리방법을 구현해야만 한다.</li>
</ol>
<p>모델과 같이 변경이 일어났을 때 이른 누군가에게 변경을 알려줘야 하는 방법을 구현해야 합니다. 뷰에서는 화면에서 사용자가 화면에 표시된 내용을 변경하게 되면 이를 모델에게 전달해서 모델을 변경해야 할 것이다. 그 작업을 하기 위해 변경 통지를 구현합니다.</p>
<p>그리고 재사용가능하게끔 설계를 해야 하며 다른 정보들을 표현할 때 쉽게 설계를 해야 합니다. </p>
<p>컨트롤러,Controller</p>
<p>데이터와 사용자인터페이스 요소들을 잇는 다리역할을 합니다. </p>
<p>즉, 사용자가 데이터를 클릭하고, 수정하는 것에 대한 &quot;이벤트&quot;들을 처리하는 부분을 뜻합니다.  </p>
<p>컨트롤러 또한 다음과 같은 규칙을 이해해야 합니다. </p>
<ol>
<li>모델이나 뷰에 대해서 알고 있어야 한다.</li>
</ol>
<p>모델이나 뷰는 서로의 존재를 모르고, 변경을 외부로 알리고, 수신하는 방법만 가지고 있는데 이를 컨트롤러가 중재하기 위해 모델과 그에 관련된 뷰에 대해서 알고 있어야 합니다. </p>
<ol start="2">
<li>모델이나 뷰의 변경을 모니터링 해야 한다.</li>
</ol>
<p>모델이나 뷰의 변경 통지를 받으면 이를 해석해서 각각의 구성 요소에게 통지를 해야 합니다. </p>
<p>또한, 애플리케이션의 메인 로직은 컨트롤러가 담당하게 됩니다. </p>
<p>왜 MVC패턴을 사용해야 할까.</p>
<p>사용자가 보는 페이지, 데이터처리, 그리고 이 2가지를 중간에서 제어하는 컨트롤, 이 3가지로 구성되는 하나의 애플리케이션을 만들면 각각 맡은바에만 집중을 할 수 있게 됩니다. 공장에서도 하나의 역할들만 담당을 해서 처리를 해서 효율적이게 됩니다. 여기서도 마찬가지입니다.</p>
<ul>
<li>사실 저 밑에 스즈키 네는 군용 볼트를, 옆집 하루노보 네는 군용 너트를 만들고 있을 뿐이다. </li>
</ul>
<p>(커티스 르메이, 도쿄 대공습 직전에 부하들에게.)</p>
<p>​</p>
<p>서로 분리되어 각자의 역할에 집중할 수 있게끔하여 개발을 하고 그렇게 애플리케이션을 만든다면, 유지보수성, 애플리케이션의 확장성, 그리고 유연성이 증가하고, 중복코딩이라는 문제점 또한 사라지게 되는 것입니다.  그러기 위한 MVC패턴입니다.</p>
<p>*유연성: 여기서 유연성이라는 것은 클라이언트의 새로운 요구사항에 대해 최소한의 비용으로 보다 유연하게 대처할 수 있는 것을 말합니다. </p>
<p>*비즈니스로직: 프로그램의 논리구조</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스 거시적으로 보기]]></title>
            <link>https://velog.io/@i-migi0104/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B1%B0%EC%8B%9C%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@i-migi0104/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B1%B0%EC%8B%9C%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sun, 08 Sep 2024 08:23:29 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터베이스란">데이터베이스란</h1>
<p>데이터베이스는 여러 사람이 공유하여 사용할 목적으로 체계화해 통합, 관리하는 데이터의 집합</p>
<p>데이터베이스에 저장될 수 있는 것들</p>
<ul>
<li>학생 데이터</li>
<li>학과 데이터</li>
<li>결제 데이터</li>
<li>품목 데이터 ,,,,</li>
<li>저장해야 하는 대부분의 것들</li>
</ul>
<blockquote>
<p>데이터베이스란 웹 서비스의 심장과도 같다.
저장되는 대상/ 저장하는 방법에 따라 웹서비스의 목적과 성능이 달라진다</p>
</blockquote>
<p>저장되는 방법 -&gt; 데이터베이스 설계 및 모델링하는 방법</p>
<h1 id="dbms">DBMS</h1>
<p>Data Base Management System
데이터베이스를 관리하는 시스템</p>
<p>DBMS의 종류</p>
<ul>
<li>MySQL</li>
<li>MongoDB</li>
<li>Oracle</li>
<li>SQL Server</li>
<li>PostgreSQL</li>
</ul>
<p>관계형 DBMS와 비관계형 DBMS로 나눌 수 있다</p>
<h2 id="관계형-dbms란">관계형 DBMS란</h2>
<ul>
<li>데이터를 테이블 형태(열 &amp; 행)로 저장</li>
<li>즉, 데이터를 표와 같은 형태로 저장하는 DBMS</li>
<li>테이블끼리 참조, 합치기도 가능하다</li>
</ul>
<p>관계형 DBMS를 다루기 위한 언어 -&gt; SQL</p>
<p>SQL은 Structured Query Language
관계형 데이터베이스 관리 시스템(RDBMS)에서 데이터를 조작하고 관리하기 위한 언어</p>
<p>ex)</p>
<pre><code class="language-SQL">CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    department VARCHAR(50)
   );</code></pre>
<pre><code class="language-SQL">SELECT first_name, last_name
FROM employees;</code></pre>
<pre><code class="language-SQL">DELETE FROM orders
WHERE order_id =456;</code></pre>
<h2 id="sql의-종류">SQL의 종류</h2>
<p>DML: Data Manipulation Language</p>
<ul>
<li>SELECT, INSERT, UPDATE, DELETE
DDL: Data Definition Language</li>
<li>CREATE, ALTER, DROP, RENAME, TRUNCATE
DCL: Data Control Language</li>
<li>GRANT, REVOKE, TCL(COMMIT, ROLLBACK, SAVEPOINT)</li>
</ul>
<p>cf) NoSQL
비관계형 데이터베이스
NoSQL은 먼저 구조를 정의할 필요없이 데이터를 저장 및 검색
확장성, 가용성이 장점(실시간 웹 어플리케이션 &amp; 빅데이터)
ex) MongoDB</p>
<h3 id="트랜잭션transaction">트랜잭션(Transaction)</h3>
<ul>
<li>데이터베이스 상호작용의 단위</li>
<li>논리적 작업 단위
ex) 돈을 계좌에서 인출하는 작업 / 학생을 테이블에서 삭제하는 작업</li>
</ul>
<h4 id="트랜잭션이-지켜야-할-성질">트랜잭션이 지켜야 할 성질</h4>
<ul>
<li><code>ACID</code></li>
<li>A(원자성, Atomicity)
트랜잭션은 하나의 논리적인 작업 단위로 간주
모든 데이터 조작 작업은 성공하거나, 실패할 때까지 적용되지 않거나 롤백
계좌에서 인출하고 다른 계좌로 입금하는 트랜잭션에서 하나의 작업이 실패하면 다른 작업도 취소</li>
<li>C(일관성, Consistency)
트랜잭션은 데이터베이스를 일관된 상태로 유지
트랜잭션이 시작하기 전과 끝난 후에도 데이터베이스는 일관된 규칙과 제약 조건을 따라야 한다</li>
<li>I(격리성, Isolation)
동시에 여러 트랜잭션이 실행될 때 각 트랜잭션은 서로 간섭하지 않고 격리되어야 한다
한 트랜잭션의 작업이 다른 트랜잭션에게 영향을 미치지 않도록 보장
두 개의 트랜잭션이 동시에 계좌에서 돈을 인출하려 할 때, 한 트랜잭션이 완료되기 전에 다른 트랜잭션은 해당 계좌에 접근하지 못해야 한다</li>
<li>D(지속성, Durablility)
트랜잭션이 성공적으로 완료되면, 그 결과는 영구적으로 저장되어야 한다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[E2E 프로젝트]]></title>
            <link>https://velog.io/@i-migi0104/E2E-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</link>
            <guid>https://velog.io/@i-migi0104/E2E-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</guid>
            <pubDate>Thu, 05 Sep 2024 09:52:15 GMT</pubDate>
            <description><![CDATA[<p>2024년 7월 29일 부터 8월 30일까지 한달 간 동시성 테스트를 경험해보기 위해 식당 예약 및 원격으로 줄을 설 수 있는 서비스를 만들었다. 서비스를 만들기 전에 만든 아키텍처 설계와 ERD 설계는 아래 사진과 같다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/96a3f0cd-be03-455a-9847-228065a8c076/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/743c28e3-b5a7-4f0a-9eb5-a8a5afcf7c01/image.png" alt=""></p>
<p>위의 사진처럼 route53에 가비아에서 구매한 도메인을 EC2서버에 연결해 localhost:8080에서 접속할 수 있었던 사이트를 <code>catchline.site</code>에서 접속할 수 있게 했고, 배포된 사이트에서 데이터베이스를 사용할 수 있기 위해서 <code>Aamazon RDS</code>에서 <code>MySQL</code>을 사용했다. 그 후 클라이언트에서 요청을 보내면 AWS Certificate Manager와 Aamazon ELB를 거쳐서 EC2서버에 요청이 가도록 설계를 했다.</p>
<p>백엔드 개발자로써 이번처럼 본격적으로 프로젝트를 하는 것이 처음이라 개발을 하기 전에 전체적인 협업규칙을 정하기로 했다. 그 결과 우리팀이 정한 협업 규칙은 여러가지가 있지만 대표적으로 특정 기술을 선택하기 전에 왜 이 기술을 사용해야 하는지 팀원들과 함께 충분한 대화를 통해서 인지를 하고 기술을 사용하는 것이었다. 프로젝트 개발이 끝난 지금도 이런 규칙을 정한 것이 되게 괜찮다고 생각이 들었다. 그 이유는 포트폴리오를 작성할 때나 면접을 볼 때 왜 Jwt를 사용하셨나요? 와 같은 질문들이 되게 많이 나온다고 알고 있고, 이러한 질문들에 대비를 할 수 있기 때문이다.</p>
<p>한달 동안 프로젝트에서 구현한 기능들은 대략적으로 설명해보자면 CRUD는 기본적으로 식당,리뷰,스크랩,웨이팅,예약,유저 추가 삭제 편집 등에서 구현했고, 스프링 시큐리티, jwt, 세션 사용과 querydsl를 사용해 페이지네이션, 검색, 정렬 기능 구현, SSE를 사용해 알림 기능등을 구현했다. 또한 동시성 테스트를 하기 위해서 <code>syncronized</code> 를 사용해 예약등록 메서드를 동시성 테스트를 해봤다.</p>
<p>위에서 내가 한달 동안 팀원들과 함께 만든 서비스의 소개, 협업 규칙, 설계, 구현한 기능을 소개했으니 한달동안 발생한 주요한 이슈들 몇가지를 소개해보겠다.</p>
<p>동시성 테스트를 하면서 발생한 이슈부터 말해보겠다.</p>
<p>첫 번쨰로 20명의 사용자가 동시에 하나의 식당에 예약을 할 때 1명의 사용자를 제외하고 19명의 사용자가 예약등록을 실패해야하는데 처음에 내가 동시성 테스트를 실행했을때에는 7명의 사용자가 예약 등록을 성공하는 이슈가 발생했다. 그래서 나는 이 이슈의 원인이 내가 <code>ExecutorService executorService = Executors.newFixedThreadPool(20)</code> 을 사용해서 스레드 풀에 총 20개의 스레드를 만들고 테스트를 실행했는데 하나의 스레드가 예약등록 메서드를 실행 중이고 아직 메서드가 종료되지 않았을 때 다른 스레드가 예약등록 메서드를 실행해서 발생한 이슈라고 생각해 예약등록 메서드에 트랜잭션과 syncronized를 추가해 이 이슈를 해결했다. 그러나 이 이슈를 해결한 후 바로 다른 이슈가 발생했다.</p>
<p>두 번쨰로 트랜잭션과 syncronized를 추가한 후에는 1명의 사용자를 제외하고 나머지 모든 사용자가 예약등록에 실패하는 결과가 올바르게 나왔는데 테스트를 10번 실행하면 3번 정도는 테스트가 실패하는 이슈가 발생했다. 결론적으로 이 이슈의 원인은 트랜잭션과 syncronized를 함께 사용해서 발생한 결과인데 그 이유는 트랜잭션을 사용하면 메서드의 앞뒤로 transaction begin, transaction commit 과 같은 코드들이 생기는데 이 코드들은 syncronized의 동시성 제어의 영역이 아니기 떄문에 transaction commit이 끝나기 전에 다른 스레드가 메서드를 실행시키는 경우가 발생해서 위와 같은 이슈가 발생했었다. 그래서 이 이슈는 예약 등록 메서드에서 트랜잭션을 제거하고 해결했다.</p>
<p>세 번쨰로 200명이 동시에 예약을 했을 때 예약 등록에 성공하는 유저가 memberId 1부터 20번 정도로만 테스트를 실행시켰을 때 나오지 않았다. 이 이슈의 원인은 디버깅을 통해서 해결했는데 내가 바라는대로 200개의 스레드가 동시에 예약 요청을 하지 않고, 20개 정도의 스레드가 먼저 예약을 시도한 후에 나머지 스레드가 예약을 동시에 실행하고 있어서 발생한 이슈였다. 그래서 나는 이 이슈를 <code>CountDownLatch</code>의 <code>startLatch.await()</code>을 사용해서 모든 스레드가 준비 상태에 도달할 때까지 대기하고 모든 스레드가 준비 완료되면 <code>startLatch.countDown()</code>을 호출해서 모든 스레드가 동일한 조건에서 동시에 메소드를 실행하게 해서 이슈를 해결했다. 아래의 이미지가 내가 동시성을 테스트한 코드이다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/afe6396b-e97e-427e-a97a-40d8f9cfd3d8/image.png" alt=""></p>
<p>다음은 배포과정 중에서 발생한 이슈인데 처음에 EC2와 RDS를 사용해서 배포를 한 후에 확인을 했을 때 카카오맵 API가 정상적으로 연동이 되지 않고, <code>HTTP</code>로 배포가 되어서 보안이 매우 취약한 이슈였다. 그래서 나는 이 이슈들을 AWS의 Route53과 Certificate Manager를 사용해서 SSL 인증을 받아 <code>HTTPS</code> 로 배포를 하면서 이 이슈를 해결했다.
나중에 알고보니 카카오맵 API 연동 문제는 HTTP를 사용해서 발생한 것이 아니라 다른 해결방법이 있었던 것 같아서 괜히 HTTPS로 배포했나 싶기도 했지만 보안 문제를 생각하면 SSL인증을 받아 HTTPS로 배포한 것이 괜찮은 선택이었던 것 같다.</p>
<p>내가 겪은 주요한 이슈들은 이정도였지만 팀원들이 겪었던 이슈들도 매우 많았던 것 같다.</p>
<p>이번 프로젝트를 하면서 제일 많이 얻은 것은 스프링에 대한 기본적인 숙련도인 것 같다. 이번 프로젝트에 기본적인 CRUD를 사용해서 만든 기능들이 많아 한달 전보다 훨씬 스프링에 익숙해졌고, 또한 수많은 멘토링을 통해서 클린코드에 좀 더 가까워지게 코드를 작성할 수 있게 되었다.</p>
<p>그러나 아직 모르는 스프링에 대한 기능들이 많기 때문에 좀 더 많이 경험을 해보고 익숙해지고 싶다. 그리고 이번에 동시성 테스트에서 <code>Redis</code>를 사용해보는 경험을 해보고 싶었는데 시간이 부족해 사용해보지 못해 약간 반쪽짜리 동시성 테스트 였다는 생각이 들었다. 그리고 만들고 보니 기능을 많이 구현했다고 생각했지만 핵심적인 기능들이 조금 적었던 것 같아 여러모로 부족했던 점이 많은 프로젝트였던 것 같다. 하지만 그만큼 얻은 것도 많고 다음 프로젝트는 무려 3달이 넘는 기간동안 만들기 때문에 훨씬 디테일을 많이 챙기면서 만들어 볼 수 있을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP, 캐시, 쿠키]]></title>
            <link>https://velog.io/@i-migi0104/HTTP-%EC%BA%90%EC%8B%9C-%EC%BF%A0%ED%82%A4</link>
            <guid>https://velog.io/@i-migi0104/HTTP-%EC%BA%90%EC%8B%9C-%EC%BF%A0%ED%82%A4</guid>
            <pubDate>Sun, 01 Sep 2024 10:01:33 GMT</pubDate>
            <description><![CDATA[<h1 id="http-메시지-개관">HTTP 메시지 개관</h1>
<h3 id="http-메시지-개요">HTTP 메시지 개요</h3>
<p>HTTP 메시지는 웹 상에서 클라이언트와 서버 간에 데이터를 교환하기 위해 사용되는 메시지 포맷입니다. HTTP 메시지는 크게 두 가지로 나뉩니다.</p>
<ul>
<li>요청 메시지: 클라이언트가 서버에 정보를 요청할 때 사용</li>
<li>응답 메시지: 서버가 클라이언트의 요청에 대해 응답할 때 사용</li>
</ul>
<h3 id="http-메시지의-구조">HTTP 메시지의 구조</h3>
<p>HTTP 메시지는 다음과 같은 기본 구조로 이루어져 있습니다.</p>
<ul>
<li><p>시작 라인(Start Line)
 요청 메시지에서는 HTTP 메서드, 요청 대상(URI), HTTP 버전으로 구성
 응답 메시지에서는 HTTP 버전, 상태 코드, 이유 문구로 구성</p>
</li>
<li><p>헤더(Header)
  다양한 헤더 필드들이 존재하며, 이들은 요청이나 응답에 대한 추가 정보를 제공</p>
</li>
<li><p>본문(Body)
  선택적으로 사용되며, 주로 POST나 PUT 요청에서 데이터 전송을 위해 사용</p>
</li>
</ul>
<h3 id="http-메서드">HTTP 메서드</h3>
<p>HTTP 메서드는 클라이언트가 서버에 어떤 동작을 요청하는지를 정의</p>
<ol>
<li><p>GET
 서버로부터 자원을 요청하는 메서드로 주로 데이터 조회에 사용되며, 요청 본문이 없다</p>
</li>
<li><p>POST
 서버로 데이터를 전송하고, 그 데이터를 처리하도록 요청하는 메서드로 주로 자원 생성이나 데이터 처리를 위해 사용</p>
</li>
<li><p>PUT
 서버의 자원을 덮어쓰는 메서드로 자원이 존재하지 않으면 새로 생성하기도 한다</p>
</li>
<li><p>PATCH
 자원의 일부를 수정하는 메서드</p>
</li>
<li><p>DELETE
 서버에서 자원을 삭제하는 메서드</p>
</li>
</ol>
<h3 id="http-상태코드">HTTP 상태코드</h3>
<p>HTTP 상태 코드는 서버가 클라이언트의 요청을 처리한 결과를 나타낸다. 5가지의 범주로 나타낸다</p>
<ol>
<li><p>1XX(정보)
 요청이 수신되었으며, 처리가 계속 진행됨을 나타낸다</p>
</li>
<li><p>2XX(성공)
 요청이 성공적으로 처리되었음을 나타낸다</p>
</li>
<li><p>3XX(리다이렉션)
 클라이언트가 요청한 리소스가 다른 위치로 이동되었음을 나타낸다</p>
</li>
<li><p>4XX(클라이언트 오류)
 클라이언트의 잘못된 요청으로 인해 오류가 발생함을 나타낸다</p>
</li>
<li><p>5XX(서버 오류)
 서버에서 요청을 처리하는 도중 오류가 발생했음을 나타낸다</p>
</li>
</ol>
<h3 id="http-요청-및-응답-헤더-필드">HTTP 요청 및 응답 헤더 필드</h3>
<p>HTTP 헤더는 요청과 응답에 대한 추가 정보를 제공하는 필드로, 매우 다양하게 사용된다.</p>
<ul>
<li><p>Request Headers
클라이언트가 서버에 요청할 때 추가적인 정보를 제공하는 헤더
ex) <code>User-Agent</code>, <code>Accept</code>, <code>Authorization</code></p>
</li>
<li><p>Response Headers
서버가 클라이언트에 응답할 때 제공하는 추가적인 정보를 담고 있다
ex) <code>Content-Type</code>, <code>Set-Cookie</code>, <code>Location</code></p>
</li>
</ul>
<blockquote>
<p>헤더 필드는 HTTP 메시지의 기능을 확장하고, 보다 효율적인 통신을 가능하게 한다</p>
</blockquote>
<h3 id="기타-중요한-개념">기타 중요한 개념</h3>
<ul>
<li><p>멱등성(Idempotency): 멱등성은 동일한 요청을 여러 번 보내더라도, 결과가 처음 요청과 동일한지를 의미한다. 예를 들어 GET, PUT, DELETE 메서드는 멱등성이 있는 메서드이다.</p>
</li>
<li><p>캐시 가능성(Cacheability): 캐시 가능성은 서버의 응답을 클라이언트나 중간 캐시가 저장할 수 있는지 여부를 나타낸다. 에를 들어, GET 응답은 캐시될 수 있지만, POST 응답은 일반적으로 캐시되지 않는다.</p>
</li>
</ul>
<hr>
<h1 id="http-헤더">HTTP 헤더</h1>
<h3 id="http-헤더의-개요">HTTP 헤더의 개요</h3>
<p>HTTP 헤더는 헤더 필드라고 불리며, 각 필드는 이름과 값으로 구성된다. 일반 적으로 다음과 같은 형식으로 구성된다.
<code>header-field = field name &quot;:&quot; field-value</code>
이 형식은 HTTP 메시지의 전송 시 필요한 추가 정보를 제공한다.</p>
<h3 id="주요-http-헤더-필드">주요 HTTP 헤더 필드</h3>
<p>HTTP 헤더에는 다양한 필드가 있으며, 그 중 자주 사용되는 헤더 필드는 아래와 같다.</p>
<ol>
<li><p>요청(Request) 관련 헤더 필드</p>
<ul>
<li>Host: 요청 대상 서버의 호스트명과 포트 정보를 나타낸다</li>
<li>Referer: 클라이언트가 이전에 방문한 URL을 가리킨다.</li>
<li>User-Agent: 클라이언트 소프트웨어의 이름과 버전을 나타내며, 주로 브라우저 정보를 담고 있다</li>
</ul>
</li>
<li><p>응답(Response) 관련 헤더 필드</p>
<ul>
<li>Server: 서버 소프트웨어의 이름과 버전을 나타낸다</li>
<li>Location: 리다이렉션 시 클라이언트를 이동시킬 새로운 URL을 나타낸다.</li>
</ul>
</li>
<li><p>대표적인 헤더 필드 예시
Host: <code>Host: www.example.com</code>
요청이 <a href="http://www.example.com">www.example.com</a> 서버에 보내졌음을 의미합니다.</p>
</li>
</ol>
<p>Date: <code>Date: Mon, 27 Sep 2021 12:00:00 GMT</code>
요청 또는 응답이 발생한 시간을 나타냅니다.</p>
<p>Referer: <code>Referer: http://previous.page.com</code>
클라이언트가 이 페이지로 이동하기 전에 방문한 페이지의 URL입니다.</p>
<p>User-Agent: <code>User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36</code>
클라이언트가 사용하는 브라우저와 운영체제에 대한 정보입니다. 여기서 클라이언트는 윈도우 10을 사용하는 크롬 브라우저입니다.</p>
<p>Server: <code>Server: Apache/2.4.41 (Ubuntu)</code>
서버가 Apache 웹 서버를 사용하고 있음을 나타냅니다.</p>
<p>Location: <code>Location: https://new-url.com</code>
리다이렉션 시 이동할 경로이다.</p>
<h3 id="콘텐츠-관련-필드">콘텐츠 관련 필드</h3>
<ol>
<li><p>Content-Type
요청 또는 응답에서 사용되는 콘텐츠의 유형을 나타낸다.</p>
<ul>
<li>text/html; charset=utf-8: HTML 문서로, UTF-8 인코딩을 사용합니다.</li>
<li>application/json: JSON 형식의 데이터를 사용합니다.</li>
<li>image/png: PNG 이미지 파일입니다.</li>
</ul>
</li>
<li><p>Content-Encoding
콘텐츠의 인코딩 및 압축 방식을 나타낸다</p>
</li>
<li><p>Content-Length
콘텐츠의 길이를 바이트 단위로 나타낸다</p>
</li>
<li><p>Content-Language
콘텐츠의 언어를 나타낸다</p>
<hr>
</li>
</ol>
<h1 id="캐시">캐시</h1>
<h3 id="캐시의-개념">캐시의 개념</h3>
<p>캐시는 네트워크에서 자원의 사본을 임시 저장하는 기술이다. HTTP 프로토콜은 기본적으로 상태를 유지하지 않는 무상태 프로토콜이기 때문에, 동일한 자원에 대한 요청이 반복될 경우 서버는 매번 자원을 새롭게 응답해야한다. 이를 보완하기 위해 캐시가 사용된다.</p>
<p>캐시된 자원은 다음과 같은 위치에 저장될 수 있다.</p>
<ul>
<li>클라이언트(브라우저) 캐시: 사용자의 브라우저가 자원을 로컬에 저장한다.</li>
<li>캐시 서버/프록시 서버: 중간 서버가 자원의 사본을 저장하여 어러 클라이언트 요청에 대해 빠르게 응답할 수 있다.</li>
</ul>
<h3 id="cache-control-헤더">Cache-Control 헤더</h3>
<p>캐시의 동작을 제어하기 위해 <code>Cache-Control</code> 헤더가 사용된다. 이 헤더는 캐시의 사용 여부와 조건을 명시한다.</p>
<ul>
<li>max-age=숫자(초): 자원을 캐시할 수 있는 최대 시간을 초 단위로 설정한다</li>
<li>no-cache: 자원을 캐시할 수는 있지만, 항상 원본 서버에 자원의 유효성을 검증해야 한다</li>
<li>no-store: 자원을 캐시해서는 안된다. 이 설정은 자원의 보안이 중요한 경우에 사용된다.</li>
</ul>
<h3 id="캐시된-자원의-유효-기간">캐시된 자원의 유효 기간</h3>
<p>캐시된 자원은 무한히 유효하지 않으며, 일정 기간 동안만 유효하다. 자원의 마지막 변경 시점을 나타내는 Last-Modified 헤더가 이를 관리하는 데 사용된다.</p>
<h3 id="캐시된-자원의-변경">캐시된 자원의 변경</h3>
<p>캐시된 자원이 변경된 경우, 클라이언트는 이를 인지하고 새로운 자원을 받아야 한다. 자원의 변경 여부를 확인하는 두 가지 주요 방법이 있다.</p>
<ol>
<li><p>If-Modified-Since 헤더
클라이언트는 요청 시 If-Modified-Since 헤더를 사용하여 특정 시점 이후 자원이 변경되었는지를 서버에 묻는다. 만약 자원이 변경되지 않았다면 서버는 304 응답을 보내며 이는 클라이언트가 캐시된 자원을 그대로 사용할 수 있음을 나타낸다.</p>
</li>
<li><p>ETag(Entity Tag)
서버는 자원에 대해 고유한 식별자인 ETag값을 부여한다. 클라이언트는 이후 요청에서 If-None-Match 헤더를 통해 이 ETag값이 변경되었는지 확인할 수 있다.</p>
</li>
</ol>
<hr>
<h1 id="쿠키">쿠키</h1>
<h3 id="쿠키란-무엇인가">쿠키란 무엇인가?</h3>
<p>쿠키는 서버에서 클라이언트로 전송된 정보를 임시로 저장하는 작은 데이터 조각이다. 쿠키는 <code>이름=값</code>의 형태로 저장되며, 주로 세션 유지, 사용자 추적, 사용자 설정 저장 등의 목적으로 사용된다. 쿠키는 다음과 같은 특징을 가지고 있다.</p>
<ul>
<li>유효 기간: 쿠키는 유효 기간이 설정되며, 그 기간이 지나면 자동으로 삭제된다.</li>
<li>도메인 및 경로: 쿠키가 적용되는 도메인과 경로가 설정되어 있어, 특정 도메인이나 경로에서만 쿠키가 전송된다.</li>
</ul>
<h3 id="쿠키의-동작-방식">쿠키의 동작 방식</h3>
<p>서버는 Set-Cookie 헤더를 통해 쿠키를 클라이언트로 전송된다. 클라이언트는 이 쿠키를 저장하고, 이후 해당 도메인으로의 요청 시 Cookie 헤더에 쿠키를 포함시켜 서버로 전송한다. 이를 통해 서버는 클라이언트를 식별하고 이전 상태를 유지할 수 있다.</p>
<h3 id="쿠키의-도메인과-경로">쿠키의 도메인과 경로</h3>
<ul>
<li>도메인: <code>Set-Cookie: domain=example.com</code>으로 설정된 쿠키는 <code>example.com</code>과 그 하위 도메인에서 사용된다.</li>
<li>경로: <code>Set-Cookie: path=/posts</code>로 설정된 쿠키는 <code>/posts</code> 경로 및 그 하위 경로에서만 유효하다</li>
</ul>
<h3 id="쿠키의-유효-기간-설정">쿠키의 유효 기간 설정</h3>
<p>쿠키의 유효 기간은 다음과 같은 두 가지 방법으로 설정된다.</p>
<ul>
<li>expires: <code>Set-Cookie: expires=Wed, 10 Aug 2023 12:00:00 GMT</code>와 같이 특정 만료일을 설정합니다.</li>
<li>max-age: <code>Set-Cookie: max-age=1000</code>과 같이 설정된 초 단위로 쿠키의 수명을 지정할 수 있습니다. 이 예에서는 쿠키가 1000초 동안 유효합니다.</li>
</ul>
<h3 id="쿠키와-보안">쿠키와 보안</h3>
<p>쿠키는 민감한 정보를 저장할 수 있으므로 보안에 대한 주의가 필요하다.</p>
<ul>
<li>Secure: 쿠키를 HTTPS를 통해서만 전송하도록 설정한다. 이를 통해 전송 중에 쿠키가 도청될 위험을 줄인다.</li>
<li>HTTPOnly: 쿠키에 자바스크립트를 통해 접근할 수 없도록 설정한다. 이는 XSS 공격을 방지하는 데 유용하다</li>
</ul>
<h3 id="쿠키와-세션">쿠키와 세션</h3>
<p>쿠키와 세션은 사용자 상태를 유지하는 두 가지 방법이다.</p>
<ul>
<li>쿠키: 클라이언트측에 저장되고 관리된다.</li>
<li>세션: 서버 측에서 관리되며, 서버는 클라이언트를 식별하기 위해 세션 ID를 쿠키로 클라이언트에 전달한다. 이후 클라이언트는 요청 시 이 세션 ID를 서버에 다시 전송하여 상태를 유지한다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[싱글톤이란?]]></title>
            <link>https://velog.io/@i-migi0104/%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@i-migi0104/%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Fri, 30 Aug 2024 12:29:49 GMT</pubDate>
            <description><![CDATA[<h1 id="싱글톤-패턴이란">싱글톤 패턴이란</h1>
<blockquote>
<p>싱글톤 패턴은 특정 클래스의 인스턴스를 1개만 생성되는 것을 보장하는 디자인 패턴입니다. 즉, 생성자를 통해서 여러 번 호출이 되더라도 인스턴스를 새로 생성하지 않고 최초 호출 시에 만들어두었던 인스턴스를 재활용하는 패턴입니다.</p>
</blockquote>
<p><code>싱글톤 패턴을 사용하면 메모리 사용을 최적화하고, 객체 생성 비용을 절감할 수 있습니다.</code></p>
<pre><code class="language-java">    @Test
    @DisplayName(&quot;스프링 없는 순수한 DI 컨테이너&quot;)
    void pureContainer() {
        AppConfig appConfig = new AppConfig();

        MemberService memberService1 = appConfig.memberService();
        MemberService memberService2 = appConfig.memberService();

        System.out.println(&quot;memberService1: &quot; + memberService1);
        System.out.println(&quot;memberService2: &quot; + memberService2);

        assertThat(memberService1).isNotSameAs(memberService2);
    }</code></pre>
<p>위의 코드는 AppConfig 클래스를 이용해 MemberService 객체를 두 번 호출을 한 후 memberService1과 memberService2의 참조값을 비교하는 테스트 코드입니다. 참조값을 비교해보면 다른 것을 확인할 수 있고, 이는 매번 객체를 호출할 때마다 새로운 인스턴스가 만들어지고, 이는 메모리 낭비로 이어질 수 있습니다.</p>
<p>위의 문제점을 해결하기 위해 싱글톤 패턴을 구현해보고 테스트를 다시 한 번 해보았습니다.</p>
<pre><code class="language-java">package springproject.basicspring.singleton;

// 싱글톤 패턴을 구현하는 방법은 여러가지가 있고, 이 코드에서 사용한 방법은 객체를 미리 생성해두는 가장 단순하고 안전한 방법이다.
public class SingleTonService {

    //1. static 영역에 객체를 딱 1개만 생성해둔다.
    private static final SingleTonService instance = new SingleTonService();

    //2. public으로 열어서 객체 인스턴스가 필요하면 이 static 메서드를 통해서만 조회하도록 허용한다.
    public static SingleTonService getInstance() {
        return instance;
    }

    //3. 생성자를 private으로 선언해서 외부에서 new 키워드를 사용한 객체 생성을 못하게 만든다.
    private SingleTonService() {
    }

    public void login() {
        System.out.println(&quot;싱글톤 객체 로직 호출&quot;);
    }

}</code></pre>
<pre><code class="language-java">    @Test
    @DisplayName(&quot;싱글톤 패턴을 적용한 객체 사용&quot;)
    void singletonServiceTest() {
        SingleTonService singleTonService1 = SingleTonService.getInstance();
        SingleTonService singleTonService2 = SingleTonService.getInstance();

        System.out.println(&quot;singleTonService1: &quot; + singleTonService1);
        System.out.println(&quot;singleTonService2: &quot; + singleTonService2);

        assertThat(singleTonService1).isSameAs(singleTonService2);
    }
</code></pre>
<p>위의 코드들은 싱글톤 패턴을 자바로 구현을 한 코드입니다.
객체의 인스턴스를 static을 사용해 static 영역에 미리 생성해 올려주고, 객체 인스턴스가 필요할 때는 getInstance() 메서드를 통해서만 접근할 수 있게 해 항상 동일한 인스턴스를 반환하게 해줍니다. 그 후 객체의 생성자를 private로 선언하여 외부에서 new 키워드를 사용해 인스턴스를 생성하지 못하도록 해주었습니다.</p>
<p>테스트 코드에서 하나의 인스턴스만 생성하는지 확인해보면 두 번의 getInstance() 호출이 동일한 객체를 반환하는 것을 확인할 수 있습니다. 이처럼 싱글톤 패턴은 객체를 하나만 생성하여 여러 곳에서 공유할 수 있도록 해줍니다.</p>
<p>하지만 이 패턴에도 몇 가지 단점이 있습니다. 우선 getInstance()와 같은 메서들르 직접 구현해야 하며 클라이언트가 구체 클래스에 의존하게 되어 DIP와 OCP를 위반할 수 있습니다. 그로 인해 코드의 유연성이 떨어지고 안티패턴이라고도 불리기도 합니다.
그러나 스프링 프레임워크를 사용하면 이러한 단점들을 해결하고 싱글톤을 사용할 수 있습니다.</p>
<p>스프링 컨테이너는 @Configuration 어노테이션이 붙은 설정 파일을 통해 객체 간의 의존관계를 설정하고 관리합니다. 이를 통해 애플리케이션 내에서 Bean을 싱글톤으로 관리할 수 있습니다. 그로 인해 직접 싱글톤 패턴을 구현할 필요 없이 객체를 효율적으로 관리할 수 있습니다. </p>
<pre><code class="language-java">package springproject.basicspring;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springproject.basicspring.discount.DiscountPolicy;
import springproject.basicspring.discount.FixDiscountPolicy;
import springproject.basicspring.discount.RateDiscountPolicy;
import springproject.basicspring.member.MemberRepository;
import springproject.basicspring.member.MemberService;
import springproject.basicspring.member.MemberServiceImpl;
import springproject.basicspring.member.MemoryMemberRepository;
import springproject.basicspring.order.OrderService;
import springproject.basicspring.order.OrderServiceImpl;

@Configuration
public class AppConfig {

    //@Bean memberService -&gt; new MemoryMemberRepository()
    //@Bean orderService -&gt; new MemoryMemberRepository()
    // -&gt; 싱글톤이 깨지는 거 아닐까??

    //AppConfig.memberService
    //AppConfig.memberRepository
    //AppConfig.memberRepository
    //AppConfig.orderService
    //AppConfig.memberRepository

    //AppConfig.memberService
    //AppConfig.memberRepository
    //AppConfig.orderService

    @Bean
    public MemberService memberService() {
        System.out.println(&quot;AppConfig.memberService&quot;);
        return new MemberServiceImpl(memberRepository());
    }
    @Bean
    public MemberRepository memberRepository() {
        System.out.println(&quot;AppConfig.memberRepository&quot;);

        return new MemoryMemberRepository();
    }

    // 스프링 컨테이너가 @Bean 메서드를 오버라이딩하여 싱글톤을 보장하는 프록시 메커니즘을 사용
    // 아래와 같이 static을 붙이면 static 메서드는 스프링 컨테이너에 의해 오버라이딩되어 관리될 수 없기 때문에
    // 싱글톤으로 관리되지 않는다.
//    @Bean
//    public static MemberRepository memberRepository() {
//        return new MemoryMemberRepository();
//    }

    @Bean
    public OrderService orderService() {
        System.out.println(&quot;AppConfig.orderService&quot;);

        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
    @Bean
    public DiscountPolicy discountPolicy() {
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }


}</code></pre>
<p>위의 AppConfig 클래스 또한 스프링 설정 파일로 각 서비스 객체를 빈으로 등록해 사용합니다. 테스트 코드를 통해 MemberService 객체를 두번 호출하고 그 참조값을 비교해보면</p>
<pre><code class="language-java">    @Test
    @DisplayName(&quot;스프링 컨테이너와 싱글톤&quot;)
    void springContainer() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

        MemberService memberService1 = ac.getBean(&quot;memberService&quot;, MemberService.class);
        MemberService memberService2 = ac.getBean(&quot;memberService&quot;, MemberService.class);

        // 참조값이 같은 것을 확인
        System.out.println(&quot;memberService1: &quot; + memberService1);
        System.out.println(&quot;memberService2: &quot; + memberService2);

        // memberService1 == memberService2
        assertThat(memberService1).isSameAs(memberService2);
    }</code></pre>
<p>예상했던 바와 같이 두 객체의 참조값이 동일함을 확인할 수 있습니다. 이를 통해 스프링이 기본적으로 빈을 싱글톤으로 관리한다는 것을 알 수 있습니다.</p>
<p>스프링에서 싱글톤 패턴을 사용할 때는 주의할 점이 있습니다. 싱글톤 빈은 여러 클라이언트가 동일한 인스턴스를 공유하기 때문에 상태를 가지지 않도록 해야 합니다. </p>
<pre><code class="language-java">package springproject.basicspring.singleton;

public class StatefulService {

    // statefulService
    private int price;

    public void order(String name, int price) {
        System.out.println(&quot;name = &quot; + name + &quot;, price = &quot; + price);
        this.price = price;
    }

    public int getPrice() {
        return price;
    }

    // 무상태로 코드 짜는 법

//    public int order(String name, int price) {
//        System.out.println(&quot;name = &quot; + name + &quot;, price = &quot; + price);
//        return price;
//
//    }



}</code></pre>
<pre><code class="language-java">package springproject.basicspring.singleton;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

import static org.junit.jupiter.api.Assertions.*;

class StatefulServiceTest {

    @Test
    void statefulServiceSingleton() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(StatefulService.class);
        StatefulService statefulService1 = ac.getBean(StatefulService.class);
        StatefulService statefulService2 = ac.getBean(StatefulService.class);

//        int userAPrice = statefulService1.order(&quot;userA&quot;,10000);
//        int userBPrice = statefulService2.order(&quot;userB&quot;,20000);

        // ThreadA: A사용자 10000원 주문
        statefulService1.order(&quot;userA&quot;, 10000);
        // ThreadB: B사용자 20000원 주문
        statefulService2.order(&quot;userB&quot;, 20000);

        // ThreadA: 사용자A 주문 금액 조회
        int price = statefulService1.getPrice();
        System.out.println(&quot;price = &quot; + price);
//        System.out.println(&quot;price = &quot; + userAPrice);


        Assertions.assertThat(price).isEqualTo(20000);

    }

    static class TestConfig {

        @Bean
        public StatefulService statefulService() {
            return new StatefulService();
        }
    }

}</code></pre>
<p>StatefulService 클래스는 상태를 유지하는 필드 price를 가지고 있습니다. 이 상태를 가지는 필드 때문에 특정 클라이언트가 값을 변경하면 다른 클라이언트들에게도 영향을 미치게 됩니다. 테스트코드를 보시면 두 사용자가 각각 주문을 했을 때, 사용자 A의 주문 금액이 10000원이 아니라 20000원이 출력되는 것을 확인할 수 있습니다. 이는 상태를 가지는 싱글톤 빈의 문제점을 잘 보여주고 그로 인해 스프링 빈은 항상 상태를 가지지 않게 설계해야 합니다.</p>
<p>추가로 스프링 프레임워크를 사용해서 만든 모든 빈이 싱글톤으로 관리되는 것은 아닙니다.스프링 프레임워크에서의 빈의 기본 스코프는 싱글톤이지만, Scope 어노테이션을 사용하면 필요에 따라 빈의 스코프를 설정할 수 있습니다.
그리고 싱글톤 객체의 크기가 클 때에는 객체를 스코프 설정을 통해 싱글톤을 관리하지 않는게 더 메모리에 효율적일 수도 있습니다. 그 이유는 싱글톤 객체가 너무 많은 메모리를 차지하는 경우, 이 객체가 애플리케이션이 종료될 때까지 메모리를 점유하기 때문에 프로그램이 더 이상 필요하지 않은 메모리를 해제하지 않고 계속해서 점유하고 있는 메모리 릭이 발생할 수 있기 때문입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DNS와 HTTP의 특성]]></title>
            <link>https://velog.io/@i-migi0104/DNS%EC%99%80-HTTP%EC%9D%98-%ED%8A%B9%EC%84%B1</link>
            <guid>https://velog.io/@i-migi0104/DNS%EC%99%80-HTTP%EC%9D%98-%ED%8A%B9%EC%84%B1</guid>
            <pubDate>Sun, 25 Aug 2024 09:58:36 GMT</pubDate>
            <description><![CDATA[<h1 id="dns">DNS</h1>
<p>DNS의 개념에 대해서 말씀드리면 주소창에 google.com 같은 주소를 검색할 때에 컴퓨터가 이런 문자로 된 주소를 이해할 수 있도록 google.com이라고 입력하면, IP주소로 바꿔주는 역할을 하는 시스템입니다.</p>
<p>예를 들어, 전화번호부에서 전화를 하고 싶은 이름을 검색하면 전화번호를 알 수 있듯이 DNS도 웹사이트 이름을 입력하면, DNS가 그 이름에 해당하는 IP 주소를 찾아서 알려주는 역할을 합니다.</p>
<p>DNS는 단순히 하나의 서버가 일을 하는 게 아니라, 여러 단계로 나눠져 있습니다. 이런 식으로 여러 단계로 나뉜 이유는 인터넷이 너무 커서 모든 정보를 한 서버에 다 저장할 수 없기 때문에 여러 서버가 나눠서 정보를 관리하고 있어.</p>
<h3 id="루트-네임-서버">루트 네임 서버</h3>
<p>인터넷의 최상위 서버로 사용자가 웹사이트에 접속하려고 할 때, 가장 먼저 이 루트 서버에 물어보는 겁니다. 이 서버는 사용자가 찾는 웹사이트가 어디에 있는지 정확히는 모르지만, 다음에 물어볼 서버를 알려줍니다.</p>
<h3 id="tld-서버">TLD 서버</h3>
<p>이건 최상위 도메인 서버입니다. 예를 들어, .com, .kr, .edu 같은 도메인을 관리하는 서버입니다. 루트 서버가 &quot;이 웹사이트는 .com에 있어&quot;라고 알려주면, 이 TLD 서버에 가서 &quot;그럼 구체적으로 어디에 있어?&quot;라고 물어보게 됩니다.</p>
<h3 id="authoritative-dns-서버">Authoritative DNS 서버</h3>
<p>마지막으로 이 서버는 우리가 찾는 웹사이트의 실제 IP 주소를 가지고 있습니다. 그러니까 이 서버가 최종적으로 &quot;구글은 여기 있어! 이 IP 주소를 써!&quot;라고 답해주게 됩니다.</p>
<h2 id="dns-질의-방식">DNS 질의 방식</h2>
<p>DNS에는 반복적 질의 방식과 재귀적 질의 방식이 있습니다.</p>
<ul>
<li><p>반복적 질의: 사용자가 루트 서버한테 물어보고, 루트 서버가 &quot;난 잘 모르겠는데, 이 서버한테 물어봐&quot;라고 하면, 사용자가 직접 그 서버한테 또 물어보는 방식입니다. 계속 사용자가 직접 물어보는 거라서 반복적 질의라고 한다고 합니다.</p>
</li>
<li><p>재귀적 질의: 이건 사용자가 처음 물어본 DNS 서버가 대신 다 알아봐 주는 방식입니다. 사용자가 한 번만 물어보면 되고, 나머지는 서버가 알아서 다 해주게 됩니다. 그래서 이걸 재귀적 질의라고 불러.</p>
</li>
</ul>
<h4 id="dns-레코드">DNS 레코드</h4>
<p>DNS 서버에는 여러 가지 정보가 저장되어 있는데 이 정보를 DNS 레코드라고 부릅니다.</p>
<ul>
<li><p>A 레코드: 도메인 이름에 대한 IPv4 주소를 저장합니다. 이게 우리가 주로 사용하는 IP 주소 형식입니다.</p>
</li>
<li><p>AAAA 레코드: 이건 IPv6 주소를 저장합니다. IPv6는 좀 더 긴 형식의 IP 주소입니다.</p>
</li>
<li><p>CNAME 레코드: 도메인의 별칭을 저장합니다. 예를 들어 <a href="http://www.example.com%EC%9D%B4">www.example.com이</a> 실제로는 example.com의 다른 이름이라면, 이걸 CNAME 레코드가 저장합니다.</p>
</li>
<li><p>NS 레코드: 이건 도메인을 관리하는 네임 서버의 정보를 저장합니다. 예를 들어, google.com을 관리하는 네임 서버가 어디 있는지를 이 레코드가 알려줘.</p>
</li>
</ul>
<h3 id="dns-캐싱">DNS 캐싱</h3>
<p>DNS 캐싱은 우리가 자주 방문하는 웹사이트의 IP 주소를 미리 저장해 두는 겁니다. 이렇게 하면 웹사이트에 접속할 때마다 DNS 서버에 물어볼 필요가 없어서 더 빠르게 접속할 수 있습니다. 이 캐시된 정보는 일정 시간 동안만 유효합니다. 이 시간을 TTL이라고 하는데, 시간이 지나면 캐시가 만료되고 다시 물어봐야합니다.</p>
<p>이런 식으로 DNS는 우리가 매일 인터넷을 편리하게 사용할 수 있게 도와주는 시스템입니다. 평소에는 잘 느끼지 못하지만, DNS로 인해서 우리는 숫자로 된 IP 주소를 외우지 않고도 편하게 웹사이트를 이용할 수 있습니다.</p>
<hr>
<h1 id="자원과-자원의-식별">자원과 자원의 식별</h1>
<h3 id="네트워크에서-자원이란">네트워크에서 자원이란?</h3>
<p>네트워크에서의 자원은 네트워크를 통해 주고받을 수 있는 모든 정보를 말합니다. 예를 들어, 우리가 웹사이트를 방문할 때 보는 HTML 파일, 웹에서 다운로드하는 이미지나 동영상, JSON 파일 같은 것들이 모두 자원에 속합니다. 간단히 말해서, 인터넷에서 우리가 요청하고 받을 수 있는 모든 데이터가 자원입니다.</p>
<p>네트워크에서 자원을 사용하려면, 이 자원을 식별할 수 있어야 합니다. 자원이 어디에 있는지, 그리고 그 자원이 무엇인지를 정확하게 지정해줘야 하기 때문에 등장한 개념이 URI입니다.</p>
<h4 id="uriuniform-resource-identifier">URI(Uniform Resource Identifier)</h4>
<p>자원을 식별할 수 있는 문자열로 쉽게 말해, 인터넷 상의 자원을 가리킬 수 있는 주소 같은 역할을 하고 이 URI는 크게 두 가지로 나뉩니다.</p>
<ul>
<li><p>URL(Uniform Resource Locator)
자원의 위치를 기반으로 식별하며 흔히 보는 웹 주소가 바로 URL이야. 예를 들어, <a href="https://www.example.com">https://www.example.com</a> 같은 게 URL이며 이 주소는 자원이 어디에 있는지를 알려줍니다.</p>
</li>
<li><p>URN(Uniform Resource Name)
자원의 이름을 기반으로 식별합니다. 위치는 상관없이, 그 자원의 고유한 이름을 사용하는 방식입니다. 하지만 일상적으로는 URL이 더 많이 사용돼서 URN은 상대적으로 덜 알려져 있어.</p>
</li>
</ul>
<h3 id="url의-구성-요소">URL의 구성 요소</h3>
<p>URL을 보면, 그냥 긴 문자열처럼 보일 수 있는데, 사실은 여러 구성 요소가 있습니다.</p>
<ul>
<li><p>Scheme: 주로 프로토콜 이름이 들어가는 부분으로 예를 들어, http://, https://, ftp:// 같은 게 여기에 들어갑니다. 이 부분은 브라우저에게 어떤 방식으로 자원에 접근해야 하는지를 알려줍니다.</p>
</li>
<li><p>Authority: 이 부분은 주로 도메인 이름과 포트 번호로 구성됩니다. 예를 들어 <a href="http://www.example.com:80%EC%97%90%EC%84%9C">www.example.com:80에서</a> <a href="http://www.example.com%EC%9D%80">www.example.com은</a> 호스트 이름(도메인 이름), :80은 포트 번호입니다. 여기서는 Authority가 자원이 어디에 있는지를 더 구체적으로 지정해줍니다.</p>
</li>
<li><p>Path: 이건 자원이 서버 내에서 어디에 있는지를 나타내는 경로입니다. 예를 들어 <a href="https://www.example.com/images/logo.png%EC%97%90%EC%84%9C">https://www.example.com/images/logo.png에서</a> /images/logo.png가 경로입니다. 서버 안에서 그 자원이 저장된 위치를 알려줍니다.</p>
</li>
<li><p>Query: 이건 주로 서버에 전달하는 추가적인 정보로 예를 들어, 검색할 때 <a href="https://www.google.com/search?q=example%EC%B2%98%EB%9F%BC">https://www.google.com/search?q=example처럼</a> URL 뒤에 ?q=example 같은 형식이 Query로 서버에 전달될 정보입니다.</p>
</li>
<li><p>Fragment: 이건 자원의 특정 부분을 가리킬 때 사용되며 예를 들어 긴 문서의 특정 섹션으로 바로 이동하고 싶을 때 사용합니다. URL 끝에 #section1 같은 형식으로 붙어. 예를 들어 <a href="https://www.example.com/page#section1">https://www.example.com/page#section1</a> 에서 #section1입니다.</p>
</li>
</ul>
<h2 id="url-해석">URL 해석</h2>
<p>URL은 처음엔 복잡해 보일 수 있지만, 위의 구성 요소들을 알고 나면 해석하기가 쉬워진다고 합니다.
<a href="https://john.doe@www.example.com:123/forum/questions/?tag=networking&amp;order=newest#top%EC%9D%B4%EB%9D%BC%EB%8A%94">https://john.doe@www.example.com:123/forum/questions/?tag=networking&amp;order=newest#top이라는</a> URL을 보면, 이 안에는 다양한 정보가 들어있어:</p>
<p>Scheme: https
Authority: <a href="mailto:john.doe@www.example.com">john.doe@www.example.com</a>:123 (여기서 john.doe는 사용자 정보, <a href="http://www.example.com%EC%9D%80">www.example.com은</a> 호스트, 123은 포트 번호야)
Path: /forum/questions/
Query: ?tag=networking&amp;order=newest (여기서 tag=networking과 order=newest가 서버에 전달될 정보야)
Fragment: #top (이건 자원의 특정 부분, 즉 페이지의 맨 위를 가리키는 거지)</p>
<hr>
<hr>
<h1 id="웹-서버와-웹-어플리케이션-서버">웹 서버와 웹 어플리케이션 서버</h1>
<h3 id="서버와-클라이언트의-기본-개념">서버와 클라이언트의 기본 개념</h3>
<p>서버와 클라이언트의 관계에 대해서 간단하게 말해보자면 클라이언트는 요청을 보내는 쪽이고, 서버는 그 요청에 대해 응답을 보내는 쪽입니다. 예를 들어, 우리가 웹 브라우저에서 google.com을 입력하면, 브라우저는 클라이언트가 되고, 구글의 서버는 우리에게 페이지를 보여주기 위해 응답을 보내는 서버가 되는 겁니다.</p>
<h3 id="웹-서버와-웹-애플리케이션-서버의-차이">웹 서버와 웹 애플리케이션 서버의 차이</h3>
<p>웹 서버(Web Server): 주로 정적인 자원을 제공합니다. 여기서 정적인 자원이라는 건, 예를 들어 HTML 파일, 이미지, 동영상처럼 언제, 어디서, 누가 보든지 내용이 변하지 않는 파일들을 말합니다. 이런 파일들은 이미 서버에 저장돼 있고, 요청이 오면 그대로 제공되기만 하면 됩니다. 그래서 웹 서버는 단순히 클라이언트의 요청에 따라 이런 정적인 파일들을 제공하는 역할을 합니다.</p>
<p>웹 애플리케이션 서버(WAS: Web Application Server): 반면에, WAS는 동적인 자원을 제공합니다. 동적인 자원이란 사용자가 누구인지, 또는 언제, 어디서 요청을 하느냐에 따라 내용이 달라질 수 있는 데이터를 말합니다. 예를 들어, &quot;오늘의 날씨는 20도입니다&quot;와 같이, 사용자가 있는 위치나 시간에 따라 변할 수 있는 정보를 제공하는 겁니다. WAS는 이런 정보를 생성하기 위해 데이터베이스와 상호작용해서, 사용자 맞춤형 데이터를 만들어 클라이언트에게 제공합니다.</p>
<p>웹 서버와 WAS의 협력
현대적인 웹 서비스는 웹 서버와 WAS가 서로 협력해서 동작합니다. 웹 서버는 빠르게 정적인 자원을 제공하고, WAS는 필요한 경우 데이터베이스와 상호작용해 동적인 정보를 생성합니다. 이렇게 역할을 분담하면, 전체 시스템이 더 효율적으로 작동하고, 과도한 부하도 막을 수 있고 또한, 보안적인 측면에서도 이점이 있습니다. 왜냐하면, 정적인 자원과 동적인 자원이 분리되어 있기 때문에 각각의 보안을 별도로 관리할 수 있기 때문입니다</p>
<hr>
<h1 id="http의-특성">HTTP의 특성</h1>
<h2 id="http의-기본-개념">HTTP의 기본 개념</h2>
<p>먼저, HTTP는 HyperText Transfer Protocol의 약자야. 이 프로토콜은 주로 웹 브라우저와 웹 서버 간에 데이터를 주고받을 때 사용됩니다. 예를 들어, 사용자가 웹사이트를 방문할 때, 브라우저는 HTTP를 사용해서 웹 서버에 &quot;이 페이지를 보여줘!&quot;라고 요청을 보내고, 서버는 그 요청에 응답해서 페이지를 보내줍니다. 이 과정은 요청-응답 기반의 클라이언트-서버 구조로 작동합니다. 여기서 클라이언트는 요청을 보내는 쪽(보통 웹 브라우저), 서버는 그 요청에 응답하는 쪽입니다.</p>
<h2 id="http의-주요-특성">HTTP의 주요 특성</h2>
<ul>
<li><p>미디어-독립적
HTTP는 어떤 형태의 데이터든지 전송할 수 있습니다. 예를 들어, HTML 문서, 이미지, 동영상, JSON 데이터, XML 파일 등 어떤 데이터라도 HTTP를 통해 전송할 수 있습니다. 그래서 HTTP는 매우 유연한 프로토콜로, 다양한 종류의 데이터를 처리할 수 있어.</p>
</li>
<li><p>비연결성
HTTP는 기본적으로 비연결성 프로토콜입니다. 클라이언트가 서버에 요청을 보내고, 서버가 응답을 보내면 그 연결이 바로 끊어집니다. 이렇게 하면 서버의 자원을 효율적으로 사용할 수 있습니다. 그 이유는 연결을 계속 유지하면 서버 자원이 많이 소모되기 때문입니다. 하지만, 최근의 HTTP/1.1부터는 이러한 비연결성을 개선하기 위해 지속 연결(Keep-Alive) 기능이 추가되었습니다. 이 기능 덕분에, 한 번 연결을 맺으면 그 연결을 유지하면서 여러 요청을 보낼 수 있게 되었습니다.</p>
</li>
<li><p>스테이트리스:
HTTP는 스테이트리스 프로토콜입니다. 이는 서버가 클라이언트의 상태를 기억하지 않는다는 뜻입니다. 예를 들어, 사용자가 웹사이트에서 여러 페이지를 방문해도, 서버는 이전 페이지에서 사용자가 뭘 했는지 기억하지 않습니다. 이 덕분에 서버 확장이 용이해지고, 사용자가 여러 서버에 요청을 보내더라도 문제가 없게 됩니다.</p>
</li>
<li><p>지속 연결 (Keep-Alive):
앞서 말한 것처럼, HTTP/1.1부터는 지속 연결 기능이 추가됐습니다. 이 기능은 한 번 연결된 상태에서 여러 개의 요청과 응답을 주고받을 수 있게 해줍니다. 이렇게 하면 매번 새로운 연결을 맺기 위해 시간을 낭비하지 않아도 돼서, 네트워크 혼잡을 줄이고, 페이지 로딩 속도도 빨라지게 됩니다.</p>
</li>
</ul>
<h3 id="http-버전의-진화">HTTP 버전의 진화</h3>
<ul>
<li>HTTP 0.9: 가장 초기 버전으로, GET 메서드만 지원하고 비지속 연결이었으며 기능이 매우 제한적이었습니다.</li>
<li>HTTP 1.0: 다양한 요청 방법과 헤더를 지원하게 되었지만여전히 비연결성 프로토콜이었어습니다.</li>
<li>HTTP 1.1: 지속 연결 기능이 추가되었고 한 번 연결된 상태에서 여러 요청을 보낼 수 있는 기능이 들어갔습니다.</li>
<li>HTTP 2.0: 성능이 크게 개선되었습니다. 예를 들어, 요청이 꼭 순서대로 처리될 필요가 없어졌고, 헤더 데이터를 압축해서 전송할 수 있게 됐습니다.</li>
<li>HTTP 3.0: 최신 버전으로, TCP 대신 UDP 기반 프로토콜인 QUIC을 사용해 더 빠르고 안전한 연결을 제공합니다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링+MySQL+RDS+EC2 배포과정]]></title>
            <link>https://velog.io/@i-migi0104/%EC%8A%A4%ED%94%84%EB%A7%81MySQLRDSEC2-%EB%B0%B0%ED%8F%AC%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@i-migi0104/%EC%8A%A4%ED%94%84%EB%A7%81MySQLRDSEC2-%EB%B0%B0%ED%8F%AC%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Sat, 17 Aug 2024 10:04:07 GMT</pubDate>
            <description><![CDATA[<p>스프링에서 MySQL DB를 쓰면서 AWS의 RDS와 EC2를 이용해서 배포를 하는 방법을 간단하게 작성해보려고 한다.</p>
<hr>
<p>우선 DB를 RDS에서 MySQL을 빌려서 연결을 한 후에 EC2를 이용해서 배포를 해보려고 한다. 그 이유는 RDS에서 DB를 사용하지 않고 EC2를 써봤는데 <code>NullPointException</code>과 <code>BeanCreationException</code>이 발생했기 떄문이다.</p>
<ol>
<li><p>스프링 프로젝트에 의존성 추가</p>
<pre><code class="language-java"> implementation &#39;org.springframework.boot:spring-boot-starter-data-jpa&#39;
 runtimeOnly &#39;com.mysql:mysql-connector-j&#39;
 implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;</code></pre>
</li>
<li><p>AWS RDS에 접속해서 MySQL 빌리기
<img src="https://velog.velcdn.com/images/i-migi0104/post/7a80eb7c-62dc-4572-a035-07033460203f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/690b3948-fb68-4230-839f-67f40d19601b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/4bdcf254-124c-446d-88b7-4b8329f75bc2/image.png" alt=""></p>
</li>
</ol>
<p>DB인스턴스 식별자, 마스터 사용자 이름, 마스터 암호는 원하는대로 작성하면 된다.</p>
<p>프리티어는 db.t3와 db.t2만 사용할 수 있어서 db.t3를 사용했다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/ceccc0a4-1167-4a48-a7b3-0b73ff888db0/image.png" alt=""></p>
<p>초기 데이터베이스 이름을 프로젝트의 db이름인 <code>catch_line</code>으로 설정하고 자동 백업을 활성화하면 비용이 청구가 되기 때문에 비활성화로 설정했다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/87e7244d-8725-45ce-a3b4-54d031cbd3db/image.png" alt=""></p>
<p>그 후 만든 인스턴스의 보안 그룹으로 들어가 인바운드 규칙 편집에서 </p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/8b490f03-98d1-40b1-9d96-5e29cf3078c8/image.png" alt=""></p>
<p>MySQL의 default 포트인 3306을 IPv4 Anywhere로 Ipv4를 사용하는 누구나 사용할 수 있게 설정했다.</p>
<ol start="3">
<li>application.properties에 데이터베이스 정보 추가</li>
</ol>
<pre><code class="language-java">spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://주소:포트/데이터베이스?useSSL=false&amp;useUnicode=true&amp;serverTimezone=Asia/Seoul&amp;allowPublicKeyRetrieval=true
spring.datasource.username=아이디
spring.datasource.password=비밀번호</code></pre>
<p>RDS를 만들면서 설정한 아이디와 비밀번호를 입력하면된다. 포트와 비밀번호는 나같은 경우에는 포트:3306, 데이터베이스는 RDS의 데이터베이스에 들어가면 엔드포인트를 볼 수 있는데 그 엔드포인트를 입력하면 된다.</p>
<p>그 후 스프링 프로젝트를 실행시켜서 제대로 동작한다면 RDS에서 빌린 MySQL 데이터베이스에 접속이 잘 된 것이다.!</p>
<hr>
<p>이번에는 EC2를 이용해서 RDS에서 MySQL 데이터베이스를 빌려서 사용하고 있는 스프링 프로젝트를 배포해보겠다.</p>
<ol>
<li>스프링 부트 jar파일 생성하기</li>
</ol>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/c7558f9a-574d-4222-b10b-0093b6091fce/image.png" alt=""></p>
<p>인텔리제이 -&gt; Gradle -&gt; Tasks -&gt; build -&gt; bootJar을 더블클릭하면 Jar파일이 프로젝트의 폴더의 build -&gt; libs 안에 생성된다. 그 파일을 바탕화면으로 옮겨놓으면 된다.!</p>
<ol start="2">
<li>EC2 인스턴스 생성</li>
</ol>
<p>EC2 -&gt; 인스턴스 생성에 들어가 설정을 완료하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/1d63b67e-76aa-4308-b724-3a4199192c44/image.png" alt=""></p>
<p>이름을 원하는대로 설정하고, db를 애플리케이션 및 OS 이미지를 Ubuntu로 설정했다.</p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/96d76170-0bf2-4db6-9eb7-e9388db1e006/image.png" alt=""></p>
<p>다음으로 키페어 생성을 클릭하고, 나는 OpenSSh(Git Bash)를 사용했기 때문에 프라이빗 키 파일 형식을 .pem으로 설정한 후 키페어를 생성하면 된다. 키 페어를 생성한 후 바탕화면에 옮겨놓는다.</p>
<p>그 후에 스토리지 구성을 약 10GIB로 설정하고 인스턴스 시작을 누른다.</p>
<ol start="3">
<li>AWS EC2 컴퓨터에 접근할 수 있는 포트를 열기</li>
</ol>
<p>EC2 좌측 카테고리 -&gt; 보안그룹 -&gt; 인바운드 규칙 편집 -&gt; 규칙 추가 -&gt; 8080과 3360 추가</p>
<p>인스턴스에 들어가 생성한 인스턴스 -&gt; 우클릭 -&gt; 연결 -&gt; SSH 클라이언트
에 들어가면 인스턴스에 접속할 수 있는 방법이 나온다.</p>
<ol start="4">
<li>인스턴스 접속</li>
</ol>
<p>Git Bash 접속 -&gt; <code>cd ~/Desktop</code> (바탕화면으로 이동) -&gt; <code>chmod 400 mainkey.pem</code> (키페어에 권한 주기) -&gt; 권한을 가진 키페어를 통해 ssh 접속을 할 수 있다 -&gt; <code>ssh -i mainkey.pem ~~~~~~</code> (<del>~</del>는 </p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/9ce791b2-07e5-42a9-9c23-06b133a12f77/image.png" alt=""></p>
<p>예: 
에서 unbuntu부터 끝까지이다.) (ssh 접속)</p>
<p>-&gt; yes -&gt; ssh 접속 완료 -&gt; 서버 컴퓨터에 jvm(자바)를 설치해야 하기 때문에 우선 java가 존재하는지 확인하기 위해 <code>java -version</code> 명령어를 친다. -&gt; java가 설치되어 있지 않다면 -&gt; java를 설치하기 위해 apt를 업데이트 한다 - &gt; <code>sudo apt update</code> ( apt 업데이트) -&gt; <code>sudo apt install openjdk-17-jre-headless</code> (java 17버전 설치) (다른 버전 설치해도 상관없다) -&gt; AWS EC2 서버에 로컬에 있는 스프링 부트 jar 파일을 서버로 가져와야 한다. -&gt; 기존 EC2에 연결된 bash 쉘에 추가로 git bash를 열어 하나 더 실행시킨다. -&gt; <code>cd ~/Desktop</code> -&gt; <code>ls</code> (jar파일 확인) -&gt; chmod 400 mainkey.pem(서버에 접근하기 위해 권한 주기) -&gt; <code>scp -i &quot;키페어.pem&quot; ~/Desktop/파일이름.jar (ubuntu@IP:~/받을서버경로)</code> (jar파일 EC2 서버에 전송) (<code>(~~~)</code>는 위에 말했던 SSH 클라이언트 글에서 <code>예:</code> 밑에 써있는 코드에서 ubuntu부터 쭉 쓰면 된다!! )</p>
<p>EC2서버에 jar 파일을 전송하면 로컬에 연결된 bash는 종료하고 ec2서버에 연결된 bash 에서 <code>ls</code>를 해 jar 파일이 제대로 전송되었는지 확인한다</p>
<p>이제 마지막으로 <code>jvm</code>으로 파일을 작동시키면 된다!</p>
<h3 id="sudo-java--jar-파일이름jar">sudo java -jar 파일이름.jar</h3>
<p>이렇게 jar을 실행시키고 </p>
<p><img src="https://velog.velcdn.com/images/i-migi0104/post/c747e847-c302-4cae-ad8d-1af4909d1e6d/image.png" alt=""></p>
<p>url에 54.180.235.56:8080 을 검색하면 내가 서버에 올린 프로젝트를 볼 수 있다!!</p>
<h2 id="마지막으로-git-bash를-꺼도-서버가-종료되지-않게-하기-위해서는">마지막으로 git bash를 꺼도 서버가 종료되지 않게 하기 위해서는</h2>
<p>jar파일을 실행 시킬 때
<code>sudo nohup java -jar [jar파일이름] &amp;</code>
이 코드로 실행시키면된다.</p>
<p>종료할때에는</p>
<p><code>ps -ef | grep catch_line-0.0.1-SNAPSHOT.jar</code> 이런식으로 PID 번호를 찾은 후 가장 위에 있는 PID 번호를
<code>sudo kill -9 [맨 위에 있는 PID 번호]</code> 종료시켜주면 된다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TCP 연결, 상태, 재전송, 혼잡 제어와 흐름제어]]></title>
            <link>https://velog.io/@i-migi0104/TCP-%EC%97%B0%EA%B2%B0-%EC%83%81%ED%83%9C-%EC%9E%AC%EC%A0%84%EC%86%A1-%ED%98%BC%EC%9E%A1-%EC%A0%9C%EC%96%B4%EC%99%80-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4</link>
            <guid>https://velog.io/@i-migi0104/TCP-%EC%97%B0%EA%B2%B0-%EC%83%81%ED%83%9C-%EC%9E%AC%EC%A0%84%EC%86%A1-%ED%98%BC%EC%9E%A1-%EC%A0%9C%EC%96%B4%EC%99%80-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4</guid>
            <pubDate>Thu, 15 Aug 2024 13:31:24 GMT</pubDate>
            <description><![CDATA[<h1 id="tcp-연결">TCP 연결</h1>
<p>TCP는 전송 제어 프로토콜로 인터넷에서 데이터를 신뢰성 있게 송수신 하기 위한 프로토콜로 TCP 연결의 주요 과정은 연결 설정, 데이터 송수신, 연결 종료로 나눌 수 있습니다</p>
<p>우선 용어에 대해서 간단히 말씀 드리면 <code>액티브 오픈 호스트</code>는 연결 설정을 시작하는 호스트이고, 주로 클라이언트의 역할을 말한다고 합니다. 그리고 <code>패시브 오픈 호스트</code>는 연결 요청을 수신하고 수락하는 호스트로 주로 서버 역할을 한다고 합니다.</p>
<ul>
<li>연결 설정: TCP 연결을 설정하는 과정은 Three-Way-Handshake라는 세 단계의 절차를 통해 이루어집니다. </li>
</ul>
<ol>
<li><p>SYN (Synchronize) 단계
액티브 오픈 호스트가 연결을 시작하고 싶을 때, 먼저 SYN 패킷을 보내는 것으로 연결 요청을 합니다. SYN 패킷은 연결 설정을 위한 초키 시퀀스 번호와 기타 정보를 포함하고 있다고 합니다.</p>
</li>
<li><p>SYN-ACK (Synchronize-Acknowledge) 단계
패시브 오픈 호스트가 이 요청을 수신하면, 연결을 수락할 준비가 되었음을 알리기 위해 SYN-ACK 패킷을 반환합니다. 이 패킷은 수신자가 보낸 SYN에 대한 응답으로 수신자의 SYN과 ACK를 포함합니다</p>
</li>
<li><p>ACK (Acknowledge)
액티브 오픈 호스트가 SYN-ACK 패킷을 수신하면 마지막으로 ACK 패킷을 보내 연결을 확인합니다. 이 패킷은 상대방의 SYN-ACK 패킷을 확인 응답하며, 이제 양쪽 호스트가 서로의 존재를 인식하고 연결이 설정 됩니다.</p>
</li>
</ol>
<ul>
<li>연결 종료 과정: Four-Way-Handshake라고 하는 네 가지 단계로 이루어진다고 합니다</li>
</ul>
<ol>
<li><p>FIN (Finish)
액티브 클로즈 호스트가 연결 종료를 시작하고, 이 호스트는 더 이상 데이터를 송신하지 않겠다는 신호를 보내기 위해 FIN 패킷을 전송합니다. 이 패킷은 연결 종료를 요청하는 신호로, 송신자는 이 패킷을 통해 자신의 데이터 전송을 종료하겠다는 의사를 표현합니다</p>
</li>
<li><p>ACK (Acknowledge)
패시브 클로즈 호스트는 FIN 패킷을 수신한 후 이를 확인하기 위해 ACK 패킷을 반환합니다. ACK 패킷은 FIN 패킷에 대한 응답으로 수신자는 송신 호스트의 종료 요청을 수락했음을 나타냅니다 이때 패시브 클로즈 호스트는 자신의 데이터 전송을 계속할 수 있다고 합니다</p>
</li>
<li><p>FIN (Finish)
패시브 클로즈 호스트가 자신의 데이터 전송을 완료하고 연결 종료를 원할 때 FIN 패킷을 송신합니다.</p>
</li>
<li><p>ACK (Acknowledge)
액티브 클로즈 호스트가 패시브 클로즈 호스트가 보낸 FIN 패킷을 수신한 후, 이를 확인하기 위해 ACK 패킷을 반환합니다.</p>
</li>
</ol>
<p>이런 과정을 통해 <code>데이터 손실 방지</code>, <code>완전한 종료</code>, <code>시간 지연 방지</code>, <code>상태 일관성 유지</code>를 할 수 있다고 합니다.</p>
<hr>
<h1 id="tcp-상태">TCP 상태</h1>
<p>TCP는 연결형 프로토콜이자 <code>스테이트풀</code> 프로토콜이라고 합니다. 이는 TCP가 연결 상태를 유지하고, 관리하며 다양한 상태를 통해 안정적이고 신뢰성 있는 통신을 제공한한다는 것을 의미합니다.</p>
<p>TCP 연결의 주요 상태 중 대표적인 것으로는 </p>
<ol>
<li><p>CLOSED: 연결이 완전히 종료된 상태로 모든 자원이 해제되고, 상태가 초기화되는 상태입니다.</p>
</li>
<li><p>LISTEN, SYN-SENT, SYN-RECEIVED: <code>연결 수립 도중 사용되는 상태</code> LISTEN은 서버가 클라이언트의 연결 요청을 기다리는 상태, SYN-SENT는 클라이언트가 연결 요청을 보내고, 서버의 응답을 기다리는 상태로 클라이언트가 이 상태에서 SYN 패킷을 보내고, SYN-ACK 패킷을 기다립니다. SYN-RECEIVED는 서버가 클라이언트의 연결 요청을 수신하고, 응답을 보낸 후에 있는 상태입니다.</p>
</li>
<li><p>ESTABLISHED: 클라이언트와 서버 간에 연결이 설정된 상태로, 데이터 송수신이 가능한 상태로, 양쪽 호스트가 데이터를 교환할 수 있습니다.</p>
</li>
<li><p>FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT: 연결 해제시 사용되는 상태</p>
</li>
</ol>
<p>이중에서 <code>TIME-WAIT</code> 상태는 액티브 클로즈 호스트가  FIN 패킷을 보내고 마지막으로 ACK 패킷을 수신한 후에 들어가는 상태로 주된 역할로는</p>
<ol>
<li>마지막 ACK 세그먼트의 유실 대비: 만약 ACK 패킷이 유실되면, 상대방 호스트는 FIN 패킷을 재전송할 수 있으며, TIME-WAIT 상태를 통해 이 재전송을 수신하고 정상적으로 종료할 수 있습니다</li>
<li>패킷 혼선 방지: 네트워크에서 이전 연결의 패킷이 지연되어 새로운 연결에 영향을 미치는 것을 막기 위해 TIME-WAIT 상태에서 일정 시간 동안 기다리며 이 시간을 통해 이전 연결의 패킷이 네트워크에서 소멸되도록 합니다.</li>
</ol>
<hr>
<h1 id="tcp-재전송-기능">TCP 재전송 기능</h1>
<p>TCP는 데이터가 정확히 전달되도록 보장하기 위해 재전송 기반의 오류 제어를 사용합니다. 이 기법은 잘못된 데이터 전송을 수정하기 위해 데이터 패킷을 재전송하는 방식입니다. 주요 특징으로는 두 가지가 있습니다.</p>
<ol>
<li><p>ARQ (Automatic Repeat reQuest): 자동 재전송 요구 방식으로, 송신자가 패킷 전송 후 확인 응답을 기다리고, 응답이 오지 않거나 오류가 발생한 경우 패킷을 재전송하는 방식입니다.</p>
</li>
<li><p>타임아웃: 송신자는 데이터 패킷을 보낸 후, 일정 시간 동안 확인 응답을 기다리고 일정 시간 초과하면 패킷이 손실된 것으로 간주하고, 해당 패킷을 재전송합니다</p>
</li>
</ol>
<p>잘못된 데이터 전송을 인지하는 오류 인지 방법에 대해서 말씀드리겠습니다.</p>
<ol>
<li>중복된 ACK 세그먼트를 수신했을 때</li>
</ol>
<ul>
<li><p>수신 호스트가 동일한 데이터 패킷을 여러 번 수신했거나, 패킷이 손실되어 후속 패킷이 도착하지 않은 경우, 송신 호스트는 중복된 ACK 세그먼트를 수신할 수 있습니다.</p>
</li>
<li><p>빠른 재전송 (Fast Retransmit): 중복된 ACK 세그먼트를 일정 횟수(일반적으로 세 번) 수신하면, 송신자는 타이머가 만료되기 전에 손실된 패킷을 즉시 재전송하는 방식또한 있습니다. 이 방식은 타이머가 만료되기 전에 오류를 빠르게 감지하고 수정할 수 있게 합니다.</p>
</li>
</ul>
<ol start="2">
<li>타임아웃이 발생했을 때</li>
</ol>
<ul>
<li>타임아웃: 송신자는 패킷을 보내고 일정 시간 동안 ACK를 기다립니다. 타이머가 만료되면, 해당 패킷이 손실된 것으로 간주하고, 패킷을 재전송합니다.</li>
</ul>
<p>재전송 기법은 데이터 패킷을 효율적으로 관리하기 위한 방법으로, 다음과 같은 방식이 있습니다</p>
<ol>
<li>Stop-and-Wait ARQ</li>
</ol>
<p>단순 재전송: 송신자는 하나의 패킷을 보내고, 그에 대한 ACK를 받을 때까지 기다립니다. ACK를 수신하면 다음 패킷을 전송합니다. 이 방법은 구현이 간단하지만, 데이터 전송 속도가 느릴 수 있습니다.
이렇게 네트워크 이용 효율이 낮아지는 문제가 생기고 이러한 문제를 해결하기 위해 파이프라이닝을 사용합니다. <code>파이프라이닝</code>은 네트워크 전송에서 여러 데이터 패킷을 연속적으로 송신할 수 있도록 하는 기법입니다. </p>
<p>파이프라이닝의 작동 방식</p>
<ul>
<li><p>연속적인 패킷 전송: 파이프라이닝에서는 송신자가 하나의 패킷에 대한 ACK를 기다리지 않고, 여러 개의 패킷을 연속적으로 전송합니다.</p>
</li>
<li><p>윈도우 기반의 관리: TCP에서 파이프라이닝을 지원하는 주요 방법은 슬라이딩 윈도우(sliding window) 기법입니다. 송신자는 윈도우 크기 내에서 여러 패킷을 전송할 수 있으며, 수신자는 이러한 패킷들을 수신하고, 순서대로 ACK를 보내면서 패킷을 처리합니다.</p>
</li>
</ul>
<p>이런 파이프라이닝과 결합한 재전송 기법에 <code>Selective Repeat ARQ</code>와 <code>Go-Back-N ARQ</code>입니다.</p>
<ol start="2">
<li>Go-Back-N ARQ
연속된 패킷 전송: 송신자는 일정 수의 패킷을 연속적으로 전송하고, 수신 호스트는 각각의 패킷에 대해 ACK를 보냅니다. 만약 N번 패킷이 손실되거나 오류가 발생하면, 송신자는 N번 패킷과 이후 모든 패킷을 재전송합니다.</li>
</ol>
<p>윈도우 크기: 송신자는 한 번에 전송할 수 있는 패킷의 범위를 정의하며, 이 범위 내에서 패킷을 보내고 ACK를 기다립니다.</p>
<ol start="3">
<li>Selective Repeat ARQ
개별 패킷 재전송: 송신자는 각각의 패킷에 대해 확인 응답을 받으며, 오류가 발생한 패킷만 재전송합니다. 이 방식은 데이터 전송의 효율성을 높이며, 필요한 패킷만 재전송하므로, 성능이 더 좋습니다.</li>
</ol>
<p>윈도우 크기: 송신자와 수신자는 각각 개별 패킷의 ACK를 수신하고 전송 상태를 추적합니다.</p>
<hr>
<h1 id="tcp의-혼잡-제어와-흐름-제어">TCP의 혼잡 제어와 흐름 제어</h1>
<p>TCP는 데이터 전송의 신뢰성을 보장하기 위해 두 가지 주요 제어 기법인 흐름 제어와 혼잡 제어를 사용합니다. </p>
<ol>
<li>흐름 제어 (Flow Control)
흐름 제어는 송신 호스트와 수신 호스트 간의 데이터 전송 속도를 조절하여 수신 호스트가 데이터를 제대로 처리할 수 있도록 보장하는 기법입니다. 송신 호스트는 수신 호스트의 처리 능력에 맞춰 데이터를 송신해야 한다고 합니다</li>
</ol>
<p>흐름 제어의 주요 구성 요소로는 <code>송신 버퍼</code>와 <code>수신 버퍼</code>가 있습니다. 송신 버퍼는 송신 호스트가 전송할 데이터를 임시로 저장하는 버퍼이고 수신 버퍼는 수신 호스트가 수신한 데이터를 임시로 저장하는 버퍼입니다.</p>
<p>TCP의 흐름 제어는 <code>슬라이딩 윈도우</code> 기법을 통해 이루어집니다.
슬라이딩 윈도우 기법은 송신자가 수신자로부터 ACK를 받기 전에도 여러 패킷을 전송할 수 있도록 하여, 전송 효율성을 높이는 방법입니다. 슬라이딩 윈도우의 구성 요소에는 윈도우가 있습니다. 윈도우는 데이터 전송에서 송신자와 수신자 간의 데이터 흐름을 조절하는 범위로 송신자가 전송할 수 있는 데이터의 범위를 정의합니다. 윈도우는 <code>송신 윈도우</code>와 <code>수신 윈도우</code>로 나뉘는데 송신 윈도우는 송신자가 전송할 수 있는 데이터의 범위이고, 수신자의 버퍼 크기와 송신자의 현재 상태에 따라 결정됩니다. 수신 윈도우는 수신자가 수신할 수 있는 데이터의 범위이고 수신자의 버퍼 크기와 현재 수신 상태에 따라 결정됩니다.</p>
<p>흐름 제어의 목적 </p>
<ol>
<li><p>수신 호스트가 데이터 처리 속도에 따라 송신 속도를 조절하여, 데이터 손실을 방지하고 효율적인 전송을 보장합니다.</p>
</li>
<li><p>수신 호스트의 버퍼가 가득 차면 송신 호스트는 데이터를 더 이상 전송하지 않으며, 이를 통해 버퍼 오버플로우를 방지합니다.</p>
</li>
<li><p>혼잡 제어 (Congestion Control)</p>
</li>
</ol>
<p>혼잡 제어는 네트워크의 트래픽이 과도해져서 패킷 손실이나 지연이 발생할 수 있는 상황을 조절하는 기법입니다. 네트워크가 혼잡해지면 패킷이 손실되거나 지연될 수 있으므로, 이를 방지하기 위해 송신 호스트가 전송 속도를 조절해야 합니다.</p>
<p>혼잡 제어의 주요 구성 요소로는 <code>혼잡 윈도우</code>가 있습니다. 
혼잡 윈도우는 송신 호스트가 네트워크 혼잡 상태를 고려하여 송신할 수 있는 데이터의 양을 정의하는 윈도우입니다. 혼잡 윈도우는 네트워크의 혼잡 정도에 따라 조정됩니다.</p>
<p>주요 혼잡 제어 알고리즘은 세 가지가 있습니다.</p>
<ol>
<li>느린 시작 (Slow Start):</li>
</ol>
<p>느린 시작 알고리즘의 작동방식은 송신 호스트가 연결을 시작할 때, 혼잡 윈도우를 작게 설정하고, ACK 세그먼트가 수신될 때마다 혼잡 윈도우 크기를 증가시킵니다. RTT(왕복 시간)마다 혼잡 윈도우를 2배로 증가시켜 네트워크 혼잡을 피하면서 최대 전송 속도를 점진적으로 찾아가는 것입니다.</p>
<ol start="2">
<li>혼잡 회피 (Congestion Avoidance):</li>
</ol>
<p>혼잡 회피 알고리즘의 작동 방식은 혼잡 윈도우가 일정 크기에 도달하면, 매 RTT마다 혼잡 윈도우를 1씩 증가시킵니다. 혼잡 회피의 목표는 혼잡 상태를 예방하기 위해, 전송 속도를 천천히 증가시키면서 네트워크의 혼잡을 방지합니다.</p>
<ol start="3">
<li>빠른 회복 (Fast Recovery):</li>
</ol>
<p>빠른 회복 알고리즘의 작동 방식은 패킷 손실이 감지되면, 혼잡 윈도우 크기를 급격히 감소시키고, 손실된 패킷을 빠르게 재전송합니다. 손실된 패킷이 재전송된 후, 혼잡 윈도우를 점진적으로 증가시킵니다. 빠른 회복 알고리즘의 목표는 패킷 손실이 발생했을 때, 느린 시작을 건너뛰고 빠르게 네트워크 상태를 회복하는 것입니다.</p>
<p>이러한 혼잡 제어를 통해서 네트워크의 혼잡을 완화하고, 자원을 효율적으로 사용할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024-08-12 ~ 18 개념 정리]]></title>
            <link>https://velog.io/@i-migi0104/2024-08-12-18-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@i-migi0104/2024-08-12-18-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 13 Aug 2024 14:28:55 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="제너럴-타입">제너럴 타입</h1>
<p>제너럴 타입은 자바에서 타입 안정성을 높이고, 코드의 재사용성을 개선하기 위해 도입된 기능</p>
<p>클래스나 메서드, 인터페이스에서 구체적인 데이터 타입을 지정하지 않고도 타입을 매개변수로 받아 사용할 수 있다. -&gt; 이는 컴파일 시 타입 체크를 통해 타입 안정성을 보장하며, 런타임 오류를 줄여준다</p>
<p>예를 들어, &#39;List<T>&#39;와 같은 형태로 제너럴 타입을 사용하면, 리스트의 요소 타입을 제너럴 타입 매개변수 &#39;T&#39;로 정의할 수 있다.</p>
<pre><code class="language-java">
  // 제너럴 타입 T를 사용하는 Box 클래스
public class Box&lt;T&gt; {
    private T content;

    public void setContent(T content) {
        this.content = content;
    }

    public T getContent() {
        return content;
    }

    public static void main(String[] args) {
        // Integer 타입의 Box 객체 생성
        Box&lt;Integer&gt; integerBox = new Box&lt;&gt;();
        integerBox.setContent(123);
        System.out.println(&quot;Integer content: &quot; + integerBox.getContent());

        // String 타입의 Box 객체 생성
        Box&lt;String&gt; stringBox = new Box&lt;&gt;();
        stringBox.setContent(&quot;Hello, Generics!&quot;);
        System.out.println(&quot;String content: &quot; + stringBox.getContent());
    }
}  </code></pre>
<p>위의 예제에서 &#39;Box&#39; 클래스는 제너럴 타입 &#39;T&#39;를 사용해 다양한 타입의 객체를 저장할 수 있는 클래스를 정의했다.</p>
<pre><code class="language-java">  public class Util {
    // 제너럴 메서드
    public static &lt;T&gt; boolean isEqual(T a, T b) {
        return a.equals(b);
    }

    public static void main(String[] args) {
        // Integer 타입 비교
        boolean result1 = Util.&lt;Integer&gt;isEqual(10, 10);
        System.out.println(&quot;Are integers equal? &quot; + result1);

        // String 타입 비교
        boolean result2 = Util.&lt;String&gt;isEqual(&quot;hello&quot;, &quot;hello&quot;);
        System.out.println(&quot;Are strings equal? &quot; + result2);
    }
}
</code></pre>
<h3 id="제너럴-타입의-장단점">제너럴 타입의 장단점</h3>
<p>  장점</p>
<ol>
<li><p>타입 안정성</p>
</li>
<li><p>코드 재사용성</p>
</li>
<li><p>코드의 가독성</p>
<p>단점</p>
</li>
<li><p>제너럴 타입의 타입 인스턴스화: 제너럴 타입의 인스턴스를 직접 생성할 수 없다. 예를 들어 &#39;new T()&#39;와 같은 생성자는 사용할 수 없다</p>
</li>
<li><p>제너럴 타입의 배열: 제너럴 타입 배열을 직접 생성할 수 없다. 예를 들어 &#39;new T[10]과 같은 형태는 허용되지 않는다.</p>
</li>
</ol>
<hr>
<h1 id="resttemplate">RestTemplate</h1>
<p><code>RestTemplate</code>는 스프링 프레임워크에서 제공하는 클래스로, Restful 웹 서비스와의 통신을 단순화하고 관리하기 위한 HTTP 클라이언트이다.</p>
<h3 id="주요-기능">주요 기능</h3>
<ol>
<li>HTTP 메서드 지원</li>
<li>요청 및 응답 처리: JSON, XML 등의 다양한 데이터 포맷을 쉽게 처리할 수 있으며, 객체와 문자열 간의 변화를 지원한다</li>
<li>타임아웃 설정: 연결 및 읽기 타임아웃을 설정할 수 있다</li>
<li>요청 헤더 설정: 요청에 다양한 헤더를 추가할 수 있다</li>
</ol>
<h3 id="사용하는-경우">사용하는 경우</h3>
<ol>
<li><p>RESTful 웹 서비스 호출: 외부 RESTful API와 통신 할 때, 데이터 조회, 생성, 업데이트, 삭제 등의 작업을 수행할 때 사용</p>
</li>
<li><p>클라이언트 측 API 통합: 스프링 애플리케이션에서 외부 시스템과의 통합이 필요할 때, <code>RestTemplate</code>를 통해 API호출을 간편하게 처리할 수 있다</p>
</li>
<li><p>서비스 간 통신</p>
<h3 id="기본-사용법-예시">기본 사용법 예시</h3>
<pre><code class="language-java">
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
</code></pre>
</li>
</ol>
<p>public class RestTemplateGetExample {</p>
<pre><code>public static void main(String[] args) {
    RestTemplate restTemplate = new RestTemplate();
    String url = &quot;https://jsonplaceholder.typicode.com/posts/1&quot;;

    // GET 요청
    ResponseEntity&lt;String&gt; response = restTemplate.getForEntity(url, String.class);

    // 응답 출력
    System.out.println(&quot;Response Body: &quot; + response.getBody());
}</code></pre><p>}</p>
<pre><code>  위의 코드에서 `restTemplate.getForEntity` 메서드를 사용해서 지정된 URL로 GET요청을 보내고 응답을 `String`으로 받아온다

  ```java
  import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

public class RestTemplatePostExample {

    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();
        String url = &quot;https://jsonplaceholder.typicode.com/posts&quot;;

        // POST 요청에 사용할 객체
        String requestJson = &quot;{ \&quot;title\&quot;: \&quot;foo\&quot;, \&quot;body\&quot;: \&quot;bar\&quot;, \&quot;userId\&quot;: 1 }&quot;;

        // 요청에 사용할 HttpHeaders 설정
        HttpHeaders headers = new HttpHeaders();
        headers.set(&quot;Content-Type&quot;, &quot;application/json&quot;);

        // 요청과 헤더를 포함한 HttpEntity 생성
        HttpEntity&lt;String&gt; request = new HttpEntity&lt;&gt;(requestJson, headers);

        // POST 요청
        ResponseEntity&lt;String&gt; response = restTemplate.exchange(url, HttpMethod.POST, request, String.class);

        // 응답 출력
        System.out.println(&quot;Response Body: &quot; + response.getBody());
    }
}
</code></pre><hr>
<h1 id="jsonignorepropertiesignoreunknownjsonproperty">JsonIgnoreProperties(ignoreUnknown)/JsonProperty</h1>
<p>  <code>@JsonIgnoreProperties</code>와 <code>@JsonPreperty</code>는 Jackson 라이브러리에서 JSON 데이터를 직렬화(객체를 JSON으로 변환)하거나 역직렬화(JSON을 객체로 변환)할 때 사용하는 어노테이션이다</p>
<h3 id="jsonignoreproperties">@JsonIgnoreProperties</h3>
<p>  JSOn 데이터의 직렬화 및 역직렬화 과정에서 특정 속성을 무시하도록 설정할 때 사용한다. 이 어노테이션을 클래스에 적용하면, 지정한 속성들이 JSON으로 직렬화되거나 JSON으로부터 역직렬화되는 과정에서 무시된다</p>
<pre><code class="language-java">  import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;

@JsonIgnoreProperties(ignoreUnknown = true) // JSON에서 알 수 없는 속성 무시
public class User {
    private String name;
    private int age;

    // getters and setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static void main(String[] args) throws Exception {
        String json = &quot;{\&quot;name\&quot;:\&quot;John\&quot;, \&quot;age\&quot;:30, \&quot;unknownProperty\&quot;:\&quot;value\&quot;}&quot;;

        ObjectMapper mapper = new ObjectMapper();
        User user = mapper.readValue(json, User.class);

        System.out.println(&quot;Name: &quot; + user.getName());
        System.out.println(&quot;Age: &quot; + user.getAge());
    }
}
    ```

  User 클래스의 name과 age만을 처리하고 나머지 속성은 무시한다
  여기에서 ObjectMapper란
```java
    // 객체를 JSON 문자열로 변환
          String jsonString = objectMapper.writeValueAsString(person);

    // JSON 문자열을 자바 객체로 변환
        Person person = objectMapper.readValue(jsonString, Person.class);
</code></pre>
<h3 id="jsonproperty">@JsonProperty</h3>
<p>  위의 어노테이션은 JSON 데이터에서 특정 속성의 이름을 자바 객체의 필드에 매핑할 때 사용한다</p>
<pre><code class="language-java">  import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;

public class User {
    @JsonProperty(&quot;user_name&quot;)
    private String name;

    @JsonProperty(&quot;user_age&quot;)
    private int age;

    // getters and setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static void main(String[] args) throws Exception {
        String json = &quot;{\&quot;user_name\&quot;:\&quot;John\&quot;, \&quot;user_age\&quot;:30}&quot;;

        ObjectMapper mapper = new ObjectMapper();
        User user = mapper.readValue(json, User.class);

        System.out.println(&quot;Name: &quot; + user.getName());
        System.out.println(&quot;Age: &quot; + user.getAge());
    }
}
</code></pre>
<hr>
<h1 id="json-xml">JSON, XML</h1>
<h3 id="json">JSON</h3>
<p>  JSON은 데이터를 간단하고 읽기 쉬운 형식으로 표현하기 위해 설계된 텍스트 기반의 데이터 포맷이다. 주로 웹 애플리케이션에서 서버와 클라이언트 간의 데이터 교환에 사용한다</p>
<p>  특징</p>
<ul>
<li><p>형식: 텍스트 기반으로 간결하고 읽기 쉽습니다.</p>
</li>
<li><p>구조: {} (객체)와 [] (배열)로 데이터를 구조화합니다.</p>
</li>
<li><p>데이터 타입: 문자열, 숫자, 객체, 배열, 불리언, null 등이 지원됩니다.</p>
</li>
<li><p>경량: XML보다 데이터 용량이 적어 네트워크 대역폭을 절약할 수 있습니다.</p>
<pre><code class="language-java">
{
  &quot;name&quot;: &quot;John Doe&quot;,
  &quot;age&quot;: 30,
  &quot;isEmployee&quot;: true,
  &quot;address&quot;: {
      &quot;street&quot;: &quot;123 Main St&quot;,
      &quot;city&quot;: &quot;Anytown&quot;,
      &quot;state&quot;: &quot;CA&quot;
  },
  &quot;phoneNumbers&quot;: [
      &quot;555-1234&quot;,
      &quot;555-5678&quot;
  ]
}
</code></pre>
<p>JSON의 장단점
장점</p>
<ul>
<li>간결성, 처리 속도, 호환성
단점</li>
<li>데이터 형식 제한, 메타데이터 부족</li>
</ul>
<h3 id="xml">XML</h3>
<p>XML은 데이터를 계층적으로 구조화하고 태그를 사용하여 데이터의 의미를 설명하는 텍스트 기반의 마크업 언어이다. 데이터 저장, 전송, 문서 작성 등 다양한 용도로 사용된다.</p>
<p>특징</p>
</li>
<li><p>형식: 텍스트 기반으로 태그와 속성을 사용하여 데이터를 설명합니다.</p>
</li>
<li><p>구조: <tag>와 </tag>로 데이터를 구조화하며, 태그 내에 속성(attribute)을 포함할 수 있습니다.</p>
</li>
<li><p>데이터 타입: 문자열, 숫자 등 단순 데이터 타입 외에 복잡한 구조를 지원합니다.</p>
</li>
<li><p>자체 설명: 태그와 속성을 통해 데이터의 의미를 설명할 수 있습니다</p>
<pre><code>&lt;person&gt;
  &lt;name&gt;John Doe&lt;/name&gt;
  &lt;age&gt;30&lt;/age&gt;
  &lt;isEmployee&gt;true&lt;/isEmployee&gt;
  &lt;address&gt;
      &lt;street&gt;123 Main St&lt;/street&gt;
      &lt;city&gt;Anytown&lt;/city&gt;
      &lt;state&gt;CA&lt;/state&gt;
  &lt;/address&gt;
  &lt;phoneNumbers&gt;
      &lt;phoneNumber&gt;555-1234&lt;/phoneNumber&gt;
      &lt;phoneNumber&gt;555-5678&lt;/phoneNumber&gt;
  &lt;/phoneNumbers&gt;
&lt;/person&gt;
</code></pre></li>
</ul>
<hr>
]]></description>
        </item>
    </channel>
</rss>