<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>haribeee.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 24 Jun 2025 18:16:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>haribeee.log</title>
            <url>https://velog.velcdn.com/images/luv_lyn/profile/808a4258-369b-412a-abbc-bc8c56ac9ec4/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. haribeee.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/luv_lyn" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[🌼Spring] Websocket의 등장 배경, 동작 방식, 한계점 (+ HTTP Poling, SSE 비교)]]></title>
            <link>https://velog.io/@luv_lyn/Spring-Websocket%EC%9D%98-%EB%93%B1%EC%9E%A5-%EB%B0%B0%EA%B2%BD-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D-%ED%95%9C%EA%B3%84%EC%A0%90-SSE%EC%99%80-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@luv_lyn/Spring-Websocket%EC%9D%98-%EB%93%B1%EC%9E%A5-%EB%B0%B0%EA%B2%BD-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D-%ED%95%9C%EA%B3%84%EC%A0%90-SSE%EC%99%80-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Tue, 24 Jun 2025 18:16:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>🙋🏻‍♀️야구 실시간 소통 플랫폼</strong>의 라이브 채팅 기능 구현 중,
<strong>websocket + stomp</strong>를 사용해 실시간 채팅기능을 구현했다.
실시간 통신을 위한
<code>1. websocket 등장 이전의 방식</code> <code>2. 단방향 통신(SSE)</code> <code>3. 양방향 통신(Websocket)</code> 에 대해 정리했다</p>
</blockquote>
<br>


<p>기존의 Http 통신은
클라이언트가 서버에게 요청을 보내고, 응답을 받는 <strong>HTTP 기반 단방향 통신</strong>이다 </p>
<p><strong>🚫한계 : HTTP 통신의 Stateless, Connectionless한 특성</strong></p>
<p>예를 들어, 내가 지금 구현하고자 하는 실시간 채팅의 경우,
채팅방에 참여중인 모든 상대방의 메세지를 받기위해서는 <strong>매번 요청을 보내야함</strong></p>
<p><code>내가 먼저 메세지를 보낼 순 있지만, 서버는 선톡을 하지 않는다..^^</code>
이를 해결하기 위한 Http 에서의 기술은 아래와 같다</p>
<br>
<br>

<h1 id="📖-http-polling">📖 <strong>HTTP Polling</strong></h1>
<h2 id="📌-polling">📌 Polling</h2>
<blockquote>
<p>클라이언트가 일정 주기로 http request를 보낸다</p>
</blockquote>
<img src = https://velog.velcdn.com/images/luv_lyn/post/40817c10-5be7-4118-a13c-440fcb7eba81/image.png width=70%>


<p><strong>🚫 단점</strong></p>
<ul>
<li>HTTP 오버헤드 발생 : 불필요한 요청 多</li>
<li>커넥션 맺을 때마다 비용 발생</li>
<li>클라이언트가 많아질 때마다 서버의 부담 ↑</li>
</ul>
<hr>
<h2 id="📌-long-polling">📌 Long Polling</h2>
<blockquote>
<p>클라이언트의 request를 서버는 일정시간 대기 후 응답을 준다
응답까지의 시간을 최대한 늘려서 이벤트 발생시까지 대기한다</p>
</blockquote>
<pre><code>1. 클라이언트에서 서버로 http request를 보내면
2. 서버는 연결을 안끊고 기다림
3. 이벤트 발생시 서버는 응답을 주고 연결 종료
4. 클라이언트는 3번에서 응답을 받고 다시 요청을 보낸다</code></pre><p><strong>🚫 단점</strong></p>
<ul>
<li>데이터가 대기시간에 비해 변화가 잦을 경우, 일반 polling보다 &lt;요청-응답&gt;이 많아짐</li>
<li>다수의 클라이언트에 동시 이벤트 발생시, 응답 받은 후 마찬가지로 동시에 요청을 보내므로 서버 부담 급증</li>
</ul>
<br>
<br>

<h1 id="📖-sseserver-sent-event">📖 <strong>SSE(Server Sent Event)</strong></h1>
<hr>
<blockquote>
<p>서버에서 클라이언트에게 실시간으로 데이터를 전송하는 기술이다
기본적으로 HTTP 연결을 길게 유지하면서 서버 ➝ 클라이언트로 데이터를 전송한다</p>
</blockquote>
<p><code>polling</code> 이후로 등장한 기술로 HTTP통신을 기반으로 <strong>단방향 통신</strong> 기술이다
주로 서버의 데이터 변경이 있을때 클라이언트에게 실시간으로 알려주기 위해 사용한다</p>
<h2 id="✅-동작-방식">✅ 동작 방식</h2>
<img src = https://velog.velcdn.com/images/luv_lyn/post/5075db67-7f07-4850-9d23-1b30a1bae4b9/image.png >


<p><strong>1. Client : SSE Subscribe 요청</strong></p>
<pre><code class="language-http">GET /connect HTTP/1.1
Accept: text/event-stream
Cache-Control: no-cache
Connection: keep-alive</code></pre>
<p>클라이언트 - <code>EventSource</code> 라는 인터페이스로 SSE 연결 요청을 한다</p>
<p><strong>2. Server: Subscription에 대한 응답</strong></p>
<pre><code>HTTP/1.1 200
Content-Type: text/event-stream;charset=UTF-8
Transfer-Encoding: chunked</code></pre><p>서버 - <code>SseEmitter</code> 를 이용해 구독 요청에 대한 응답을 한다</p>
<p><strong>3. Server: 이벤트 전달</strong></p>
<ul>
<li><strong>서버가 이벤트 스트림을 전송하여 실시간으로 업데이트된 정보를 제공한다</strong></li>
<li>데이터는 UTF-8로 인코딩된 텍스트 데이터만 가능(바이너리 데이터❌)</li>
</ul>
<blockquote>
<p><strong>🔍 데이터 형식</strong> - <code>name:value</code></p>
</blockquote>
<pre><code>event: type1
data: An event of type1.
&gt;
event: type2
data: An event of type2.</code></pre><blockquote>
</blockquote>
<h2 id="📌-장단점">📌 장단점</h2>
<p><strong>⭕️ 장점</strong></p>
<ul>
<li>HTTP 프로토콜을 사용하기에 구현이 비교적 간단하다
(특별한 라이브러리/추가설정 없이도 사용가능)</li>
<li>클라이언트 측에서 데이터를 이벤트로 처리하기 용이하다</li>
<li>네트워크 단절시 <code>EventSource</code>가 자동으로 재연결을 시도함</li>
</ul>
<p><strong>❌ 단점</strong></p>
<ul>
<li><code>payload</code>의 크기가 제한적이다</li>
<li>브라우저 호환성</li>
<li><strong>단방향 통신</strong>만 가능하다</li>
</ul>
<br>
<br>

<h1 id="📖-websocket">📖 <strong>Websocket</strong></h1>
<hr>
<blockquote>
<p>WebSocket은 기본 HTTP 핸드셰이크를 통해 연결을 시작하고,
이후에는 <strong>지속적인 TCP 연결</strong>을 통해 <strong>양방향 통신이 가능</strong>한 프로토콜이다</p>
</blockquote>
<p>🔍 TCP = 연결지향 프로토콜 (http의 connectionless와 대비됨)</p>
<h2 id="✅-동작-방식-1">✅ 동작 방식</h2>
<img src =https://velog.velcdn.com/images/luv_lyn/post/199c55dc-683d-4a41-8138-aef3d5bafc9a/image.png width= 70%>

<p>Websocket의 동작은 <strong>1️⃣ handshake</strong> , <strong>2️⃣ Data transfer</strong>, <strong>3️⃣ Close Handshake</strong> 의 과정을 거친다
과정 별로 자세히 살펴보자 !</p>
<br>

<h2 id="📌-❶-handshake">📌 ❶ Handshake</h2>
<ul>
<li>처음 연결 시에는 HTTP 요청을 보낸다 <code>GET</code></li>
<li>이후 응답이 101 Switching Protocols면 WebSocket 연결 성공!</li>
<li><strong>🔍 port</strong><ul>
<li>ws://의 기본 포트는 80</li>
<li>wss://의 기본 포트는 443</li>
</ul>
</li>
</ul>
<blockquote>
<h3 id="✔️-request-예시">✔️ Request 예시</h3>
</blockquote>
<pre><code class="language-http">GET /ws-endpoint HTTP/1.1 
Host: server.example.com 
Upgrade: websocket ---------------------------&gt; ✅ 웹소켓 프로토콜로 변경할래
Connection: Upgrade ---------------------------&gt; ✅ Upgrade 헤더를 사용한다
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== 
Origin: http://example. com 
Sec-WebSocket-Protocol: chat, superchat ---------------------------&gt; ✅ 서브 프로토콜
Sec-WebSocket-Version: 13 </code></pre>
<p>✅ Upgrade, Connection 헤더 → 웹소켓 전환 요청임을 표시
✅ Sec-WebSocket-Key → 서버가 인증을 위해 확인하는 값</p>
<blockquote>
<hr>
</blockquote>
<h3 id="✔️-response-예시">✔️ Response 예시</h3>
<pre><code class="language-http">HTTP/1.1 101 Switching Protocols -------------------&gt; ✅ 웹소켓 연결 성공 (101)
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=</code></pre>
<ul>
<li>웹소켓 연결 성공은 <strong>상태코드 101</strong> (그 외의 코드 응답시 http 통신 유지)</li>
<li><strong><code>Sec-WebSocket-Accept</code> 작동 방식</strong><ul>
<li>서버는 <code>Sec-WebSocket-Key</code>에 고정 문자열<code>258EAFA5-E914-47DA-95CA-C5AB0DC85B11</code>을 붙이고</li>
<li>SHA-1 해시 → Base64 인코딩하여 응답함 👉 연결 위조 방지용 보안 절차<blockquote>
</blockquote>
</li>
</ul>
</li>
</ul>
<br>


<h2 id="📌-❷-data-transfer">📌 ❷ Data Transfer</h2>
<p>WebSocket은 <strong>양방향(bidirectional) 통신</strong>이 가능하며,
데이터는 메시지 단위로, 그 메시지는 <strong>프레임(Frame) 단위로 분할 전송</strong>된다</p>
<p>🔍 <code>Bidirectional WebSocket Message</code></p>
<ul>
<li>서버 ↔ 클라이언트 모두 메시지를 자유롭게 보낼 수 있음</li>
<li>메시지를 프레임으로 나눠 보내는 구조</li>
</ul>
<h3 id="✔️-websocket-frame-구조">✔️ Websocket Frame 구조</h3>
<pre><code>[WebSocket 메시지]
 └── 하나 이상의 프레임으로 구성됨
       └── 각 프레임은 다음을 포함:
           - FIN (메시지의 끝인지)
           - Opcode (텍스트인지, 바이너리인지 등)
           - Masking key (보안용)
           - Payload (실제 데이터)</code></pre><p><strong>타입, 길이, 실제 데이터(payload) 등을 포함한다 !</strong>
.... 만약 조금 더 자세히 웹소켓의 프레임을 살펴보면 다음과 같다 
( <del>여기서부턴 진짜 안봐도 된다</del> 나는 그냥 실제 프레임 모습이 궁금했다 )</p>
<blockquote>
<p><strong>💡 Websocket Frame</strong></p>
</blockquote>
<pre><code class="language-http"> 0               1               2               3
 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S| (4)   |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 4               5               6               7
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
 8               9               10              11
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
 12              13              14              15
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+</code></pre>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<ul>
<li><strong>FIN</strong> : 마지막 메세지임을 나타냄 / FIN = 0이면 다음 메세지 있음, FIN = 1이면 전체메세지 도착완료</li>
<li><strong>opcode</strong> : payload의 데이터 포맷을 나타냄 <ul>
<li>0x0은 continuation, 0x1은 텍스트(UTF-8), 0x2 바이너리 데이터...</li>
</ul>
</li>
<li><strong>MASK</strong> : 메세지의 마스킹 여부 / 클라이언트 - 서버로 가는 메세지는 항상 마스킹되어야..</li>
<li><strong>payload</strong> : 보내고자 하는 실제 데이터</li>
<li><strong>payload length</strong> : 페이로드의 길이<blockquote>
</blockquote>
</li>
<li><em>🔍 opcode 0x0 ?*</em>
하나의 메세지를 여러 프레임으로 나눠 전송할 때 사용! 
이거 n개의 메세지 중 중간이야! 뒤에 더 있음 근데 한 메세지임~을 표시</li>
</ul>
<br>

<h3 id="✔️-ping-pong으로-연결-상태-확인">✔️ ping pong으로 연결 상태 확인</h3>
<ul>
<li>ping : Websocket 연결 중이라면 클라이언트는 ping을 보낼 수 있음 <code>opcode = 0x9</code></li>
<li>pong : 서버는 ping을 받으면 payload를 그대로 담아 pong을 보냄 <code>opcode = 0xA</code></li>
<li><em>🌟 ping pong을 활용해 웹소켓 연결의 Open-Close 여부를 주기적으로 확인 가능*</em></li>
</ul>
<br>

<h3 id="➕-실제-코드-예시로-이해하기">➕ 실제 코드 예시로 이해하기</h3>
<ul>
<li>Websocket은 양방향 메세지를 <strong>send()</strong> 메서드로 보내고,</li>
<li><strong>onmessage</strong>나 <strong>handleTextMessage()</strong> 같은 <strong>이벤트 핸들러</strong>에서 수신 메세지를 처리한다</li>
</ul>
<blockquote>
</blockquote>
<p><strong>클라이언트 → 서버로 메시지 보내기</strong></p>
<pre><code class="language-javascript">const socket = new WebSocket(&quot;ws://localhost:8080/ws-endpoint&quot;);
socket.send(&quot;Hello, Server&quot;); // 메시지 전송
socket.onmessage = (event) =&gt; {
  console.log(&quot;Message from Server:&quot;, event.data); // 서버로부터 메시지를 받으면 실행되는 리스너
};</code></pre>
<blockquote>
</blockquote>
<p><strong>서버 → 클라이언트로 응답 처리 (Spring WebSocket)</strong></p>
<pre><code class="language-java">@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    String payload = message.getPayload(); // 클라이언트가 보낸 메시지 추출
    session.sendMessage(new TextMessage(&quot;Received: &quot; + payload)); // 응답 보내기
}</code></pre>
<blockquote>
</blockquote>
<p><strong>❗ 다수의 클라이언트에게 브로드캐스트하려면?</strong></p>
<pre><code class="language-java">for (WebSocketSession sess : sessions) {
    sess.sendMessage(new TextMessage(&quot;Broadcast: &quot; + payload));
}</code></pre>
<br>

<h2 id="📌-❸-close">📌 ❸ Close</h2>
<ul>
<li>Websocket 연결 종료를 의미한다 <code>opcode = 0x8</code></li>
<li>클라이언트, 서버 어느 쪽이든 종료를 요청할 수 있다.</li>
<li>Websocket에도 상태코드가 존재하지만, 연결 종료시 (Close Frame)에서만 쓰인다!
( 최초 Handshake시 성공 응답인 101은 Http status code임 )</li>
</ul>
<blockquote>
<p>📎<a href="https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1"><strong>Websocket Close Status Code</strong></a>
1000 : 정상 종료
1001 : 연결 주체 중 한쪽 소실
1009 : 메세지가 커서 처리 실패
1011 : 서버의 비정상 에러 발생</p>
</blockquote>
<hr>
<h2 id="🚫-websocket의-한계점">🚫 Websocket의 한계점</h2>
<h3 id="1-데이터-해석의-어려움">1. 데이터 해석의 어려움</h3>
<ul>
<li>payload에 실제 데이터를 담아 전송하지만, 일정한 형식(json, xml 등,,,)이 정해져 있지않다</li>
<li>따라서 서버나 클라이언트에서는 문자열을 해석하는 작업(<code>파싱</code>)이 필요한데</li>
</ul>
<p><strong>📌 해결책</strong> : <strong>서브 프로토콜</strong>을 사용한다 (대표적인 하위 프로토콜 : stomp)</p>
<h3 id="2-연결-유실-및-호환성-이슈">2. 연결 유실 및 호환성 이슈</h3>
<ul>
<li>일부 브라우저는 WebSocket의 Upgrade 헤더를 인식하지 못하거나</li>
<li>오랜 유휴상태일 TCP 연결을 조기 종료시킬 수 있음
➝ <strong>HTTP와 달리 WebSocket은 기존 인프라와 완전히 호환되지 않을 수 있다</strong></li>
</ul>
<p><strong>📌 해결책</strong> : SockJS(스프링&gt;stomp) , Socket.io(노드)로 웹소켓 미지원환경에서 우회 연결 시도할 수 있음</p>
<br>
<br>

<blockquote>
<p>🙋🏻‍♀️이렇게 실시간 통신을 위한 몇가지 기술들을 살펴봤다
<strong>SSE</strong>는 실시간 통신이 필요한 서비스에서 Polling을 대체할 수 있는 간단하고 강력한 옵션이지만, 
양방향 통신이나 고급 메시징 구조가 필요하다면 <strong>WebSocket</strong> 기반으로 넘어가는 것이 일반적이다.
따라서 나는 <strong>Websocket</strong> + 서브 프로토콜인 <strong>STOMP</strong>를 사용했다</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌼Spring JPA 심화 ❶ - H2 엔진,  DB 드라이버]]></title>
            <link>https://velog.io/@luv_lyn/Spring-JPA-%EC%8B%AC%ED%99%94-H2-%EC%97%94%EC%A7%84-DB-%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B2%84</link>
            <guid>https://velog.io/@luv_lyn/Spring-JPA-%EC%8B%AC%ED%99%94-H2-%EC%97%94%EC%A7%84-DB-%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B2%84</guid>
            <pubDate>Wed, 14 May 2025 05:10:06 GMT</pubDate>
            <description><![CDATA[<h1 id="📘-db-생성하기">📘 DB 생성하기</h1>
<blockquote>
<p>💡 <strong>의존성 옵션</strong> (build.gradle &gt; dependencies)</p>
</blockquote>
<ul>
<li><strong><code>implemenataion</code> 옵션</strong><ul>
<li>직접적인 의존성을 추가할 때 사용</li>
<li>프로젝트 컴파일 시 필요하지만, 외부로 공개 ❌</li>
</ul>
</li>
<li><strong><code>runtimeOnly</code> 옵션</strong><ul>
<li>런타임 시점에 필요한 라이브러리</li>
</ul>
</li>
<li><strong><code>testImplementation</code> 옵션</strong> <ul>
<li>테스트 코드를 수행할 때 적용할 라이브러리</li>
</ul>
</li>
</ul>
<h1 id="📖-h2">📖 H2</h1>
<blockquote>
<p>💡Java 기반의 RDBMS(Database Engine) 즉, <strong>DB 엔진</strong>
  SQL을 사용하고, JDBC로 연결할 수 있는 관계형 데이터베이스 시스템</p>
</blockquote>
<h2 id="📌-db-사용방식--h2-">📌 DB 사용방식 ( H2 )</h2>
<h3 id="1️⃣-server-mode">1️⃣ Server Mode</h3>
<ul>
<li>애플리케이션과 상관없는 <strong>외부에서 DB엔진 구현</strong></li>
<li>애플리케이션 / 데이터가 분리되어 있으므로 서로 영향 ❌</li>
<li>[H2 Database 엔진 다운로드하기 (배포할때 사용해보자 ^^)] (<a href="https://www.h2database.com/html/main.html">https://www.h2database.com/html/main.html</a>)</li>
</ul>
<h3 id="2️⃣-in-memory-mode">2️⃣ In memory Mode</h3>
<ul>
<li>엔진 별도 설치 ❌, 애플리케이션 내부 엔진 사용
( <code>build.gradle</code> or <code>application.properties</code> 설정)</li>
<li>데이터는 애플리케이션 <strong>내부 메모리</strong>에 저장! 
➝ <strong>앱 종료시 DB 날아감</strong></li>
</ul>
<h3 id="3️⃣-embedded-mode">3️⃣ Embedded Mode</h3>
<ul>
<li>엔진 별도 설치 ❌, 애플리케이션 내부 엔진 사용
( <code>build.gradle</code> or <code>application.properties</code> 설정)</li>
<li>데이터는 애플리케이션 <strong>외부</strong>에 저장! 
➝ <strong>앱 종료해도 DB 안날아감</strong></li>
</ul>
<br>

<table>
<thead>
<tr>
<th align="center">Mode</th>
<th align="center">H2 다운로드 여부</th>
<th align="center">실행 주체</th>
<th align="center">DB 저장 위치</th>
<th align="center">사용 용도</th>
</tr>
</thead>
<tbody><tr>
<td align="center">✅ <strong>Server Mode</strong></td>
<td align="center">O</td>
<td align="center">외부</td>
<td align="center">로컬<br>(파일 시스템)</td>
<td align="center">배포 용도</td>
</tr>
<tr>
<td align="center">✅ <strong>In-Memory Mode</strong></td>
<td align="center">X</td>
<td align="center">스프링</td>
<td align="center">메모리</td>
<td align="center">테스트 용도</td>
</tr>
<tr>
<td align="center">✅ <strong>Embedded Mode</strong></td>
<td align="center">X</td>
<td align="center">스프링</td>
<td align="center">로컬<br>(파일 시스템)</td>
<td align="center">개발 용도</td>
</tr>
</tbody></table>
<br>

<h2 id="📌-실습하기---embedded-mode">📌 실습하기 - Embedded Mode</h2>
<h3 id="❶-파일-설정하기">❶ 파일 설정하기</h3>
<blockquote>
<p>💡 <code>jbbc:h2:~/test</code> : <code>~</code> 은 홈디렉토리, 홈디렉토리의 test에 저장된다</p>
<p>✔️ <strong><code>application.yml</code></strong></p>
</blockquote>
<pre><code class="language-java">spring:  
    datasource:    
        driver-class-name: org.h2.Driver
        ✅ url: jdbc:h2:mem:{DB 이름} ----&gt; 2️⃣ In-Memory Mode
        ✅ url: jdbc:h2:{DB가 저장될 경로} ----&gt; 3️⃣ Embedded Mode
        username: sa
        password:  </code></pre>
<blockquote>
<hr>
<p>✔️ <strong><code>application.properties</code></strong></p>
</blockquote>
<pre><code class="language-java"># application.properties
spring.datasource.driver-class-name=org.h2.Driver
✅ spring.datasource.url=jdbc:h2:mem:{DB 이름} ----&gt; 2️⃣ In-Memory Mode
✅ spring.datasource.url=jdbc:h2:{DB가 저장될 경로} ----&gt; 3️⃣ Embedded Mode
spring.datasource.username=sa
spring.datasource.password=</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/cb70dfbb-932a-450e-b322-38c404ba43d0/image.png" alt=""></p>
<h3 id="❷-의존성-등록하기">❷ 의존성 등록하기</h3>
<blockquote>
<p>✔️ <strong><code>build.gradle</code></strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/bf39951c-a9b5-4e70-b85e-58d73754677a/image.png" alt=""></p>
<h3 id="❸-콘솔-띄우기">❸ 콘솔 띄우기</h3>
<blockquote>
<img src='https://velog.velcdn.com/images/luv_lyn/post/94e3bbf1-03cb-4a9c-b28e-09a836af812a/image.png' width=50%>

</blockquote>
<p>➝ <code>Path</code> 참고해서 콘솔 url 접속</p>
<blockquote>
</blockquote>
<img src='https://velog.velcdn.com/images/luv_lyn/post/d176b798-00d8-48fe-9349-aabd0235ba0b/image.png' width=50%>
>
➝ properties 설정과 똑같이 작성하기 (`url`, `username`, `password`...)
>
<img src='https://velog.velcdn.com/images/luv_lyn/post/3d216614-d810-452e-b01e-b4c840e5fe77/image.png' width=80%>
>
>
>>💡 **In-Memory 모드에서 콘솔 조회가 안되는 경우**
`#url: 'jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE'`

<br>

<hr>
<h1 id="sql">SQL</h1>
<h2 id="ddl">DDL</h2>
<p>create table
alter table
drop table
TRUNCATE TABLE orders; -&gt; 데이터 초기화 ( 테이블 내용 지우기 )
TRUNCATE TABLE products;
TRUNCATE TABLE users;</p>
<h2 id="dml">DML</h2>
<p>데이터 찾기
select where 등..
order by group by
join</p>
<p>데이터 조작하기
insert into
update
delete from</p>
<h2 id="dcl">DCL</h2>
<h2 id="트랜젝션-관리">트랜젝션 관리</h2>
<hr>
<h1 id="db-드라이버">DB 드라이버</h1>
<blockquote>
<p>애플리케이션 - 데이터베이스 간의 통신을 중개하는 역할</p>
</blockquote>
<h2 id="동작-방식">동작 방식</h2>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/e76a1c6e-172e-4381-9207-df16e0c8c004/image.png" alt=""></p>
<ol>
<li>연결 초기화 - 드라이버 연결 요청 수신후 연결 완료</li>
<li>SQL 전송 및 실행 - SQL 쿼리를 변환하고 처리</li>
<li>결과 처리 - 결과 수신 후 변환된 결과 전달</li>
<li>연결 종료 - 다음 세션을 위해 시스템 초기화</li>
</ol>
<hr>
<h1 id="jdbc-실습">JDBC 실습</h1>
<blockquote>
<p>Spring Boot는 다양한 JDBC 드라이버를 지원한다</p>
</blockquote>
<p>-<code>spring-boot-starter-jdbc</code> 
: 스타터 패키지, 주요 의존성, 자동구성 기능이 포함되어 있음
: 코드 간소화, 자동 구성, <strong>예외처리</strong> ( JDBC에서 발생하는 예외 -&gt; Spring 예외체계로 변환)</p>
<ul>
<li>JDBC 드라이버
: 자바 애플리케이션에서 데이터베이스에 접근할 수 있도록 하는 API</li>
</ul>
<h2 id="jdbc-driver">JDBC Driver</h2>
<h3 id="try-with-resources란">try-with-resources란?</h3>
<p>✅ 자동으로 close()를 호출해주는 try 구문이야. (자원 해제)</p>
<p>JDBC에서 Connection, Statement, ResultSet 같은 객체는 반드시 닫아줘야 해.
그런데 실수로 connection.close()를 안 쓰거나, return 전에 빠지면 리소스 누수가 나지.</p>
<p>그래서 Java7부터는 AutoCloseable 인터페이스를 구현한 객체는 try 괄호 안에 넣으면 자동으로 close()해줘!</p>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/544b03d5-eabc-4ab3-b765-1e1c9decfb0f/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>구분</th>
<th>try-catch</th>
<th>try-with-resources</th>
</tr>
</thead>
<tbody><tr>
<td>목적</td>
<td>예외 처리</td>
<td>예외 처리 + 리소스 자동 반환</td>
</tr>
<tr>
<td>리소스 close</td>
<td>직접 명시해야 함 (<code>finally</code>에서)</td>
<td>자동 호출 (<code>close()</code> 호출됨)</td>
</tr>
<tr>
<td>코드 길이</td>
<td>더 길고 복잡함</td>
<td>짧고 안정적임</td>
</tr>
<tr>
<td>사용 조건</td>
<td>제한 없음</td>
<td><code>AutoCloseable</code> 구현 객체 필요</td>
</tr>
</tbody></table>
<h3 id="예제-코드">예제 코드</h3>
<pre><code class="language-java">@SpringBootApplication
public class JdbcApplication {

    public static void main(String[] args) throws SQLException {
        // 어플리케이션 실행 컨텍스트 생성
        SpringApplication.run(JdbcApplication.class, args);

        // ▶️ 데이터베이스 연결
        String url = &quot;jdbc:h2:mem:test&quot;;     // ✅in-memory 모드 H2 DB
        String username = &quot;sa&quot;;                // 사용자명

        // 🔹 DriverManager.getConnection()
        // - JDBC 드라이버를 통해 DB와 연결
        try (Connection connection = DriverManager.getConnection(url, username, null)) { // ✅ 파라미터로 연결 정보 넣어주기

            try {
                // ▶️ 테이블 생성
                // 🔹 connection.prepareStatement(String sql)
                // - SQL 문을 미리 컴파일된 상태로 준비 
                String creatSql = &quot;CREATE TABLE USERS (id SERIAL, username varchar(255))&quot;;
                try (PreparedStatement statement = connection.prepareStatement(creatSql)) {
                    // 🔹 statement.execute()
                    // - 컴파일 된 SQL문을 실행
                    statement.execute();
                }

                // ▶️ 데이터 추가
                String insertSql = &quot;INSERT INTO USERS (username) VALUES (&#39;teasun kim&#39;)&quot;;
                try (PreparedStatement statement = connection.prepareStatement(insertSql)) {
                    statement.execute();  
                }

                // ▶️ 데이터 조회
                String selectSql = &quot;SELECT * FROM USERS&quot;;
                try (PreparedStatement statement = connection.prepareStatement(selectSql)) {
                    // 🔹 statement.executeQuery()
                    // - SELECT 문 실행
                    var rs = statement.executeQuery();

                    // 🔹 rs.next()
                    while (rs.next()) {
                        // 🔹 rs.getInt(&quot;컬럼명&quot;)
                        // 🔹 rs.getString(&quot;컬럼명&quot;)
                        System.out.printf(&quot;%d, %s&quot;, rs.getInt(&quot;id&quot;), rs.getString(&quot;username&quot;));
                    }
                }
            } catch (SQLException e) {
                // 예외 메시지를 문자열로 비교해 예외 상황을 분기 처리
                if (e.getMessage().equals(&quot;ERROR: relation \&quot;account\&quot; already exists&quot;)) {
                    System.out.println(&quot;USERS 테이블이 이미 존재합니다.&quot;);
                } else {
                    // 그 외 예외는 런타임 예외로 감싸서 던짐
                    throw new RuntimeException();
                }
            }
        }
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌼 Spring 심화 ③ - JPQL 기초, fetch join]]></title>
            <link>https://velog.io/@luv_lyn/Spring-%EC%8B%AC%ED%99%94-JPQL-%EA%B8%B0%EC%B4%88-fetch-join</link>
            <guid>https://velog.io/@luv_lyn/Spring-%EC%8B%AC%ED%99%94-JPQL-%EA%B8%B0%EC%B4%88-fetch-join</guid>
            <pubDate>Tue, 13 May 2025 05:47:07 GMT</pubDate>
            <description><![CDATA[<h1 id="📘-jpql">📘 JPQL</h1>
<blockquote>
<ul>
<li><strong>특징</strong><ul>
<li>대상은 객체 (테이블이 아님)</li>
<li>영속성 컨택스트 활용 가능</li>
<li>컴파일 시점에 엔티티-필드타입 확인 후 오류 감소</li>
</ul>
</li>
</ul>
</blockquote>
<hr>
<h1 id="📖-jpql-문법">📖 JPQL 문법</h1>
<h2 id="📌-결과-조회">📌 결과 조회</h2>
<h3 id="▶️-타입-정하기">▶️ 타입 정하기</h3>
<blockquote>
<p>❶ <code>TypeQuery</code></p>
</blockquote>
<ul>
<li>타입이 명확할때 ! (주로 엔티티나 특정 타입이 있을때)</li>
<li>주로 사용한다!<blockquote>
</blockquote>
<pre><code class="language-java">TypedQuery&lt;Tutor&gt; typeQuery1 = em.createQuery(&quot;select t from Tutor t&quot;, Tutor.class);
TypedQuery&lt;String&gt; typeQuery2 = em.createQuery(&quot;select t.name from Tutor t&quot;, String.class);</code></pre>
<blockquote>
<hr>
<p>❷<code>Query</code></p>
</blockquote>
</li>
<li>반환 타입 명확하지 않을때 ( = 다양한 타입의 데이터 반환)</li>
<li>Object로 반환하므로 형변환 필요할 수도<blockquote>
</blockquote>
<pre><code class="language-java">Query query = em.createQuery(&quot;select t.name, t.age from Tutor t&quot;);</code></pre>
</li>
</ul>
<h3 id="▶️-결과-조회">▶️ 결과 조회</h3>
<blockquote>
<p>❶ <code>getResultList()</code></p>
</blockquote>
<ul>
<li>결과가 하나 이상일 때 사용한다.</li>
<li><code>List</code> 반환 (결과가 없다면 빈 <code>List</code> 반환)<pre><code class="language-java">List resultList = em.createQuery(&quot;select t from Tutor t&quot;).getResultList();</code></pre>
<blockquote>
<hr>
<p>❷ <code>getSingleResult()</code></p>
</blockquote>
</li>
<li>결과가 딱 하나일때</li>
<li>쿼리 뒤에 엔티티 클래스를 명시해준다 (반환타입)<pre><code class="language-java">Tutor singleResult = em.createQuery(&quot;select t from Tutor t where t.id = 1L&quot;, Tutor.class).getSingleResult();</code></pre>
</li>
</ul>
<hr>
<h2 id="📌-파라미터-바인딩">📌 파라미터 바인딩</h2>
<blockquote>
<p>동적으로 값을 전달한다, 쿼리 재사용 가능</p>
</blockquote>
<pre><code class="language-java">Tutor wonuk = em.createQuery(&quot;select t from Tutor t where t.name = ✅:name&quot;, Tutor.class)
                    .setParameter(&quot;name&quot;, &quot;wonuk&quot;)✅파라미터 지정
                    .getSingleResult();
System.out.println(&quot;wonuk.getName() = &quot; + wonuk.getName());
System.out.println(&quot;wonuk.getAge() = &quot; + wonuk.getAge());</code></pre>
<hr>
<h2 id="📌-embedded--embeddedable">📌 @Embedded / @Embeddedable</h2>
<p>▶️ JPA에서 <strong>여러 필드를 하나의 값 객체로 묶어</strong> 사용하는 어노테이션</p>
<blockquote>
<p>✅<code>@Embeddable</code> - 값을 정의하는 쪽. 내장될 클래스 (예: <code>Period</code>, <code>Address</code>) 
✅ <code>@Embedded</code> - 값을 사용하는 쪽. 엔티티에 포함시키는 필드 (예: <code>Tutor.period</code>)</p>
</blockquote>
<p>▶️  <strong>상황 예시</strong></p>
<table>
<thead>
<tr>
<th>상황</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>값들을 <strong>한 덩어리로 표현하고 싶을 때</strong></td>
<td>기간(시작~종료), 주소(시/도/우편번호), 이름(성/이름) 등</td>
</tr>
<tr>
<td>여러 엔티티에서 <strong>중복된 값 구조가 나올 때</strong></td>
<td>예: Period를 Tutor, Employee에서 함께 쓰고 싶을 때</td>
</tr>
<tr>
<td>값 자체의 <strong>비즈니스 로직을 포함할 수 있게 하고 싶을 때</strong></td>
<td><code>Period.isWork(Date)</code> 처럼 해당 값만의 메서드 만들 수 있음</td>
</tr>
</tbody></table>
<blockquote>
<p>❶ <strong><code>@Embedded</code> 사용</strong></p>
</blockquote>
<pre><code class="language-java">@Entity
public class Tutor {
&gt;        
        @Id
        @GeneratedValue
        private Long id;
&gt;        
        private String name;
&gt;        
        @Embedded // ✅ Embedded 사용
        private Period period;
&gt;
}</code></pre>
<blockquote>
<hr>
<p>❷ <strong><code>@Embeddable</code> 사용</strong></p>
</blockquote>
<pre><code class="language-java">@Embeddable// ✅ Embedded 정의
public class Period {
&gt;  
  @Temporal(TemporalType.DATE) &lt;--- ✅ 날짜 매핑에 사용
  Date startDate;
&gt;  
  @Temporal(TemporalType.Date)
  Date endDate;
&gt;  
  public boolean isWork (Date date) { &lt;--- ✅ 자체 검증 로직
        // startDate &lt;= date &lt;= endDate 확인
      return (startDate == null || !date.before(startDate)) &amp;&amp;
             (endDate == null || !date.after(endDate));
  }
}</code></pre>
<blockquote>
<hr>
<p>❸ <strong>데이터 조회</strong></p>
</blockquote>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>start_date</th>
<th>end_date</th>
</tr>
</thead>
</table>
<blockquote>
</blockquote>
<img src='https://velog.velcdn.com/images/luv_lyn/post/ae3a4c8e-b3ad-4d3b-85ed-3861570ee920/image.png' width = 50% >
>
- tutor 테이블에 `period` 가 아닌, 
period 내 필드인 `startDate`, `endDate`가 나온다

<hr>
<h2 id="📌-프로젝션projection">📌 프로젝션(Projection)</h2>
<p>▶️ 쿼리에서 어떤 데이터를 가져올 지 선택하는 것 ( <code>select</code> 어떤거 할지? )</p>
<p>▶️ 왜 사용하는가?</p>
<ul>
<li>모든 데이터를 다 가져오는 건 비효율적</li>
<li>필요한 필드만 조회하면 성능 최적화 가능</li>
<li>네트워크 비용 절감, 조회 속도 향상</li>
</ul>
<h3 id="❶-entity-프로젝션">❶ Entity 프로젝션</h3>
<blockquote>
<ul>
<li><strong>엔티티 전체를 통째로 조회하는 방식</strong></li>
</ul>
</blockquote>
<ul>
<li><p>조회된 객체는 <strong>영속성 컨텍스트</strong>에 의해 관리된다 (Dirty Checking 가능)</p>
<blockquote>
</blockquote>
<pre><code class="language-java">Tutor tutor = new Tutor(&quot;wonuk&quot;, 100);
em.persist(tutor);
&gt;
// 영속성 컨텍스트 초기화 → 조회한 tutor는 더 이상 관리되지 않음
em.flush();
em.clear();
&gt;
// Tutor 엔티티 전체 조회
List&lt;Tutor&gt; tutorList = em.createQuery(&quot;select t from Tutor t&quot;, Tutor.class).getResultList();
Tutor wonuk = tutorList.get(0);
&gt;
// 엔티티가 다시 영속성 컨텍스트에 들어와서 변경 감지 가능
wonuk.setName(&quot;wonuk2&quot;);</code></pre>
</li>
<li><p>** 🧷 연관 엔티티 조회: 묵시적 vs 명시적 JOIN**</p>
<blockquote>
<pre><code class="language-java">// 묵시적 JOIN → JPQL에서는 작성하지 않았지만 내부적으로 JOIN 발생
Company company = em.createQuery(
  &quot;select t.company from Tutor t&quot;, Company.class
).getSingleResult();
</code></pre>
</blockquote>
<p>// 명시적 JOIN → 쿼리에서 JOIN을 명확히 선언
Company companyV2 = em.createQuery(
  &quot;select t from Tutor t join t.company&quot;, Company.class
).getSingleResult();</p>
<blockquote>
<pre><code></code></pre></blockquote>
<ul>
<li>💡 <strong>묵시적 JOIN은 SQL을 예측하기 어려워 위험함</strong>
→ <strong>명시적으로 JOIN</strong>을 선언하는 것이 좋다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="❷-embedded-프로젝션">❷ Embedded 프로젝션</h3>
<blockquote>
</blockquote>
<pre><code class="language-java">List&lt;Period&gt; periods = em.createQuery(
    &quot;select t.period from Tutor t&quot;, Period.class).getResultList();
    &gt;
* `select p from Period p` → ❌ 불가능
* Embedded 객체는 독립적인 엔티티가 아니기 때문</code></pre>
<blockquote>
<hr>
<p>✔️ 조건 검색 예시</p>
</blockquote>
<pre><code class="language-java">// Embedded 내부 필드 접근 가능
&quot;select t from Tutor t where t.period.startDate &lt; :today&quot;</code></pre>
<blockquote>
</blockquote>
<p>✔️ 상속 구조 → 직관적</p>
<pre><code class="language-java">&quot;select t from Tutor t where t.startDate &lt; :today&quot;
// → 이건 period 없이 바로 접근 가능 (MappedSuperclass의 경우)</code></pre>
<hr>
<h3 id="❸-scalar-프로젝션">❸ Scalar 프로젝션</h3>
<blockquote>
<ul>
<li>엔티티가 아닌 <strong>단일 필드(문자열, 숫자 등)</strong> 또는 <strong>여러 필드</strong>만 조회할 때</li>
</ul>
</blockquote>
<pre><code class="language-java">List&lt;Object[]&gt; resultList = em.createQuery(
    &quot;select t.name, t.age from Tutor t&quot;
).getResultList();
&gt;
Object[] result = resultList.get(0);
System.out.println(&quot;이름: &quot; + result[0]);  // name
System.out.println(&quot;나이: &quot; + result[1]);  // age</code></pre>
<p>📌 결과는 <code>Object[]</code> 배열로 리턴되므로, <strong>형변환</strong> 필요</p>
<ul>
<li>💡 확장 → DTO 프로젝션<blockquote>
</blockquote>
<pre><code class="language-java">// DTO에 생성자 필요: public TutorDto(String name, int age)
List&lt;TutorDto&gt; dtos = em.createQuery(
  &quot;select new com.example.TutorDto(t.name, t.age) from Tutor t&quot;, TutorDto.class ).getResultList();</code></pre>
<blockquote>
<p>Scalar 프로젝션을 더 명확하게 사용하고 싶다면 <strong>DTO에 직접 매핑</strong>하는 방법이 가장 깔끔하고 안전</p>
</blockquote>
</li>
</ul>
<hr>
<h2 id="📌-페이징paging">📌 페이징(Paging)</h2>
<p>▶️ JPQL은 기본적으로 <code>LIMIT</code>, <code>OFFSET</code> 같은 SQL 문법을 직접 쓰지 않고
<strong>JPA가 제공하는 메서드로 페이징 처리를 한다.</strong></p>
<blockquote>
<p>▶️ <strong>사용 메서드</strong></p>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>setFirstResult(int)</code></td>
<td>조회 시작 위치 (OFFSET)</td>
</tr>
<tr>
<td><code>setMaxResults(int)</code></td>
<td>가져올 데이터 개수 (LIMIT)</td>
</tr>
</tbody></table>
<hr>
</blockquote>
<pre><code class="language-java">List&lt;Tutor&gt; tutorList = em.createQuery(
    &quot;select t from Tutor t order by t.age desc&quot;, Tutor.class
)
.setFirstResult(5)     // 6번째 데이터부터 조회 (OFFSET = 5)
.setMaxResults(10)     // 총 10개 조회 (LIMIT = 10)
.getResultList();
&gt;
for (Tutor tutor : tutorList) {
    System.out.println(&quot;tutor = &quot; + tutor.getId() + &quot;, &quot; + tutor.getName() + &quot;, &quot; + tutor.getAge());
}</code></pre>
<ul>
<li><code>order by t.age desc</code>
→ 나이 순서대로 내림차순 정렬 (가장 나이 많은 튜터부터)</li>
<li><code>setFirstResult(5)</code>
→ <strong>6번째 데이터부터 시작</strong></li>
<li><code>setMaxResults(10)</code>
→ <strong>10개 데이터만 조회</strong></li>
</ul>
<br>

<hr>
<h1 id="📘-fetch-join">📘 fetch join</h1>
<blockquote>
<p>JPA에서의 <code>N+1</code> 문제를 해결한다</p>
</blockquote>
<hr>
<h1 id="📖-fetch-join-사용법">📖 fetch join 사용법</h1>
<h2 id="📌-entity-fetch-join-n1-11">📌 Entity fetch join (N:1, 1:1)</h2>
<p>▶️ <strong>지연로딩(LAZY)</strong> 설정된 연관 엔티티를 <strong>JOIN과 동시에 한 번에 가져오는 방법</strong></p>
<blockquote>
</blockquote>
<pre><code class="language-java">String query = &quot;select t from Tutor t join fetch t.company&quot;;</code></pre>
<blockquote>
</blockquote>
<p>❶ Tutor가 Company를 참조하고 있고,
❷ <code>t.getCompany().getName()</code> 로 접근하면 원래는 쿼리 또 날아가야하는데
❸ <code>fetch join</code>을 쓰면 <strong>그 전에 미리 Company까지 같이 가져온다</strong>
→ 쿼리 한 번으로 둘 다 가져오니까 N+1 문제 해결!</p>
<ul>
<li>✅ <strong>장점</strong>: 깔끔하게 한 번에 가져와서 성능 좋음</li>
<li>⚠️ <strong>주의</strong>: 무조건 다 들고오니까 필요 없을 땐 오히려 과하게 가져올 수도</li>
</ul>
<hr>
<h2 id="📌-collection-fetch-join">📌 Collection fetch join</h2>
<p>▶️ 한 엔티티가 여러 개를 갖고 있는 컬렉션(예: 회사 → 튜터 목록)을 <strong>한 번에 가져오는</strong> 방식</p>
<blockquote>
<pre><code class="language-java">String query = &quot;select c from Company c join fetch c.tutorList&quot;;</code></pre>
</blockquote>
<pre><code>&gt;
❶ Company 하나에 Tutor 여러 명 붙어있으면,
❷ SQL은 Tutor 수만큼 Company가 중복돼서 나온다
→ 근데 JPA는 알아서 같은 Company는 한 번만 만들어준다!

- ✅ **장점**: N+1 문제 없이 Company와 Tutor를 한 번에 다 가져옴
- ⚠️ **단점**: **페이징(setFirstResult, setMaxResults)** 안 됨
→ 전부 다 불러온 다음 자바 메모리에서 잘라버림 (데이터 많으면 위험)

---

## 📌 @BatchSize

▶️ **지연로딩은 유지하면서**, **한 번에 여러 개를 IN 쿼리로 가져오는 방식**

&gt;```java
@BatchSize(size = 100)
@OneToMany(mappedBy = &quot;company&quot;)
private List&lt;Tutor&gt; tutorList;</code></pre><blockquote>
</blockquote>
<p>❶ 예를 들어 Company 2개가 있을 때,
❷ 각 Company의 tutorList를 조회하려고 하면
❸ Tutor를 하나씩 쿼리하는 게 아니라
→ <strong>Tutor WHERE company_id IN (1, 2)</strong> 이런 쿼리로 한 번에!</p>
<p>✅ <strong>장점</strong>: 페이징 가능! 쿼리도 줄어듦
⚠️ <strong>주의</strong>: 한 번에 너무 많이 로딩하면 메모리 터질 수도 있으니 size 조절 중요</p>
<br>

<hr>
<h1 id="🌟-jpql-실습-코드">🌟 JPQL 실습 코드</h1>
<h2 id="--쿼리-예시">- 쿼리 예시</h2>
<blockquote>
<p>❶ Repository의 <strong>튜터별 학생 수 메서드</strong></p>
</blockquote>
<pre><code class="language-java">public interface TutorRepository extends JpaRepository&lt;Tutor, Long&gt; {
&gt;
    @Query(&quot;select new com.example.springjpql.dto.TutorStudentCountDto(t.name, count(s)) &quot; +
            &quot;from Tutor t join t.students s group by t.name&quot;)
    List&lt;TutorStudentCountDto&gt; findTutorStudentCounts();
}</code></pre>
<p>❷ Repository의 <strong>학생 조건부 검색 메서드</strong></p>
<pre><code class="language-java">@Query(&quot;select new com.example.springjpql.dto.StudentDto(s.name, s.age) &quot; +
        &quot;from Student s where s.name like %:keyword% &quot; +
        &quot;and s.age &gt;= :minAge and s.age &lt;= :maxAge&quot;)
List&lt;StudentDto&gt; findByNameContainingAndAgeBetween(
        @Param(&quot;keyword&quot;) String keyword,
        @Param(&quot;minAge&quot;) int minAge,
        @Param(&quot;maxAge&quot;) int maxAge
);</code></pre>
<h2 id="--테스트-데이터-추가하기">- 테스트 데이터 추가하기</h2>
<blockquote>
<p>❶ <strong>프로필 설정하기</strong> (<code>application.yml</code> )</p>
</blockquote>
<pre><code class="language-java">profiles:
  active: dev ✅</code></pre>
<blockquote>
<p>❷ <strong>테스트용 데이터 추가</strong> ( <code>config</code> 패키지 )</p>
</blockquote>
<ul>
<li><code>@PostConstruct</code> 로 Application 최초 실행 시에만 초기화 하도록 설정</li>
<li><code>@Profile(&quot;dev&quot;)</code> dev 프로필에서만 동작하도록 설정  <pre><code class="language-java">@Slf4j
@Component
@Profile(&quot;dev&quot;) ✅
public class DataInitializer {
&gt;
  @Autowired
  private TutorRepository tutorRepository;
&gt;
  @Autowired
  private StudentRepository studentRepository;
&gt;
  @PostConstruct ✅
  public void init() {
      // Tutor 데이터 초기화
      Tutor tutor1 = new Tutor(&quot;tutor1&quot;);
      Tutor tutor2 = new Tutor(&quot;tutor2&quot;);
      Tutor tutor3 = new Tutor(&quot;tutor3&quot;);
&gt;
      tutorRepository.save(tutor1);
      tutorRepository.save(tutor2);
      tutorRepository.save(tutor3);
&gt;
      // Student 데이터 초기화
      for (int i = 0; i &lt; 30; i++) {
          Student student = new Student(&quot;student&quot; + i, 20 + i);
          // 튜터를 순차적으로 할당
          if (i % 3 == 0) {
              student.setTutor(tutor1);
          } else if (i % 3 == 1) {
              student.setTutor(tutor2);
          } else {
              student.setTutor(tutor3);
          }
          studentRepository.save(student);
      }
      log.info(&quot;===== Test Data Initialized =====&quot;);
  }
}</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[🛵 아웃소싱 프로젝트 - 배달 어플 만들기 ❶]]></title>
            <link>https://velog.io/@luv_lyn/%EA%B3%BC%EC%A0%9C%EB%A5%BC-%ED%95%98%EC%9E%90</link>
            <guid>https://velog.io/@luv_lyn/%EA%B3%BC%EC%A0%9C%EB%A5%BC-%ED%95%98%EC%9E%90</guid>
            <pubDate>Thu, 24 Apr 2025 16:07:27 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="📖-개요">📖 개요</h1>
<blockquote>
<p>이번 팀플 과제에서 내가 맡게된 부분은 <strong>주문/장바구니</strong>
필수단계에는 주문까지 구현이고, 장바구니는 도전 과제이지만
처음부터 <strong>우리는 장바구니를 같이 구현하는 것을 목표</strong>로 설계했다 !</p>
</blockquote>
<hr>
<h2 id="▶️-요구사항과-설계-내용-정리">▶️ 요구사항과 설계 내용 정리</h2>
<h3 id="✔️-주문">✔️ 주문</h3>
<ul>
<li>고객은 메뉴 여러개를 주문할 수 있다</li>
<li>사장님은 주문을 수락할 수 있고, 배달 완료까지의 모든 상태를 순서대로 변경한다</li>
<li>주문 요청 및 상태 변경<ul>
<li>새로운 주문이거나 주문 상태가 변경될 때는 <code>AOP</code>로 로그를 남긴다</li>
<li>로그 내용 - <code>요청 시각</code>,<code>가게 id</code>,<code>주문 id</code></li>
</ul>
</li>
<li>제약조건<ul>
<li>가게의 최소 주문금액을 만족해야 한다</li>
<li>가게의 오픈/마감시간이 지나면 주문할 수 없다</li>
</ul>
</li>
</ul>
<h3 id="✔️-장바구니">✔️ 장바구니</h3>
<ul>
<li>한 가게의 메뉴만 담을 수 있고, 가게가 변경되면 장바구니는 초기화 된다</li>
<li>장바구니는 마지막 업데이트 시점으로부터 최대 하루만 유지된다</li>
</ul>
<hr>
<h1 id="📖-설계">📖 설계</h1>
<hr>
<h2 id="1️⃣-연관관계-설정">1️⃣ 연관관계 설정</h2>
<h3 id="✔️-연관관계-비교하기">✔️ 연관관계 비교하기</h3>
<ul>
<li><p><strong>🅰️ / 튜터님</strong></p>
<blockquote>
</blockquote>
<p><code>User</code> 1 : N <code>Cart</code> 1 : 1 <code>Menu</code>
유저는 여러 개의 Cart 항목을 가질 수 있음</p>
<blockquote>
<pre><code>User
├─ Cart1 (Menu: 짬뽕, Quantity: 1)
├─ Cart2 (Menu: 탕수육, Quantity: 2)
└─ Cart3 (Menu: 볶음밥, Quantity: 1)</code></pre></blockquote>
<ul>
<li>각 Cart는 메뉴 하나만 담고 있음 , 수량은 Cart에 저장</li>
<li>복수의 메뉴를 담고 싶으면? → Cart를 여러 개 만들면 됨</li>
</ul>
</li>
<li><p><strong>🅱️안 / 내가 최초 생각한 연관관계</strong></p>
<blockquote>
</blockquote>
<p><code>User</code> 1 : 1 <code>Cart</code> 1 : N <code>Menu</code>
유저는 Cart 딱 하나만 가짐</p>
<blockquote>
<pre><code>User
└─ Cart1
     ├─ CartMenu (Menu: 짬뽕, Quantity: 2)
     └─ CartMenu (Menu: 탕수육, Quantity: 1)</code></pre></blockquote>
<ul>
<li>Cart 안에 메뉴들이 여러 개 담겨 있음</li>
<li>&quot;장바구니 하나 안에 메뉴가 여러 개, 거기에 수량까지 포함된 테이블&quot;이 들어있는 구조</li>
</ul>
</li>
</ul>
<hr>
<h3 id="✔️-a안의-장점">✔️ A안의 장점</h3>
<p><strong>1. 데이터 정규화가 깔끔함</strong></p>
<ul>
<li>Cart가 메뉴 항목 + 수량으로 분리돼 있음
→ Cart만 보면 어떤 메뉴가 몇 개 담겼는지 쉽게 알 수 있음
→ 메뉴 + 수량 단위로 계산, 조회, 삭제가 아주 편해!</li>
</ul>
<p><strong>2. 주문 생성 로직이 깔끔해짐</strong></p>
<ul>
<li>이 구조에선 <strong>같은 주문 ID로 여러 Cart를 묶기</strong> 만 하면 끝
→ 주문 생성을 위해 <strong>메뉴 리스트 분해가 필요 없음</strong></li>
</ul>
<p><strong>3. 확장성이 좋음</strong></p>
<ul>
<li>나중에 옵션, 리뷰, 쿠폰 등 기능 확장할 때도 편리할 것
→ CartItem 단위로 복잡한 로직을 분리할 수 있음</li>
</ul>
<h3 id="✔️-b안의-장점">✔️ B안의 장점</h3>
<ol>
<li><p>Cart가 한 유저당 1개로 고정됨 
→ 메뉴 1개만 빼려면 <code>List&lt;Menu&gt;</code>에서 꺼내서 수량 갱신하고 다시 저장해야 함</p>
</li>
<li><p>Cart가 메뉴 리스트를 직접 갖고 있음 → 중간 테이블 필요
→ 메뉴와 수량을 따로 저장하려면 중간 엔티티가 필요해짐 </p>
</li>
<li><p>비즈니스 로직이 엉켜버릴 수 있음
→ 장바구니에서 메뉴 수량 늘리기, 줄이기, 삭제하기 모두 복잡한 로직을 요구함</p>
</li>
</ol>
<blockquote>
<h3 id="🌟-이해한-내용-요약-정리">🌟 이해한 내용 요약 정리</h3>
<p><strong>A안</strong> - cart1, cart2, cart3은 각각 짬뽕 카트, 탕수육 카트, 볶음밥 카트
<strong>B안</strong> - cart1, cart2, cart3은 각각 수린이 카트, 광수 카트, 영식이 카트</p>
</blockquote>
<p><em>수린이 카트에 탕수육, 짬뽕을 담을 수는 있는데 
각각의 수량을 정하려면 엔티티가 하나 더 있어서 그 엔티티에 (메뉴이름,메뉴수량) 을 담아서 구현해야한다</em></p>
<blockquote>
<hr>
<p>✔️ A안의 <code>Cart1,2,3</code>은 메뉴 하나씩 담긴 독립적인 항목<br>✔️ B안은 <code>Cart1</code>이 유저 전체 장바구니 역할, 안에 메뉴와 수량 따로 들어가야 해서 <strong>중간 엔티티</strong> 필요<br>✔️ 그래서 실무에서는 A안을 쓰고, 필요 시 <code>CartGroup</code>, <code>Order</code>, <code>CartItem</code>을 붙여서 확장하는 방식 사용</p>
</blockquote>
<hr>
<h2 id="2️⃣-응답">2️⃣ 응답</h2>
<ul>
<li><strong>가게의 최소주문금액을 충족했을 때만 주문이 가능하다</strong>는 조건을 보고 더 생각을 해보니
사용자 입장에서는 장바구니에 담을때마다 현재 가격 + 주문이 가능한지 나와야 하지 않을까?</li>
</ul>
<h3 id="✔️-api-명세서-수정">✔️ API 명세서 수정</h3>
<img src='https://velog.velcdn.com/images/luv_lyn/post/2a348c04-b535-4740-9b5d-e96b92c180d5/image.png' width=100%>



<h3 id="✔️-상황별-응답-dto-생성">✔️ 상황별 응답 Dto 생성</h3>
<p><strong><code>❶ 장바구니 추가시</code></strong></p>
<ul>
<li>방금 추가한 카트 ( 메뉴 / 수량 )</li>
<li>유저의 모든 카트의 메뉴 총합 + 유저의 모든카트 현재 금액 + 주문가능여부
<img src="https://velog.velcdn.com/images/luv_lyn/post/86a9440d-3401-476d-a03b-ca046cb8b32f/image.png" alt=""></li>
</ul>
<p><strong><code>❷ 장바구니 전체 조회 / 장바구니 수정시</code></strong></p>
<ul>
<li>유저의 모든카트 내용 ( 메뉴 / 수량 ) + 현재 금액 + 주문가능여부
<img src="https://velog.velcdn.com/images/luv_lyn/post/75834a70-662a-44bb-a95f-6314c0caa9db/image.png" alt=""></li>
</ul>
<blockquote>
<p>🌟 Dto에서 카트 금액을 구해서 서비스단의 코드를 줄일 수 있을 것 같다...!</p>
</blockquote>
<hr>
<h1 id="🤯-나의-회고">🤯 나의 회고</h1>
<hr>
<ul>
<li>역시 협업에서 가장 중요한 것은 <strong>설계</strong>라는 걸 다시 한번 깨달았다..^^
협업에서 설계단계가 70% 이상이라는 성규튜터님의 말씀이 설계단계가 다 끝나고 나니 한 번 더 생각난다..!</li>
<li>dto에서 전부 가격까지 계산을 해버리게 코드를 구성했는데 이게 맞나 싶지만 stream을 활용해 가격을 다 계산해버리니까 서비스단 코드가 확줄어서 뿌듯하다~!</li>
<li>오더까지 쭉 구현해보자~!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌼 Spring - Interceptor와 AOP를 활용한 API 로깅]]></title>
            <link>https://velog.io/@luv_lyn/Interceptor%EC%99%80-AOP%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-API-%EB%A1%9C%EA%B9%85</link>
            <guid>https://velog.io/@luv_lyn/Interceptor%EC%99%80-AOP%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-API-%EB%A1%9C%EA%B9%85</guid>
            <pubDate>Mon, 21 Apr 2025 07:06:34 GMT</pubDate>
            <description><![CDATA[<h1 id="📖-interceptor">📖 Interceptor</h1>
<blockquote>
<p>Spring Mvc에서 <code>Controller</code>에 도달하기 전이나 후의 흐름을 제어할 수 있다</p>
</blockquote>
<img src='https://velog.velcdn.com/images/luv_lyn/post/e57b57ba-c37f-4801-948d-943d1c69e176/image.png' width=60%>

<ul>
<li><p><code>HandlerInterceptor</code>를 implements 받는다</p>
</li>
<li><p><strong>컨트롤러-디스패쳐서블릿 사이에 위치</strong>해서 특정 컨트롤러의 요청과 응답을 컨트롤</p>
</li>
<li><p><code>WebMvcConfigurer</code>에 등록해서 사용한다</p>
<hr>
<h2 id="📌-메서드">📌 메서드</h2>
<h3 id="✔️-prehandle">✔️ preHandle()</h3>
</li>
<li><p><strong>호출시점</strong> : 컨트롤러 호출 전</p>
</li>
<li><p>반환 타입이 <code>boolean</code> - true 다음 인터셉터 or 컨트롤러 호출 / false 작업 중단</p>
</li>
</ul>
<h3 id="✔️-posthandle">✔️ postHandle()</h3>
<ul>
<li><strong>호출시점</strong> : 컨트롤러 호출 후</li>
<li>(결과값이 반환된 이후에 실행되기에 )예외 발생시 실행 ❌</li>
</ul>
<h3 id="✔️-aftercompletion">✔️ afterCompletion()</h3>
<ul>
<li><strong>호출시점</strong> : 뷰가 랜더링된 이후</li>
<li>예외 발생 여부와 무관하게 클라이언트에게 응답 전달시 실행</li>
</ul>
<br>

<h2 id="▶️-사용예시">▶️ 사용예시</h2>
<hr>
<h4 id="▪️-admininterceptor">▪️ AdminInterceptor</h4>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/3d41338b-4a34-4481-8414-2e8f95c2ece0/image.png" alt=""></p>
<ul>
<li><code>HandlerInterceptor</code>의 구현 클래스 생성</li>
<li>API에 접근한 시간과 요청 URI를 로그에 남기도록 했다</li>
</ul>
<h3 id="▪️-webconfig">▪️ Webconfig</h3>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/479ee86d-49aa-4c20-80b2-64adbdd40fa2/image.png" alt=""></p>
<ul>
<li>등록해준뒤에 <strong>uri 범위</strong>를 지정했다
( 해당 예제에서는 admin 접근시 로깅하라는 요구사항이 있었음 )</li>
</ul>
<hr>
<h1 id="📖-aop">📖 AOP</h1>
<blockquote>
<p>애플리케이션의 <strong>공통기능</strong>을 분리하고 모듈화한다 (로깅, 트랜젝션 관리, 보안 등)</p>
</blockquote>
<p><a href="https://mooonstar.tistory.com/entry/SpringAOP-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5%ED%95%98%EA%B8%B0%EA%B0%9C%EB%85%90%EA%B3%BC-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C">참고링크</a></p>
<h2 id="📌-aspect">📌 Aspect</h2>
<ul>
<li><strong>공통 관심 사항을 정의하는 모듈</strong></li>
</ul>
<pre><code class="language-java">    public AdminLogAspect(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }</code></pre>
<h2 id="📌-advice">📌 Advice</h2>
<ul>
<li><p>Aspect의 동작을 정의하는 부분</p>
<table>
<thead>
<tr>
<th>이름</th>
<th>동작 시점</th>
</tr>
</thead>
<tbody><tr>
<td><code>@Before</code></td>
<td>메서드 <strong>실행 전</strong> 동작추가</td>
</tr>
<tr>
<td><code>@AfterReturning</code></td>
<td>메서드 <strong>정상종료 후</strong> 동작추가</td>
</tr>
<tr>
<td><code>@AfterThrowing</code></td>
<td>메서드 <strong>예외발생 시</strong> 동작추가</td>
</tr>
<tr>
<td><code>@After</code></td>
<td>메서드가 <strong>실행 후 무조건</strong> 동작추가 (성공/예외 발생 여부와 무관)</td>
</tr>
<tr>
<td><code>@Around</code></td>
<td>메서드 <strong>실행 전후 모두</strong> 동작추가 (가장 강력한 유형)</td>
</tr>
</tbody></table>
</li>
</ul>
<blockquote>
<p><strong>✔️ 예시</strong></p>
</blockquote>
<pre><code class="language-java"> @Around(&quot;all()&quot;)
    public Object logging (ProceedingJoinPoint pjp) throws Throwable {
    ...</code></pre>
<h2 id="📌-pointcut">📌 Pointcut</h2>
<ul>
<li><strong>어떤 메서드가</strong> 어떤 <code>Advice</code>를 받을지 결정하는 표현식 ⇢ <code>정규식</code>으로 작성
( 패키지, 특정 클래스의 메서드 등으로 작성 가능 )</li>
</ul>
<blockquote>
<p><strong>✔️ 예시</strong></p>
</blockquote>
<pre><code class="language-java">    @Pointcut(&quot;execution(* org.example.expert.domain.user.controller.UserAdminController.*(..))&quot;)
    public void adminUser() {}</code></pre>
<h2 id="📌-join-point">📌 Join Point</h2>
<ul>
<li>프로그램 실행 중 특정한 시점을 의미, <code>Advice</code>가 <strong>언제</strong> 적용될 지</li>
<li><code>JoinPoint</code>: 메서드 실행 전까지만 접근 가능 (<code>@Before</code>, <code>@After</code> 등에서 사용)</li>
<li><code>ProceedingJoinPoint</code>: 메서드 실행 자체를 제어할 수 있음 (<code>@Around</code>에서만 사용)</li>
</ul>
<h2 id="📌-weaving">📌 Weaving</h2>
<ul>
<li>Aspect를 대상코드에 적용하는 프로세스를 의미</li>
<li>컴파일 시점, 클래스 로딩 시점, 런타임 시점에 Weaving을 수행할 수 있음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/418aa802-9e7f-4ff7-af8b-f7fc551d53cd/image.png" alt=""><img src="https://velog.velcdn.com/images/luv_lyn/post/27116e61-9975-4ef0-aff1-a054622cad7f/image.png" alt=""></p>
<p>일단 블로그 보고 무난하게 구현,,</p>
<p>포인트 컷으로 적용할 컨트롤러를 정규식!!으로 적어준다~
첨엔 코멘트 유저 나눴다가
around로 또 범위 정해주는데~~ 걍 all로 합침</p>
<p>문제는 요청 본문과 응답 본문을 json 형태로 가져와서 로깅하라는 요구사항이 있는데..</p>
<p>문제점</p>
<p>다른 정보들 처럼 그냥 request.get<del>~ 으로 불러올수있을거라고 생각해서 메서드를 찾아보았으나
tomcat에서 한번 read한 body는 두번 읽을 수가 없다고 한다
내부에서 요청 파라미터를 바인딩할때 한번 리드 하므로,,
내가 메서드 실행 전에 채가서 read 해버리면 바인딩이 불가한 상황!
그럼 어떻게 하냐?
읽는 메서드도 내가 따로 만들어야한다 이말이다</del>
한번밖에 못읽어? 그럼 직접 만들어 읽어~~
ㅎ..
진짜 여기서부터 모르겠는데
1.HttpServletRequestWrapper를 상속받아 메서드를 오버라이딩 하면된다
2.ContentCachingRequestWrapper를 사용한다 -&gt; 요걸 선택! 1번을 정리한 글이 어마무시했기때문</p>
<p>으아아아아아악</p>
<p>ContentCachingRequestWrapper를 필터로 만든다
왜 필터로 만드냐? 걍 클래스로 만들면 안댐?
안댐 톰캣이 읽을려고 가져가기 전에 감싸놔야하거든~</p>
<p>암튼 필터를 만들고 필터를 등록한다
적용범위는 걍 전체로 함 어차피 aop에서 적용범위가 정해져있음(포인트컷)
+언제 다른 메서드의 요청 본문을 로깅해야할지 모르므로~</p>
<p>그리고 사용을 하면되는데
일단 블로그에서 if로 instanceof로 확인하길래 따라하고 이유를 찾아보니
내가불러온 request가 아아까 감싸뒀던!! 걔가 맞는지 검증하는 거라는데
그래서 검증을 꼭 해야하냐고? 안해밨다 모른다 !! 급하니까 하란대로 했다
암튼간에 스트링으로 변환하면 끝~</p>
<p>응답 본문은 걍 objectMapper로 긁어오면 된다~
얘는 요청이 아니라 응답이라서 톰캣 이새기가 읽을일이 없기 때문이다..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🚀chaper 4 )  과제 트러블 슈팅 / TIL day 35]]></title>
            <link>https://velog.io/@luv_lyn/chaper-4-%EA%B3%BC%EC%A0%9C-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-TIL-day-35</link>
            <guid>https://velog.io/@luv_lyn/chaper-4-%EA%B3%BC%EC%A0%9C-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-TIL-day-35</guid>
            <pubDate>Mon, 21 Apr 2025 00:14:48 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="📖-과제-개요">📖 과제 개요</h1>
<hr>
<p>이번 개인 과제는 예제를 가져와
코드를 개선하고, 테스트 코드를 작성해보고, 리팩터링 해보는 과제였다</p>
<blockquote>
<p>✔️ <strong>개발 환경</strong></p>
</blockquote>
<ul>
<li>Java: 17</li>
<li>JDK: 17.0.1</li>
<li>IDE: IntelliJ</li>
<li>Spring Frame Work : 3.4.4</li>
<li>API 테스트 도구 : postman</li>
</ul>
<h2 id="🌟-git-hub-링크"><a href="https://github.com/harinbeee/spring-advanced">🌟 Git Hub 링크</a></h2>
<br>

<hr>
<h1 id="📖-코드-리뷰">📖 코드 리뷰</h1>
<hr>
<h2 id="📌-lv-1-코드-개선하기">📌 [Lv 1] 코드 개선하기</h2>
<h3 id="✅-1-early-return">✅ 1. Early Return</h3>
<blockquote>
<pre><code class="language-java">    @Transactional
    public SignupResponse signup(SignupRequest signupRequest) {
</code></pre>
</blockquote>
<pre><code>   if (userRepository.existsByEmail(signupRequest.getEmail())) {
        throw new InvalidRequestException(&quot;이미 존재하는 이메일입니다.&quot;);
    } // 💡 수정한 위치</code></pre><blockquote>
</blockquote>
<pre><code>    String encodedPassword = passwordEncoder.encode(signupRequest.getPassword());</code></pre><blockquote>
<pre><code>    //if (userRepository.existsByEmail(signupRequest.getEmail())) {
    //   throw new InvalidRequestException(&quot;이미 존재하는 이메일입니다.&quot;);
    //} 기존 위치

    UserRole userRole = UserRole.of(signupRequest.getUserRole());</code></pre></blockquote>
<pre><code>    ...</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<h4 id="✔️-배운점">✔️ 배운점</h4>
<ul>
<li>불필요한 로직 실행을 줄이고, 코드 가독성을 높일 수 있다</li>
<li>특히 passwordEncoder.encode()와 같은 큰 연산을 줄일 수 있어 효율적이다</li>
</ul>
<br>


<h3 id="✅-2-불필요한-if-else문-줄여-리팩토링">✅ 2. 불필요한 if-else문 줄여 리팩토링</h3>
<blockquote>
<pre><code class="language-java">if (!HttpStatus.OK.equals(responseEntity.getStatusCode())) {
    throw new ServerException(&quot;날씨 데이터를 가져오는데 실패했습니다. 상태 코드: &quot; + responseEntity.getStatusCode());
</code></pre>
</blockquote>
<p>if (weatherArray == null || weatherArray.length == 0) {
    throw new ServerException(&quot;날씨 데이터가 없습니다.&quot;);
    }
}</p>
<blockquote>
<pre><code></code></pre></blockquote>
<h4 id="✔️-배운점-1">✔️ 배운점</h4>
<ul>
<li>불필요한 else나 중첩을 줄이자</li>
</ul>
<br>

<h3 id="✅-3-validation">✅ 3. Validation</h3>
<blockquote>
<p><strong>UserService 클래스</strong></p>
</blockquote>
<pre><code class="language-java">if (userChangePasswordRequest.getNewPassword().length() &lt; 8 ||
        !userChangePasswordRequest.getNewPassword().matches(&quot;.*\\d.*&quot;) ||
        !userChangePasswordRequest.getNewPassword().matches(&quot;.*[A-Z].*&quot;)) {
    throw new InvalidRequestException(&quot;새 비밀번호는 8자 이상이어야 하고, 숫자와 대문자를 포함해야 합니다.&quot;);
}</code></pre>
<p><strong>UserChangePasswordRequest 클래스</strong></p>
<pre><code class="language-java"> @NotBlank
    @Pattern(regexp = &quot;^(?=.*[0-9])(?=.*[A-Z])[A-Za-z\\d]{9,}$&quot;, message = &quot;새 비밀번호는 8자 이상이어야 하고, 숫자와 대문자를 포함해야 합니다.&quot;)
    private String newPassword;
&gt;```

#### ✔️ 배운점
- 정규식 쓰는 것이 어려웠다. 기존에 있는 클래스,메서드들을 잘 활용하자


&lt;br&gt;

---

## 📌 [Lv 2] N+1 문제
### ✅ N+1을 방지하는 기능을 찾고, @EntityGraph 기반 구현으로 수정


&gt;|기존|![](https://velog.velcdn.com/images/luv_lyn/post/0afbc5b3-7dce-429f-821f-34c7a9b266aa/image.png)`fetch join`을 사용해 연관된 데이터를 **즉시 로딩**|
|:---:|:---:|
|**수정**|![](https://velog.velcdn.com/images/luv_lyn/post/76bf29fa-8c9d-4a3d-a9d5-9acc55f8eb6f/image.png)**JPA의 `@EntityGraph`로 리팩토링**|

---
## 📌 [Lv 3] 테스트코드 연습
### ✅ 1. 성공 케이스

&gt; 
#### ✔️ 문제
&gt;
&lt;img src=&#39;https://velog.velcdn.com/images/luv_lyn/post/06cfbe69-27cc-4d64-9b8a-103287611c2e/image.png&#39; width=60%&gt;
&lt;img src=&#39;https://velog.velcdn.com/images/luv_lyn/post/6b7425e9-9925-4ae5-90b5-daa4f81ac74a/image.png&#39; width=50%&gt; 
&gt;
&gt; ---
&gt;
#### ✔️ 해결
&gt;
![](https://velog.velcdn.com/images/luv_lyn/post/62043888-0eda-42d0-b6be-5e089c961276/image.png)
&gt;

- [`passwordEncoder.matches()`](https://hs-backend.tistory.com/207)
- 해싱 알고리즘을 비교할때는 비교 대상이 앞에, 해싱된 암호가 뒤에 와야한다



&lt;br&gt;



### ✅ 2. 예외 처리 성공 케이스

&gt;| | ✔️ 문제 | ✔️ 해결 |
|---|---|---|
|1|&lt;img src=&#39;https://velog.velcdn.com/images/luv_lyn/post/e5ec0068-41dd-43c6-b798-7d16fe0b1475/image.png&#39; width=100%&gt; |&lt;img src=&#39;https://velog.velcdn.com/images/luv_lyn/post/b37af196-a2d5-4efd-82f5-fe3709985be0/image.png&#39; width=100%&gt; | |
|2|&lt;img src=&#39;https://velog.velcdn.com/images/luv_lyn/post/78bff037-8e4f-4814-869f-b4ed616bd322/image.png&#39; width=100%&gt; |&lt;img src=&#39;https://velog.velcdn.com/images/luv_lyn/post/0e9ca091-8e57-458c-acb1-57e79c3f766b/image.png&#39; width=100%&gt; | |
|3|&lt;img src=&#39;https://velog.velcdn.com/images/luv_lyn/post/11e49451-37fc-4214-bb3f-a638231b3c64/image.png&#39; width=100%&gt; |&lt;img src=&#39;https://velog.velcdn.com/images/luv_lyn/post/1f5d7515-fe8f-467d-85d8-5202f5fce804/image.png&#39; width=100%&gt;|

- 알맞은 `Exception`으로 변경해 테스트 코드 실행시 성공하도록 수정했다

---

## 📌 Level 4. API 로깅

&gt; ### [Interceptor와 AOP를 활용한 API 로깅](/@luv_lyn/Interceptor와-AOP를-활용한-API-로깅) 
~~양이 방대해서 따로 썼다..🤯~~



### ✅ Interceptor 사용
- **Interceptor를 사용해 요청 정보 사전 처리**
- 인증 성공 시, **요청 시각과 URL을 로깅**
- **어드민 인증 여부 확인**

### ✔️ 작성한 코드

![](https://velog.velcdn.com/images/luv_lyn/post/e36b268f-fa31-4c63-84c2-f5ccb29435fe/image.png)

![](https://velog.velcdn.com/images/luv_lyn/post/e8d43bf0-81f8-442b-8ac5-8be640ebc7d6/image.png)



&lt;br&gt;


### ✅ AOP 사용
- `@Around`를 사용해 **어드민 API 실행 전후에 로깅**
- 로깅 내용: 요청한 사용자 ID, API 요청 시각, 요청 URL, 요청 본문 (`RequestBody`), 응답 본문 (`ResponseBody`)
- 요청/응답 바디는 **JSON 형태로 기록**
- `Logger` 클래스 활용

![](https://velog.velcdn.com/images/luv_lyn/post/0e3e4dc5-9ff4-4746-802d-e08d483f6b3a/image.png)

![](https://velog.velcdn.com/images/luv_lyn/post/65c5aaa8-9aa5-4131-a937-7090a1d8f8bf/image.png)


&gt;#### ✔️ 어려웠던점 + 배운점
&gt;
1. **본문을 JSON으로 불러올 것**
   - `request.getInputStream()` 등으로는 한 번만 읽을 수 있음
   - **Tomcat이 먼저 읽으면 바디는 null**로 나옴
   - 해결책: `ContentCachingRequestWrapper`를 필터로 감싸 요청 바디를 캐싱
&gt;
&gt;
2. **왜 필터를 만들어야 하는가?**
   - AOP나 일반 클래스에서는 이미 **Tomcat이 읽은 뒤라 감싸도 무용지물**
   - 필터로 **가장 먼저 요청을 가로채고 감싸야** 이후 AOP에서 꺼내 읽을 수 있음
   - 그래서 `OncePerRequestFilter`를 구현한 필터로 직접 감쌈
&gt;
&gt;
3. **캐싱해서 감싸져 있는지 `instanceof`로 확인**
   - `request`가 내가 감싼 `ContentCachingRequestWrapper` 객체인지 체크해야 함
   - 그렇지 않으면 `getContentAsByteArray()` 호출 자체가 불가능
   - `instanceof` 검사 후 → 바이트 배열 → 문자열 변환 → 로깅

---

# 📖 나의 회고

- 필수단계만 착실히 하던 나에게 쉬어가는 느낌의 이번 필수 단계들..
덕분에 시간이 남아서 도전단계까지 해보게 되었는데..! 정말 오래 걸렸다..^^
- 일단 다른 사람이 작성한 코드를 보는게 얼마나 힘든지 깨달았다.
지금 강의 수준에 맞춰 작성한 코드를 주신걸 텐데도 내가 작성한게 아니다보니 어려웠다
- **API 명세서의 중요성**을 깨달았다
레벨 4 구현을 위해 테스트 코드가 아닌 어플리케이션 전체를 실행하는데..
요청시 뭐가 들어가야하는지 Path가 뭔지 컨트롤러 DTO 다 눌러보고 하나씩 확인하며 테스트했다
나도 이런데 정말 엔지니어가 아닌 팀과 협업해야 한다면? API 명세서는 상세할 수록 좋은거구나~
- **AOP**를 알게되고 재밌어서(?) 홀린 듯 블로그를 뒤지며 따라해봤다..
모든곳에 공통으로 적용해야할 작업이라던지 이런 부분들을 내가 커스텀해서 사용할 수 있을 것 같다
- 이번에도 **시간**을 잘 분배하지 못했다. 내가 게으르고 느린걸까 아님 할일이 너무 많은걸까?
여기까지 오니 중간에 하차하신 분들이 꽤 보이는데.. 잠깐씩 부럽기도 했다
이런 생각하지 않도록 꾸준하게 열심히 하자 !
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌼 Spring 심화 ② 연관관계, 상속관계 매핑, Proxy, 영속성 전이  / TIL - day 34]]></title>
            <link>https://velog.io/@luv_lyn/Spring-%EC%8B%AC%ED%99%94-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EC%83%81%EC%86%8D%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91-Proxy-%EC%98%81%EC%86%8D%EC%84%B1-%EC%A0%84%EC%9D%B4-TIL-day-34</link>
            <guid>https://velog.io/@luv_lyn/Spring-%EC%8B%AC%ED%99%94-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EC%83%81%EC%86%8D%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91-Proxy-%EC%98%81%EC%86%8D%EC%84%B1-%EC%A0%84%EC%9D%B4-TIL-day-34</guid>
            <pubDate>Fri, 18 Apr 2025 06:18:29 GMT</pubDate>
            <description><![CDATA[<h1 id="📖-jpa-연관관계">📖 JPA 연관관계</h1>
<ul>
<li>관계 종류: <code>@ManyToOne</code>, <code>@OneToMany</code>, <code>@OneToOne</code>, <code>@ManyToMany</code></li>
<li>방향: 단방향 / 양방향 (<code>mappedBy</code> 사용 시 비주인)</li>
<li>연관관계의 주인: 외래키(FK)를 가진 쪽 (<code>@JoinColumn</code> 선언됨)</li>
</ul>
<hr>
<h2 id="📌-1n-연관관계-onetomany">📌 1:N 연관관계 (@OneToMany)</h2>
<h3 id="✔️-단방향">✔️ 단방향</h3>
<ul>
<li>한 엔티티가 @OneToMany를 통해 여러 엔티티와 관계를 맺는 경우</li>
<li>주인은 <code>1</code>, 비주인의 리스트를 컬렉션으로 보유해야 함</li>
<li>실제 FK는 주인 테이블에 있어야 하므로, DB 설계상 부자연스럽고 update 쿼리 추가 발생</li>
<li><code>@JoinColumn</code>으로 외래키 컬럼 지정 가능</li>
<li>유지보수, 성능 이슈로 실무에서는 잘 사용하지 않음</li>
</ul>
<h3 id="✔️-양방향">✔️ 양방향</h3>
<ul>
<li>하나의 엔티티가 다른 엔티티와 관계를 맺고 그 반대 방향에서도 서로 참조 가능</li>
<li>필요한 경우 <code>insertable = false, updatable = false</code>로 읽기 전용 설정 가능</li>
</ul>
<pre><code class="language-java">@ManyToOne
@JoinColumn(name = &quot;company_id&quot;, insertable = false, updatable = false)
private Company company;</code></pre>
<hr>
<h2 id="📌-11-연관관계-onetoone">📌 1:1 연관관계 (@OneToOne)</h2>
<h3 id="✔️-단방향-지원-❌">✔️ 단방향 (지원 ❌)</h3>
<ul>
<li>외래키 주인 선택 가능</li>
<li>JPA에서 지원하지 않는다</li>
</ul>
<h3 id="✔️-양방향-1">✔️ 양방향</h3>
<ul>
<li><p>외래 키가 있는 곳이 연관관계의 주인 (UNIQUE 사용 ❌)</p>
<table>
<thead>
<tr>
<th></th>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td>주 테이블</td>
<td>주테이블만 조회해도 대상 테이블을 조회할 수 있다</td>
<td>null 허용 (대상 테이블 값이 없을때) <br> 삭제 시 외래 키 값 처리필요</td>
</tr>
<tr>
<td>대상 테이블</td>
<td>연관관계 변경시에도 테이블 구조 유지</td>
<td>지연로딩을 설정해도 즉시 로딩</td>
</tr>
</tbody></table>
</li>
</ul>
<hr>
<h2 id="📌-nm-연관관계-manytomany">📌 N:M 연관관계 (ManyToMany)</h2>
<ul>
<li><code>@ManyToMany</code>로 설정 가능하지만 실무에서는 지양<ul>
<li>중간 테이블이 숨겨짐</li>
<li>추가 컬럼(ex. level, license 등) 넣을 수 없음</li>
<li>복잡한 쿼리, 제약 조건 관리 어려움</li>
</ul>
</li>
</ul>
<h3 id="✔️-단방향--양방향">✔️ 단방향 / 양방향</h3>
<ul>
<li>@ManyToMany는 단방향도 가능, 필요하면 양방향으로도 설정 가능 (mappedBy 사용)</li>
<li>실무에서는 중간 테이블을 별도 Entity로 만들어 @OneToMany - @ManyToOne으로 분리하여 매핑</li>
</ul>
<hr>
<h2 id="🌟-연관관계-비교하기">🌟 연관관계 비교하기</h2>
<blockquote>
<table>
<thead>
<tr>
<th>연관관계</th>
<th>추천</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>1:N 단방향</td>
<td>❌ 비추천</td>
<td>FK가 자식에 있는데 부모가 관리하는 구조 → 비효율적</td>
</tr>
<tr>
<td>N:1 양방향</td>
<td>✅ 추천</td>
<td>객체-DB 일관성 유지, 가장 자연스러운 구조</td>
</tr>
<tr>
<td>1:1</td>
<td>✅ 조건부 추천</td>
<td>상황에 따라 단방향 또는 양방향 선택 (FK 위치 고려)</td>
</tr>
<tr>
<td>N:M</td>
<td>❌ 비추천</td>
<td>중간 테이블을 별도 엔티티로 분리하는 방식 추천</td>
</tr>
</tbody></table>
</blockquote>
<hr>
<h1 id="📖-jpa-테이블-상속-전략">📖 JPA 테이블 상속 전략</h1>
<blockquote>
<p>관계형 데이터베이스의 테이블에는 상속관계가 없지만
<strong>JPA를 활용해 상속처럼</strong> 사용할 수 있다!</p>
</blockquote>
<br>

<h2 id="✅-1-single_table-전략-default">✅ 1. <code>SINGLE_TABLE</code> 전략 (default)</h2>
<h3 id="▶️-사용법">▶️ 사용법</h3>
<ul>
<li><code>@Inheritance(strategy = InheritanceType.</code><strong><code>SINGLE_TABLE)</code></strong></li>
<li><strong>부모 클래스</strong> :<code>@DiscriminatorColumn(name = &quot;dtype&quot;)</code> ← 생략 가능</li>
<li><strong>자식 클래스</strong> : <code>@DiscriminatorValue(&quot;원하는구분값&quot;)</code></li>
</ul>
<h3 id="📌-특징">📌 특징</h3>
<ul>
<li><strong>하나의 테이블에 모든 자식 클래스의 정보를 저장</strong> ( 객체 지향적인 개발에 어울림 )</li>
<li><code>@DiscriminatorColumn</code>으로 구분 ( 선언하지 않아도 <code>DTYPE</code>으로 컬럼 생성)</li>
<li>단순하고 확장가능성이 없는 경우 사용</li>
</ul>
<h3 id="📌-장단점">📌 장단점</h3>
<table>
<thead>
<tr>
<th align="left">장점</th>
<th align="left">단점</th>
</tr>
</thead>
<tbody><tr>
<td align="left">쿼리 빠름 (조인 없음)</td>
<td align="left">단일 테이블이므로 컬럼이 많아지고, <br>자식이 매핑한 컬럼에 NULL 많아짐</td>
</tr>
<tr>
<td align="left">관리 용이</td>
<td align="left">DB 정규화 X</td>
</tr>
</tbody></table>
<br>



<h3 id="▪️-사용-예시">▪️ 사용 예시</h3>
<hr>
<h4 id="✔️-엔티티-코드">✔️ 엔티티 코드</h4>
<pre><code class="language-java">@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) ✅
@DiscriminatorColumn(name = &quot;dtype&quot;) ✅
public abstract class Item {
    @Id @GeneratedValue
    private Long id;
    private String name;
}

@Entity
@DiscriminatorValue(&quot;B&quot;) ✅
public class Book extends Item {
    private String author;
}

@Entity
@DiscriminatorValue(&quot;M&quot;) ✅
public class Movie extends Item {
    private String director;
}</code></pre>
<h4 id="✔️-실행-코드">✔️ 실행 코드</h4>
<pre><code class="language-java">Item book = new Book();
book.setName(&quot;JPA Book&quot;);
((Book) book).setAuthor(&quot;수린&quot;);

em.persist(book); ✅</code></pre>
<h4 id="✔️-결과-테이블">✔️ 결과 테이블</h4>
<p><strong>item</strong></p>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>author</th>
<th>director</th>
<th>dtype</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>JPA Book</td>
<td>수린</td>
<td>null</td>
<td>B</td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-2-joined-전략">✅ 2. <code>JOINED</code> 전략</h2>
<h3 id="▶️-사용법-1">▶️ 사용법</h3>
<ul>
<li><code>@Inheritance(strategy = InheritanceType.</code><strong><code>JOINED)</code></strong></li>
<li><strong>부모 클래스</strong> :<code>@DiscriminatorColumn(name = &quot;dtype&quot;)</code> ← 생략 불가</li>
<li><strong>자식 클래스</strong> : <code>@DiscriminatorValue(&quot;원하는구분값&quot;)</code></li>
</ul>
<h3 id="📌-특징-1">📌 특징</h3>
<ul>
<li>부모, 자식 각각 테이블 생성  </li>
<li><code>JOIN</code>을 통해 조회</li>
<li>비즈니스적으로 복잡한 경우 사용 ( 객체 지향적인 개발에 어울림 )</li>
</ul>
<h3 id="📌-장단점-1">📌 장단점</h3>
<table>
<thead>
<tr>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td>정규화 잘됨 (DB 깔끔)</td>
<td>성능 느림 (JOIN 발생)</td>
</tr>
<tr>
<td>자식 테이블마다 필요한 컬럼만 가짐</td>
<td>INSERT 시 쿼리 2번 발생</td>
</tr>
</tbody></table>
<br>

<h3 id="▪️-사용예시">▪️ 사용예시</h3>
<hr>
<h4 id="✔️-엔티티-코드-1">✔️ 엔티티 코드</h4>
<pre><code class="language-java">@Entity
@Inheritance(strategy = InheritanceType.JOINED) ✅
@DiscriminatorColumn(name = &quot;dtype&quot;) ✅
public abstract class Item {
    @Id @GeneratedValue
    private Long id;
    private String name;
}

@Entity
@DiscriminatorValue(&quot;B&quot;) ✅
public class Book extends Item {
    private String author;
}</code></pre>
<h4 id="✔️-실행-코드-1">✔️ 실행 코드</h4>
<pre><code class="language-java">Book book = new Book();
book.setName(&quot;JPA Book&quot;);
book.setAuthor(&quot;수린&quot;);

em.persist(book); ✅</code></pre>
<h4 id="✔️-결과-테이블-1">✔️ 결과 테이블</h4>
<p><strong>item</strong></p>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>dtype</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>JPA Book</td>
<td>B</td>
</tr>
</tbody></table>
<p><strong>book</strong></p>
<table>
<thead>
<tr>
<th>id</th>
<th>author</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>수린</td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-3-table_per_class-전략-사용-❌">✅ 3. <code>TABLE_PER_CLASS</code> 전략 (사용 ❌)</h2>
<h3 id="▶️-사용법-2">▶️ 사용법</h3>
<ul>
<li><p><code>@Inheritance(strategy = InheritanceType.</code><strong><code>TABLE_PER_CLASS)</code></strong></p>
</li>
<li><p>❌<code>@DiscriminatorColumn</code>, <code>@DiscriminatorValue</code> 사용 안 함</p>
</li>
</ul>
<h3 id="📌-특징-2">📌 특징</h3>
<ul>
<li>부모 테이블 없이 자식마다 테이블 <strong>개별 생성</strong></li>
<li>자식끼리도 완전 독립</li>
<li>부모 클래스 대신, <strong>추상 클래스 사용해야</strong> 함</li>
<li>❌<code>GenerationType.IDENTIFY</code> 사용 불가</li>
</ul>
<h3 id="📌-장단점-사용하지-않는-것-권장">📌 장단점 (사용하지 않는 것 권장)</h3>
<table>
<thead>
<tr>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td>자식 테이블 완전 독립, 빠름</td>
<td>쿼리 복잡, UNION 필요(자식 테이블을 전부 합쳐야함)</td>
</tr>
<tr>
<td>not null 사용 가능</td>
<td>부모 객체 타입 조회시 자식 전부 조회해야 함</td>
</tr>
</tbody></table>
<br>

<h3 id="▪️-사용-예시-1">▪️ 사용 예시</h3>
<hr>
<h4 id="✔️-엔티티-코드-2">✔️ 엔티티 코드</h4>
<pre><code class="language-java">@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) ✅
public abstract class Item {
    @Id @GeneratedValue
    private Long id;
    private String name;
}

@Entity
public class Book extends Item {
    private String author;
}</code></pre>
<h4 id="✔️-실행-코드-2">✔️ 실행 코드</h4>
<pre><code class="language-java">Book book = new Book();
book.setName(&quot;JPA Book&quot;);
book.setAuthor(&quot;수린&quot;);

em.persist(book); ✅</code></pre>
<h4 id="✔️--결과-테이블">✔️  결과 테이블</h4>
<p><strong>book</strong></p>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>author</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>JPA Book</td>
<td>수린</td>
</tr>
</tbody></table>
<p>💡 <code>item</code> 테이블은 없음! <code>Book</code>, <code>Movie</code> 각각 독립 테이블로 생성됨.</p>
<hr>
<h2 id="🌟-테이블-전략-annotation-정리">🌟 테이블 전략 @Annotation 정리</h2>
<blockquote>
<ul>
<li><strong><code>@Inheritance(strategy = InheritanceType.${전략})</code></strong> <ol>
<li><code>JOINED</code> : 조인</li>
<li><code>SINGLE_TABLE</code> : 단일 테이블(Default)</li>
<li><code>TABLE_PER_CLASS</code> : 구현 클래스마다 테이블</li>
</ol>
</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li><strong><code>@DiscriminatorColumn(name = &quot;dtype&quot;)</code></strong> <ul>
<li><code>dtype</code> 컬럼을 생성함 (관례)</li>
<li>이름 변경 가능</li>
<li>기본 값: <code>DTYPE</code></li>
</ul>
</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li><strong><code>@DiscriminatorValue(&quot;${값}&quot;)</code></strong> <ul>
<li><code>dtype</code> 값 지정</li>
<li>기본 값: 클래스 이름</li>
</ul>
</li>
</ul>
</blockquote>
<hr>
<h1 id="📖-proxy">📖 Proxy</h1>
<blockquote>
<p><code>em.getReference()</code>는 <strong>프록시 객체</strong>를 반환</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/eee34afb-cd5f-460b-85e2-b291eb011a1c/image.png" alt=""></p>
<ul>
<li><strong>지연로딩</strong>을 지원하기 위해 대리 객체를 사용 </li>
<li>실제 엔티티 생성, DB 조회하지 않고도 참조할 수 있음</li>
<li><code>target</code> : <strong>실제 객체의 참조값</strong>을 보관
(<code>==</code> 로는 비교 불가, <code>instanceof</code> 사용)</li>
</ul>
<hr>
<h2 id="📌-emgetreference">📌 em.getReference()</h2>
<pre><code class="language-java">Tutor proxyTutor = em.getReference(Tutor.class, tutor.getId()); ✅ 프록시 객체 생성 (실제 객체의 참조값만 기억)
System.out.println(&quot;proxyTutor.getName() = &quot; + proxyTutor.getName()); ✅ 필드 접근 후 조회 SQL 실행 (초기화)</code></pre>
<ul>
<li><code>em.getReference()</code>는 프록시 객체를 생성</li>
<li>프록시 객체 생성 후 <strong>최초 사용시</strong>(엔티티에 접근시)에 한번만 <strong>초기화</strong></li>
<li>다만, 이미 영속성 컨텍스트에 해당 Entity가 있으면 프록시 생성하지 않고 그 객체 그대로 반환</li>
</ul>
<h3 id="✔️-🆚-영속성-컨텍스트">✔️ 🆚 영속성 컨텍스트</h3>
<ul>
<li>영속성 컨텍스트 = JPA가 관리 중인 모든 엔티티의 저장소 (1차 캐시)</li>
<li>프록시 = getReference()로 등록된, 아직 초기화되지 않은 객체(껍데기)</li>
<li>영속성 컨텍스트는 프록시를 포함하는 상위 개념</li>
</ul>
<h3 id="✔️-관련-예외---lazyinitalizationexception">✔️ 관련 예외 - LazyInitalizationException</h3>
<ul>
<li>프록시 객체를 생성 후, <code>detach</code>했거나, 프록시 객체를 생성하지 않고 객체에 접근하고자 했을때!</li>
<li>프록시 객체가 없다 or 영속상태가 아니다!</li>
</ul>
<hr>
<h2 id="📌-emfind와-비교">📌 em.find()와 비교</h2>
<table>
<thead>
<tr>
<th>메서드</th>
<th>em.find()</th>
<th>em.getReference()</th>
</tr>
</thead>
<tbody><tr>
<td>DB 쿼리 발생 여부</td>
<td>조건부 ✅</td>
<td>❌ ➝ 프록시 객체 반환</td>
</tr>
<tr>
<td>설명</td>
<td>1차 캐시 ➝ DB 조회 + 캐시에 추가</td>
<td>실제 DB 조회하지 않고 프록시 객체만 등록</td>
</tr>
</tbody></table>
<hr>
<h2 id="🌟-지연로딩-즉시로딩-정리">🌟 지연로딩, 즉시로딩 정리</h2>
<blockquote>
<table>
<thead>
<tr>
<th align="center">지연로딩</th>
<th align="center">즉시로딩</th>
</tr>
</thead>
<tbody><tr>
<td align="center">fetch = FetchType.<strong>LAZY</strong></td>
<td align="center">fetch = FetchType.<strong>EAGER</strong></td>
</tr>
<tr>
<td align="center">proxy 객체를 조회</td>
<td align="center">proxy 조회 ❌, 연관된 객체 한번에 조회</td>
</tr>
<tr>
<td align="center">연관된 객체를 <strong>매번 조회하는게 낭비</strong>인 경우</td>
<td align="center">연관된 객체를 <strong>매번 함께 조회하는 것이 효율적</strong>인 경우</td>
</tr>
</tbody></table>
</blockquote>
<hr>
<h1 id="📖-영속성-전이">📖 영속성 전이</h1>
<h2 id="📌-cascade">📌 Cascade</h2>
<pre><code class="language-java">    @OneToMany(mappedBy = &quot;category&quot;, cascade = CascadeType.ALL) 
                                      🔼 카테고리-프로덕트 리스트 간의 영속성 전이 설정함
    private List&lt;Product&gt; productList = new ArrayList&lt;&gt;();</code></pre>
<ul>
<li>특정 엔티티를 가지고 작업할 때, <strong>연관된 엔티티에도 동일한 작업을 자동</strong>으로 하게 설정</li>
<li>지연로딩, 즉시로딩과는 무관</li>
<li><code>@OneToMany</code>가 선언된 쪽 = 부모 쪽에 선언</li>
</ul>
<br>


<h2 id="📌-casecadetype별-의미">📌 CasecadeType별 의미</h2>
<table>
<thead>
<tr>
<th align="center">✔️ CascadeType.ALL</th>
<th align="center">✔️ CascadeType.PERSIST</th>
<th align="center">✔️ CascadeType.REMOVE</th>
</tr>
</thead>
<tbody><tr>
<td align="center">PERSIST + REMOVE + MERGE <br>+ REFRESH + DETACH ... 등</td>
<td align="center"><code>em.persist(parent)</code> 했을 때 <br>→ 자식들도 <strong>자동으로 persist()</strong></td>
<td align="center"><code>em.remove(parent)</code> 했을 때<br>→ 자식들도 <strong>자동으로 remove()</strong></td>
</tr>
<tr>
<td align="center"><strong>모든 전이 포함</strong></td>
<td align="center">INSERT 쿼리 같이 발생</td>
<td align="center">DELETE 쿼리 같이 발생</td>
</tr>
</tbody></table>
<br>

<br>


<h2 id="▶️-고아-객체">▶️ 고아 객체</h2>
<ul>
<li>부모 엔티티와의 관계가 끊긴 자식 엔티티</li>
</ul>
<h3 id="✔️-orphanremoval">✔️ orphanRemoval</h3>
<ul>
<li><p>부모-자식 관계가 끊긴 자식 객체를 <strong>DB에서 자동으로 삭제할지</strong> 설정</p>
</li>
<li><p><code>@OneToMany</code>가 선언된 쪽 = <strong>부모</strong>에 선언</p>
<table>
<thead>
<tr>
<th>false (기본값)</th>
<th>true</th>
</tr>
</thead>
<tbody><tr>
<td>자식은 DB에 그대로 남음 (❗수동 삭제 필요)</td>
<td>자식은 DELETE 쿼리 발생, DB에서도 제거됨</td>
</tr>
</tbody></table>
</li>
</ul>
<hr>
<p><strong>▪️ 엔티티 코드</strong></p>
<pre><code class="language-java">@Entity
@Table(name = &quot;category&quot;)
public class Category {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = &quot;category&quot;, cascade = CascadeType.ALL, orphanRemoval = true) ✅
    private List&lt;Product&gt; productList = new ArrayList&lt;&gt;();

    public Category() {
    }

    ...

    public void addProduct(Product product) {
        productList.add(product);
        product.setCategory(this); // 연관관계 주인 설정을 메서드로!
    }
}</code></pre>
<p><strong>▪️ 실행 코드</strong></p>
<pre><code class="language-java">public class OrphanRemovalMain {
    public static void main(String[] args) {

        ...

        try {
            // 1. 부모 생성
            Category category = new Category(&quot;food&quot;);

            // 2. 자식 생성 (아직 연관관계 X)
            Product product1 = new Product(&quot;pizza&quot;);
            Product product2 = new Product(&quot;kimchi&quot;);

            // 3. 연관관계 설정 (addProduct가 양방향 연관관계 주입)
            category.addProduct(product1);
            category.addProduct(product2);

            // 4. 부모만 persist했지만, cascade 덕분에 자식도 자동 persist됨
            em.persist(category); // cascade = ALL

            // 5. DB에 반영 + 영속성 컨텍스트 비움
            em.flush(); // INSERT 실행
            em.clear(); // 1차 캐시 초기화

            // 6. 다시 부모 조회
            Category findCategory = em.find(Category.class, category.getId());

            // 7. 자식을 컬렉션에서 제거!
            findCategory.getProductList().remove(0);
            // ✅ orphanRemoval = true 이므로,
            // 여기서 &quot;자식 1개가 부모에서 제거됐네 → 고아 객체!&quot; 라고 판단
            // → 트랜잭션 커밋 시 DELETE 쿼리 날림! 🔥

            transaction.commit(); // ✅ 여기서 자식 1개 DELETE 쿼리 발생

            ...

    }
}
</code></pre>
<hr>
<h2 id="🌟-영속성-삭제-전이-🆚-고아-객체-제거">🌟 영속성 삭제 전이 🆚 고아 객체 제거</h2>
<blockquote>
<table>
<thead>
<tr>
<th>항목</th>
<th>CascadeType.Remove</th>
<th>orphanRemoval=true</th>
</tr>
</thead>
<tbody><tr>
<td>목적</td>
<td>부모 객체 삭제시 자식 객체도 삭제</td>
<td>부모가 없어진 자식 객체는 자동 삭제</td>
</tr>
<tr>
<td>작동 시점</td>
<td>부모가 remove 될 때</td>
<td>부모 컬렉션에서 자식을 제거할 때 (관계 끊길 때)</td>
</tr>
<tr>
<td>사용 조건</td>
<td>부모-자식 연관관계 + cascade 설정</td>
<td>부모-자식 연관관계만 있어도 가능 (단독 사용 가능 ✅)</td>
</tr>
</tbody></table>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌼 Spring 심화 ① HttpMessageConverter, Converter, Formatter  / TIL - day 33]]></title>
            <link>https://velog.io/@luv_lyn/Spring-%EC%8B%AC%ED%99%94-HttpMessageConverter-TIL-day-33</link>
            <guid>https://velog.io/@luv_lyn/Spring-%EC%8B%AC%ED%99%94-HttpMessageConverter-TIL-day-33</guid>
            <pubDate>Thu, 17 Apr 2025 10:29:46 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="📦-httpmessageconverter">📦** HttpMessageConverter**</h1>
<hr>
<blockquote>
<p>기존 구조의 <code>view resolver</code> 가 화면을 보여주는 대신, <strong>http 메세지를 보여줌</strong></p>
</blockquote>
 <br>


<h1 id="📖-httpmessageconverter의-구조">📖 HttpMessageConverter의 구조</h1>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/cc386aab-9fd1-4087-857a-445d5f56c5a1/image.png" alt=""></p>
<ul>
<li><p><strong>요청시</strong> ⇢ <strong><code>ArgumentResolver</code></strong> 사용
: <code>@RequestBody</code>, <code>HttpEntity&lt;&gt;</code>,  <code>RequestEntity&lt;&gt;</code> </p>
</li>
<li><p><strong>응답시</strong> ⇢ <strong><code>ReturnValueHandler</code></strong> 사용
: <code>@ResponseBody</code>, <code>HttpEntity&lt;&gt;</code>, <code>ResponseEntity&lt;&gt;</code></p>
<p><strong>💡 구조상 양방향으로 (요청, 응답) 전부 작동한다</strong></p>
<br>

</li>
</ul>
<h2 id="📌-우선순위">📌 우선순위</h2>
<blockquote>
<p>▶️ <code>Byte</code> ⇢ <code>String</code> ⇢ <strong><code>JSON</code></strong></span></p>
</blockquote>
<ul>
<li>요청이나 응답을 변환할때 <strong>어떤 Media Type인지 확인</strong></li>
<li>순서대로 검사해서 가능한 게 있으면 그걸로 처리하고 나머지는 무시함</li>
</ul>
 <br>


<h2 id="📌-동작-흐름">📌 동작 흐름</h2>
<blockquote>
<p><strong>요청</strong></p>
</blockquote>
<ul>
<li>1️⃣ <code>RequestBody</code>나 <code>파라미터</code>로 바인딩</li>
<li>2️⃣ <code>canRead()</code>로 데이터 타입을 조회, <code>Content-Type</code> 헤더의 미디어타입 지원여부 확인</li>
<li>3️⃣ <code>read()</code>로 객체로 변환</li>
</ul>
<blockquote>
<p><strong>응답</strong></p>
</blockquote>
<ul>
<li>1️⃣ 컨트롤러가 <code>@ResponseBody</code>나 <code>HttpEntity&lt;&gt;</code> 리턴함</li>
<li>2️⃣ <code>canWrite()</code>로 데이터 타입을 조회, <code>Accept</code> 헤더의 미디어타입 지원여부 확인</li>
<li>3️⃣ <code>write()</code> 로 응답메세지 바디에 직접 데이터 입력</li>
</ul>
<hr>
<h1 id="📥-argumentresolver-요청">📥 ArgumentResolver (요청)</h1>
<blockquote>
<p>🌟 컨트롤러 메서드의 <strong>파라미터를 자동으로 바인딩</strong>해주는 인터페이스</p>
</blockquote>
<ul>
<li>Spring MVC 구조에서 요청이 컨트롤러로 가기 전 <code>RequestMappingHandlerAdapter</code>가 호출함</li>
<li>Controller가 <strong>필요한 파라미터 값을 생성</strong>함</li>
<li><strong>값이 준비되면</strong> 실제 컨트롤러를 호출한다</li>
</ul>
<p>📍 <strong>실제 클래스 이름 : <code>HandlerMethodArgumentResolver</code></strong></p>
<h3 id="✔️-argumentresolver의-종류">✔️ <a href="https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/arguments.html">ArgumentResolver</a>의 종류</h3>
<hr>
<h1 id="📤-returnvaluehandler-응답">📤 ReturnValueHandler (응답)</h1>
<blockquote>
<p>🌟 컨트롤러 메서드가 반환하는 값을 <strong>HTTP 응답으로 변환</strong>하는 인터페이스</p>
</blockquote>
<ul>
<li><code>ModelAndView</code>, <code>@ResponseBody</code>, <code>HttpEntity&lt;&gt;</code> 등</li>
<li>예를 들어, 컨트롤러에서 String 리턴해도 <strong>문자 그대로가 아니라 View 이름</strong>임을 인식함</li>
</ul>
<p>📍 <strong>실제 클래스 이름 : <code>HandlerMethodReturnValueHandler</code></strong></p>
<h3 id="✔️-returnvaluehandler의-종류">✔️ ReturnValueHandler의 종류</h3>
<ul>
<li>인터페이스이므로 확장가능, 구현체 많음</li>
</ul>
<hr>
<blockquote>
<h3 id="💡-요청과-응답-모두에서-사용되는-클래스">💡 요청과 응답 모두에서 사용되는 클래스</h3>
<p>▶️<code>ArgumentResolver</code> + <code>ReturnValueHandler</code>를 둘다 구현한 클래스</p>
</blockquote>
<table>
<thead>
<tr>
<th>클래스명</th>
<th>처리 대상</th>
</tr>
</thead>
<tbody><tr>
<td>RequestResponseBodyMethodProcessor</td>
<td>@RequestBody, @ResponseBody</td>
</tr>
<tr>
<td>HttpEntityMethodProcessor</td>
<td>HttpEntity&lt;&gt;, RequestEntity&lt;&gt;, ResponseEntity&lt;&gt;</td>
</tr>
<tr>
<td>→ 내부에서 HttpMessageConverter를 호출해 객체 생성 또는 응답 변환을 처리함</td>
<td></td>
</tr>
</tbody></table>
 <br>


<hr>
<h1 id="📦-converter-fomatter">📦 <strong>Converter, Fomatter</strong></h1>
<hr>
<h1 id="⚙️-webmvcconfigurer">⚙️ WebMvcConfigurer</h1>
<blockquote>
<p>설정을 통해 <code>Converter</code>, <code>Formatter</code>를 추가할 수 있다</p>
</blockquote>
<ul>
<li>커스텀 <code>Converter</code>, <code>Formatter</code>, <code>HttpMessageConverter</code> 등을 등록할 때 <strong>오버라이딩</strong>해서 사용</li>
<li>속에 <code>@Configuration</code> 도 있음 ⇢ <code>@Component</code>를 포함 ⇢ Spring Bean으로 등록</li>
</ul>
<hr>
<h1 id="📖-converter">📖 Converter</h1>
<blockquote>
<p><strong>객체의 타입을 서로 변환할때</strong></p>
</blockquote>
<ul>
<li><p><strong><code>HttpServletRequest</code></strong>
기본적으로 요청 파라미터는 문자열로 처리가 되기 때문에
숫자로 변환하고 싶다면, 하나씩 <strong>직접</strong>변환해줘야한다</p>
<pre><code class="language-java">@Slf4j
 @RestController
 public class TypeConverterController {

     @GetMapping(&quot;/param&quot;)
     public void param(HttpServletRequest request) {

         // 조회시 : String
         String stringExample = request.getParameter(&quot;example&quot;);

         // ✅Integer로 Type 변환
         Integer integerExample = Integer.valueOf(stringExample);
         log.info(&quot;integerExample = {}&quot;, integerExample);

     }
   }</code></pre>
</li>
</ul>
<ul>
<li><strong><code>@RequestParam</code></strong>
변환 안거쳐도 알아서 나온다~~ (바인딩될 타입을 정해놨음)
요걸 누군가 알아서 타입을 자동으로 변환해준다! 이게 타입 컨버터??<pre><code class="language-java">@GetMapping(&quot;/v2/param&quot;)
public void paramV2(@RequestParam Integer example) {
      // Integer 타입으로 바인딩
  log.info(&quot;example = {}&quot;, example);
}</code></pre>
</li>
</ul>
<br>

<h2 id="📌-converter-interface">📌 Converter Interface</h2>
<blockquote>
<p>특정타입을 다른 타입으로 변환할때 사용하는 인터페이스</p>
</blockquote>
<ul>
<li><p><strong>새로운 타입의 변환</strong></p>
<ul>
<li><code>localhost:8080/type-converter?person=wonuk:120</code></li>
<li>입력받은 문자열 데이터를 <code>Person</code> 객체로 변환</li>
</ul>
<pre><code class="language-java">public class Person {
  private String name;
  private String age;
}</code></pre>
<ul>
<li><code>wonuk:120</code> → <code>TypeConverter</code> → name: <code>wonuk</code> , age : <code>10</code> (개월수를 나이로)</li>
<li>이럴때 컨버터 인터페이스 사용!</li>
</ul>
</li>
</ul>
<ul>
<li>스프링이 제공하는 인터페이스이므로 implements해서 컨버터로 등록하면 된다</li>
<li>변환하고자 하는 타입에 맞춰서 Type Converter를 구현하고 등록하면 된다</li>
</ul>
<br>

<h2 id="📌-converter-사용하기">📌 Converter 사용하기</h2>
<blockquote>
<p>⚠️ <strong>주의</strong> : 같은 이름을 가진 <code>interface</code>가 많으니 주의
<code>org.springframework.core.convert.converter</code></p>
</blockquote>
<ul>
<li>1️⃣ Converter implements 하기</li>
<li>2️⃣ Converter를 열고<ul>
<li>S : 변환할 소스</li>
<li>T : 변환할 타입</li>
</ul>
</li>
</ul>
<h3 id="✔️-예시">✔️ 예시</h3>
<hr>
<p>❶ <code>String</code> → <code>Integer</code></p>
<pre><code class="language-java">@Slf4j
public class StringToIntegerConverter implements Converter&lt;String, Integer&gt; {

    @Override
    public Integer convert(String source) {
        log.info(&quot;source = {}&quot;, source);
        // 검증
        return Integer.valueOf(source);
    }

 }</code></pre>
<p>❷ <code>Integer</code> → <code>String</code></p>
<pre><code class="language-java">@Slf4j
public class IntegerToStringConverter implements Converter&lt;Integer, String&gt; {

    @Override
    public String convert(Integer source) {
        log.info(&quot;source = {}&quot;, source);
        return String.valueOf(source);
    }
}</code></pre>
<p>❸ <code>String</code> → <code>Person</code></p>
<p>(요청예시 : <code>localhost:8080/type-converter?person=wonuk:1200</code>)</p>
<pre><code class="language-java">@Getter
public class Person {

        // 이름
        private String name;
        // 나이
        private int age;

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

}

------------------

public class StringToPersonConverter implements Converter&lt;String, Person&gt; {
        // source = &quot;wonuk:1200&quot;
        @Override
        public Person convert(String source) {
            // &#39;:&#39; 를 구분자로 나누어 배열로 만든다.
            String[] parts = source.split(&quot;:&quot;);

            // 첫번째 배열은 이름이다. -&gt; wonuk
        String name = parts[0];
        // 두번째 배열은 개월수이다. -&gt; 1200
        int months = Integer.parseInt(parts[1]);

            // 개월수 나누기 12로 나이를 구하는 로직 (12개월 단위만 고려)
            int age = months / 12;

            return new Person(name, age);
        }
}

------------------

public class PersonToStringConverter implements Converter&lt;Person, String&gt; {

        @Override
        public String convert(Person source) {
                // 이름
                String name = source.getName();
                // 개월수
                int months = source.getAge * 12;
                // &quot;wonuk:1200&quot;
                return name + &quot;:&quot; + months;
        }

}
------------------

PersonToStringConverter converter = new PersonToStringConverter();
String source = &quot;wonuk:1200&quot;;
converter.convert(source);</code></pre>
<br>

<h2 id="📌-spring이-제공하는-converter">📌 Spring이 제공하는 Converter</h2>
<ol>
<li><p><code>Converter</code> </p>
<ul>
<li>기본적인 변환을 담당하는 인터페이스</li>
<li>단일 타입에서 단일 타입으로 변환할 때 사용한다.<ul>
<li><code>Converter&lt;Source, Type&gt;</code></li>
</ul>
</li>
</ul>
</li>
<li><p><code>ConverterFactory</code></p>
</li>
<li><p><code>GenericConverter</code></p>
</li>
<li><p><code>ConditionalGenericConverter</code> </p>
</li>
</ol>
<blockquote>
<p>1번만 외우고, 나머지는 필요할때 <a href="https://docs.spring.io/spring-framework/reference/core/validation/convert.html">공식문서</a>에서 찾던지~</p>
</blockquote>
<hr>
<h1 id="🔄-conversionservice">🔄 ConversionService</h1>
<blockquote>
<p>컨버터를 모아서 편리하게 사용하게 해준다</p>
</blockquote>
<ul>
<li>인터페이스인데 컨버터를 사용할 수 있는지도 확인해주고, 사용도 해준다</li>
<li>우선 순위 = 내가 커스텀해 구현한 컨버터 &gt;&gt;&gt;&gt; 기본제공 컨버터</li>
</ul>
<h2 id="📌-defalutconversionservice">📌 DefalutConversionService</h2>
<p>ConversionService를 구현한 구현체, ConvertRegistry에 다양한 Converter를 등록</p>
<h2 id="📌-converterregistry">📌 ConverterRegistry</h2>
<p>Converter를 등록하고 관리</p>
<h3 id="✔️-예시-1">✔️ 예시</h3>
<hr>
<pre><code class="language-java">import static org.assertj.core.api.Assertions.*;// ✅ Assertions static 임포트 필요

public class ConversionServiceTest {

    @Test
    void defaultConversionService() {
        // given
        DefaultConversionService dcs = new DefaultConversionService();
        dcs.addConverter(new StringToPersonConverter());
        Person wonuk = new Person(&quot;wonuk&quot;, 100);

        // when
        Person stringToPerson = dcs.convert(&quot;wonuk:1200&quot;, Person.class);

        // then 
        assertThat(stringToPerson.getName()).isEqualTo(wonuk.getName());
        assertThat(stringToPerson.getAge()).isEqualTo(wonuk.getAge());
    }

}</code></pre>
<p>위 코드를 통해 알수있는 것 ⇢&gt; 컨버터 <strong>등록 / 사용은 분리</strong> ! (ISP 원칙 적용)</p>
<h2 id="📌-사용해보기">📌 사용해보기</h2>
<ul>
<li>이미 내부적으로 ConversionService 가 있으니까</li>
</ul>
<blockquote>
</blockquote>
<p>1️⃣ <strong><code>WebMvcConfigurer</code>를 구현</strong>
<img src="https://velog.velcdn.com/images/luv_lyn/post/36c9d939-a801-4bf9-b2e7-9284f125f9be/image.png" alt="">
2️⃣ <strong><code>addFomatters()</code>로 컨버터를 등록만하고 사용</strong>
<img src="https://velog.velcdn.com/images/luv_lyn/post/db724247-5c49-4d3f-bc32-a92f7b9383c5/image.png" alt="">
▶️ <strong>결과</strong>
<img src="https://velog.velcdn.com/images/luv_lyn/post/c57dbd2c-37da-46ae-ae2f-be14a0aa2264/image.png" alt=""></p>
<br>


<hr>
<h1 id="📖-formatter">📖 Formatter</h1>
<blockquote>
<p>객체를 특정한 포맷에 맞춰서 <strong>문자로 출력</strong>하는데 특화 (<code>Converter</code>보다 더 세부기능 )</p>
</blockquote>
<ul>
<li><code>printer</code>, <code>parser</code> 상속 ( 객체를 문자로 변환 / 문자를 객체로 변환 )</li>
</ul>
<p><strong>Locale</strong></p>
<ul>
<li>지역 / 언어 정보를 나타내는 객체
언어코드 <code>en</code>, <code>ko</code> / 국가코드 <code>US</code>, <code>KR</code></li>
</ul>
<br>

<h2 id="📌-사용하기">📌 사용하기</h2>
<hr>
<p><strong>1️⃣ <code>Formatter</code> 만들기</strong></p>
<pre><code class="language-java">@Slf4j
public class PriceFormatter implements Formatter&lt;Number&gt; {

    @Override
  public Number parse(String text, Locale locale) throws ParseException {
    log.info(&quot;text = {}, locale={}&quot;, text, locale);

        // 변환 로직
        // NumberFormat이 제공하는 기능
        NumberFormat format = NumberFormat.getInstance(locale);
        // &quot;10,000&quot; -&gt; 10000L
        return format.parse(text);
  }

  @Override
  public String print(Number object, Locale locale) {
            log.info(&quot;object = {}, locale = {}&quot;, object, locale);
            // 10000L -&gt; &quot;10,000&quot;
      return NumberFormat.getInstance(locale).format(object);
  }

}</code></pre>
<p><strong>2️⃣ 변환하기</strong></p>
<pre><code class="language-java">class PriceFormatterTest {

        PriceFormatter formatter = new PriceFormatter();

    @Test
    void parse() throws ParseException {
        // given, when
        Number result = formatter.parse(&quot;1,000&quot;, Locale.KOREA);

        // then
        // parse 결과는 Long
        Assertions.assertThat(result).isEqualTo(1000L);
    }

    @Test
    void print() {
        // given, when
        String result = formatter.print(1000, Locale.KOREA);

        // then
        Assertions.assertThat(result).isEqualTo(&quot;1,000&quot;);
    }
}</code></pre>
<br>

<h2 id="📌-spring이-제공하는-formatter">📌 Spring이 제공하는 Formatter</h2>
<h3 id="✔️-formattingconversionservice">✔️ FormattingConversionService</h3>
<blockquote>
<p><code>ConversionService</code> + <code>Formatter</code> 결합 구현체</p>
</blockquote>
<p>어댑터 패턴으로 포매터가 컨버터처럼 동작하도록 만들어준다</p>
<h3 id="✔️-defaultformatteingconversionservice">✔️ DefaultFormatteingConversionService</h3>
<p><code>FormattingConversionService</code> + 통화, 숫자관련 Formatter를 추가한 것</p>
<pre><code class="language-java">public class FormattingConversionServiceTest {

    @Test
    void formattingConversionService() {

        // given
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();

        // Converter 등록
        conversionService.addConverter(new StringToPersonConverter());
        conversionService.addConverter(new PersonToStringConverter());
        // Formatter 등록
        conversionService.addFormatter(new PriceFormatter());

        // when
        String result = conversionService.convert(10000, String.class);

        // then
        Assertions.assertThat(result).isEqualTo(&quot;10,000&quot;);

    }

}</code></pre>
<h3 id="✔️-annotation-dto">✔️ Annotation (DTO)</h3>
<p><code>@NumberFormat</code> - 숫자관련 지정 Formatter 사용
<code>@DateTimeFormat</code> - 날짜관련 지정 Formatter 사용 등등,,, </p>
<p>💡외울 필요 없음 <a href="https://docs.spring.io/spring-framework/reference/core/validation/format.html#format-CustomFormatAnnotations">공식문서</a>에서 찾으셈</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🚀chaper 3 ) 일정관리 앱 Develop 과제 [Level 4] / TIL day 31]]></title>
            <link>https://velog.io/@luv_lyn/chaper-3-%EC%9D%BC%EC%A0%95%EA%B4%80%EB%A6%AC-%EC%95%B1-Develop-%EA%B3%BC%EC%A0%9C-Level-4-TIL-day-31</link>
            <guid>https://velog.io/@luv_lyn/chaper-3-%EC%9D%BC%EC%A0%95%EA%B4%80%EB%A6%AC-%EC%95%B1-Develop-%EA%B3%BC%EC%A0%9C-Level-4-TIL-day-31</guid>
            <pubDate>Fri, 04 Apr 2025 02:16:46 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="📝-과제-개요">📝 과제 개요</h1>
<hr>
<h3 id="✔️-개발-환경">✔️ 개발 환경</h3>
<ul>
<li>Java: 17</li>
<li>JDK: 17.0.1</li>
<li>IDE: IntelliJ</li>
<li>Spring Frame Work : 3.4.4</li>
<li>API 테스트 도구 : postman</li>
</ul>
<br>

<h3 id="📌-공통-조건">📌 공통 조건</h3>
<ul>
<li>모든 테이블은 고유 식별자(<code>ID</code>)를 가짐</li>
<li><code>3 Layer Architecture</code>에 따라 각 계층의 역할에 맞춰 개발</li>
<li>모든 CRUD 기능은 <strong>JPA 기반</strong>으로 구현</li>
<li>인증/인가 절차는 <code>Cookie/Session</code> 방식으로 처리</li>
<li>JPA 연관관계는 <strong>기본 단방향</strong>, 꼭 필요한 경우에만 양방향 설정</li>
</ul>
<h4 id="🔑-키워드-crud-관련-어노테이션">🔑 키워드 (CRUD 관련 어노테이션)</h4>
<ul>
<li><code>@Entity</code> : 테이블과 매핑되는 클래스에 사용</li>
<li><code>@Id</code> : 기본 키 필드 지정</li>
<li><code>@GeneratedValue</code> : 기본 키 자동 생성 전략 설정</li>
<li><code>@Repository</code> : DAO 클래스임을 명시</li>
<li><code>@ManyToOne</code> / <code>@OneToMany</code> : 테이블 간 연관관계 설정</li>
<li><code>@JoinColumn</code> : join 대상 컬럼 지정</li>
</ul>
<br>

<hr>
<h1 id="🎨-구현한-내용">🎨 구현한 내용</h1>
<hr>
<blockquote>
<h3 id="🌟-git-hub-링크--readme--api-명세서">🌟 <a href="https://github.com/harinbeee/scheduler-develop-project">Git Hub 링크 &amp; ReadMe</a> / <a href="https://documenter.getpostman.com/view/43200298/2sB2cU9Mng">API 명세서</a></h3>
</blockquote>
<table>
<thead>
<tr>
<th align="center">필수기능</th>
<th align="center">단계별</th>
<th align="left">요구사항</th>
</tr>
</thead>
<tbody><tr>
<td align="center">✅</td>
<td align="center"><strong>Level 0</strong></td>
<td align="left">API 명세서 작성하기<br> ERD 작성하기<br> SQL 작성하기</td>
</tr>
<tr>
<td align="center">✅</td>
<td align="center"><strong>Level 1</strong></td>
<td align="left">일정 생성 (작성자명, 제목, 내용, 작성일, 수정일) <br> 전체 일정 조회<br>일정 수정 <br>일정 삭제</td>
</tr>
<tr>
<td align="center">✅</td>
<td align="center"><strong>Level 2</strong></td>
<td align="left">유저 생성 (유저명, 이메일, 작성일, 수정일)<br>유저 조회<br>유저 수정<br>유저 삭제<br>일정에 유저 식별자로 연관관계 설정</td>
</tr>
<tr>
<td align="center">✅</td>
<td align="center"><strong>Level 3</strong></td>
<td align="left">회원가입시 비밀번호 필드 추가</td>
</tr>
<tr>
<td align="center">✅</td>
<td align="center"><strong>Level 4</strong></td>
<td align="left">로그인 기능 구현 (Cookie/Session 활용)<br>로그인 필터 등록, 필터로 인증<br>회원가입/로그인은 인증 제외<br>로그인 실패 시 HTTP 401 응답, 예외 처리</td>
</tr>
</tbody></table>
<br>

<hr>
<h1 id="📋-참고했던-링크">📋 참고했던 링크</h1>
<hr>
<h2 id="1️⃣-커밋-컨벤션">1️⃣ 커밋 컨벤션</h2>
<p><a href="https://sungwookoo.tistory.com/1">https://sungwookoo.tistory.com/1</a></p>
<ul>
<li>이번에는 커밋 히스토리를 깔끔하게 하고자 노력했다</li>
</ul>
<h2 id="2️⃣커스텀-익셉션">2️⃣커스텀 익셉션</h2>
<p><a href="https://dev-allday.tistory.com/77">https://dev-allday.tistory.com/77</a>
<a href="https://beaniejoy.tistory.com/93">https://beaniejoy.tistory.com/93</a></p>
<ul>
<li>지난 미니세션을 듣고 커스텀 익셉션을 꼭 활용해야겠다 생각했는데,
생각보다 어려웠다</li>
</ul>
<h2 id="3️⃣로그인-기능-구현">3️⃣로그인 기능 구현</h2>
<p><a href="https://congsong.tistory.com/38">https://congsong.tistory.com/38</a></p>
<br>

<hr>
<h1 id="🛠️-troubleshooting">🛠️ TroubleShooting</h1>
<hr>
<h2 id="❶-patch-요청시-새로운-데이터-생성-문제">❶ Patch 요청시 새로운 데이터 생성 문제</h2>
<h3 id="📌-문제">📌 문제</h3>
<ul>
<li>스케줄 수정 후 <code>PATCH</code> 요청을 보냈는데, 응답이 잘 와서 문제가 없는 줄 알았다!
나중에 DB를 보니까 응답과 다르게 기존 스케줄이 수정된 게 아니라 <strong>새로운 아이디로 스케줄이 추가</strong>되고 있었다</li>
<li>문제는 <code>Repository</code>의 <code>save()</code> 때문이었다. insert/update를 자동으로 처리해줄거라 생각했는데, 내 코드에서는 일부 수정시 기존 엔터티가 없는 것으로 판단해서인지 <strong><code>INSERT</code>만 하고 있었다</strong></li>
</ul>
<h3 id="💡-해결방법">💡 해결방법</h3>
<blockquote>
<p><strong>❎ 문제 코드</strong></p>
<pre><code class="language-java">Schedule schedule = new Schedule(title, description);
Schedule updatedSchedule = scheduleRepository.save(schedule);</code></pre>
<hr>
<p><strong>✅ 해결 코드</strong></p>
<img src='https://velog.velcdn.com/images/luv_lyn/post/58460274-8523-471f-bad4-296c939b899f/image.png' width=100% >
</blockquote>
<ul>
<li><p><a href="https://brunch.co.kr/@anonymdevoo/37">Save는 Insert와 Update를 어떻게 구분할까?</a> / <a href="https://karla.tistory.com/466">JPA 변경 감지</a></p>
</li>
<li><p>구글링 한 결과 <code>@DynamicUpdate</code> 나 다른 방법이 있는 것 같았으나,,
나는 일단 <strong><code>@Transactional</code></strong>을 추가하고 <code>update</code> 를 사용했다</p>
<hr>
</li>
</ul>
<h2 id="❷-patch-요청시-부분-수정이-아닌-전체-덮어씌워짐null까지">❷ Patch 요청시 부분 수정이 아닌 전체 덮어씌워짐(null까지)</h2>
<h3 id="📌-문제-1">📌 문제</h3>
<ul>
<li>스케줄 수정시 제목만 바꾸고 싶어서 제목만 넣어서 <code>PATCH</code> 했더니, 
다른 값들은 <strong>null로 다 덮어씌워짐..!</strong>  심지어 기존에 있던 값들도 null이 되어버렸다</li>
</ul>
<h3 id="💡-해결방법-1">💡 해결방법</h3>
<blockquote>
<p><strong>✅ 해결 코드</strong></p>
<img src='https://velog.velcdn.com/images/luv_lyn/post/48dbebd9-eadd-4eff-b557-4c3f6edc05fd/image.png' width=100% >
</blockquote>
<ul>
<li><p>조건문으로 기존 데이터가 존재하는지 찾아두고 
새로 받은 요청이 <code>null</code>이면 기존 값을 유지, <code>null</code>이 아니면 그 값으로 업데이트 하도록 했다</p>
<hr>
<h2 id="❸-로그인-필터-white_list-경로-오류">❸ 로그인 필터 white_list 경로 오류</h2>
<h3 id="📌-문제-2">📌 문제</h3>
</li>
<li><p>예제와 같이 로그인 필터에서 로그인 없이 접근 가능한 경로를 whiteList로 설정해뒀는데,
/user, /login으로 해두고도 계속 필터링이 안 됐다</p>
</li>
<li><p>알고 보니 실제 내 컨트롤러 경로는 /user/login이라서… 매칭이 안 됐다😇</p>
</li>
</ul>
<h3 id="💡-해결방법-2">💡 해결방법</h3>
<ul>
<li><p>WhiteList 를 실제 URI 경로에 맞게 수정했다</p>
</li>
<li><p>항상 URI를 확인하고 작성해야 한다..!</p>
<hr>
<h2 id="❹-post-요청에서-username-직접-받지-않고-세션에서-꺼내쓰기">❹ Post 요청에서 username 직접 받지 않고 세션에서 꺼내쓰기</h2>
<h3 id="📌-문제-3">📌 문제</h3>
</li>
<li><p>일정 생성 요청에서 <code>username</code>을 request로 받아서 쓰고 있었는데,
로그인 구현 후에는 세션에 이미 로그인 정보가 있는 상태니까
굳이 클라이언트에서 <code>username</code>을 또 받을 필요가 없어졌다.</p>
</li>
</ul>
<h3 id="💡-해결방법-3">💡 해결방법</h3>
<blockquote>
<p><strong>❎ 기존 코드</strong></p>
<pre><code class="language-java">// 1. 스케줄 생성
@PostMapping
public ResponseEntity&lt;ScheduleResponseDto&gt; save(
           @RequestBody ScheduleRequestDto requestDto
       ){
       ScheduleResponseDto scheduleResponseDto = scheduleService.save(requestDto.getTitle(), requestDto.getDescription(), requestDto.getUsername());
       return new ResponseEntity&lt;&gt;(scheduleResponseDto, HttpStatus.CREATED);</code></pre>
<hr>
<p><strong>✅ 해결 코드</strong></p>
<img src='https://velog.velcdn.com/images/luv_lyn/post/fd13e065-f2d5-4a0a-8b86-e2ae466e00f6/image.png' width=100% >
</blockquote>
<ul>
<li><p><strong><code>request.getSession().getAttribute(&quot;username&quot;)</code></strong> 작성,
그리고 <code>ScheduleResopnseDto</code> 에서는 username 필드를 아예 없애버렸다</p>
<hr>
</li>
</ul>
<h2 id="❺-에러코드-enum-사용-중-instance-오류">❺ 에러코드 enum 사용 중 instance 오류</h2>
<h3 id="📌-문제-4">📌 문제</h3>
<ul>
<li>enum으로 정의한 <code>errorCode.USER_NOT_FOUND</code>를 사용하는데, 자꾸만 인스턴스 오류 발생,,
enum을 사용해본적도 없고(ㅋㅋ) 틀린 부분을 찾을 수 없어 한참을 해맸다</li>
</ul>
<blockquote>
<p><strong>❎ 에러 메세지</strong></p>
<pre><code>Static member &#39;com. example. schedulerproject. exception. ErrorCode. USER_NOT_FOUND&#39; accessed via instance reference</code></pre></blockquote>
<pre><code>&gt;**❎ 에러 코드**
```java
public class UserNotFoundException extends CustomException{
    public UserNotFoundException(ErrorCode errorCode) {
        super(errorCode.USER_NOT_FOUND);
    }
}  
&gt;```

### 💡 해결방법
- `ErrorCode`에서 enum의 `static`으로 선언된 것들을 인스턴스 사용하듯 사용해서 문제가 있었다!
클래스 이름으로 접근해야한다..!
- **`super(ErrorCode.USER_NOT_FOUND);`** 한글자씩만 바꾸니 해결😂



&lt;br&gt;

---

# 🌟 나의 회고
---



</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[🚀chaper 3 ) 일정관리 앱 과제 [Level 2] / TIL day  24]]></title>
            <link>https://velog.io/@luv_lyn/chaper-3-%EC%9D%BC%EC%A0%95%EA%B4%80%EB%A6%AC-%EC%95%B1-%EA%B3%BC%EC%A0%9C-Level-12-TIL-day</link>
            <guid>https://velog.io/@luv_lyn/chaper-3-%EC%9D%BC%EC%A0%95%EA%B4%80%EB%A6%AC-%EC%95%B1-%EA%B3%BC%EC%A0%9C-Level-12-TIL-day</guid>
            <pubDate>Wed, 26 Mar 2025 02:52:48 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="📝-과제-개요">📝 과제 개요</h1>
<hr>
<h3 id="✔️-개발-환경">✔️ 개발 환경</h3>
<ul>
<li>Java: 17</li>
<li>JDK: 17.0.1</li>
<li>IDE: IntelliJ</li>
<li>Spring Frame Work : 3.4.4</li>
<li>API 테스트 도구 : postman</li>
</ul>
<h3 id="✔️-공통-조건">✔️ 공통 조건</h3>
<ul>
<li>일정 작성, 수정, 조회 시 - <code>비밀번호</code> 안 보이게</li>
<li>일정 수정, 삭제 - <code>요청 비밀번호</code>와 <code>비밀번호</code>가 같을 때</li>
<li><code>3 Layer Architecture</code> 활용</li>
<li>CRUD 필수 기능은 데이터 베이스 연결 + <code>JDBC</code> 활용</li>
</ul>
<br>




<hr>
<h1 id="🎨-구현한-내용">🎨 구현한 내용</h1>
<hr>
<blockquote>
<h4 id="🌟-git-hub-링크--readme--api-명세서">🌟 <a href="https://github.com/harinbeee/scheduler-project">Git Hub 링크 &amp; ReadMe</a> / <a href="https://documenter.getpostman.com/view/43200298/2sAYkKJy7F">API 명세서</a></h4>
</blockquote>
<table>
<thead>
<tr>
<th align="center">필수기능</th>
<th align="center">단계별</th>
<th align="left">요구사항</th>
</tr>
</thead>
<tbody><tr>
<td align="center">✅</td>
<td align="center"><strong>Level 0</strong></td>
<td align="left">API 명세서 작성하기, ERD 작성하기, SQL 작성하기</td>
</tr>
<tr>
<td align="center">✅</td>
<td align="center"><strong>Level 1</strong></td>
<td align="left">일정 생성 (고유 식별자 자동 생성하여 관리)</td>
</tr>
<tr>
<td align="center"></td>
<td align="center"></td>
<td align="left">전체 일정 조회 (수정일, 작성자명 바탕으로 일정 조회)</td>
</tr>
<tr>
<td align="center"></td>
<td align="center"></td>
<td align="left">선택 일정 조회 (고유 식별자로 조회 )</td>
</tr>
<tr>
<td align="center">✅</td>
<td align="center"><strong>Level 2</strong></td>
<td align="left">선택 일정 수정 (비밀번호 검증)</td>
</tr>
<tr>
<td align="center"></td>
<td align="center"></td>
<td align="left">선택 일정 삭제 (비밀번호 검증)</td>
</tr>
</tbody></table>
<br>


<hr>
<h1 id="📋-고민했던-부분">📋 고민했던 부분</h1>
<hr>
<h3 id="1️⃣-적절한-관심사-분리를-적용했는가">1️⃣ 적절한 관심사 분리를 적용했는가?</h3>
<ul>
<li><p><strong><code>Controller</code></strong>
최대한 요청에 대한 응답을 반환하는 역할만 하도록 분리했다</p>
</li>
<li><p><strong><code>Service</code></strong>
비즈니스 로직을 담당하고, 검증이나 예외처리를 여기서 하려고 노력했다.
(생각해보니 전체 일정 조회-조건부 필터링 할 때 null 검증을 이곳 저곳에서 한 것 같다🥲)</p>
</li>
<li><p><strong><code>Repository</code></strong>
DB를 입력하고 꺼내오고 기존 DB와 비밀번호가 일치하는지 등등.. 
JDBC를 기반으로 sql을 활용해 조건을 필터링했다</p>
</li>
</ul>
<br>

<h3 id="2️⃣-restful한-api를-설계했는가">2️⃣ RESTful한 API를 설계했는가?</h3>
<ul>
<li><p><strong>✔️ RESTful API 체크리스트</strong></p>
<table>
<thead>
<tr>
<th>항목</th>
<th>준수 여부</th>
</tr>
</thead>
<tbody><tr>
<td><strong>리소스 명사 사용</strong></td>
<td>✅</td>
</tr>
<tr>
<td><strong>복수형 사용</strong></td>
<td>✅</td>
</tr>
<tr>
<td><strong>동사 허용 조건</strong></td>
<td>동사 사용 없음</td>
</tr>
<tr>
<td><strong>자원 계층 관계 표현</strong></td>
<td>✅ 공통 계층<code>/schedules</code>사용, 기능에 따라<code>/{id}</code> 사용</td>
</tr>
<tr>
<td><strong>끝에 슬래시 금지</strong></td>
<td>✅</td>
</tr>
<tr>
<td><strong>(언더바 사용 금지)하이픈 사용</strong></td>
<td>하이픈 사용 없음</td>
</tr>
<tr>
<td><strong>소문자 사용</strong></td>
<td>✅</td>
</tr>
<tr>
<td><strong>파일 확장자 포함 금지</strong></td>
<td>✅</td>
</tr>
<tr>
<td><strong>CRUD 함수명 미사용</strong></td>
<td>✅ <code>POST</code>, <code>DELETE</code> 등 HTTP 메서드 사용</td>
</tr>
<tr>
<td><strong>필터/정렬/페이징</strong></td>
<td>✅ 일정 전체 조회(필터)시 query param 사용 /schedules?user=Surin&amp;date=2025-03-25</td>
</tr>
</tbody></table>
</li>
</ul>
<br>



<h3 id="3️⃣-수정-삭제-api의-request를-어떤-방식으로-사용했는가">3️⃣ 수정, 삭제 API의 request를 어떤 방식으로 사용했는가?</h3>
<ul>
<li><strong>id : Path로 전달</strong></li>
<li><strong>비밀번호 : Body 내의 dto로 전달</strong>
수정과 삭제 API 모두 ID는 <code>@PathVariable</code>로 전달하고
비밀번호는 query나 param에 노출하지 않고 <code>@RequestBody</code>로 전달하여 repository에서 검증했다</li>
</ul>
<br>

<hr>
<h1 id="🛠️-troubleshooting">🛠️ TroubleShooting</h1>
<hr>
<h2 id="❶-관심사-분리">❶ 관심사 분리</h2>
<h3 id="📌-문제">📌 문제</h3>
<ul>
<li>아직 모든게 미숙했기에 강의 내 &#39;메모장 프로젝트&#39;를 참고해서
모든 로직을 컨트롤러에 다 넣어 만들고 ➝ 관심사를 분리하고 ➝ DB에 연결하며 더 확실하게 역할을 나누는 과정으로 작성했다.</li>
</ul>
<h3 id="💡-해결방법">💡 해결방법</h3>
<ul>
<li><code>Controller</code>는 요청/응답 담당</li>
<li><code>Service</code>는 검증 및 로직 담당</li>
<li><code>Repository</code>는 DB와 직접 통신</li>
<li>강의대로 하다보니 <code>Service</code>, <code>Repository</code> 인터페이스를 만들었는데,
생성자나 매개변수가 바뀔때마다 인터페이스에서 한번에 수정하고 임플리먼트하니까 편했다</li>
</ul>
<hr>
<h2 id="❷-수정--삭제시-비밀번호-처리">❷ 수정 / 삭제시 비밀번호 처리</h2>
<h3 id="📌-문제-1">📌 문제</h3>
<ul>
<li>공통 조건 충족을 위해 수정이나 삭제시에는 비밀번호를 받아 검증해야 했고,
반환될 때는 응답되어선 안됐다... 이건 해본 적이 없었다..! 실습예제에 없다..😂</li>
</ul>
<h3 id="💡-해결방법-1">💡 해결방법</h3>
<ul>
<li><p>id는 @PathVariable / 비밀번호는 @RequestBody를 통해 dto로 받아 전달했다</p>
<img src='https://velog.velcdn.com/images/luv_lyn/post/b03c013b-2ebd-44f0-bdc5-1669b49c33f3/image.png' width=70%>
<img src='https://velog.velcdn.com/images/luv_lyn/post/b6f33561-0571-4d8f-a456-6ad09794728d/image.png' width=70%>
</li>
<li><p>둘다 비밀번호를 활용해야 해서 <code>ScheduleRequestDto</code>를 활용하고
강의에서 나온대로 Service에서 <code>int</code>타입을 사용해서 예외 상황을 가정했다</p>
</li>
<li><p>Repository에서 비밀번호가 일치하는지 검증하기 전에 Service에서 예외상황에 대한 상태코드와 메세지를 나타냈다</p>
<img src='https://velog.velcdn.com/images/luv_lyn/post/6261f883-48be-43a1-9484-0e11ad4bf139/image.png' width=100%>
<img src='https://velog.velcdn.com/images/luv_lyn/post/a05b494a-fb40-436c-a589-94fb35b923ad/image.png' width=100%>





</li>
</ul>
<hr>
<h2 id="❸-비밀번호-검증하기">❸ 비밀번호 검증하기</h2>
<h3 id="📌-문제-2">📌 문제</h3>
<ul>
<li>수정 / 삭제시 받아온 비밀번호가 기존 테이블의 비밀번호와 일치하는지 확인해야했다.</li>
<li>java에서 sql문 활용이 익숙치 않아 생각보다 시간이 많이 걸렸다</li>
</ul>
<h3 id="💡-해결방법-2">💡 해결방법</h3>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/dae242b7-01f4-450e-80c6-5108751619c7/image.png' width=100%>

<ul>
<li>비밀번호를 검증하는 sql쿼리를 작성했다.</li>
<li>입력값들은 쿼리 내에 직접 작성하지 않고 <strong><code>placeholder</code></strong> <code>?</code> 로 대체하고 파라미터로 바인딩했다</li>
<li><a href="https://codedragon.tistory.com/10320">place</a><a href="https://kokodakadokok.tistory.com/entry/JDBC-DB-%EC%A0%91%EA%B7%BC%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%9E%90%EB%B0%94-%ED%91%9C%EC%A4%80-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4">holder</a></li>
</ul>
<hr>
<h2 id="❹-수정일-미반영-문제">❹ 수정일 미반영 문제</h2>
<h3 id="📌-문제-3">📌 문제</h3>
<ul>
<li>수정후에 수정일이 null이거나, 수정 완료후에 작성일이 null이거나.. 
계속 null 파티였는데 심지어 테이블 생성시에 날짜는 전부 NOT NULL로 등록해둬서 exception 파티였다</li>
</ul>
<h3 id="💡-해결방법-3">💡 해결방법</h3>
<ul>
<li><p>일단 DB에 DEFAULT값을 추가해줬다 <code>TIMESTMAP = CURRENT_TIMESTAMP</code> 사용</p>
<pre><code class="language-sql">
ALTER TABLE schedule 
MODIFY created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;

ALTER TABLE schedule 
MODIFY updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;</code></pre>
</li>
<li><p>일정 생성시 : <code>작성일</code> <code>수정일</code>을 둘다 현재시간으로 초기화 해주고 데이터에 저장했다</p>
<img src='https://velog.velcdn.com/images/luv_lyn/post/eaff7a33-3629-492f-932a-4eabd5806022/image.png' width=60%>
</li>
<li><p>일정 수정시 : <code>수정일</code>만 사용하고 현재시간으로 업데이트 했다</p>
<img src='https://velog.velcdn.com/images/luv_lyn/post/0c4c2edf-b204-427d-b0e8-a2d3af352eba/image.png' width=100%>


</li>
</ul>
<hr>
<h2 id="❺-조건부-조회-설계">❺ 조건부 조회 설계</h2>
<h3 id="📌-문제-4">📌 문제</h3>
<ul>
<li>조건을 작성자명이나 수정일로 필터링을 해야하는데 이 조건이 둘다 있을 수도, 하나만 있을 수도, 둘다 없을 수도 있었다.</li>
<li>조건에 따라 sql문을 다르게 사용해야 했다. </li>
</ul>
<h3 id="💡-해결방법-4">💡 해결방법</h3>
<ul>
<li>일단 Controller에서는 둘다 null이 들어올 수 있게 해놓고 <code>required = false</code><img src='https://velog.velcdn.com/images/luv_lyn/post/9636acae-20d6-430a-9acc-7037929460cd/image.png' width=60%>


</li>
</ul>
<ul>
<li>Repository에서 <code>user</code>만 있을때, <code>date</code>만 있을때 각각 다르게 sql문을 이어붙였다<img src='https://velog.velcdn.com/images/luv_lyn/post/e6b63dc6-2696-4643-831a-56bf6976c70f/image.png' width=60%>


</li>
</ul>
<ul>
<li><p><a href="https://thcoding.tistory.com/267">sql문 동적으로 생성</a>하는 방법을 찾아 작성했다</p>
<pre><code class="language-java">@Override
  public List&lt;ScheduleResponseDto&gt; findAllSchedules(String user, String date) {

      StringBuilder sql = new StringBuilder(&quot;select * from schedule where 1=1 &quot;);
      List&lt;Object&gt; fillteringSql = new ArrayList&lt;&gt;();

      if (user != null){
          sql.append(&quot;AND user = ?&quot;);
          fillteringSql.add(user);
      }

      if (date != null) {
          sql.append(&quot;AND DATE(updated_at) = ?&quot;);
          fillteringSql.add(Date.valueOf(date));
      }

      sql.append(&quot;Order by updated_at desc&quot;);

      return jdbcTemplate.query(sql.toString(),scheduleRowMapper(), fillteringSql.toArray());
  }</code></pre>
</li>
</ul>
<br>

<hr>
<h1 id="🌟-나의-회고">🌟 나의 회고</h1>
<hr>
<ul>
<li>Spring 기초에서 가장 중요한 개념은 <strong>‘관심사 분리</strong>라는 걸 이번 과제를 통해 확실히 알게 되었다.<br>처음에는 어떻게 시작해야 할지 몰라서, Spring이 처음인 나는 4주차부터 6주차 강의 중 실습 위주로 반복하며 따라 만들었다.<br>처음엔 코드를 통으로 짜고, 그걸 분리하고, 또 분리하고… 데이터를 연결하면서 구조를 바꾸다 보니<br>자연스럽게 <strong>Controller, Service, Repository의 역할 분리</strong>가 왜 중요한지를 체감했다.<br>한 번 잘 분리해두니까 이후 기능을 추가하거나 수정할 때도 훨씬 편했다.</li>
</ul>
<ul>
<li>데이터를 다루면서 <strong>JDBC 방식의 장단점</strong>도 느꼈다.<br>DB 연결 자체가 처음이라 <code>application.properties</code> 설정부터 많이 헤맸고,<br>사전캠프 때와 달리 이번에는 java에서 SQL 쿼리를 사용하느라 구글링하면서 하나씩 해나갔다. 
특히 조건에 따라 데이터를 필터링하는 부분이나 테이블 구조를 계속 바꾸는 과정이 쉽지 않았다.  </li>
<li><em>직접 쿼리를 써야 한다는 점은 좋았지만*</em>, 반복적으로 구조가 바뀔 땐 번거로운 점도 있었다.</li>
</ul>
<ul>
<li>그리고 이번 과제를 하며 <strong>API 명세서와 ERD의 중요성</strong>도 크게 느꼈다.<br>튜터님이 “API 설계와 ERD 작업에 전체 개발 시간의 80%를 쓸 수도 있다”고 하셨는데,그 말이 정말 맞았다 !
중간중간 막힐 때마다 내가 임의로 정리해둔 <strong>API 명세서를 계속 참고하면서</strong>
코드를 짤 수 있었고, 덕분에 시간을 아낄 수 있었다.<br>특히 ERD를 처음부터 그려두고 사용하니 <strong>Repository 단에서 설계에 집중할 수 있어</strong> 훨씬 수월했다.<br>물론 이번엔 테이블이 하나라 단순했지만, 앞으로는 테이블이 여러 개로 늘어날 거고,, 
이번처럼 대충 작성해두면 중간에 계속 수정하게 될 것 같다 😂</li>
<li><em>앞으로는 요구사항을 정확히 파악하고, 초반에 ERD를 더 꼼꼼히 설계*</em>해두면 정말 유용하게 활용할 수 있을 것 같다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌼 Spring 기초 ④ 파라미터 매핑하기, 어노테이션 활용  / TIL - day 23]]></title>
            <link>https://velog.io/@luv_lyn/Spring-%EA%B8%B0%EC%B4%88-TIL-day-23</link>
            <guid>https://velog.io/@luv_lyn/Spring-%EA%B8%B0%EC%B4%88-TIL-day-23</guid>
            <pubDate>Mon, 24 Mar 2025 12:22:05 GMT</pubDate>
            <description><![CDATA[<h1 id="▪️spring-annotation▪️">▪️Spring Annotation▪️</h1>
<hr>
<h1 id="📖-slf4j">📖 @Slf4j</h1>
<blockquote>
<p>기본적으로 인터페이스로 구성, 구현체를 사용해야 한다
log back 라이브러리 사용</p>
</blockquote>
<h2 id="📌-logging">📌 Logging</h2>
<p><strong>Thread 정보나 클래스 이름 같은 부가정보를 확인</strong>
➝ 이제까지는 <code>println</code>으로 콘솔에 출력했다면 <strong>별도의 로깅 라이브러리를 사용</strong>해 로그를 출력해야한다</p>
<pre><code class="language-java">@Slf4j // 로깅 사용할 수 있도록 인터페이스 활용
@RestController // 컨트롤러 만들기
public class Slf4jController {

    @RequestMapping(&quot;/logging&quot;) // 스프링빈 만들기
    public String logging() {

        String sparta = &quot;Sparta&quot;;
        // 로그 레벨 : TRACE -&gt; DEBUG -&gt; INFO -&gt; WARN -&gt; ERROR
        // 디폴트 : INFO

        // ❎ 실행되지 않는다
        log.trace(&quot;문자 trace={}&quot;, sparta);
        log.debug(&quot;문자 debug={}&quot;, sparta);

        // ✅ 실행된다
        log.info(&quot;문자 info={}&quot;, sparta);
        log.warn(&quot;문자 warn={}&quot;, sparta);
        log.error(&quot;문자 error={}&quot;, sparta);

        return &quot;success&quot;;
    }

}</code></pre>
<blockquote>
<h3 id="✔️log-level"><strong>✔️log level</strong></h3>
</blockquote>
<ul>
<li><strong>용도</strong> : 이 설정을 통해 error만 출력한다던지 (출력모으기)
로그메세지를 일자별로 모아서 외부 저장소에 보관하기도 한다 (로그 모아놓고 에러 찾기)<blockquote>
</blockquote>
<h4 id="🌟-trace--debug--info--warn--error">🌟 <strong>TRACE &gt; DEBUG &gt; INFO &gt; WARN &gt; ERROR</strong></h4>
<img src="https://velog.velcdn.com/images/luv_lyn/post/41df27d0-6c61-4b4a-93a6-3b93397f1950/image.png" alt=""><blockquote>
</blockquote>
</li>
<li>위 코드로 설정할 수 있음 (레벨 시작 = TRACE 라는 뜻)</li>
<li>따로 설정하지 않으면 <strong>기본 레벨(defalut) : INFO</strong></li>
</ul>
<h2 id="📌-log-사용시-주의할-것">📌 log 사용시 주의할 것</h2>
<table>
<thead>
<tr>
<th align="center">✅<img src='https://velog.velcdn.com/images/luv_lyn/post/23defb77-f79b-4791-af04-2047ced8782e/image.png' width=80%></th>
<th align="center">❎<img src="https://velog.velcdn.com/images/luv_lyn/post/fe7c07bf-593c-4c3c-b64e-e0942282a6aa/image.png" alt=""></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>중괄호 사용⭕️</strong></td>
<td align="center"><strong>중괄호 사용 ❌</strong> (문자열+문자열) 연산 형태 사용</td>
</tr>
<tr>
<td align="center">알아서 괄호랑 문자가 치환되고 <strong>로그레벨 적용</strong></td>
<td align="center"><strong>로그 레벨 상관없이 연산 먼저</strong> 해버린다</td>
</tr>
</tbody></table>
<br>

<hr>
<h1 id="▪️controller-vs-restcontroller▪️">▪️@Controller VS @RestController▪️</h1>
<blockquote>
<p>💡 스프링에서 컨트롤러를 만들때 사용하는 어노테이션</p>
</blockquote>
<hr>
<h1 id="📖-controller">📖 @Controller</h1>
<pre><code class="language-java">@Controller
public class ViewController {

    @RequestMapping(&quot;/view&quot;)
    public String example() {
        // logic
        return &quot;sparta&quot;; // ViewName이 return된다 (리소스의 타임리프로 만든 뷰)
    }

}</code></pre>
<ul>
<li>응답할 <strong>View</strong>가 있는 경우 = *타임리프나 jsp를 쓰는경우에 사용</li>
</ul>
<blockquote>
<p><strong>*타임리프 사용</strong>
그레이들에 타임리프 의존성을 추가하면 뷰의 기본 경로가 <code>main/resources/templates</code> 로 설정된다
➝ 알맞은 View를 찾는 과정에서 return값이 string이면 ThymeleafViewResolver에 의해 view name으로 인식된다</p>
</blockquote>
<hr>
<h1 id="📖-restcontroller">📖 @RestController</h1>
<pre><code class="language-java">@RestController
public class ResponseController {

    @RequestMapping(&quot;/string&quot;)
    public String example() {
        // logic
        return &quot;sparta&quot;; // ViewName이 return 되는게 아니라, String Data가 반환된다.
    }

}</code></pre>
<ul>
<li>응답할 <strong>Data</strong>가 있는 경우에 사용</li>
<li>현재는 대부분 이것을 사용해 API가 만들어진다 (Restful API)
➝ <em>return 값으로 View를 찾는 것이 아니라 *</em>HTTP Message Body에 Data를 입력**한다</li>
</ul>
<blockquote>
<p>*알맞는 View를 찾고 View Resolver한테 가는게 아니라! 바로 데이터를 반환한다
(화면이 그려지는게 아니라 데이터가 나온다)</p>
</blockquote>
<hr>
<h1 id="▪️annotation-자세히-보기▪️">▪️Annotation 자세히 보기▪️</h1>
<hr>
<h1 id="📖-conponent">📖 @Conponent</h1>
<ul>
<li><strong>스프링 빈에 등록하는 역할</strong> = 서블릿 컨테이너와 비슷한 역할</li>
<li>빈? 애플리케이션의 구성요소를 정의하는 객체</li>
</ul>
<p>💡 <strong><code>@Indexed</code></strong></p>
<ul>
<li>하위 클래스가 스프링 빈에 더 빠르게 등록되도록 도와준다</li>
</ul>
<hr>
<h1 id="📖-target">📖 @Target</h1>
<ul>
<li><strong>하위 어노테이션이 어떤 범위에 적용되는지 설정</strong></li>
<li>엘리먼트 타입의 이넘으로 속성을 설정..</li>
</ul>
<hr>
<h1 id="📖-retention">📖 @Retention</h1>
<ul>
<li><strong>하위의 어노테이션이 얼마나 오래 유지되는지를 설정</strong></li>
</ul>
<ol>
<li><strong>SOURCE</strong><ul>
<li>소스 코드(.java)에서만 유지</li>
<li>컴파일러에 의해 클래스 파일로 저장되지 않는다</li>
</ul>
</li>
<li><strong>CLASS</strong><ul>
<li>컴파일된 클래스 파일(.class)에 저장되지만, JVM이 실행 시 읽지 않는다(주석과 같음)</li>
<li>Default 값임</li>
</ul>
</li>
<li><strong>RUNTIME</strong><ul>
<li>클래스 파일(.class)에 저장되고, JVM에 의해 런타임 시점에 읽을 수 있다</li>
<li>즉, 실제 런타임 시점의 코드에 반영되어 영향을 준다</li>
</ul>
</li>
</ol>
<hr>
<h1 id="📖-documented">📖 @Documented</h1>
<ul>
<li><strong>문서화 도구에 의해 문서화되어야 함을 나타냄</strong></li>
</ul>
<hr>
<h1 id="▪️request-mapping▪️">▪️Request Mapping▪️</h1>
<hr>
<h1 id="1️⃣-request-mapping">1️⃣ Request Mapping</h1>
<blockquote>
<p>💡 특정 URL로 요청을 보내면, 그 요청과  컨트롤러 내부의 특정 메서드를 연결 / 매핑함 
(실제로는 여러요소를 조합해 매핑한다)</p>
</blockquote>
<h3 id="✔️-사용예시">✔️ 사용예시</h3>
<p> <strong><code>@RequestMapping(value = &quot;/v1&quot;, method = RequestMethod.GET)</code></strong></p>
<ul>
<li><p>버전 별로 선언 방식 다르다! ➝ 현재는 맨 뒤에 슬래시 없어야한다</p>
</li>
<li><p>속성값들을 설정할때 배열형태로 선언하면 다중설정이 가능하다</p>
</li>
<li><p>기본적으로 모든 Http 메서드를 허용한다 (메서드 속성 설정 안했을때)</p>
</li>
<li><p>메서드 속성으로 따로 지정을 해버리면 지정된 것만 허용도 가능</p>
<pre><code class="language-java">   // HTTP Method 는 GET만 허용한다.
   @RequestMapping(value = &quot;/v1&quot;, method = RequestMethod.GET)
   public String exampleV1() {
       // logic
    return &quot;this is sparta!&quot;;
   }</code></pre>
</li>
</ul>
<h1 id="2️⃣-getmapping">2️⃣ @GetMapping</h1>
<h2 id="✔️-사용예시-1">✔️ 사용예시</h2>
<p><strong><code>@GetMapping(value = &quot;/v2&quot;)</code></strong></p>
<ul>
<li><code>method = {RequestMethod.GET}</code> 이 설정되어 있는 클래스</li>
<li>POST, PUT, PATCH, DELETE 모두 사용가능 !</li>
<li><strong><code>@RequestMapping</code> 보다 직관적이므로 주로 사용한다</strong></li>
</ul>
<pre><code class="language-java">@GetMapping(value = &quot;/v2&quot;)
public String exampleV2() {
    // logic
    return &quot;this is sparta!&quot;;
}</code></pre>
<h3 id="📌-requestmapping-을-사용하는-경우">📌 @RequestMapping 을 사용하는 경우</h3>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="center">@PostMapping, @PutMapping, @DeleteMapping, @PatchMapping</th>
<th align="center">@RequestMapping</th>
</tr>
</thead>
<tbody><tr>
<td align="center">Target</td>
<td align="center">Method Level</td>
<td align="center">Class Level, Method Level</td>
</tr>
</tbody></table>
<ul>
<li>예시 : <code>users/{userId}, category/{categoryId}/product/{productId}</code>
prefix로 선언할 URL을 class 레벨에 적용하는 것에 주로 사용</li>
</ul>
<hr>
<h1 id="3️⃣-pathvariable">3️⃣ @PathVariable</h1>
<blockquote>
<p>💡 경로 변수임, 비연결성을 극복하기 위함
URL로 전달된 값을 파라미터로 받아온다</p>
</blockquote>
<h3 id="✔️-사용예시-2">✔️ 사용예시</h3>
<p><strong><code>user/{id}</code></strong></p>
<ul>
<li><strong>경로 변수를 중괄호에 둘러 싸서 사용</strong></li>
<li>반드시 경로변수 값을 가져야한다!</li>
<li>Restful API 설계를 위해 사용빈도가 높아지고 있음</li>
</ul>
<br>


<p>** 1️⃣ 파라미터 변수명과 PathVariable 변수명이 같으면 생략 가능**</p>
<pre><code class="language-java">[생략전❌]

@RequestMapping(&quot;/posts&quot;)
@RestController
public class PathVariableController {

    // postId로 된 post 단건 조회
    @GetMapping(&quot;/{postId}&quot;)
    public String pathVariableV1(@PathVariable(&quot;postId&quot;) Long data) {
        // logic
        String result = &quot;PathvariableV1 결과입니다 : &quot; + data;
        return result;
    }
}

---------------

[생략후⭕️]

@RequestMapping(&quot;/posts&quot;)
@RestController
public class PathVariableController {

    // 변수명과 같다면 속성값 생략가능
    @GetMapping(&quot;/{postId}&quot;)
    public String pathVariableV2(@PathVariable Long postId) {
        // logic
        String result = &quot;PathvariableV2 결과입니다 : &quot; + postId;
        return result;
    }

}
</code></pre>
<p><strong>2️⃣ @PathVariable 다중 사용 가능</strong></p>
<pre><code class="language-java">[1개 사용]

@RestController
public class PathVariableController {

    @GetMapping(&quot;/{postId}/comments/{commentId}&quot;)
    public String pathVariableV3(
                                @PathVariable Long postId,
                                   @PathVariable Long commentId
                                 ) {
        // logic
        String result = &quot;PathvariableV3 결과입니다 postId : &quot; + postId + &quot;commentsId : &quot; + commentId;
        return result;
    }

}

---------

[다중 사용]

@RequestMapping(&quot;/posts/{postId}&quot;)
@RestController
public class PathVariableController {

    @GetMapping(&quot;/comments/{commentId}&quot;)
    public String pathVariableV4(
                                @PathVariable Long postId,
                                @PathVariable Long commentId
                                ) {
        // logic
        String result = &quot;PathvariableV4 결과입니다 postId : &quot; + postId + &quot;commentsId : &quot; + commentId;
        return result;
    }

}</code></pre>
<hr>
<h1 id="4️⃣-request-parameter">4️⃣ Request Parameter</h1>
<blockquote>
<p>💡 요청과 속성의 파라미터를 매핑할 수 있음</p>
</blockquote>
<h2 id="📌-1-특정-파라미터와-매핑하기">📌 1. 특정 파라미터와 매핑하기</h2>
<h3 id="✔️-사용예시-3">✔️ 사용예시</h3>
<p><strong><code>@GetMapping(value = &quot;/users&quot;, params = &quot;gender=man&quot;)</code></strong></p>
<p>(요청 :<code>URL GET http://localhost:8080/users?gender=man</code> )</p>
<h3 id="✔️-규칙">✔️ 규칙</h3>
<ol>
<li><p><code>params = &quot;gender&quot;</code></p>
<ul>
<li>params의 key값은 커스텀이 가능하다</li>
<li>value는 없어도 된다.</li>
</ul>
</li>
<li><p><code>params = &quot;!gender&quot;</code></p>
<ul>
<li>gender가 없어야 한다는 의미</li>
</ul>
</li>
<li><p><code>params = &quot;gender=man&quot;</code></p>
<ul>
<li>gender=man 이어야 한다는 의미</li>
</ul>
</li>
<li><p><code>params = &quot;gender!=man&quot;</code> </p>
<ul>
<li>params의 value값이 man가 아니여야 한다는 의미</li>
</ul>
</li>
<li><p><code>params = {&quot;gender=man&quot;, &quot;gender=woman&quot;}</code></p>
<ul>
<li>배열로 속성 값을 여러 개 설정이 가능</li>
</ul>
<hr>
</li>
</ol>
<h2 id="📌-2-특정-헤더와-매핑하기">📌 2. 특정 헤더와 매핑하기</h2>
<h3 id="✔️-사용예시-4">✔️ 사용예시</h3>
<p><strong><code>@PostMapping(value = &quot;/users&quot;, headers = &quot;Content-Type=application/json&quot;)</code></strong></p>
<ul>
<li><p>특정 헤더와 매핑할 수 있다</p>
</li>
<li><p>속성 작성 규칙은 위 params 속성 값의 규칙과 같다</p>
<hr>
</li>
</ul>
<h2 id="📌-3-mediatype-매핑-consume수용">📌 3. MediaType 매핑, consume(수용)</h2>
<h3 id="✔️-사용예시-5">✔️ 사용예시</h3>
<p><strong><code>@PostMapping(value = &quot;/users&quot;, consumes = &quot;application/json&quot;)</code></strong></p>
<ul>
<li>🌟<strong>HTTP Header Content-Type(요청)과 매핑</strong></li>
<li><code>consumes</code> 값은 <em>//MediaType.APPLICATION_JSON_VALUE</em></li>
<li>최대한 <code>MediaType</code>의 Enum을 사용해라!</li>
</ul>
<h3 id="✔️-규칙-1">✔️ 규칙</h3>
<ol>
<li><p><code>consumes=”application/json”</code></p>
<ul>
<li>application/json 미디어 타입 허용</li>
</ul>
</li>
<li><p><code>consumes=”!application/json”</code></p>
<ul>
<li>application/json 제외 미디어 타입 허용</li>
</ul>
</li>
<li><p><code>consumes=”application/*”</code> </p>
<ul>
<li>application/  으로 시작하는 모든 미디어 타입 허용</li>
</ul>
</li>
<li><p><code>consumes=”*\/*”</code></p>
<ul>
<li>모두 허용</li>
</ul>
<hr>
</li>
</ol>
<h2 id="📌-4-mediatype-매핑-produces제공">📌 4. MediaType 매핑, produces(제공)</h2>
<h3 id="✔️-사용예시-6">✔️ 사용예시</h3>
<p><strong><code>@GetMapping(value = &quot;/users&quot;, produces = &quot;text/plain&quot;)</code></strong></p>
<ul>
<li>🌟<strong>요청 헤더의 Accept 값에 따라서 produces 하는 값이 변함</strong></li>
<li>최대한 <code>MediaType</code>의 Enum을 사용해라!</li>
</ul>
<hr>
<h1 id="📖-spring이-지원하는-파라미터">📖 Spring이 지원하는 파라미터</h1>
<ul>
<li><p><a href="https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/arguments.html">@Controller의 사용 가능한 파라미터 목록</a></p>
</li>
<li><p><a href="https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/return-types.html">@Controller의 사용 가능한 Response 값 목록</a></p>
</li>
</ul>
<h2 id="📌-http-헤더-조회하기">📌 Http 헤더 조회하기</h2>
<p><strong>[예시]</strong></p>
<pre><code class="language-java">// 로깅
@Slf4j
@RestController
public class RequestHeaderController {

    @GetMapping(&quot;/request/headers&quot;)
    public String headers(
            HttpServletRequest request, // Servlet에서 사용한것과 같음
            HttpServletResponse response, // Servlet에서 사용한것과 같음
            @RequestHeader MultiValueMap&lt;String, String&gt; headerMap,
            @RequestHeader(&quot;host&quot;) String host,
            @CookieValue(value = &quot;cookie&quot;, required = false) String cookie,
            HttpMethod httpMethod,
            Locale locale
    ) {
            // Servlet
        log.info(&quot;request={}&quot;, request);
        log.info(&quot;response={}&quot;, response);

        // @RequestHeader
        log.info(&quot;headerMap={}&quot;, headerMap);
        log.info(&quot;host={}&quot;, host);

        // @CookieValue
        log.info(&quot;cookie={}&quot;, cookie);

        // HttpMethod
        log.info(&quot;httpMethod={}&quot;, httpMethod);

        // Locale
        log.info(&quot;Locale={}&quot;, locale);

        return &quot;success&quot;;
    }
}</code></pre>
<ul>
<li><p>API 호출
<img src="https://velog.velcdn.com/images/luv_lyn/post/74d5831c-d18b-49b1-b5ae-32831ff5adb5/image.png" alt=""></p>
</li>
<li><p>Log 출력 결과
<img src="https://velog.velcdn.com/images/luv_lyn/post/7a7fcd96-aa63-41ef-8cd2-313d21859e19/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌼 Spring 기초 ③ - MVC 패턴 / TIL - day 22]]></title>
            <link>https://velog.io/@luv_lyn/Spring-%EA%B8%B0%EC%B4%88-MVC-%ED%8C%A8%ED%84%B4-TIL-day-22</link>
            <guid>https://velog.io/@luv_lyn/Spring-%EA%B8%B0%EC%B4%88-MVC-%ED%8C%A8%ED%84%B4-TIL-day-22</guid>
            <pubDate>Thu, 20 Mar 2025 13:32:13 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="▪️mvc-패턴-개요▪️">▪️MVC 패턴 개요▪️</h1>
<hr>
<h1 id="📖-template-engine">📖 Template Engine</h1>
<blockquote>
<p>💡동적인 웹페이지를 생성하기 위해 사용되는 도구 / 흔히 말하는 UI (SSR에 사용)</p>
</blockquote>
<h2 id="📌-등장-배경">📌 등장 배경</h2>
<ul>
<li><p><strong>HTML 문서 중 동적으로 변경해야 하는 부분만 자바 코드로 넣을 수 있어 더 편리하다</strong></p>
</li>
<li><p>예를 들어 웹페이지가 있을 때
특정 글을 조회할 때, 변하는 부분만 변하고 나머지 요소들은 고정임
변해야 하는 곳만!! 코드로 넣어준다</p>
</li>
</ul>
<h2 id="📌-대표적인-템플릿-엔진">📌 대표적인 템플릿 엔진</h2>
<ul>
<li>1️⃣ 타임리프 - 스프링과 연동이 잘되고, 다양한 기능이 많다</li>
<li>2️⃣ JSP</li>
</ul>
<hr>
<h1 id="📖-mvc-패턴-등장-배경">📖 MVC 패턴 등장 배경</h1>
<blockquote>
<p>💡서블릿과 jsp만으로 만들면 비지니스 로직과 뷰 렌더링까지 처리하기가 구조가 넘 복잡함 (유지보수 힘듦)
➝ 따라서 mvc (모델 뷰 컨트롤러) 패턴을 사용한다</p>
</blockquote>
<h3 id="✔️-servlet-jsp만-사용시-문제점">✔️ Servlet, JSP만 사용시 문제점</h3>
<table>
<thead>
<tr>
<th align="center">Servlet</th>
<th align="center">JSP</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="https://velog.velcdn.com/images/luv_lyn/post/e29e96b1-96ea-4640-9d7d-ffcc94c6f95a/image.png" alt=""></td>
<td align="center"><img src="https://velog.velcdn.com/images/luv_lyn/post/4a51175a-d1da-4a0d-96c1-2caf292aac4c/image.png" alt=""></td>
</tr>
<tr>
<td align="center">화면을 그리는 로직과, 비즈니스 로직이 <br>하나의 Servlet 안에 다 섞여 들어있음</td>
<td align="center">우리가 많이 본 html코드가 자바코드랑 같이 있음<br> ➝ 서블릿에서 뷰만 분리된것</td>
</tr>
<tr>
<td align="center"><strong>다 섞임</strong></td>
<td align="center"><strong>뷰는 분리되었지만 로직이 섞임</strong></td>
</tr>
</tbody></table>
<hr>
<h1 id="📖-mvc-패턴">📖 MVC 패턴</h1>
<hr>
<blockquote>
<p>💡하나의 jsp나 servlet으로 처리하던걸 <strong>M</strong>odel , <strong>V</strong>iew , <strong>C</strong>ontroller 로 분리</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/ceea31ba-8cbb-426d-889e-bfd72cc5cdcb/image.png" alt=""></p>
<ul>
<li>🌟 <strong>비즈니스 로직과 뷰의 완전한 분리!!</strong></li>
</ul>
<h3 id="✔️-controller">✔️ Controller</h3>
<ul>
<li>HTTP Request를 전달받아 파라미터 검증</li>
<li>View에 전달할 결과 조회 후 Model에 임시저장</li>
<li><code>Layered architecture</code> 구조 이용해 역할이 또 세분화 된다!<ul>
<li>1️⃣  <strong><code>Controller Layer</code></strong> ➝ HTTP Request를 전달받아 파라미터 검증</li>
<li>2️⃣ <strong><code>Service Layer</code></strong> ➝ 비즈니스 로직 실행 </li>
<li>3️⃣ <strong><code>Repository Layer</code></strong> ➝ 데이터 베이스와 상호작용 </li>
</ul>
</li>
</ul>
<h3 id="✔️-model">✔️ Model</h3>
<ul>
<li>View에 출력할 데이터를 저장</li>
</ul>
<h3 id="✔️-view">✔️ View</h3>
<ul>
<li>Model 객체의 데이터를 이용해 화면을 Rendering</li>
</ul>
<hr>
<h2 id="📌-mvc-패턴의-문제점">📌 MVC 패턴의 문제점</h2>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/d29d8070-2e69-428c-b3bc-888c2dfb7f27/image.png' width=50% >

<p>기능별로 서블릿이 계속 필요함..</p>
<ul>
<li>중복 호출</li>
<li>뷰의 경로도 계속 입력해줘야한다</li>
<li>리퀘스트는 사용하지만 리스폰스를 안씀 ( jsp가 다 처리하기 때문에 )</li>
<li>*공통 기능이 추가 될 수록 컨트롤러가 처리할 부분이 많아짐
➝ 메서드로 만들어서 호출하면 되는거 아닌가요?<ul>
<li>이것도 메서드도 중복 호출해야하고 까먹을 수도 있음</li>
<li>메서드 늘어날때마다 컨트롤러 작업범위 커짐</li>
</ul>
</li>
</ul>
<p><em>*공통기능?</em>
<em>모든 컨트롤러에서 공통으로 적용되는 기능 (log 출력, 인가 등)</em></p>
<hr>
<h1 id="📖-프론트-컨트롤러-패턴">📖 프론트 컨트롤러 패턴</h1>
<blockquote>
<p>💡컨트롤러 호출 전에 공통기능을 하나의 서블릿에서 처리해주는 패턴
➝ 입구가 하나다! 공통기능의 서블릿을 꼬옥 지나가야해</p>
</blockquote>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/d298d401-7451-4539-b6a5-e0af37341db7/image.png' width = 60%>

<ol>
<li><strong>모든 요청을 받고 공통기능을 처리</strong></li>
<li>요청을 처리할 수 있는 컨트롤러를 찾아서 호출한다 ➝ <strong>컨트롤러 맵핑</strong></li>
<li>프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 된다
( 나머지 애들은 상속이나 webservlet을 안써도 된다 )</li>
</ol>
<p><strong>➝ 응답은 전부 다른 형태로 나올 수 있으니 어댑터 필요</strong></p>
<hr>
<h1 id="📖-어댑터-패턴">📖 어댑터 패턴</h1>
<blockquote>
<p>💡 다양한 컨트롤러 핸들러를 유연하게 만들기 위해 도입</p>
</blockquote>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/6c935744-411c-404f-81f6-b0e9fac239ff/image.png' width=50%>

<ol>
<li>컨트롤러는 로직 처리 후 그것에 맞는 결과를 반환</li>
<li><strong>반환된 응답을 어댑터가 공통로직과 맞춰 변환한다</strong></li>
<li>프론트 컨트롤러는 공통 로직 수행</li>
</ol>
<ul>
<li>모두 각자의 역할만 수행한다(책임분리)</li>
<li>새로운 핸들러가 추가되어도 어댑터만 추가하면 공통로직에 영향 없음</li>
</ul>
<hr>
<blockquote>
<h3 id="🚀-spring-mvc-패턴-개요"><strong>🚀 Spring MVC 패턴 개요</strong></h3>
</blockquote>
<p><strong>❶ Servlet만 사용 ** - 비즈니스 로직, View가 분리되지 않음
*<em>❷ JSP만 사용 *</em> - View는 분리하였으나 로직이 Jsp에 포함
**❸ MVC 패턴 사용</strong>  - 모델, 뷰, 컨트롤러를 분리하였으나 공통기능임에도 코드를 중복입력해야함
<strong>❹ 프론트 컨트롤러 패턴</strong>  - 하나의 입구를 만들어 공통기능을 처리하지만 모든 핸들러의 응답을 이것에 맞게 바꿔야함
<strong>❺ 어댑터 컨트롤러 패턴</strong>  - 핸들러의 응답을 프론트 컨트롤러에 맞게 연결해준다</p>
<blockquote>
</blockquote>
<hr>
<h1 id="▪️-spring-mvc-패턴▪️">▪️ Spring MVC 패턴▪️</h1>
<hr>
<h1 id="📖-spring-mvc-패턴">📖 Spring MVC 패턴</h1>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/bcab5d33-2b93-4d55-9fa8-e7ef1d37a99e/image.png" alt=""></p>
<p>📌 <strong><code>Front Controller</code></strong> (디스패쳐 서블릿)</p>
<ul>
<li>요청을 받고 알맞는 핸들러, 어댑터를 찾고 반환한다</li>
<li>핸들러 목록 , 어댑터 목록을 가지고있다</li>
</ul>
<p>📌 <strong><code>Handler Adapter</code></strong></p>
<ul>
<li>내가 처리할 수 있는지? 확인하는 메서드 필요</li>
<li>핸들러에게 요청을 지시하는 기능</li>
<li>리턴할 때 알맞는 응답으로 변환한다</li>
</ul>
<p>📌 <strong><code>Handler</code></strong></p>
<ul>
<li>로직만 처리하면 된다 애들이 다 걸러옴</li>
</ul>
<hr>
<h1 id="📖-spring-mvc의-주요-interface">📖 Spring MVC의 주요 Interface</h1>
<blockquote>
<p>대부분의 기능들이 인터페이스로 만들어져 있기 때문에 <br><code>DispatcherServlet</code> 코드의 변경없이 <strong>기능 변경이나 확장이 가능</strong>하다</p>
</blockquote>
<h2 id="✔️-주요-인터페이스-목록">✔️ 주요 인터페이스 목록</h2>
<ol>
<li>HandlerMapping</li>
<li>HandlerAdapter</li>
<li>ViewResolver</li>
<li>View</li>
</ol>
<hr>
<h1 id="📖-controller-interface">📖 Controller Interface</h1>
<ul>
<li>💡인터페이스를 임플리먼트하여 원하는 핸들러를 만들 수 있다</li>
<li><strong>Annotation 기반의 <code>Controller</code>와는 상관없다</strong></li>
</ul>
<h2 id="📌component">📌@Component</h2>
<ul>
<li>Spring Bean으로 등록하기 위한 기본 어노테이션</li>
<li>Spring이 실행될 때, @Component가 붙은 클래스를 찾아서 <strong>객체(인스턴스)</strong>로 만들고 컨테이너에 등록한다.</li>
</ul>
<blockquote>
<p><code>@Component</code> = &quot;너 이거 빈으로 등록해!&quot;
<code>@Service</code>, <code>@Repository</code>, <code>@Controller</code> 등 = &quot;이거 빈인데, 역할은 이거야!&quot;</p>
</blockquote>
<h3 id="✔️-spring-bean">✔️ Spring Bean</h3>
<ul>
<li>컨테이너가 관리하는 객체, 즉 <strong>인스턴스</strong> !</li>
<li>Spring은 내부적으로 @Component 계열의 클래스들을 객체화 시키고 
<code>Spring Bean</code>으로 등록해둔다</li>
<li>특정 경로로 요청이 들어오면, 해당 경로와 바인딩 된 <code>Spring Bean</code>을 찾아서
그 <strong><code>Spring Bean</code>에 매핑된 <code>Controller</code></strong>를 찾아 실행해준다.</li>
</ul>
<h2 id="📌-handler-mapping-handler-adapter의-우선-순위">📌 Handler Mapping, Handler Adapter의 우선 순위</h2>
<blockquote>
</blockquote>
<p>스프링에서 사용자가 /class/1 같은 요청을 보내면,
중앙에서 모든 요청을 받는 DispatcherServlet이 이걸 받아.</p>
<blockquote>
</blockquote>
<p>근데 DispatcherServlet 혼자선 이렇게 생각함</p>
<blockquote>
<blockquote>
<p>&quot;이 URL은 누구한테 맡기지...? 누구 메서드 실행해야 하지...?&quot;</p>
</blockquote>
</blockquote>
<p><strong><code>HandlerMapping</code></strong>    URL을 기반으로 어떤 Bean의 어떤 메서드를 실행할지 찾음
<strong><code>HandlerAdapter</code></strong>    찾은 Bean을 실제로 어떻게 실행할지 방법을 제공</p>
<table>
<thead>
<tr>
<th align="center">우선순위</th>
<th align="left">Handler Mapping</th>
<th align="left">Handler Adapter</th>
<th align="left">의미</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1️⃣</td>
<td align="left"><strong>RequestMappingHandlerMapping</strong><br>- Annotation 기반 Controller의 <code>@RequestMapping</code>에 사용</td>
<td align="left"><strong>RequestMappingHandlerAdapter</strong><br>- Annotation 기반 Controller의 <code>@RequestMapping</code>에 사용</td>
<td align="left">지금 우리가 쓰는 방식<br> (@RequestMapping, @GetMapping 등)</td>
</tr>
<tr>
<td align="center">2️⃣</td>
<td align="left">BeanNameUrlHandlerMapping<br> - Spring Bean Name으로 HandlerMapping</td>
<td align="left">HttpRequestHandlerAdapter<br> - HttpRequestHandler 처리</td>
<td align="left">특정 URL과 Bean 이름을 매핑하거나, HttpRequestHandler 구현체 사용 시</td>
</tr>
<tr>
<td align="center">3️⃣</td>
<td align="left">-</td>
<td align="left">SimpleControllerHandlerAdapter<br> - Controller Interface 처리</td>
<td align="left">과거의 Controller 인터페이스 기반 방식</td>
</tr>
</tbody></table>
<h1 id="📖-view-resolver">📖 View Resolver</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌼 Spring 기초 ② - 프레임워크, 빌드 관리 도구 / TIL - day 21]]></title>
            <link>https://velog.io/@luv_lyn/Spring-%EA%B8%B0%EC%B4%88-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EB%B9%8C%EB%93%9C-%EA%B4%80%EB%A6%AC-%EB%8F%84%EA%B5%AC-TIL-day-21</link>
            <guid>https://velog.io/@luv_lyn/Spring-%EA%B8%B0%EC%B4%88-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EB%B9%8C%EB%93%9C-%EA%B4%80%EB%A6%AC-%EB%8F%84%EA%B5%AC-TIL-day-21</guid>
            <pubDate>Wed, 19 Mar 2025 11:49:23 GMT</pubDate>
            <description><![CDATA[<h1 id="▪️프레임워크-라이브러리">▪️프레임워크, 라이브러리</h1>
<hr>
<h1 id="📖-프레임워크">📖 프레임워크</h1>
<blockquote>
<p>💡 붕어빵을 만들땐 붕어빵틀을 사용해야 한다 ! <strong>소프트웨어를 만들때 틀</strong></p>
</blockquote>
<h2 id="📌-장점">📌 장점</h2>
<ul>
<li><strong>일관된 구조로 팀 협업 편해짐</strong></li>
<li>필요한 기능과 도구(보안관련 기능까지)를 제공, 개발자들은 <strong>비즈니스 로직에 집중 가능</strong></li>
<li>인기 있는 프레임 워크는 다양한 문서를 활용할 수 있다 (커뮤니티)</li>
</ul>
<h2 id="📌-단점">📌 단점</h2>
<ul>
<li>구조가 복잡하다 보니 처음 배울때 시간 많이 소요된다</li>
<li>버전이 달라지면 기존 코드와 호환이 불가할수도</li>
<li>틀이 있다보니 자유롭게 변경은 불가</li>
</ul>
 <br>


<hr>
<h1 id="📖-라이브러리">📖 라이브러리</h1>
<blockquote>
<p>💡 도서관에서 책 찾아읽듯, 소프트웨어 만들때 필요한 도구를 꺼내서 사용할 수 있다</p>
</blockquote>
<h2 id="📌-장점-1">📌 장점</h2>
<ul>
<li>누군가 만들어 두었기에 직접 개발하지 않아도 되고, <strong>개발생산성</strong> 높아진다</li>
<li>검증된 라이브러리 ➝ <strong>품질 보장</strong></li>
</ul>
<h2 id="📌-단점-1">📌 단점</h2>
<ul>
<li>라이브러리가 업데이트되거나 지원이 중단될 수도</li>
<li>버전 호환성 문제로 <strong>기존 코드와 충돌</strong>일어날 수도 ➝ 빈번함</li>
<li>일부 불필요한 기능 있을 수도</li>
<li>내부 구현은 직접 수정이 어려워서 입맛대로 변경 불가</li>
</ul>
 <br>


<hr>
<h1 id="▪️-annotation">▪️ Annotation</h1>
<hr>
<h1 id="📖-annotation">📖 Annotation</h1>
<blockquote>
<p>💡 Java의 주석과 비슷 ( 사람에게 정보 제공 ) , <strong>어노테이션은 프로그램에게 정보 제공</strong>
코드에 특별한 의미를 부여하거나, 특정 동작을 트리거하기 위해 사용</p>
</blockquote>
<ul>
<li><strong>Annotation <code>@</code>로 시작</strong>, 다양한 레벨에 위치할 수 있다</li>
</ul>
<h2 id="📌-내장-annotation">📌 내장 Annotation</h2>
<ul>
<li><p><strong><code>@Override</code>
: 상위 클래스나 인터페이스의 메서드를 오버라이드 하고 있음</strong> (컴파일러는 실제 오버라이드 중인지 확인)</p>
</li>
<li><p><strong><code>@Deprecated</code>
: 더 이상 사용하지 않는 요소를 나타낼 때</strong> (이 코드를 사용하면 컴파일 경고 발생)</p>
</li>
<li><p><strong><code>SuppressWarnings</code>
: 컴파일러 경고 억제</strong> (사용되지 않는 변수에 대한 경고를 무시할 수 있다)</p>
</li>
</ul>
<h2 id="📌-사용자-정의-annotation">📌 사용자 정의 Annotation</h2>
<ul>
<li>필요에 따라 어노테이션을 커스텀 할 수 있다</li>
<li>메타데이터를 추가하거나, <strong>AOP</strong>를 결합할 수도 있다</li>
</ul>
 <br>


<hr>
<h1 id="📖-lombok">📖 Lombok</h1>
<blockquote>
<p>💡 <strong>어노테이션 기반</strong>으로 동작, 보일러 플레이트 코드를 줄여주는 라이브러리
주로 컴파일 시점에 필요한 메서드로 변환
*보일러 플레이트 코드 -<code>getter</code> <code>setter</code> 메서드 생성자 등 반복적으로 작성되는 코드</p>
</blockquote>
<h2 id="📌-주요-lombok-annotation">📌 주요 Lombok Annotation</h2>
<ul>
<li><p><strong><code>@Getter</code>, <code>@Setter</code></strong></p>
<ul>
<li>클래스 내부에 모든 필드에 대해 <code>getter</code> ,<code>setter</code>를 자동으로 생성</li>
</ul>
<br></li>
<li><p><strong><code>@ToString</code></strong></p>
<ul>
<li>객체의 <code>toString()</code> 메서드를 자동으로 생성<br></li>
</ul>
</li>
<li><p><strong><code>@EqualsAndHashCode</code></strong></p>
<ul>
<li><code>equals()</code>와 <code>hashCode()</code> 메서드를 자동으로 생성<br></li>
</ul>
</li>
<li><p>** ❶<code>@NoArgsConstructor</code> ❷<code>@AllArgsConstructor</code> ❸<code>@RequiredArgsConstructor</code>**</p>
<ul>
<li>❶ 기본 생성자를 생성</li>
<li>❷ 모든 필드를 매개변수로 하는 생성자를 생성</li>
<li>❸ 필수(final) 필드만을 매개변수로 하는 생성자를 자동으로 생성   </li>
</ul>
</li>
<li><p><strong><code>@Data</code></strong></p>
<ul>
<li><code>@Getter</code>, <code>@Setter</code>, <code>@ToString</code>, <code>@EqualsAndHashCode</code>,<code>@RequiredArgsConstructor</code>를 한꺼번에 적용</li>
<li>주로 테스트 용도로 사용 (잘 안씀)</li>
</ul>
</li>
<li><p><strong><code>@Builder</code></strong></p>
<ul>
<li>복잡한 객체들을 필드 이름을 지정하면서 편하게 생성 가능  <pre><code class="language-java">User user = User.builder()
          .name(&quot;John&quot;)
          .age(30)
          .build();</code></pre>
</li>
</ul>
</li>
<li><p><strong><code>@Slf4j</code></strong></p>
<ul>
<li>클래스에 로그를 남기기 위한 <code>Logger</code> 객체를 자동으로 생성<pre><code class="language-java">@Slf4j
public class UserService {
  public void logMessage() {
      log.info(&quot;This is a log message&quot;);
  }
}</code></pre>
<br>

</li>
</ul>
</li>
</ul>
<hr>
<h1 id="▪️spring-framework와-spring-boot-▪️">▪️Spring Framework와 Spring Boot ▪️</h1>
<hr>
<h1 id="📖-spring-framework">📖 Spring Framework</h1>
<blockquote>
<p>💡 자바로 <strong>엔터프라이즈 애플리케이션 개발에 자주 사용</strong>
*엔터프라이즈 애플리케이션 : 대규모 비즈니스 프로세스와 데이터를 처리하는 어플리케이션</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/1e3a31b6-312a-4fd2-b0e0-f4081f60a0e6/image.png" alt=""></p>
<h2 id="📌-특징">📌 특징</h2>
<ul>
<li>다양한 구성요소를 유연하게 관리</li>
<li>누구나 사용할 수 있는 오픈 소스</li>
<li>모듈화되어 있어 특정 기능만 선택적 사용도 가능</li>
<li><strong>Java의 가장 큰 특징인 객체지향 언어의 특징을 살려낸 프레임워크</strong><br>

</li>
</ul>
<hr>
<h1 id="📖-spring-boot">📖 Spring Boot</h1>
<blockquote>
<p>💡Spring Framework를 기반으로 <strong>간편하게 애플리케이션을 개발</strong>할 수 있도록 도와주는 도구</p>
</blockquote>
<h2 id="📌-특징-1">📌 특징</h2>
<ul>
<li><p><strong>자동구성</strong>(Auto-configuration) 기능을 제공해서 직접 복잡한 설정할 필요가 없다 -&gt; 생산성 높아짐</p>
</li>
<li><p>내장 WAS(Tomcat)를 제공해서 서버 설정 없이 바로 실행 가능</p>
</li>
<li><p><strong>빌드 관리 도구</strong>(<code>spring-boot-starter-web</code>)를 추가하면 모든 종속성과 설정도 자동 구성</p>
</li>
<li><p>라이브러리의 버전을 명시하지 않아도 <strong>알아서 호환 가능한 버전 찾고 설정까지!!</strong>
( 100% 해결은 아닐지도 )</p>
<br>

</li>
</ul>
<hr>
<h1 id="▪️-빌드-관리-도구-▪️">▪️ 빌드 관리 도구 ▪️</h1>
<hr>
<h1 id="📖-gradle">📖 Gradle</h1>
<blockquote>
<p>Java와 유사한 문법 구조로 Groovy기반의 스크립트 언어를 사용하는 <strong>빌드 자동화 도구</strong></p>
</blockquote>
<h3 id="✔️-빌드">✔️ 빌드?</h3>
<ul>
<li>소스 코드를 컴퓨터가 실행할 수 있도록 변환</li>
<li>빌드에 필요한 과정
<img src="https://velog.velcdn.com/images/luv_lyn/post/6b619790-a056-43d4-ba04-11a31cd4060d/image.png" alt=""></li>
</ul>
<h3 id="✔️-빌드-자동화-도구">✔️ 빌드 자동화 도구</h3>
<ul>
<li>Gradle은 빌드 뿐 아니라 라이브러리 테스트 관리하고 배포도 할 수 있다</li>
</ul>
<h2 id="📌-특징-2">📌 특징</h2>
<ul>
<li><p>1️⃣ <strong>유연성</strong></p>
<ul>
<li>직접 커스터마이징 가능</li>
</ul>
</li>
<li><p>2️⃣ <strong>성능 좋다</strong></p>
<ul>
<li>빌드 결과물을 캐싱해서 재사용</li>
<li>점진적 빌드 ( 마지막 빌드 호출 이후 변경된 부분만 빌드 -&gt; 효율적 )</li>
<li>데몬 프로세스 ( 다음 빌드를 위해 대기하는 프로세스, 초기화 이후 초기화 안함 )</li>
</ul>
</li>
<li><p>3️⃣ <strong>멀티 프로젝트 빌드 지원</strong> ( 다같이 쓰는 클래스를 모듈화 시켜서 각 프로젝트에서 사용 가능 )</p>
</li>
<li><p>4️⃣ <strong>설정 주입 방식</strong></p>
<ul>
<li>프로젝트 별로 설정을 다르게 하거나, 공통을 한번에 주입하거나</li>
</ul>
</li>
</ul>
<h2 id="🐘-buildgradle를-살펴보자">🐘 build.gradle를 살펴보자</h2>
<ol>
<li><strong>플러그인</strong><ul>
<li>특정 작업을 위해 모아 놓은 task들의 모음집</li>
</ul>
</li>
<li>의존성 관리<ul>
<li>우리가 프로젝트에서 사용할 패키지나 라이브러리를 관리</li>
<li>의존성 설정?</li>
<li>라이브러리를 추가하는 시점을 설정할 수 있다.</li>
<li>Implementation<ul>
<li>컴파일, 런타임 시점 모두에서 사용한다.</li>
</ul>
</li>
<li>compileOnly<ul>
<li>컴파일할 때만 사용되고 런타임 때에는 사용하지 않는다.</li>
</ul>
</li>
<li>runtimeOnly<ul>
<li>런타임 때만 사용한다.</li>
</ul>
</li>
<li>testImplementation<ul>
<li>테스트할 때만 사용한다.</li>
</ul>
</li>
</ul>
</li>
<li>repositories<ul>
<li>라이브러리가 저장된 위치를 정의하고 온라인 저장소에서 라이브러리를 가져온다</li>
</ul>
</li>
</ol>
<hr>
<h1 id="java-웹-기술의-역사">Java 웹 기술의 역사</h1>
<ol>
<li>Servlet의 등장</li>
</ol>
<ul>
<li>Servlet만으로 요청과 응답을 다 했음 -&gt; 코드 복잡</li>
</ul>
<ol start="2">
<li>JSP(JavaServer Pages) 도입</li>
</ol>
<ul>
<li>jsp는 html내에 java코드를 삽입할 수 있다 -&gt; jsp 내에 비즈니스 로직을 분리할 수가 없다</li>
</ul>
<ol start="3">
<li>MVC 패턴 도입</li>
</ol>
<ul>
<li>UI, 비즈니스 로직, 데이터를 분리할 수 있다</li>
<li>Servlet으로 컨트롤러를 만들고 JSP로 뷰를 만든다 -&gt; 유지보수성과 확장성 향상, 중복 코드 발생</li>
</ul>
<ol start="4">
<li>MVC 프레임워크 등장</li>
</ol>
<ul>
<li>MVC 패턴을 구현할 수 있게 도와주는 프레임워크가 등장했다</li>
</ul>
<br>

<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌼 Spring 기초 ① - HTTP, Web Application  / TIL - day 20]]></title>
            <link>https://velog.io/@luv_lyn/Spring-%EA%B8%B0%EC%B4%88-HTTP-Web-Application-TIL-day-20</link>
            <guid>https://velog.io/@luv_lyn/Spring-%EA%B8%B0%EC%B4%88-HTTP-Web-Application-TIL-day-20</guid>
            <pubDate>Tue, 18 Mar 2025 10:21:27 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="▪️-http-▪️">▪️ HTTP ▪️</h1>
<hr>
<h1 id="📖-httphypertext-transfer-protocol">📖 HTTP(HyperText Transfer Protocol)</h1>
<blockquote>
<p>💡 JSON 같은 다양한 형태의 데이터가 HTTP를 통해 전송
주로 <strong>HTML/1.1 <code>TCP</code></strong>를 사용 / 최근 <strong>HTML/2, HTML/3 <code>UDP</code></strong>의 사용량도 증가중</p>
</blockquote>
<h2 id="📌-동작-순서">📌 동작 순서</h2>
<ul>
<li><p>클라이언트 : 요청을 보내고 응답을 기다린다</p>
</li>
<li><p>서버 : 요청을 수행하고 결과를 응답한다</p>
<hr>
</li>
</ul>
<h2 id="📌-특징">📌 특징</h2>
<ul>
<li>HTTP는 인터넷 상에서 불특정 다수의 통신환경을 고려하여 설계되었다
(다수의 클라이언트와 상태나 연결을 유지해야한다면 - 많은 서버의 리소스가 필요하다)</li>
</ul>
<blockquote>
</blockquote>
<h4 id="1️⃣-클라이언트---서버-각각-독립적-구성">1️⃣** 클라이언트 - 서버 각각 독립적 구성**</h4>
<blockquote>
</blockquote>
<h4 id="2️⃣-무상태-stateless">2️⃣ <strong>무상태 <code>Stateless</code></strong></h4>
<pre><code>- 서버는 클라이언트의 상태를 보존하지 않으므로 수평 확장성 높다
- 한계 : 무상태의 한계 ( = 전부 Stateless하게 설계할 수는 없다)
➡︎ 캐시, 세션, 토큰으로 해결</code></pre><blockquote>
</blockquote>
<h4 id="3️⃣-비연결connectionless">3️⃣ <strong>비연결<code>connectionless</code></strong></h4>
<pre><code>- 연결을 유지하지 않으므로 자원 효율적 사용
- 한계 : 비연결의 한계 ( = 매 요청마다 연결하고 정적자원 다운로드 )
➡︎ HTTP 지속연결로 해결</code></pre><blockquote>
</blockquote>
<p><strong>🌟기억 안나면 용어모음집 살펴보자🌟</strong></p>
<br>

<hr>
<h1 id="📖-http-message-구조">📖 HTTP Message 구조</h1>
<blockquote>
<p>💡<strong><code>요청메세지</code> <code>응답메세지</code> 두가지</strong>가 있다</p>
</blockquote>
<ul>
<li><strong>&lt;기본 구조&gt;</strong><img src = 'https://velog.velcdn.com/images/luv_lyn/post/d0d473c6-c270-4d9b-afa9-996c86cd6ef1/image.png' width=30%>

</li>
</ul>
<h2 id="📌-구조-분석하기">📌 구조 분석하기</h2>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="left">요청 메세지(Request Message)</th>
<th align="left">응답 메세지(Response Message)</th>
</tr>
</thead>
<tbody><tr>
<td align="center"></td>
<td align="left"><img src="https://velog.velcdn.com/images/luv_lyn/post/e88a1ddf-1462-439a-8c57-cb251999216c/image.png" alt=""></td>
<td align="left"><img src="https://velog.velcdn.com/images/luv_lyn/post/6740d6d4-5ed5-45a5-b06c-0ef0e59c95fe/image.png" alt=""></td>
</tr>
<tr>
<td align="center"></td>
<td align="left"><strong>HTTP Method</strong> - GET, POST, PUT, PATCH, DELETE 등</td>
<td align="left"><strong>HTTP Version</strong></td>
</tr>
<tr>
<td align="center"><span style='background-color:#ffdce0'><strong>Start Line</strong></span></td>
<td align="left"><strong>path</strong> - 전송되는 대상의 절대 경로(쿼리포함)</td>
<td align="left"><strong>Status Code</strong>-요청 성공여부 나타내는 코드</td>
</tr>
<tr>
<td align="center"></td>
<td align="left"><strong>HTTP Version</strong></td>
<td align="left"><strong>Status Text</strong>-코드와 함께 전달될 메세지</td>
</tr>
<tr>
<td align="center"></td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center"><span style='background-color:#fff5b1'><strong>Header</strong></span></td>
<td align="left">- 요청의 추가 정보들을 가지고 있다.</td>
<td align="left">- 응답에 쓰는 Header 값 따로 존재</td>
</tr>
<tr>
<td align="center"></td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center"><span style='background-color:#dcffe4'><strong>Empty Line</strong></span></td>
<td align="left">- 공백 한 줄</td>
<td align="left">- 공백 한 줄</td>
</tr>
<tr>
<td align="center"></td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr>
<td align="center"><span style='background-color:#d1ebf9'><strong>Message Body</strong></span></td>
<td align="left">- 실제 전송하는 데이터가 담겨 있는 부분</td>
<td align="left">- 실제 전송하는 데이터가 담겨 있는 부분</td>
</tr>
</tbody></table>
<pre><code>* OWS : 띄어쓰기 허용
* field-name은 대소문자 구분을 하지 않는다.
* 서버가 값을알고 있어야 한다</code></pre><br>

<hr>
<h1 id="📖-http-method">📖 HTTP Method</h1>
<h2 id="📌-주요-메서드-crud">📌 주요 메서드 (CRUD)</h2>
<h3 id="1️⃣-post---리소스-생성-">*<em>1️⃣ POST - 리소스 생성 *</em></h3>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/aae0a8fc-0e28-454b-9926-5e5d109e8973/image.png' width = 80%>

<ul>
<li><p>보통 생성될 때 데이터를 식별하기위해서 id값(식별자) 생김</p>
</li>
<li><p>html form에 사용</p>
</li>
<li><p>데이터를 처리하는 방식(리소스)에 정해진 것은 없다</p>
</li>
<li><p><strong>message body를 통해 요청 데이터를 전달</strong>한다</p>
<hr>
</li>
</ul>
<h3 id="2️⃣-get---리소스-조회"><strong>2️⃣ GET - 리소스 조회</strong></h3>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/ed4cb4ae-db42-4ca9-8c81-ed9277e93f62/image.png' width = 80%>

<ul>
<li>❶ <code>Query String</code> 미포함시
 : message body가 지원되지 않는 경우가 많아 권장안한다</li>
</ul>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/dac079a8-3252-481a-aeaa-d792d25b0fba/image.png' width = 80%>

<ul>
<li><p>❷ <code>Query String</code> 포함인 경우
 : 서버에 추가적인 데이터 전송이 필요하면 query string 을 사용!</p>
<hr>
</li>
</ul>
<h3 id="3️⃣-put---리소스-덮어쓰기"><strong>3️⃣ PUT - 리소스 덮어쓰기</strong></h3>
<ul>
<li>클라이언트가 항상 <strong>리소스를 식별</strong>해야한다 (post와 다름)</li>
</ul>
<table>
<thead>
<tr>
<th>❶</th>
<th>❷</th>
<th>❸</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/luv_lyn/post/37d7aa52-213d-4668-995c-ff27c6ff129f/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/luv_lyn/post/64d7672e-b701-4321-a341-d5eeb166d5b6/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/luv_lyn/post/4b62e1ef-c8eb-44bb-9c62-696a120b2368/image.png" alt=""></td>
</tr>
<tr>
<td>- ❶ 기존 리소스 존재한다면 ➝ 리소스 전체 수정</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- ❷ <strong>기존 리소스에서 일부만 변경하고 싶은데? ➝ 그래도 리소스 완전히 덮어씌워짐</strong></td>
<td></td>
<td></td>
</tr>
<tr>
<td>- ❸ 기존 리소스가 없는 경우 ➝ 리소스 생성</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<hr>
<h3 id="-4️⃣-patch---리소스-부분-수정">** 4️⃣ PATCH - 리소스 부분 수정**</h3>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/4926400d-5c5f-40d4-9c53-4de78f5686c7/image.png' witdh=50%>

<ul>
<li><p>put이랑 다르다, 필요한 부분만 수정할 수 있다</p>
<hr>
</li>
</ul>
<h3 id="5️⃣-delete---리소스-삭제"><strong>5️⃣ DELETE - 리소스 삭제</strong></h3>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/98f1f852-88c2-4ab5-8803-41db1216c515/image.png" alt=""></p>
<h2 id="📍-http-method-속성">📍 HTTP Method 속성</h2>
<blockquote>
<p>💡 <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">속성표</a>
*optional : 있을수도 없을수도</p>
</blockquote>
<h3 id="💡-안전성-safe">💡 안전성 (Safe)</h3>
<ul>
<li><strong>🌟 데이터 변경되면 안전하지 않다</strong><ul>
<li>안정 : <code>GET</code> (조회만 하니까)</li>
<li>불안정 : <code>POST</code>, <code>DELETE</code>, <code>PUT</code>, <code>PATCH</code></li>
</ul>
</li>
</ul>
<h3 id="💡-멱등성-idempotent">💡 멱등성 (Idempotent)</h3>
<ul>
<li><strong>🌟 한번을 호출하거나 수천번을 호출하거나 항상 결과는 같다</strong>(요청이 실패한 경우 재시도 하기위해 필요)<ul>
<li><strong>⭕️ 멱등성 보장</strong>
<code>GET</code> → 같은 결과가 계속 조회된다. (리소스 조회 재요청중 수정요청이 추가된다면? 멱등성 고려X)
<code>PUT</code> → 수정해서 대체된 후의 결과는 계속 같다.
<code>DELETE</code> → 같은 요청을 여러번해도 삭제된 결과는 같다.</li>
<li><strong>❌ 멱등성 보장 안함</strong></li>
</ul>
</li>
<li><em><code>POST</code>*</em> → 계좌 송금을 두번한다면?, 게시판 글쓰기, 회원가입</li>
</ul>
<h3 id="💡-캐시가능성-cacheable">💡 캐시가능성 (Cacheable)</h3>
<ul>
<li><strong>🌟 재사용을 위해 요청에 대한 응답을 저장할 수 있는가</strong> <ul>
<li>캐시(Cache) : 매번 요청때마다 데이터를 다시 전송할 필요가 없도록 웹브라우저가 임시보관하는 장소</li>
<li>변경 가능성이 적은 정적자원을 주로 캐싱</li>
<li><strong>캐시 가능</strong> : <code>GET</code>, <code>HEAD</code>, <code>POST</code></li>
</ul>
</li>
</ul>
<br>


<hr>
<h1 id="📖-http-상태-코드">📖 HTTP 상태 코드</h1>
<blockquote>
<p>아까 응답 메세지의 <code>200 ok</code> 부분 , 모두 외울필요 없음 백단위로 알고 있으셈</p>
</blockquote>
<h2 id="📌-1xx-정보">📌 1xx (정보)</h2>
<ul>
<li><p><strong>🌟 요청 수신 후 처리중</strong> (잘 사용 안함)</p>
<h2 id="📌-2xx-성공">📌 2xx (성공)</h2>
<ul>
<li><strong>🌟 정상 처리 완료</strong></li>
</ul>
<table>
<thead>
<tr>
<th>대표코드</th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td><strong>200 OK</strong></td>
<td>요청 성공</td>
</tr>
<tr>
<td><strong>201 Created</strong></td>
<td>새로운 리소스 생성</td>
</tr>
<tr>
<td><strong>202 Accepted</strong></td>
<td>요청이 수신되었으나 처리가 완료되지 않음 (주로 Batch 처리에서 사용)</td>
</tr>
<tr>
<td><strong>204 No Content</strong></td>
<td>요청은 성공했지만, 응답 데이터가 없음 (저장 버튼, 작성 버튼 )</td>
</tr>
</tbody></table>
</li>
</ul>
<h2 id="📌-3xx-리다이렉션">📌 3xx (리다이렉션)</h2>
<ul>
<li><strong>🌟 요청을 완료하려면 추가 행동이 필요한 상태</strong> (+ 서버가 너 이거 해바방! 알려줌)</li>
</ul>
<ul>
<li><p><strong>리다이렉션의 종류</strong></p>
<ul>
<li><strong>영구 리다이렉션</strong> 
url이 영구적으로 변경된 경우 - 기존 url 사용 안함</li>
</ul>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td><strong>301 Moved Permanently</strong></td>
<td>메서드 변경(get), 메세지 변경될 수도</td>
</tr>
<tr>
<td><strong>308 Permanent Redirect</strong></td>
<td>메서드와 본문 유지</td>
</tr>
</tbody></table>
<ul>
<li><strong>일시 리다이렉션</strong>
url이 일시적으로 변경된 경우</li>
</ul>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td><strong>302 Found</strong></td>
<td>메서드 변경(get)</td>
</tr>
<tr>
<td><strong>303 See Other</strong></td>
<td>메서드 변경(get)</td>
</tr>
<tr>
<td><strong>307 Temporary Redirect</strong></td>
<td>메서드와 본문 유지</td>
</tr>
<tr>
<td>```</td>
<td></td>
</tr>
<tr>
<td>💡 URL이 일시적으로 변경된 경우 예시</td>
<td></td>
</tr>
<tr>
<td>-- PRG 패턴 : 게시글 작성(Post) → 응답(Redirect) → 리다이렉트 요청(Get)</td>
<td></td>
</tr>
<tr>
<td>-- 이 패턴이 없으면 새로고침을 할때마다 요청이 중복 처리(post는 멱등성이 없으므로)</td>
<td></td>
</tr>
</tbody></table>
</li>
<li><ul>
<li><p>적용하면 새로고침 할때 get 요청을 한다</p>
<pre><code>
- **기타 리다이렉션**
캐시를 활용할 것인지에 대한 여부

|  ||
|---|---|
|**304 Not Modified**|캐시 목적으로 사용, 리소스가 수정되지 않았다 -&gt; 클라이언트가 캐시 데이터를 조회하도록 유도|

</code></pre></li>
</ul>
</li>
</ul>
<h2 id="📌-4xx-클라이언트-에러">📌 4xx (클라이언트 에러)</h2>
<ul>
<li><p>🌟 <strong>잘못된 문법 등으로 서버가 요청 수행 불가</strong> (재시도 해봤자 실패)</p>
<table>
<thead>
<tr>
<th>대표코드</th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>*<em>400 Bad Request *</em></td>
<td>요청 내용을 수정해야한다</td>
</tr>
<tr>
<td><strong>401 Unauthorized</strong></td>
<td>리소스에 대한 인증이 필요하다 (로그인같은)</td>
</tr>
<tr>
<td><strong>403 Forbidden</strong></td>
<td>승인 거부, 주로 인가 문제 (권한 없음)</td>
</tr>
<tr>
<td><strong>404 Not Found</strong></td>
<td>요청 리소스가 서버에 없다</td>
</tr>
</tbody></table>
</li>
</ul>
<h2 id="📌-5xx-서버-에러">📌 5xx (서버 에러)</h2>
<ul>
<li>🌟 <strong>서버 오류, 요청은 정상이지만 서버가 처리하지 못함</strong> (재시도시 성공할 수도)</li>
<li>대표코드<ul>
<li><strong>500 Internal Server Error</strong> 대부분 이거임 <ul>
<li><strong>503 Service Unavailable</strong> 서비스 이용 불가</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h1 id="📖-http-header">📖 HTTP header</h1>
<blockquote>
<p>💡 부가적인 정보 전달에 쓰임 (Message Body 내용, 크기, 인증, 브라우저 정보, 서버 정보 등)</p>
</blockquote>
<img src='https://velog.velcdn.com/images/luv_lyn/post/f4eaa40d-4442-4839-bf2c-bef891a52b56/image.png' width=60%>

<ul>
<li><code>field-name: OWS field-value OWS</code> (OWS : 띄어쓰기 허용)</li>
<li><code>field-name</code>은 대소문자 구분을 하지 않는다.</li>
<li>HTTP 전송에 필요한 <strong>모든 부가정보를 표현</strong>할 수 있다.</li>
<li>임의의 Header를 추가할 수 있다. 단, 서버가 값을 알고있어야 함</li>
<li><strong>텍스트 (plain text)로</strong> 이루어져 있다.</li>
<li>각각의 헤더는 <strong>하나의 줄로 구분</strong>된다.</li>
</ul>
<h3 id="✔️실제-header-확인하는-법">✔️실제 Header 확인하는 법</h3>
<ul>
<li>개발자도구(F12) → Network 탭 클릭 → Fetch/XHR 탭 클릭 → 우측 Header 정보</li>
</ul>
<br>

<h2 id="📌-http-header-모음">📌 HTTP Header 모음</h2>
<h3 id="✔️-표준-header">✔️ <a href="https://en.wikipedia.org/wiki/List_of_HTTP_header_fields"><strong>표준 Header</strong></a></h3>
<ul>
<li>외우기보다 찾아서 사용하자 !</li>
</ul>
<h3 id="✔️-대표적인-header">✔️ 대표적인 Header</h3>
<h3 id="1️⃣-표현-헤더">1️⃣ <strong>표현 헤더</strong></h3>
<p>📎<strong>리소스에 대한 표현 정보</strong> (우리 데이터는 이런 형식이에요 ~)
📎 요청과 응답에서 모두 사용</p>
<ul>
<li><strong><code>Content-Type</code> : 형식</strong> <pre><code>- 데이터의 미디어 타입이나 문자 인코딩을 나타냄</code></pre></li>
<li><strong><code>Content-Encoding</code> : 압축 방식</strong><ul>
<li>데이터를 압축 후 Encoding 헤더를 추가하면, 읽는 쪽에서 해당 정보로 이동</li>
</ul>
</li>
<li><strong><code>Content-Language</code> : 언어</strong></li>
<li><strong><code>Content-Length</code> : 길이</strong><pre><code> - byte 단위로 나타냄</code></pre></li>
</ul>
<h3 id="2️⃣-컨텐츠-협상">2️⃣ <strong>컨텐츠 협상</strong></h3>
<p>📎 <strong>클라이언트가 선호하는 표현을 요청</strong>
📎 우선순위가 존재 (1에 가까울 수록 우선순위 높다, 1인경우 숫자 생략 가능)</p>
<ul>
<li><strong><code>Accept</code> : 선호하는 미디어 타입</strong></li>
<li><strong><code>Accept-Charset</code> : 선호하는 문자 인코딩</strong></li>
<li><strong><code>Accept-Encoding</code> : 선호하는 압축 인코딩</strong></li>
<li><strong><code>Accept-Language</code> : 선호하는 언어</strong></li>
</ul>
<h3 id="3️⃣-일반-정보">3️⃣ 일반 정보</h3>
<ul>
<li><p><strong><code>From</code> : 클라이언트 이메일 정보</strong> - 잘 사용 안함   </p>
</li>
<li><p><strong><code>Referer</code> : 현재 요청된 페이지의 이전 웹 페이지 주소</strong> - 유입 경로 파악 가능</p>
</li>
<li><p><strong><code>User-Agent</code> : 클라이언트 애플리케이션 정보(PC, Mobile)</strong>- 어떤 환경에서 주로 접속하는지 통계</p>
</li>
<li><p><strong><code>Server</code> : 요청을 처리하는 ORIGIN 서버의 Software 정보</strong></p>
</li>
<li><p><strong><code>Date</code> : HTTP 요청이 발생한 날짜와 시간</strong></p>
</li>
</ul>
<h3 id="4️⃣-특별-정보">4️⃣ 특별 정보</h3>
<ul>
<li><p><strong><code>Host</code> : 요청한 도메인 정보</strong> (필수)</p>
</li>
<li><p><strong><code>Location</code> : 생성된 리소스 URI, 리다이렉트 주소</strong></p>
<ul>
<li>응답코드 3xx와 함께 응답되면 리다이렉트 주소</li>
<li>응답코드 201(Created)와 함께 응답되면 생성된 리소스의 URI</li>
</ul>
</li>
<li><p><strong><code>Allow</code> : 허용 가능한 HTTP Method</strong></p>
<ul>
<li>405 (Method Not Allowed)와 함께 응답</li>
</ul>
</li>
<li><p><strong><code>Retry-After</code> : 다음 요청까지 대기 해야하는 시간</strong></p>
<ul>
<li>503 (Service Unavailable)와 함께 서비스가 언제까지 사용이 불가한지 알려줌</li>
</ul>
</li>
</ul>
<h3 id="5️⃣-인증">5️⃣ 인증</h3>
<ul>
<li><p><strong><code>Authorization</code> : 클라이언트 인증 정보</strong></p>
</li>
<li><p><strong><code>WWW-Authenticate</code> : 리소스에 필요한 인증 방법</strong></p>
<ul>
<li>401 (Unauthorized) 응답과 함께 사용된다.</li>
</ul>
</li>
</ul>
<h3 id="6️⃣-cookie">6️⃣ Cookie</h3>
<p>📎 Cookie를 사용해 요청마다 상태를 전달한다 (세션관리나 광고 트래킹에 주로 사용)</p>
<ul>
<li><p><strong><code>Set-Cookie</code> : 서버에서 응답시 클라이언트로 Cookie 값 전달</strong></p>
<ul>
<li>만료기간(expire, max-age), 사용될 위치(domain, path) 설정</li>
<li>주의 ❌
항상 서버에 전달되니 최소한의 정보만 사용하여 트래픽을 최적화 시켜야 한다
탈취 당하기 쉬우니 보안에 민감한 개인정보 등은 저장하지 않는다</li>
</ul>
</li>
<li><p><strong><code>Cookie</code> : 클라이언트가 서버에서 받은 쿠키를 Cookie 헤더를 통해 전송한다.</strong></p>
</li>
<li><p><strong><code>Secure</code> : 해당 헤더가 적용되면 https인 경우에만 쿠키를 전송한다.</strong></p>
</li>
<li><p><strong><code>HttpOnly</code> : http 전송에만 사용한다.</strong></p>
<ul>
<li>자바스크립트에서 쿠키를 접근하지 못하게 만든다.</li>
</ul>
</li>
<li><p><strong><code>SameSite</code> : 쿠키에 설정된 도메인이 같은 경우만 쿠키를 전송한다.</strong></p>
<h3 id="7️⃣-cache">7️⃣ cache</h3>
</li>
<li><p><strong>Cache-Control : 응답시 사용</strong></p>
<ul>
<li><code>Cache-Control: max-age</code><ul>
<li>캐시 유효 시간이 지나면 다시 서버를 통해 데이터를 응답받고 캐시를 갱신</li>
</ul>
</li>
<li><code>Cache-Control: no-cache</code><ul>
<li>캐시 가능한 데이터지만, 서버에 검증하고 사용</li>
</ul>
</li>
<li><code>Cache-Control: no-store</code><ul>
<li>보안에 민감한 데이터, 캐시하지 않는다</li>
</ul>
</li>
</ul>
</li>
<li><p><strong><code>if-modified-since</code> : 캐시로 저장된 데이터 최종 수정일</strong></p>
</li>
<li><p><strong><code>Last-Modified</code> : 데이터가 마지막으로 수정된 시간</strong></p>
</li>
<li><p><strong><code>ETag</code> : 캐시용 데이터에 날짜, 시간이 아닌 이름을 지정</strong></p>
</li>
</ul>
<br>

<hr>
<h1 id="📖-restful-api">📖 Restful API</h1>
<blockquote>
<p>💡 REST를 잘 준수하는 API , HTTP API를 잘 설계하는 규칙</p>
</blockquote>
<h3 id="✔️-restrepresentational-state-transfer-">✔️ REST(Representational State Transfer) ?</h3>
<ul>
<li>자원을 이름으로 구분해서 자원의 상태를 주고 받는 것</li>
<li>URL로 자원 명시, 메서드를 통해 CRUD를 구현하는 것</li>
</ul>
<h3 id="✔️-설계-원칙">✔️ <a href="https://restfulapi.net/resource-naming/">설계 원칙</a></h3>
<ol>
<li>리소스는 명사를 사용해야 한다.</li>
<li>단수가 아닌 복수 형태를 사용해야 한다.</li>
<li>만약, REST만으로 해결하기 어려운 경우라면 동사를 허용한다.</li>
<li>자원의 계층 관계를 슬래시(/)로 표현한다.</li>
<li>마지막 문자에는 슬래시(/)가 있으면 안된다.</li>
<li>언더바(_)가 아닌 하이픈(-)을 사용해야 한다.</li>
<li>소문자를 사용해야 한다.</li>
<li>URI에 파일 확장자를 포함하면 안된다.</li>
<li>CRUD 함수명은 사용하지 않고, HTTP Method를 활용해야 한다.</li>
<li>정렬, 필터링, 페이징은 신규 API를 만드는것이 아닌 Query Parameter를 사용해야 한다.</li>
</ol>
<blockquote>
<h2 id="📌-maturity-model-성숙도-모델">📌 Maturity Model (성숙도 모델)</h2>
<p> <em>REST의 제약 조건에 따라 API를 등급화하는 방법</em></p>
<p><strong>level 0 *<em>- URL만 만듦
*</em>level 1</strong> - 의미있는 URL, but http 메서드 사용 부적절
<strong>level 2</strong> (최소 권장 레벨) - crud와 매칭되는 메서드를 이용하는것
<strong>level 3</strong> - 어떤 작업을 할 수 있는지 상태정보를 함께 넘겨준다</p>
</blockquote>
<br>

<hr>
<h1 id="▪️-web-application-▪️">▪️ Web Application ▪️</h1>
<hr>
<h1 id="📖-web-server">📖 Web Server</h1>
<blockquote>
<p>💡 HTTP 기반으로 동작, <strong>정적 리소스</strong> 제공
*정적리소스 : 이미 완성된 채로 서버에 존재하는 리소스, 원본 그대로 대답하는 데이터</p>
</blockquote>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/8ee89100-e33a-48d8-849e-bc45328f5332/image.png' width=50%>

<hr>
<h1 id="📖-was-web-application-server">📖 WAS (Web Application Server)</h1>
<blockquote>
<p>💡 HTTP 기반으로 동작, <strong>웹서버의 기능을 포함</strong> 
(추가적으로 코드를 실행 ➝ 로직 수행 ➝ 데이터와 상호작용 후(CRUD) <strong>동적 컨텐츠 생성</strong>)</p>
</blockquote>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/6520200c-ec70-4fce-ae71-978ea64c6840/image.png' width=50%>

<ul>
<li><strong>대표적인 WAS</strong> : <code>Tomcat</code> - spring boot에 내장되어있다</li>
</ul>
<blockquote>
<h3 id="✔️-web-server-🆚-was">✔️ Web Server 🆚 WAS</h3>
</blockquote>
<ol>
<li>실제로는 웹서버에서도 애플리케이션 로직을 포함할 수는 있다</li>
<li>와스는 애플리케이션 코드를 실행하는데 더욱 특화되어 있다</li>
<li>자바에서는 <strong>Servlet Container</strong> 기능을 제공하면 와스이다</li>
</ol>
<br>

<hr>
<h1 id="📖-web-system-구성">📖 Web System 구성</h1>
<h2 id="📌-was만-사용하는-경우">📌 WAS만 사용하는 경우</h2>
<p>(WAS 안에 로직 정적리소스 다 넣고 쓰기)</p>
<p>1️⃣ WAS가 너무 많은 역할을 하게된다 -&gt; <strong>서버 과부하</strong>
2️⃣ WAS에서는 애플리케이션 로직이 가장 중요한데 정적 리소스 때문에 <strong>로직 수행 불가할 수도</strong>
3️⃣ WAS에 장애가 생기면 화면 출력 불가 (+ 클라이언트에게 오류임을 응답할 수 없다)</p>
<h2 id="📌-실제-웹시스템-구성">📌 실제 웹시스템 구성</h2>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/14985ecd-5765-4a97-9f47-2b13cec97e8d/image.png' width=50%>

<ul>
<li>웹서버는 정적자원 처리 와스는 로직만! 분업을 시키자</li>
</ul>
<h3 id="✔️-장점">✔️ 장점</h3>
<ul>
<li><strong>효율적인 관리</strong> : 자원 늘어나면 서버를 추가해서 수평적 확장 가능 
( 정적 자원이 많이 쓰이면 Web Server / App관련 자원이 증가되면 WAS를 스케일 아웃 )</li>
<li>WAS가 다운되어도 Web Server는 정상 동작 (+ 에러페이지 전달 가능)</li>
</ul>
<hr>
<h1 id="📖-servlet">📖 Servlet</h1>
<ul>
<li>💡 HTTP 통신에서 <strong>요청과 응답을 처리</strong>하는데 사용된다
자바에서는 Http servlet을 상속받아 구현</li>
</ul>
<h2 id="📌-역할">📌 역할</h2>
<blockquote>
<p>📎 원래 서버에서 데이터 처리하는 과정</p>
</blockquote>
<pre><code>1. 서버와 TCP/IP 연결
2. HTTP Request Message 필요한 형태로 변환하여 읽기
    1. HTTP Method 및 URL 분석
    2. HTTP Header 분석
    3. HTTP Message Body 읽기 및 변환
3. 분석한 결과를 통해 프로세스 실행
🌟4. 비지니스 로직 실행
5. HTTP Response Message 생성
    1. HTTP Start Line 생성
    2. Header 생성
    3. HTTP Message Body에 응답 데이터를 요청한 형식에 맞춰 응답
    4. 처리가 불가하다면 예외처리
6. 응답 전달
7. 연결 종료</code></pre><p><strong>🌟 Servlet이 4번빼고 다해줌, 개발자는 개발만 하면된다</strong></p>
<h2 id="📌-동작-순서-1">📌 동작 순서</h2>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/325ef6d0-9094-4af6-ac45-a5bd084aa914/image.png' width=70%>

<ul>
<li><strong>개발자가 하는 일</strong><ol>
<li>Request 객체에 담겨져있는 HTTP 요청 정보를 꺼내서 사용<ul>
<li>요청 정보(URL, Method, Message Body)를 통해 필요한 기능(비지니스 로직)을 수행</li>
</ul>
</li>
<li>생선된 Response 객체에 HTTP 응답 정보를 입력</li>
</ol>
</li>
</ul>
<br>

<hr>
<h1 id="📖-servlet-container">📖 Servlet Container</h1>
<blockquote>
<p>Servlet을 지원하는 WAS 내부에 존재한다
💡<strong>Servlet을 초기화, 생성, 관리, 호출, 종료</strong>하는 역할을 수행한다</p>
</blockquote>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/1a616628-e5fb-471f-80f8-1b67e5c9352c/image.png' width=60%>


<ul>
<li><p><strong>생명주기</strong>
개발자가 직접 객체화 안해도, 코드만 작성하면 알아서 컨테이너가 알아서 Servlet을 생성한다
WAS가 종료될때 Servlet도 함께 종료</p>
</li>
<li><p><strong>Servlet Container가 하는일</strong>
Servlet을 생성, 관리, 호출, 종료하는 역할 - <code>싱글톤</code>으로 관리
동시 요청 처리를 위해 <code>멀티 스레드</code>를 지원</p>
</li>
</ul>
<p><em>*<code>싱글톤</code>? 객체를 하나만 생성하여 생성된 인스턴스를 공유하며 사용하는것</em></p>
<hr>
<h1 id="📖-thread">📖 Thread</h1>
<blockquote>
<p>코드를 하나하나 순차적으로 실행하는 것
<strong>한번에 하나만 가능</strong>하기에 동시에 여러개를 처리하려면 Thread를 추가 생성해야한다</p>
</blockquote>
<h2 id="📌-싱글-스레드---단일-요청">📌 싱글 스레드 - 단일 요청</h2>
<img src='https://velog.velcdn.com/images/luv_lyn/post/4d5204f7-70fc-4e41-89f2-5acd1b6daaee/image.png' width=40%>

<ul>
<li><strong>Thread가 하나 / 요청도 하나</strong>
요청이 종료되면 원래위치로 돌려놓는다</li>
</ul>
<h2 id="📌-싱글-스레드---동시-요청">📌 싱글 스레드 - 동시 요청</h2>
<img src='https://velog.velcdn.com/images/luv_lyn/post/bb15b7c5-0cf7-4dce-8381-0c062f6cfb38/image.png' width=40%>

<ul>
<li><strong>Thread가 하나 / 요청이 여러개</strong>
두번째 요청이오면 Thread를 할당받기위해 <strong>대기</strong>한다
첫번째 요청이 끝나고 Thread가 반환되면 그때 다시 할당받고,,,반복</li>
</ul>
<br>

<hr>
<h1 id="📖-multi-thread">📖 Multi Thread</h1>
<blockquote>
<p>💡 여러개의 스래드를 만들자! WAS는 멀티스레드를 지원한다</p>
</blockquote>
<h2 id="📌-1-요청이-올때마다-새로운-thread를-생성하기">📌 1. 요청이 올때마다 새로운 Thread를 생성하기</h2>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/45ce6d62-025d-4460-a4c4-508f0b6e9358/image.png' width=40%>

<h3 id="✔️-장점-1">✔️ 장점</h3>
<ul>
<li>동시요청을 처리할 수 있다</li>
<li>하나에 문제가 발생해도 나머지 Thread는 정상적으로 동작한다</li>
</ul>
<h3 id="✔️-단점">✔️ 단점</h3>
<ul>
<li>Thread드 생성수에 제한이 없고 생성 비용이 높다
(동시요청이 많이 발생하면 리소스 부족으로 서버 다운)</li>
<li>Thread를 사용하면 <code>context switching</code> 비용이 발생한다</li>
</ul>
<p><em>*<code>context switching</code> ?
윈도우의 작업관리자 - 백그라운드 프로세스(순차적으로 실행되는 것)
1번 프로그램을 실행하다 2번으로 스위칭 할때 이 사이에 생기는 것 !</em></p>
<h2 id="📌-2-thread-pool을-이용하기">📌 2. Thread Pool을 이용하기</h2>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/c8a057f0-6bd2-464c-95ff-6e70d81da625/image.png' width=60%>

<p><strong>🌟 미리 Thread를 여러개 만들어 두고 요청이 발생할 때 사용, 요청 종료시 반납</strong></p>
<ul>
<li>모든 Thread가 사용중이라면?
일을 마친 Thread가 생길때까지 대기하거나 거절할 수 있다 (예시 - 대기번호)</li>
</ul>
<br>

<blockquote>
<h3 id="💡-정리하기">💡 정리하기</h3>
</blockquote>
<p>1️⃣ <strong>WAS는 Multi Thread를 지원</strong> </p>
<ul>
<li>개발자는 멀티스레드 관련코드 고려하지 않아도된다</li>
<li>멀티 스레드 환경에서는 싱글톤 객체 사용 → <strong>공유변수 조심</strong><blockquote>
</blockquote>
</li>
</ul>
<hr>
<p>2️⃣ <strong>Thread Pool</strong></p>
<ul>
<li>생성가능한 스레드의 최대갯수를 관리 (tomcat? 기본 200개 설정, 갯수 변경가능)<blockquote>
</blockquote>
</li>
<li><strong>장점</strong><ul>
<li>요청마다 생성하는 단점 보완</li>
<li>미리 Thread를 생성해두었기 때문에 생성하거나 종료하는 비용 절약 + 응답이 빠르다</li>
<li>Thread의 최대갯수가 제한되어 있기때문에 초과 요청이 와도 안전하게 처리</li>
</ul>
</li>
<li><strong>단점</strong><ul>
<li>최대 Thread 수를 너무 낮게 한다면 응답 지연</li>
<li>최대 Thread 수가 높으면 요청 증가시 리소스 부족으로 서버 다운<blockquote>
</blockquote>
</li>
</ul>
</li>
</ul>
<br>

<hr>
<h1 id="▪️-ssr-csr-▪️">▪️ SSR, CSR ▪️</h1>
<hr>
<h1 id="📖-ssr-server-side-rendering">📖 SSR (Server Side Rendering)</h1>
<blockquote>
<p>💡 <strong>서버에서 화면을 그려준다</strong>! 동적으로 html을 만들어서 클라이언트에게 제공하는 기술
*Java에서 대표적으로 사용되는 것 : JSP, Thymeleaf</p>
</blockquote>
<h2 id="📌-동작-흐름">📌 동작 흐름</h2>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/d1235c41-2d4e-4c8d-bb54-561fc3aff19b/image.png" alt=""></p>
<h3 id="✔️-장점-2">✔️ 장점</h3>
<ul>
<li>초기 페이지 로드시 완전하게 랜더링된 html을 반환하기 때문에 <strong>로딩이 빠르다</strong></li>
<li><code>SEO</code>에 유리 </li>
</ul>
<p><em>*<code>SEO</code>?
검색 엔진에서 상위 노출될 수있도록 최적화하는 과정</em></p>
<h3 id="✔️-단점-1">✔️ 단점</h3>
<ul>
<li><strong>모든</strong> 요청에 대해 <strong>페이지를 렌더링</strong>해야함</li>
<li>만들어 진 채로 응답해야하기 때문에 <strong>느리다</strong></li>
</ul>
<hr>
<h1 id="📖-csr">📖 CSR</h1>
<blockquote>
<p>💡 <strong>클라이언트가 화면을 그린다</strong>! 자바스크립트로 html을 생성</p>
</blockquote>
<h2 id="📌-동작-흐름-1">📌 동작 흐름</h2>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/997ec785-fbaf-4b64-93b5-0bf13d41c15f/image.png" alt=""></p>
<ul>
<li><strong>클라이언트</strong>- html을 서버로 요청</li>
<li><strong>서버</strong> - 비어있는 html + js 링크 ( 클라이언트 로직, 렌더링 포함 )</li>
<li>동작하는 요청에 따라 필요한 형태(json 등)로 응답받는다</li>
<li>이 응답 데이터로 html을 동적으로 그린다</li>
</ul>
<h3 id="✔️-장점-3">✔️ 장점</h3>
<ul>
<li>클라이언트가 렌더링을 하므로 <strong>사용자의 상호작용에 대한 반응이 빠르다</strong>
(구글맵 기억 - 사용자가 바로바로 움직인당)</li>
<li><strong>초기 로딩</strong>(모든 로직과 js 링크 전달) <strong>후</strong>에는 서버와 통신없이 <strong>바로 전환 가능</strong></li>
</ul>
<h3 id="✔️-단점-2">✔️ 단점</h3>
<ul>
<li><strong>초기로딩 시간이 길다</strong></li>
<li>검색엔진이 js를 실행하지 못하면 <code>SEO</code>에 불리</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌼 Spring 기초 ① - 네트워크, 용어모음  / TIL - day 19]]></title>
            <link>https://velog.io/@luv_lyn/%EA%B8%B0%EC%B4%88-Spring-1-TIL-day-19</link>
            <guid>https://velog.io/@luv_lyn/%EA%B8%B0%EC%B4%88-Spring-1-TIL-day-19</guid>
            <pubDate>Mon, 17 Mar 2025 03:53:03 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="▪️-네트워크-▪️">▪️ 네트워크 ▪️</h1>
<hr>
<blockquote>
<p>💡 <strong>컴퓨터들은 어떻게 서로 통신할까?</strong>
_ 초기에는 물리적인 형태(usb)였다. 통신할 컴퓨터가 멀리있다면? <strong>인터넷 사용!</strong>_</p>
</blockquote>
<h3 id="✔️-인터넷-internet">✔️ 인터넷 (Internet)</h3>
<ul>
<li>TCP / IP 기반으로 연결된 <strong>컴퓨터 네트워크 통신망</strong></li>
<li>해저 광케이블(물리적 연결) / 인공위성 (무선 통신)
➝ *<em>World Wide Web(www)구축 *</em></li>
</ul>
<br>

<hr>
<h1 id="📖-ip-internet-protocol">📖 IP (Internet Protocol)</h1>
<blockquote>
<p>💡 <em><strong>데이터 통신에 대한 규약</strong> - 숫자는 IP 자체가 아닌 주소임
 (데이터를 안전하게 전달하기위해 최소한의 인터넷 프로토콜이 필요하다)</em></p>
</blockquote>
<h2 id="📌-packet">📌 Packet</h2>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/a285a1c9-df44-4163-a2d7-14a28a6f606c/image.png' width=50%>

<ul>
<li>소스IP<code>출발지</code> + 대상IP<code>도착지</code>를 포함 ➝ *<em>어떤 컴퓨터에 전송할 지 판별 가능 *</em></li>
<li><strong>구분</strong> : 헤더 ,페이로드, 트레일러(수신여부 포함)</li>
<li>단지 데이터를 주기만 하는게 아니라 받고 <strong>응답한다</strong></li>
</ul>
<h2 id="📌-ip-방식의-문제점">📌 IP 방식의 문제점</h2>
<ul>
<li><p><strong>❶ 애플리케이션 구분</strong> (어떤 프로그램에 사용하지? 구분이 어렵다)</p>
</li>
<li><p><strong>❷ 비연결성</strong> (수신 대상의 상태에 상관없이 데이터를 전송 - 컴퓨터가 꺼져 있어도)</p>
</li>
<li><p><strong>❸ 비신뢰성</strong></p>
<ul>
<li>패킷이 소실되는 경우, 손상여부를 양측 모두 알 수 없다.</li>
<li>데이터가 큰 경우에는 여러개로 나눠 전송되고, 순서가 섞여버림 
➝ 이런 경우에도 데이터를 재전송 하지 않는다</li>
</ul>
</li>
</ul>
<br>


<hr>
<h1 id="📖-tcp-transmission-control-protocol">📖 TCP (Transmission Control Protocol)</h1>
<blockquote>
<p><em>💡 데이터를 <strong>신뢰성</strong>있게 전달 (IP방식의 문제점 보완)</em></p>
</blockquote>
<h2 id="📌-3-way-handshake">📌 3 Way HandShake</h2>
<p><strong>📍 최소한의 논리적 연결로 연결되었다</strong>고 가정하는것 !</p>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/67749da7-7d2d-4a78-a848-89e6e9673160/image.png' width=70% >

<h3 id="✔️-syn--ack">✔️ SYN , ACK</h3>
<table>
<thead>
<tr>
<th align="center">SYN (Synchronize)</th>
<th align="center">ACK (Acknowledge)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">클라이언트 ➝ 서버</td>
<td align="center">서버 ➝ 클라이언트</td>
</tr>
<tr>
<td align="center"><strong>접속 요청</strong></td>
<td align="center"><strong>요청 수락</strong></td>
</tr>
<tr>
<td align="center">SYN 플래그 + 시퀀스 번호</td>
<td align="center">SYN 플래그 + ACK 플래그 + (시퀀스 번호+1)</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">데이터 전송 가능</td>
</tr>
</tbody></table>
<h3 id="✔️-특징">✔️ 특징</h3>
<ul>
<li>IP 방식의 단점 보완 (데이터가 잘 전송되었는지 응답 반환)</li>
<li>패킷이 나뉘어져 오더라도 <strong>순서를 보장</strong>한다 ➝ 느리다</li>
</ul>
<br>


<hr>
<h1 id="📖-udp-user-datagram-protocol">📖 UDP (User Datagram Protocol)</h1>
<blockquote>
<p><em>💡 비연결형이고 신뢰성이 없는 프로토콜 ➝ 증말로 빠르당 ( <strong>실시간성 보장</strong> )
 ( 인터넷 전화, 스트리밍, 온라인 게임 등 )</em></p>
</blockquote>
<h2 id="📌-udp의-특징">📌 UDP의 특징</h2>
<ol>
<li><strong>IP 방식과 비슷하다</strong> - 3 way handshake 하지 않는다 ** (비신뢰성)**</li>
<li><strong>추가적인 기능이 거의 없다</strong> - 하지만 빨랐죠?</li>
<li><strong><code>PORT</code></strong>가 존재한다</li>
<li>잘못된 데이터가 전송되지 않도록 <strong>체크섬(Checksum)을 포함</strong></li>
</ol>
<h2 id="📌-port">📌 PORT</h2>
<ul>
<li><p><strong>같은 IP 내에서 프로세스 구분</strong>을 함 
➝ IP의 단점 (애플리케이션 구분)을 보완</p>
</li>
<li><p><code>Packet</code>에 포함되어 있다</p>
<blockquote>
<h4 id="✔️-자주-사용되는-port">✔️ 자주 사용되는 PORT</h4>
</blockquote>
<ol>
<li><strong>0~65535</strong> 까지 할당 가능</li>
<li><strong>0~1023</strong> : 이미 사용되고 있는 포트  <del>애지간하면 쓰지말자</del>
<strong>💡 <code>HTTP</code> 80 (TCP) / <code>HTTPS</code> 443 (TCP)</strong></li>
</ol>
</li>
</ul>
<br>

<hr>
<h1 id="▪️-web-기초-▪️">▪️ Web 기초 ▪️</h1>
<hr>
<h1 id="📖-dnsdomain-name-system">📖 DNS(Domain Name System)</h1>
<blockquote>
<p>IP주소는 유동적이기도 하고, 변경될 수도 있고, 외우기가 힘들다 
💡 <em>사람이 읽을 수 있는 <strong>도메인 주소</strong>를 컴퓨터가 읽을 수 있는 <strong>IP 주소로 변환</strong>한다</em></p>
</blockquote>
<h2 id="📌-dns-동작-순서">📌 DNS 동작 순서</h2>
<ol>
<li>원하는 도메인 구매후 DNS 서버에 등록한다</li>
<li>도메인 명을 입력하면 DNS는 IP주소를 반환한다</li>
<li>IP가 변경되면 DNS는 그대로, dns에 등록된 IP주소만 바꾸면 댐 
➝ 대표적인 예시 : <strong>URL</strong></li>
</ol>
<br>


<hr>
<h1 id="📖-uri-uniform-resource-identifier">📖 URI (Uniform Resource Identifier)</h1>
<blockquote>
<p><em>💡 인터넷 자원(<strong>데이터</strong>)을 나타내는 <strong>고유식별자</strong></em></p>
</blockquote>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/e72b14f8-021e-4683-aa93-b72a63f8ea7d/image.png' width=50%>


<ul>
<li><strong>자원</strong> : 페이지, 텍스트, 이미지, 동영상, 파일 등</li>
<li><em>➝  URI는 URL, URN을 포함하는 개념*</em></li>
</ul>
<br>

<h2 id="📌-urluniform-resource-locator">📌 URL(Uniform Resource Locator)</h2>
<blockquote>
<p>💡 <strong>자원의 위치</strong>를 나타낸다</p>
</blockquote>
<h3 id="✔️-url의-구조">✔️ URL의 구조</h3>
<blockquote>
<p>** 💡 예시**
<strong><span style='background-color:#d1ebf9'>scheme</span>:[//<span style='background-color:#fff5b1'>[user[:password]</span>@]<span style='background-color:#ffdce0'>host[:port]</span>]<span style='background-color:#dcffe4'>[/path]</span><span style='background-color:#f5f0ff'>[?query]</span><span style='background-color:#F7DDBE'>[#fragment]</span></strong>
<a href="https://www.google.com:443/search?q=%EC%8A%A4%ED%8C%8C%EB%A5%B4%ED%83%80+%EC%BD%94%EB%94%A9%ED%81%B4%EB%9F%BD">https://www.google.com:443/search?q=스파르타+코딩클럽</a></p>
</blockquote>
<p><span style='background-color:#d1ebf9'></span><span style='background-color:#fff5b1'></span><span style='background-color:#ffdce0'></span><span style='background-color:#dcffe4'></span><span style='background-color:#f5f0ff'></span><span style='background-color:#F7DDBE'></span></p>
<table>
<thead>
<tr>
<th align="center">scheme</th>
<th align="center">user[:password]</th>
<th align="center">host[:port]</th>
<th align="center">[/path]</th>
<th align="center">[?query]</th>
<th align="center">[#fragment]</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong><span style='background-color:#d1ebf9'>프로토콜</span></strong></td>
<td align="center"><strong><span style='background-color:#fff5b1'>사용자 정보</span></strong></td>
<td align="center"><strong><span style='background-color:#ffdce0'>도메인 or IP주소</span></strong></td>
<td align="center"><strong><span style='background-color:#dcffe4'>리소스의 경로</span></strong></td>
<td align="center"><strong><span style='background-color:#f5f0ff'>key=value</span></strong></td>
<td align="center"><strong><span style='background-color:#F7DDBE'>html 내부 북마크</span></strong></td>
</tr>
<tr>
<td align="center">주로 http, https, ftp 사용</td>
<td align="center">URL은 사용 X</td>
<td align="center">일반적으로 생략</td>
<td align="center">계층 구조</td>
<td align="center">?로 시작되고 &amp;로 구분</td>
<td align="center">특정위치로 이동</td>
</tr>
</tbody></table>
<br>

<h2 id="💡-url-urn-비교-예시-참고">💡 URL, URN 비교 (예시 참고)</h2>
<table>
<thead>
<tr>
<th align="center">URL(Locator)</th>
<th align="center">URN(Name)</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>자원의 위치</strong></td>
<td align="center"><strong>자원의 이름</strong></td>
</tr>
<tr>
<td align="center">프로토콜 O</td>
<td align="center">프로토콜 X</td>
</tr>
<tr>
<td align="center">위치 변경시 사용불가</td>
<td align="center">위치 변경시에도 사용 가능</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">(이름으로 검색하기 때문에)</td>
</tr>
<tr>
<td align="center"><strong>대중화 O</strong></td>
<td align="center"><strong>대중화 X</strong></td>
</tr>
</tbody></table>
<br>


<hr>
<h1 id="▪️-용어-모음집-▪️">▪️ 용어 모음집 ▪️</h1>
<hr>
<h1 id="📖-프로그램-명명-규칙-casing">📖 프로그램 명명 규칙 (casing)</h1>
<h3 id="1️⃣-snake_case">1️⃣ snake_case</h3>
<ul>
<li><code>데이터 베이스</code>에서 주로 사용<ul>
<li><strong>사용법 :</strong> 소문자 or 대문자 </li>
<li><strong>단어연결 :</strong> 언더바 <code>_</code> 사용</li>
</ul>
</li>
</ul>
<h3 id="2️⃣-camelcase">2️⃣ camelCase</h3>
<ul>
<li>Java, JavaScript, TypeScript에서 변수, 함수, 메서드 이름<ul>
<li><strong>사용법 :</strong>  첫 글자 소문자</li>
<li><strong>단어연결 :</strong> 연결시에만 대문자</li>
</ul>
</li>
</ul>
<h3 id="3️⃣-pascalcase">3️⃣ PascalCase</h3>
<ul>
<li>클래스 이름<ul>
<li><strong>사용법 :</strong> 첫 글자 대문자</li>
<li><strong>단어연결 :</strong> 대문자로 연결</li>
</ul>
</li>
</ul>
<h3 id="4️⃣-kebab-case">4️⃣ kebab-case</h3>
<ul>
<li>거의 사용 안함<ul>
<li><strong>사용법 :</strong> 모든 단어 소문자</li>
<li><strong>단어연결 :</strong> 대시 <code>-</code> 로 연결</li>
</ul>
</li>
</ul>
<hr>
<h1 id="📖-json">📖 JSON</h1>
<blockquote>
<p>💡_ 클라이언트, 서버가 통신할 때 사용하는 데이터 양식_</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/c20c4ed7-4fbc-4a08-b008-a6736787237f/image.png" alt=""></p>
<ul>
<li>통일된 양식 : <strong>언어와 관계없이 데이터를 주고 받을 수 있다</strong></li>
<li><strong>이해가 쉽고 용량이 작다</strong> ➝ XML 대체</li>
<li><code>클라이언트 to 서버</code> <code>서버 to 클라이언트</code> <code>서버 to 서버</code> 모두 사용</li>
<li>예시 - <a href="https://www.sktenterprise.com/bizInsight/blogDetail/dev/2714">MSA(MicroService Architecture)</a>
: <strong><code>JSON</code> 형태로 데이터 통신</strong>을 하기 때문에 애플리케이션 별 언어가 달라도 서로 통신가능</li>
</ul>
<h2 id="📌-json의-구조">📌 JSON의 구조</h2>
<pre><code class="language-js">{
  &quot;user&quot;: [
    {
      &quot;first_name&quot;: &quot;wonuk&quot;,
      &quot;last_name&quot;: &quot;Hwang&quot;,
      &quot;age&quot;: 100,
      &quot;phone_agree&quot;: false,
      &quot;hobby&quot;: [&quot;Java&quot;, &quot;Spring&quot;]
    },
    {
      &quot;firstName&quot;: &quot;sparta&quot;,
      &quot;lastName&quot;: &quot;Team&quot;,
      &quot;age&quot;: 200,
      &quot;phone_agree&quot;: true,
      &quot;hobby&quot;: [&quot;React&quot;, &quot;Spring&quot;, &quot;Node&quot;]
    },
  ]
}</code></pre>
<ul>
<li><code>snake_case</code>, <code>camelCase</code> 모두 사용 가능</li>
<li><code>key - value</code> 형태</li>
</ul>
<br>

<hr>
<h1 id="📖-서버-성능향상">📖 서버 성능향상</h1>
<blockquote>
<p>💡 <strong>서버의 성능향상</strong>을 위한 두가지 방법</p>
</blockquote>
<br>

<h2 id="📌-scale-up-수직적-확장">📌 Scale Up (수직적 확장)</h2>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/b209644b-0962-43cb-86c1-ac560b4ce706/image.png' width=70%>

<ul>
<li><p><strong>단일 서버의 하드웨어의 사용을 높인다 ➝ 비용 증가</strong>
(cpu나 메모리의 스펙을 올린다)</p>
</li>
<li><p>요청에 대한 <strong>처리가 빨라</strong>진다</p>
<hr>
</li>
</ul>
<h2 id="📌-scale-out-수평적-확장">📌 Scale Out (수평적 확장)</h2>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/6d0341d6-9927-4709-a328-11522fb62f9a/image.png' width=70%>

<ul>
<li><strong>같은 사양의 서버를 여러대 배치한다 ➝ 비용 증가 크지 않음</strong></li>
<li>동시에 더 <strong>많은 사용자 요청을 처리</strong>할 수 있다</li>
</ul>
<br>

<hr>
<h1 id="📖-통신상태-유지-여부">📖 통신상태 유지 여부</h1>
<blockquote>
<p>💡 클라이언트와 서버간의 <strong>통신상태 유지 여부</strong>에 따라 나뉘는 특성
 *예시 - 수강생 (클라이언트) / 상담원 (서버)</p>
</blockquote>
<br>

<h2 id="📌-stateful상태-유지">📌 Stateful(상태 유지)</h2>
<img src='https://velog.velcdn.com/images/luv_lyn/post/b14312b5-c3c8-49c9-875b-6c7ab8033ce4/image.png' width=70%>

<ul>
<li>수강생 1명 : 상담원 1명 ➝ <strong>클라이언트의 요청을 기억(상태 유지)</strong>한다</li>
</ul>
<h3 id="✔️-문제점">✔️ 문제점</h3>
<ul>
<li><p>① 같은 서버가 유지되어야 한다 (같은 상담원에게 요청해야~)</p>
</li>
<li><p>② 서버는 다양한 이유로 동작하지 않을 수 있음</p>
</li>
<li><p>③ 요청 트래픽이 많아지면 상태를 유지하는데에 리소스가 소모된다</p>
<hr>
</li>
</ul>
<h2 id="📌-stateless-무상태">📌 Stateless (무상태)</h2>
<img src='https://velog.velcdn.com/images/luv_lyn/post/5ec126f0-a412-4b97-899d-89c4fecb22c2/image.png' width=70%>
- 수강생 1명 : 여러명의 상담원  ➝ **요청 기억 불가!**
- **수강생이 이전 요청을 함께 전달한다 ➡︎ 상담원이 기억할 필요가 없음**

<h3 id="✔️-장점">✔️ 장점</h3>
<ul>
<li>모든 요청을 하나의 서버에 보낼 필요가 없음 ➝ <strong>수평적 확장 <code>Scale Out</code></strong><h3 id="✔️-단점">✔️ 단점</h3>
</li>
<li>클라이언트가 데이터를 추가로 전송해야한다 ➝ 데이터양 증가 (비용이 증가될 수도)<h3 id="✔️-🌟-한계점">✔️ 🌟 한계점</h3>
</li>
<li>** 원칙** 
: 항상 서버의 확장성을 고려해 <strong>최대한 <code>Stateless</code>하게 만들어야</strong> 한다</li>
<li><strong>한계</strong> 
: but 전부 다 <code>Stateless</code>하게 만들 수는 없다 (<strong>상태유지</strong>가 필요한 로그인)</li>
<li>*<em>한계 극복 *</em>
: Cookie, Session, Token등으로 극복하며 상태유지는 최소화 하면 된다</li>
</ul>
<br>

<hr>
<h1 id="📖-연결-유지-여부">📖 연결 유지 여부</h1>
<blockquote>
<p>💡 클라이언트-서버간 <strong>연결 유지 여부</strong>에 따른 특성</p>
</blockquote>
<br>

<h2 id="📌-connection-연결">📌 Connection (연결)</h2>
<img src='https://velog.velcdn.com/images/luv_lyn/post/31fdd170-92ec-4e98-9595-b577510d26f6/image.png' width=60%>

<ul>
<li>클라이언트 수만큼 연결횟수가 늘어남 ➝ 자원 소모된다</li>
<li>클라이언트가 아무 요청이 없어도 연결은 유지한다
(수많은 사람들이 서비스를 이용해도 실제 서버에서 동시에 요청이 들어올 경우는 적다!)</li>
</ul>
<h3 id="✔️-장점-1">✔️ 장점</h3>
<ul>
<li><p>새로운 연결을 안해도 된다, 그만큼 <strong>응답속도가 빨라진다</strong></p>
<h3 id="✔️-단점-1">✔️ 단점</h3>
</li>
<li><p>클라이언트가 지속적으로 요청을 보낸단 보장이 없는데도 해야함,, <strong>자원 낭비</strong>!</p>
<hr>
</li>
</ul>
<h2 id="📌-connectionless-비연결">📌 Connectionless (비연결)</h2>
<img src='https://velog.velcdn.com/images/luv_lyn/post/e8b9595b-1fbf-43b3-ab55-d82207346716/image.png' width=60%>

<ul>
<li><code>TCP/IP 연결</code> ➝ <code>요청-응답</code> ➝ <strong><code>연결 해제</code></strong> 
🌟 <strong>서버는 최소한의 자원만 사용하게 된다</strong>
( 예를들어,, 인터넷이 끊겨도 보던 페이지는 그대로 보인다!)</li>
</ul>
<h3 id="✔️-장점-2">✔️ 장점</h3>
<ul>
<li>당연! 자원 효율적 사용된다<h3 id="✔️-단점-2">✔️ 단점</h3>
</li>
<li>요청이 추가될 때마다 연결(3 Way Handshake)을 새로 해야한다 ➝ <strong>응답시간 증가</strong></li>
<li>연결할 때마다 웹페이지의 정적자원을 전부! 다시 다운로드해야한다
➝<code>캐시</code>, <code>브라우저 캐싱</code>으로 해결 (쉽게말해 임시저장)
🌟<strong>해결방법 :</strong>  현재는** HTTP 지속연결(Persistent Connections)**로 문제를 해결한다.</li>
</ul>
<br>

<h3 id="💡-http-지속연결persistent-connections">💡 HTTP 지속연결(Persistent Connections)</h3>
<img src='https://velog.velcdn.com/images/luv_lyn/post/35d25644-2492-46f7-9b8c-14b9adee735a/image.png' width=60%>

<ul>
<li><strong>모든 요청을 전부 응답한 후에 연결을 해제한다</strong></li>
<li>연결 횟수는 <code>Connectionless</code>보다 적고, 속도는 더 빠르다!</li>
</ul>
<br>

]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래밍 3주차) 키오스크 과제 [도전기능 Level 1] /  TIL day 18]]></title>
            <link>https://velog.io/@luv_lyn/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-3%EC%A3%BC%EC%B0%A8-%ED%82%A4%EC%98%A4%EC%8A%A4%ED%81%AC-%EA%B3%BC%EC%A0%9C-%EB%8F%84%EC%A0%84%EA%B8%B0%EB%8A%A5-Level-1-TIL-day-17</link>
            <guid>https://velog.io/@luv_lyn/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-3%EC%A3%BC%EC%B0%A8-%ED%82%A4%EC%98%A4%EC%8A%A4%ED%81%AC-%EA%B3%BC%EC%A0%9C-%EB%8F%84%EC%A0%84%EA%B8%B0%EB%8A%A5-Level-1-TIL-day-17</guid>
            <pubDate>Fri, 14 Mar 2025 03:44:23 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="📖-개요">📖 개요</h1>
<hr>
<h2 id="📌-시스템-환경">📌 시스템 환경</h2>
<ul>
<li>Java: 17</li>
<li>JDK: 17.0.1</li>
<li>IDE: IntelliJ</li>
</ul>
<h2 id="📌-요구사항">📌 요구사항</h2>
<h3 id="✔️-필수기능"><strong>✔️ 필수기능</strong></h3>
<table>
<thead>
<tr>
<th align="center">필수기능</th>
<th align="center">단계별</th>
<th align="left">요구사항</th>
</tr>
</thead>
<tbody><tr>
<td align="center">✅</td>
<td align="center"><strong>Level 1</strong></td>
<td align="left">메뉴 출력하기, <code>Scanner</code>로 입력 받기 ( + 숫자 0 입력시 프로그램 종료 )</td>
</tr>
<tr>
<td align="center">✅</td>
<td align="center"><strong>Level 2</strong></td>
<td align="left"><code>MenuItem</code> 클래스 생성 (이름, 가격, 설명), <code>List&lt;MenuItem&gt;</code> 활용해 메뉴 목록 저장 + 출력</td>
</tr>
<tr>
<td align="center">✅</td>
<td align="center"><strong>Level 3</strong></td>
<td align="left"><code>Kiosk</code> 클래스 생성, <code>start()</code>로 프로그램 흐름 관리 ( + 메뉴 선택 기능 구현 )</td>
</tr>
<tr>
<td align="center">✅</td>
<td align="center"><strong>Level 4</strong></td>
<td align="left"><code>Menu</code> 클래스 생성, <code>MenuItem</code>을 카테고리별로 나누기</td>
</tr>
<tr>
<td align="center">✅</td>
<td align="center"><strong>Level 5</strong></td>
<td align="left">캡슐화 , <code>Getter</code>, <code>Setter</code> 활용하여 데이터 접근 및 수정</td>
</tr>
</tbody></table>
<h3 id="✔️-도전기능">✔️ 도전기능</h3>
<table>
<thead>
<tr>
<th align="center">도전기능</th>
<th align="center">단계별</th>
<th align="left">요구사항</th>
</tr>
</thead>
<tbody><tr>
<td align="center">✅</td>
<td align="center"><strong>Level 1</strong></td>
<td align="left">장바구니에 담기 (목록 + 금액 출력), 주문 완료 시 장바구니 초기화</td>
</tr>
<tr>
<td align="center">❌</td>
<td align="center"><strong>Level 2</strong></td>
<td align="left">Enum, 람다 &amp; 스트림을 활용한 주문 및 장바구니 관리</td>
</tr>
</tbody></table>
<br>

<h2 id="📌-git-hub-링크--readme">📌 <a href="https://github.com/harinbeee/kiosk-project">Git Hub 링크 &amp; ReadMe</a></h2>
<br>

<hr>
<h1 id="📖-구현한-기능">📖 구현한 기능</h1>
<hr>
<h2 id="📌--실행예시">📌 ** 실행예시**</h2>
<img src='https://velog.velcdn.com/images/luv_lyn/post/9dea941f-7f11-454e-990a-cf6afecf60d1/image.png' width=60%>


<br>

<h3 id="✔️-카테고리별-메뉴-출력">✔️ <strong>카테고리별 메뉴 출력</strong></h3>
<ul>
<li>전체 메뉴를 카테고리별로 나누어 구성했고, 숫자 입력과 뒤로가기(0)번을 통해 <strong>필요한 카테고리로 쉽게 이동</strong>할 수있다.  </li>
</ul>
<h3 id="✔️-장바구니-기능">✔️ <strong>장바구니 기능</strong></h3>
<ul>
<li>선택한 메뉴를 장바구니에 추가할 수 있고, 장바구니에 담긴 아이템 리스트와 현재 장바구니의 총 금액을 확인할 수 있도록 했다.  </li>
</ul>
<h3 id="✔️-주문하기">✔️ <strong>주문하기</strong></h3>
<ul>
<li>장바구니에 추가한 아이템들을 9번을 통해 주문할 수 있다.</li>
<li>주문 완료시 내가 <strong>주문한 아이템들과 총 금액</strong>을 알 수 있다. (주문 완료 시 장바구니는 초기화)</li>
</ul>
<br>

<hr>
<h1 id="📖-고민했던-부분">📖 고민했던 부분</h1>
<hr>
<h3 id="📌-요구사항을-잘-지키고-있는가">📌 <strong>요구사항을 잘 지키고 있는가?</strong></h3>
<ul>
<li>지난 과제 해설을 들으면서, 내가 과제의 요구사항을 준수하며 코딩했는지 다시 돌아보게 됐다. 레벨이 올라갈수록 요구사항을 제대로 지키는 게 중요하다는 걸 깨달았고, 이번에는 <strong>level 5까지의 필수 기능</strong>이 있는 만큼, 요구사항을 요약해 <strong>체크리스트처럼 정리</strong>하고 신경 쓰면서 구현하려고 했다.  </li>
</ul>
<h3 id="📌-클래스-간의-관계">📌 <strong>클래스 간의 관계</strong></h3>
<ul>
<li>레벨이 올라갈 때마다 새로운 클래스가 추가되고, 기존에 만든 객체를 이동해야 하거나 클래스간의 관계를 변경하면서 계속 혼란스러웠고 객체의 위치까지 헷갈리기 시작했다. 그래서 최대한 <strong><a href="https://velog.io/@luv_lyn/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-3%EC%A3%BC%EC%B0%A8-%ED%82%A4%EC%98%A4%EC%8A%A4%ED%81%AC-%EA%B3%BC%EC%A0%9C-Lv1-TIL-day-15">클래스 다이어그램</a></strong>을 그리면서 진행하려고 노력했다.  </li>
</ul>
<h3 id="📌-객체의-위치">📌 <strong>객체의 위치</strong></h3>
<ul>
<li>가장 고민했던 건 <strong>메뉴 아이템 객체가 최종적으로 어디에 위치해야 하는지</strong>였다. 대분류-소분류 개념으로 접근하다 보니 상하 관계로만 생각하게 됐고, 나름 배운 개념을 활용해보겠다고 <code>extends</code>(상속)도 시도해봤다. 하지만 튜터님 피드백을 듣고 나서 내가 정반대로 접근했다는 걸 깨달았다.</li>
<li>마지막으로 요구사항을 다시 한 번 확인해보니, 메뉴는 <code>메인</code>에서 관리했어야 하는 듯 하지만.. 제출일 전날 병가로 출석하지 못했던 터라 이 부분을 적용하지 못한 게 아쉬움으로 남는다...🥹</li>
</ul>
<h3 id="📌-리스트-사용-방식">📌 <strong>리스트 사용 방식</strong></h3>
<ul>
<li>처음엔 <strong>메뉴 아이템을 카테고리별로 담은 리스트 3개</strong>와 <strong>카테고리 이름만 담은 리스트</strong>를 따로 만들었는데, 사실상 이 둘이 따로 존재할 필요가 없고 서로 연결되어야 하는데 이 부분 해결이 제일 헷갈렸던것 같다. </li>
<li>튜터님께서 <strong>전체 메뉴 리스트 하나</strong>면 충분하다고 하셨고, 리스트 하나를 이용해서 카테고리를 필터링하기 위해 <code>MenuItem</code> 클래스에서 카테고리 속성을 추가하고, 객체에 하나씩 입력해 보기도 했다. (이건 아니라는 생각이 들었다..) 최종적으로는 <code>Menu</code> 클래스에 속성과 생성자를 추가하고, <code>Kiosk</code>에서 <code>Menu</code> 객체를 생성하는 방식으로 진행했다.</li>
</ul>
<br>

<hr>
<h1 id="📋-트러블-슈팅">📋 트러블 슈팅</h1>
<hr>
<h2 id="❶-list-관리">❶ List 관리</h2>
<h3 id="▪️-문제">▪️ 문제</h3>
<ul>
<li>객체들을 이리저리 이동하며 <strong>List의 관리 주체를 바꾸다보니 코드가 꼬였다.</strong></li>
<li>처음 내가 원한 건 &quot;리스트 안에 리스트 안에 리스트&quot; 처럼 <strong>계층적으로 데이터를 관리</strong>하는 방식이었는데, 이 방식은 효율적이지 않았다. (그러나 여기에 집착하느라 시간을 많이 낭비했다) 원하던 데이터 구조를 만드려면 다른 자료구조(HashMap, Enum)를 활용하면 되려나..? 싶었으나 아직 미숙한 관계로 사용하지 않기로 결정했다.</li>
</ul>
<h3 id="▪️-해결방법">▪️ 해결방법</h3>
<img src='https://velog.velcdn.com/images/luv_lyn/post/5e1ff29c-9de2-4e5d-b704-c16de574590c/image.png' width=50%>
<img src='https://velog.velcdn.com/images/luv_lyn/post/f60ae141-d4e0-4d4f-b000-22144b812a7a/image.png' width=50%>
<img src='https://velog.velcdn.com/images/luv_lyn/post/cfc88579-d142-41d1-b356-4fc77dda759d/image.png' width=50%>
<img src='https://velog.velcdn.com/images/luv_lyn/post/97adf36a-e887-4822-abda-a0d92c36101f/image.png' width=50%>

<ul>
<li><p><code>kiosk</code>에서 menus(전체메뉴 리스트)를 사용하고, <strong><code>menu</code> 객체 생성</strong>을 통해 카테고리 별로 필터링하기로 했다.</p>
<hr>
</li>
</ul>
<h2 id="❷-메서드-관리">❷ 메서드 관리</h2>
<h3 id="▪️-문제-1">▪️ 문제</h3>
<ul>
<li>최대한 반복되는 코드를 줄이고 메서드를 적극 활용하고 싶었지만, 입출력을 정리하는 것이 어려웠다.</li>
<li>계속 sout을 통해 수기로 출력하는 대신, 출력 기능을 메서드로 분리하고 싶었다. [전체 리스트 → 카테고리별 메뉴 → 특정 메뉴 선택] 과정에서 <strong>인덱스를 활용</strong>해 데이터를 가져오는 방법이 어려웠다. (<code>menuItem</code>의 <code>selectMenuNum</code>도 지금 보니 필요가 없다,,,😂)</li>
</ul>
<h3 id="▪️-해결방법-1">▪️ 해결방법</h3>
<img src='https://velog.velcdn.com/images/luv_lyn/post/012352f5-e9c0-4b8d-b9ed-dd360d5e29e9/image.png' width=80%>
<img src='https://velog.velcdn.com/images/luv_lyn/post/0e92ed81-c1de-4eff-842e-0cd167a764ad/image.png' width=80%>


<ul>
<li>메서드들도 분리하고, 향상된 for문과 index값, 리스트의 크기    <code>.size</code>를 활용했다.</li>
</ul>
<h3 id="💡-배운점">💡 배운점</h3>
<ul>
<li><p>다양한 <strong>메서드들을 만들어 분리해 사용</strong>하는 것이 코드의 가독성을 높이고 중복 코드를 줄일 수 있다.</p>
</li>
<li><p>나중에 수정할 때도 쉽다.</p>
<hr>
</li>
</ul>
<h2 id="❸-장바구니-만들기">❸ 장바구니 만들기</h2>
<h3 id="▪️-문제-2">▪️ 문제</h3>
<ul>
<li>지난 과제에서 누적 연산을 했던 덕분에 처음엔 수월하게 구현했다. 그러나 <del>착각도 잠시</del> 누적 금액이 이상하게 계산되는 오류가 있었다.</li>
<li>예를 들어 8,000원짜리 메뉴를 2번 담았을 때, <code>16,000원</code>이 되어야 하는데 
<code>(8,000) + (8,000 + 8,000) = 24,000원</code>이 출력되는 것.</li>
<li>누적합 계산을 위해 <code>+=</code> 연산자를 사용했었는데 계속 틀린 값이 나와서 반복문을 계속 뜯어봤다.</li>
</ul>
<h3 id="▪️-해결방법-2">▪️ 해결방법</h3>
<ul>
<li><code>totalPrice</code> 값을 <code>List&lt;String&gt;</code>으로 관리하고 있었다. <code>cart</code> 초기화하면서 아무 생각 없이 복붙해서 만들었던 것,, ➡︎ <code>int</code> 타입으로 변경했다.</li>
</ul>
<h3 id="💡-배운점-1">💡 배운점</h3>
<ul>
<li><p>생각없이 코드 복붙하지 말자 😇</p>
</li>
<li><p>(팀원 분들과 해결했음) 해결이 안될 때는 다른 사람에게 보여주는 것도 나쁘지 않은 것 같다. 계속 이 코드를 봐서인지.. 이상한데 매몰되어 있어서인지 이 간단한 해결방법이 눈에 안들어오는 경우가 많았다.</p>
<hr>
</li>
</ul>
<h2 id="❹-메뉴판-금액-장바구니-금액-차이">❹ 메뉴판 금액, 장바구니 금액 차이</h2>
<h3 id="▪️-문제-3">▪️ 문제</h3>
<ul>
<li>메뉴판 가격을 감성 카페마냥 4500원 -&gt; 4.5 이런식으로 표기하고 싶어서 <code>double</code>을 사용했다.</li>
<li>장바구니에서는 4500원으로 출력하고 싶어서 <code>int</code> 타입으로 형변환을 했다.
➡︎ 세상에 뒷자리 백원단위가 사라지고 있었다.. 할인을 해주고 말았다..</li>
</ul>
<h3 id="▪️-해결방법-3">▪️ 해결방법</h3>
<img src='https://velog.velcdn.com/images/luv_lyn/post/952f02c5-0308-46f0-b30e-1a76eb4ad18a/image.png' width=50%>

<ul>
<li><code>(int)(item.getPrice())*1000;</code> ➡︎ <strong><code>(int)((item.getPrice())*1000);</code></strong></li>
</ul>
<h3 id="💡-배운점-2">💡 배운점</h3>
<ul>
<li><p>괄호 하나로 값이 완전히 달라질 수 있다. <del>(어지간하면 정수형을 사용하자..)</del></p>
<hr>
</li>
</ul>
<h2 id="❺-예외-처리">❺ 예외 처리</h2>
<h3 id="▪️-문제-4">▪️ 문제</h3>
<ul>
<li>기존에 <code>if</code>로 인덱스 값을 벗어난 숫자에만 예외처리를 해뒀었기 때문에 문자열을 입력하면 프로그램이 멈췄다.</li>
<li>예외처리를 카테고리 선택, 메뉴선택 등으로 기능을 나눴고, 고로 사용자에게 입력받은 숫자가 기능마다 다른 변수에 들어있었기에 수기로 일일히 <code>try-catch</code>를 여러번 작성하기로 했다 ➡︎ 코드가 지저분했다.</li>
</ul>
<h3 id="▪️-해결방법-4">▪️ 해결방법</h3>
<img src='https://velog.velcdn.com/images/luv_lyn/post/c05d70b1-2aed-4607-92d0-9a4b3765d0ae/image.png' width=80%>

<ul>
<li><p>각 메서드에서 직접 <code>try-catch</code>를 쓰는 대신, <code>inputInteger()</code>를 만들어 사용했다.</p>
</li>
<li><p><a href="https://its-ward.tistory.com/entry/Java-IntegerparseInt-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC"><code>Integer.parseInt</code></a> ➡︎ String타입의 숫자를 int로 바꿔준다, 이를 시도하며 예외를 잡는다!</p>
<hr>
</li>
</ul>
<h2 id="❻-디버깅">❻ 디버깅</h2>
<h3 id="▪️-문제-5">▪️ 문제</h3>
<ul>
<li>List에서 값을 받아서 사용하거나 다른 클래스에서 가져오고 하다보니 출력이 안되거나 값이 없는 경우 발생, 알고보니 <strong>데이터 자체가 넘어오지 않았다</strong>(!)</li>
</ul>
<h3 id="▪️-해결방법-5">▪️ 해결방법</h3>
<ul>
<li><code>getter</code>를 통해 확인해보기도 하고 <code>list.size()</code>를 찍어보면서 데이터가 어디서 안 들어오는지 일일히 확인했다.</li>
</ul>
<h3 id="💡-배운점-3">💡 배운점</h3>
<ul>
<li>하나씩 데이터를 찍어보는 습관을 들이자. 돌다리도 두드려보고..</li>
</ul>
<br>

<hr>
<h1 id="🌟-나의-회고">🌟 나의 회고</h1>
<hr>
<p>오타, 형변환, 대소문자 입력 같은 잔실수가 너무 많다.
또 중요한 것은 잔실수들이 야기하는 오류는 꼭 나중에 확인하게 되는데, 수정할때 어느부분에서 실수한건지 찾는게 더 힘든 것 같다. 항상 데이터를 가져오면 찍어보는 습관을 들이자.</p>
<p>아직도 요구사항 그대로! 설계하지 못하는 것 같다. 레벨별로 클래스 다이어그램을 그리는게 나름 도움이 됐다고 생각했었는데.. 그냥 공부자체를 더 해야할 것 같다.</p>
<p>과제를 진행하면서 시간이 여러모로 많이 부족했다. 
나는 아직 강의 내용도 다 숙지하지 못한 것 같은데.. 부딪혀보듯 코딩해보는게 맞는건가? 이런 생각도 꽤 자주했다. 지금 과제를 제출하기 직전 느낀 점은, 과제를 하는 과정에서 강의자료를 뒤지고 나랑 비슷한 상황을 찾아 구글링을 하기도 하고 이것저것 시도하다 보니 자연히 체득하는 내용이 더 오래가고 잊어버리지 않을 것 같다.
또한 이번 과제때는 시간때문에 구현하지 못한 부분들이 있어 너무 아쉬웠으므로 다음엔 시간분배를 잘 해야겠다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[☕️ Java 문법 종합반 (3) - ② 컬렉션, 제네릭,  / TIL - day 15]]></title>
            <link>https://velog.io/@luv_lyn/Java-%EB%AC%B8%EB%B2%95-%EC%A2%85%ED%95%A9%EB%B0%98-3-%EC%BB%AC%EB%A0%89%EC%85%98-%EC%A0%9C%EB%84%A4%EB%A6%AD-TIL-day-15</link>
            <guid>https://velog.io/@luv_lyn/Java-%EB%AC%B8%EB%B2%95-%EC%A2%85%ED%95%A9%EB%B0%98-3-%EC%BB%AC%EB%A0%89%EC%85%98-%EC%A0%9C%EB%84%A4%EB%A6%AD-TIL-day-15</guid>
            <pubDate>Wed, 12 Mar 2025 14:40:13 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="▪️-자바-컬렉션-프레임워크-▪️">▪️ 자바 컬렉션 프레임워크 ▪️</h1>
<hr>
<blockquote>
<p>💡 다양한 자료구조들을 쉽게 활용하도록 <strong>인터페이스와 구현체</strong>(<code>ArrayList</code>, <code>HashSet</code>, <code>HashMap</code>) 를 제공하는 집합</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/ce6040db-7196-4544-80b9-94712bbc1b24/image.png" alt=""></p>
<ul>
<li><code>Collection</code>계열은 <code>Collection</code> 인터페이스를 중심으로 구성됨</li>
<li><code>Map</code> 계열은 <code>Collection</code>을 상속받지 않지만, <code>컬렉션 프레임워크</code>에 포함</li>
</ul>
<blockquote>
</blockquote>
<pre><code>자바 컬렉션 프레임워크
 │
 ├── 📌Collection 인터페이스 (데이터 모음, 요소 중심)
 │     ├── List (순서O, 중복O)
 │     ├── Set (순서X, 중복X)
 │     ├── Queue (FIFO 구조)
 │
 ├── 📌Map 인터페이스 (키-값 쌍, 요소 중심X)
 │     ├── HashMap 등
 │
 ├── 📌기타 유틸리티
       ├── Iterator (반복자)
       ├── Collections (유틸 클래스, 정렬 등 제공)</code></pre><hr>
<h1 id="📖-collection">📖 Collection</h1>
<ul>
<li>데이터 저장, 조회, 삭제, 정렬 기능등을 간편하게 구현할 수 있다</li>
<li>길이를 동적으로 변경할 수 있다 (배열과 다름)</li>
</ul>
<pre><code>ArrayList&lt;Integer&gt; arrayList = new ArrayList&lt;&gt;();
arrayList.add(10);
arrayList.add(20);
arrayList.add(30);
arrayList.add(40); // ✅ 정상 동작 (길이 제한 없음)</code></pre><blockquote>
</blockquote>
<table>
<thead>
<tr>
<th>인터페이스</th>
<th>특징</th>
<th>구현체</th>
</tr>
</thead>
<tbody><tr>
<td><code>List</code></td>
<td>순서 유지, 중복 허용</td>
<td><code>ArrayList</code></td>
</tr>
<tr>
<td><code>Set</code></td>
<td>순서 없음, 중복 불가</td>
<td><code>HashSet</code></td>
</tr>
<tr>
<td><code>Map</code></td>
<td>키-값 구조, 키 중복 불가</td>
<td><code>HashMap</code></td>
</tr>
</tbody></table>
<hr>
<h2 id="📌-arraylist-순서유지-중복가능">📌 ArrayList (순서유지 중복가능)</h2>
<ul>
<li><strong><code>List 인터페이스</code></strong>를 상속받고 있다 </li>
</ul>
<pre><code class="language-java">// List 를 구현한 ArrayList
ArrayList&lt;String&gt; names = new ArrayList&lt;&gt;();
names.add(&quot;Spartan&quot;);      // 1 번째 요소 추가
names.add(&quot;Steve&quot;);        // 2 번째 요소 추가
names.add(&quot;Isac&quot;);         // 3 번째 요소 추가
names.add(&quot;1&quot;);
names.add(&quot;2&quot;);

 // ✅ 순서 보장
System.out.println(&quot;names = &quot; + names);

// ✅ 중복 데이터 허용
names.add(&quot;Spartan&quot;);
System.out.println(&quot;names = &quot; + names);

// ✅ 단건 조회
System.out.println(&quot;1 번째 요소 조회: &quot; + names.get(0)); // 조회 Spartan

// ✅ 데이터 삭제
names.remove(&quot;Steve&quot;); 
System.out.println(&quot;names = &quot; + names);
</code></pre>
<h3 id="✔️-add---데이터-추가">✔️ add() - 데이터 추가</h3>
<ul>
<li>()안에 추가할 값</li>
<li>중복 데이터를 허용한다</li>
</ul>
<h3 id="✔️-get---데이터-단건-조회">✔️ get() - 데이터 단건 조회</h3>
<ul>
<li>()안에는 인덱스 번호</li>
</ul>
<h3 id="✔️-remove---데이터-삭제">✔️ remove() - 데이터 삭제</h3>
<ul>
<li>()안에 삭제할 데이터</li>
</ul>
<hr>
<h2 id="📌-hashset-순서없음-중복가능">📌 HashSet (순서없음 중복가능)</h2>
<ul>
<li><strong><code>Set 인터페이스</code></strong>를 상속받고 있다</li>
</ul>
<pre><code class="language-java">// Set 을 구현한 HashSet
HashSet&lt;String&gt; uniqueNames = new HashSet&lt;&gt;();

// ✅ 추가
uniqueNames.add(&quot;Spartan&quot;);
uniqueNames.add(&quot;Steve&quot;);
uniqueNames.add(&quot;Isac&quot;);
uniqueNames.add(&quot;1&quot;);
uniqueNames.add(&quot;2&quot;);

// ⚠️ 순서를 보장 안함
System.out.println(&quot;uniqueNames = &quot; + uniqueNames); 
uniqueNames.get(0); // ❌ get 사용 불가

// ⚠️ 중복 불가
uniqueNames.add(&quot;Spartan&quot;);
System.out.println(&quot;uniqueNames = &quot; + uniqueNames); 

// ✅ 제거
uniqueNames.remove(&quot;Spartan&quot;);
System.out.println(&quot;uniqueNames = &quot; + uniqueNames); </code></pre>
<h3 id="✔️-add---데이터-추가-1">✔️ add() - 데이터 추가</h3>
<ul>
<li>()안에 추가할 값</li>
<li>중복 데이터는 한번만 나옴</li>
</ul>
<h3 id="✔️-데이터-단건-조회-불가-get❌">✔️ 데이터 단건 조회 불가 (get❌)</h3>
<h3 id="✔️-remove---데이터-삭제-1">✔️ remove() - 데이터 삭제</h3>
<ul>
<li>()안에 삭제할 데이터</li>
</ul>
<hr>
<h2 id="📌-hashmap-순서없음">📌 HashMap (순서없음)</h2>
<ul>
<li><strong><code>Map 인터페이스</code></strong>를 상속받고 있다</li>
</ul>
<pre><code class="language-java">// Map 을 구현한 HashMap
HashMap&lt;String, Integer&gt; memberMap = new HashMap&lt;&gt;();

// ✅ 추가
memberMap.put(&quot;Spartan&quot;, 15);
memberMap.put(&quot;Steve&quot;, 15); // ✅ 값은 중복 가능
memberMap.put(&quot;Isac&quot;, 1);
memberMap.put(&quot;John&quot;, 2);
memberMap.put(&quot;Alice&quot;, 3);

// ⚠️ 순서 보장 안함 
System.out.println(&quot;memberMap = &quot; + memberMap);

// ⚠️ 키 중복 불가: 값 덮어쓰기 발생
memberMap.put(&quot;Alice&quot;, 5);
System.out.println(&quot;memberMap = &quot; + memberMap);

// ✅ 조회: 15
System.out.println(memberMap.get(&quot;Steve&quot;));

// ✅ 삭제 가능
memberMap.remove(&quot;Spartan&quot;); 
System.out.println(&quot;memberMap = &quot; + memberMap);

// ✅ 키 확인
Set&lt;String&gt; keys = memberMap.keySet();
System.out.println(&quot;keys = &quot; + keys);

// ✅ 값 확인
Collection&lt;Integer&gt; values = memberMap.values();
System.out.println(&quot;values = &quot; + values);</code></pre>
<ul>
<li><code>키(Key) - 값(Value)</code>  구조로 데이터 저장</li>
<li><code>키(Key)</code> 중복 불가 / <code>값(Value)</code> 중복 가능</li>
</ul>
<h3 id="✔️-put키-값----데이터-추가">✔️ put(&quot;키&quot;, 값 ) - 데이터 추가</h3>
<h3 id="✔️-get키---데이터-단건-조회">✔️ get(&quot;키&quot;) - 데이터 단건 조회</h3>
<ul>
<li>키값을 이용해 데이터 조회</li>
</ul>
<h3 id="✔️-remove키---데이터-삭제">✔️ remove(&quot;키&quot;) - 데이터 삭제</h3>
<ul>
<li>키값을 이용해 데이터 세트를 삭제</li>
</ul>
<h3 id="✔️-keyset---키-확인">✔️ keySet() - 키 확인</h3>
<ul>
<li>모든 키값 무작위 순서로 나옴</li>
</ul>
<h3 id="✔️-values---값-확인">✔️ values() - 값 확인</h3>
<ul>
<li>모든 벨류들 무작위 순서로 나옴</li>
</ul>
<hr>
<h1 id="▪️-제네릭-generic-▪️">▪️ 제네릭 (Generic) ▪️</h1>
<blockquote>
<p><strong><code>&lt;T&gt;타입 매개변수</code></strong>로 클래스나 메서드에 사용된다. 코드 재사용성, 타입안정성을 보장</p>
</blockquote>
<hr>
<h1 id="📖-generic-class">📖 Generic Class</h1>
<p><strong>🌟 클래스의 선언부에 <code>&lt;T&gt;</code>가 선언된 클래스</strong></p>
<pre><code class="language-Java">[클래스 선언부]
public class GenericBox&lt;T&gt; { // ✅ 제네릭 클래스
    private T item;

    public GenericBox(T item) {
        this.item = item;
    }

    public T getItem() {
        return this.item;
    }
}</code></pre>
<p>객체 생성시에 해당 타입을 &lt;&gt;에 넣는다
인스턴스 생성시에 타입소거후 타입이 결정된다</p>
<p>컴파일 시점에 타입소거가 된다
T -&gt; Object 가 된다
getter 사용시에 알아서 다운캐스팅도 해줌 &lt;&gt;에 넣은걸로!</p>
<hr>
<h1 id="📖-generic-method">📖 Generic Method</h1>
<blockquote>
<p>메서드 선언부에 <code>&lt;T&gt;</code>가 선언된 것 클래스인 제네릭과 메서드인 제네릭은 별개이다
클래스의 타입매개변수와는 달라도 됨 별도로 동작한다</p>
</blockquote>
<p>메서드 실행시 () 매개변수에 아무거나 들어가도 된다</p>
<hr>
<h1 id="▪️-람다-▪️">▪️ 람다 ▪️</h1>
<hr>
<h1 id="📖-익명-클래스">📖 익명 클래스</h1>
<blockquote>
<p>별도의 클래스파일을 만들지 않고 코드 내에서 일회성으로 정의해 사용하기 때문에
인터페이스나 클래스의 구현과 상속을 통해 구현한다</p>
</blockquote>
<p>보통은 클래스를 만들어 사용하는데,
익명 클래스는 그냥 메인에서 바로 new로 정의해서 활용
필요할 때만 1회성으로 활용할수 있다</p>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/0674451c-5684-4826-9bd4-23d280c954c9/image.png" alt=""></p>
<p>단점 : 코드가 좀 길어짐..</p>
<hr>
<h1 id="📖-람다-lambda">📖 람다 Lambda</h1>
<blockquote>
<p>익명클래스를 더 간결하게 표현하는 방법!
함수형 인터페이스를 통해 구현하는 것을 적극 권장한다 -&gt; 하나의 추상메서드만 가져야한다</p>
</blockquote>
<pre><code class="language-java">//함수형 인터페이스
@FunctionalInterface // ✅ 함수형 인터페이스 선언
public interface Calculator {

    int sum(int a, int b); // ✅ 오직 하나의 추상 메서드만 선언해야합니다.
}

// 람다 표현식
Calculator calculator1 = (a, b) -&gt; a + b;

// 람다가 추론한 익명클래스
Calculator calculator1 = new Calculator() {
        @Override
        public int sum(int a, int b) {
                return a + b;
        }
};

</code></pre>
<p>함수형 인터페이스를 만들고, 람다 표현식을 쓰면 알아서 추론해서 익명클래스를 구현해준다!</p>
<h2 id="📌-주의할-것">📌 주의할 것</h2>
<ol>
<li>꼭 함수형 인터페이스를 선언할 것</li>
<li>하나의 추상메서드만 가지고 있을 것! (오버로딩을 통해 같은 메서드를 여러형태로 정의하지 말것)</li>
</ol>
<pre><code class="language-java">@FunctionalInterface // ✅ 함수형 인터페이스 선언
public interface Calculator {
    int sum(int a, int b); // ✅ 오직 하나의 추상 메서드만 선언해야합니다.
    int sum(int a, int b, int c); // ❌ 선언 불가 에러발생!
}</code></pre>
<p>여러개의 추상메서드가 있으면 컴파일러가 뭐가 진짜 sum인지 알수가 없어진다!</p>
<blockquote>
<p>💡 
<code>오버로딩</code> : 같은 클래스내에서 동일한 메서드이름 사용
<code>오버라이딩</code> : 부모클래스의 메서드를 자식클래스에서 재정의하는것</p>
</blockquote>
<h1 id="📖-람다식을-매개변수로-전달하는-방법">📖 람다식을 매개변수로 전달하는 방법</h1>
<h2 id="1️⃣-익명클래스를-변수에-담아-전달">1️⃣ 익명클래스를 변수에 담아 전달</h2>
<pre><code class="language-java">public class Main {
    public static int calculate(int a, int b, Calculator calculator) {
        return calculator.sum(a, b);
    }

    public static void main(String[] args) {

        Calculator cal1 = new Calculator() {
            @Override
            public int sum(int a, int b) {
                return a + b;
            }
        };

        // ✅ 익명 클래스를 변수에 담아 전달
        int ret3 = calculate(3, 3, cal1);
        System.out.println(&quot;ret3 = &quot; + ret3); // 출력: ret3 = 6
    }
}</code></pre>
<h2 id="2️⃣-람다식을-변수에-담아-전달">2️⃣ 람다식을 변수에 담아 전달</h2>
<pre><code class="language-java">public class Main {

    public static int calculate(int a, int b, Calculator calculator) {
        return calculator.sum(a, b);
    }

    public static void main(String[] args) {
        Calculator cal2 = (a, b) -&gt; a + b;

        // ✅ 람다식을 변수에 담아 전달
        int ret4 = calculate(4, 4, cal2);
        System.out.println(&quot;ret4 = &quot; + ret4); // 출력: ret4 = 8
    }
}</code></pre>
<h2 id="3️⃣-람다식을-직접-전달">3️⃣ 람다식을 직접 전달</h2>
<pre><code class="language-java">public class Main {

    public static int calculate(int a, int b, Calculator calculator) {
        return calculator.sum(a, b);
    }

    public static void main(String[] args) {
        // ✅ 람다식을 직접 매개변수로 전달
        int ret5 = calculate(5, 5, (a, b) -&gt; a + b);
        System.out.println(&quot;ret5 = &quot; + ret5); // 출력: ret5 = 10
    }
}
</code></pre>
<hr>
<h1 id="▪️스트림▪️">▪️스트림▪️</h1>
<blockquote>
<p>데이터를 효율적으로 처리할 수 있는 흐름
선언형이라 가독성이 뛰어나다
<code>데이터 준비 -&gt; 중간연산 -&gt; 최종연산</code> 순으로 처리</p>
</blockquote>
<hr>
<table>
<thead>
<tr>
<th>단계</th>
<th>설명</th>
<th>주요 API</th>
</tr>
</thead>
<tbody><tr>
<td><strong>1. 데이터 준비</strong></td>
<td>컬렉션을 스트림으로 변환</td>
<td><code>stream()</code>, <code>parallelStream()</code></td>
</tr>
<tr>
<td><strong>2. 중간 연산 등록(즉시 실행되지 않음)</strong></td>
<td>데이터 변환 및 필터링</td>
<td><code>map()</code>, <code>filter()</code>, <code>sorted()</code></td>
</tr>
<tr>
<td><strong>3. 최종 연산</strong></td>
<td>최종 처리 및 데이터 변환</td>
<td><code>collect()</code>, <code>forEach()</code>, <code>count()</code></td>
</tr>
</tbody></table>
<p>보기 쉽게 3줄로 나열</p>
<pre><code class="language-java">// 1. 데이터 준비: 스트림 생성
Stream&lt;Integer&gt; stream = arrayList.stream();

// 2. 중간 연산 등록: 각 요소를 10배로 변환 로직 등록
Stream&lt;Integer&gt; mappedStream = stream.map(num -&gt; num * 10);

// 3. 최종 연산: 최종 결과 리스트로 변환
List&lt;Integer&gt; ret2 = mappedStream.collect(Collectors.toList());</code></pre>
<p>이것을 한줄로 표현할 수 있따</p>
<pre><code class="language-java">// ✅ 한 줄로 표현 가능
List&lt;Integer&gt; ret2 = arrayList.stream() // 1. 데이터 준비
    .map(num -&gt; num * 10)               // 2. 중간 연산 등록
    .collect(Collectors.toList());  // 3. 최종 연산</code></pre>
<h2 id="스트림과-람다를-조합해서-활용하는-세가지-방법">스트림과 람다를 조합해서 활용하는 세가지 방법</h2>
<pre><code class="language-java">public class Main {

    public static void main(String[] args) {

        // ArrayList 선언
        List&lt;Integer&gt; arrayList = new ArrayList&lt;&gt;(List.of(1, 2, 3, 4, 5));

        // 스트림 없이: 각 요소 * 10 처리
        ArrayList&lt;Integer&gt; ret1 = new ArrayList&lt;&gt;();
        for (Integer num : arrayList) {
            int multipliedNum = num * 10;
            ret1.add(multipliedNum);
        }
        System.out.println(&quot;ret1 = &quot; + ret1);

        // 스트림 활용: 각 요소 * 10 처리
        List&lt;Integer&gt; ret2 = arrayList.stream().map(num -&gt; num * 10).collect(Collectors.toList());
        System.out.println(&quot;ret2 = &quot; + ret2);

        // 직접 map() 활용해보기
        // 1. 익명클래스를 변수에 담아 전달
        Function&lt;Integer, Integer&gt; function = new Function&lt;&gt;() {

            @Override
            public Integer apply(Integer integer) {
                return integer * 10;
            }
        };
        List&lt;Integer&gt; ret3 = arrayList.stream()
                .map(function)
                .collect(Collectors.toList());
        System.out.println(&quot;ret3 = &quot; + ret3);

        // 2. 람다식을 변수에 담아 전달
        Function&lt;Integer, Integer&gt; functionLambda = (num -&gt; num * 10);
        List&lt;Integer&gt; ret4 = arrayList.stream()
                .map(functionLambda)
                .collect(Collectors.toList());
        System.out.println(&quot;ret4 = &quot; + ret4);

                // 람다식 직접 활용
        List&lt;Integer&gt; ret5 = arrayList.stream()
                .map(num -&gt; num * 10)
                .collect(Collectors.toList());
        System.out.println(&quot;ret5 = &quot; + ret5);
    }
}
</code></pre>
<h2 id="스트림-중간-연산-함께-사용하기">스트림 중간 연산 함께 사용하기</h2>
<pre><code class="language-java">List&lt;Integer&gt; arrayList = new ArrayList&lt;&gt;(List.of(1, 2, 3, 4, 5));

// ✅ filter() + map() 사용 예제
List&lt;Integer&gt; ret6 = arrayList.stream() // 1. 데이터 준비: 스트림 생성
        .filter(num -&gt; num % 2 == 0)    // 2. 중간 연산: 짝수만 필터링
        .map(num -&gt; num * 10)           // 3. 중간 연산: 10배로 변환
        .collect(Collectors.toList()); // 4. 최종 연산: 리스트로 변환

System.out.println(ret6); // 출력: [20, 40]</code></pre>
<hr>
<h1 id="▪️스레드▪️">▪️스레드▪️</h1>
<blockquote>
<p>프로그램 내에서 독립적으로 실행되는 작업 단위!
<code>싱글 스레드</code> -&gt; 한번에 하나의 작업만 / <code>멀티스레드</code> -&gt; 여러 작업 동시에 처리 ( 병렬로 수행)</p>
</blockquote>
<hr>
<h1 id="📖-싱글-쓰레드">📖 싱글 쓰레드</h1>
<ul>
<li>일꾼이 한명이기에 여러개의 작업이 잇다면 순차적으로 처리</li>
<li><code>main()</code>도 하나의 쓰레드이다</li>
</ul>
<pre><code class="language-java">public class Main {

    public static void main(String[] args) {
        System.out.println(&quot;::: main 쓰레드 시작 :::&quot;);
        String threadName = Thread.currentThread().getName();

        // ✅ 하나의 작업 단위: 숫자를 0 부터 9 까지 출력
        for (int i = 0; i &lt; 10; i++) {
            System.out.println(&quot;현재 쓰레드: &quot; + threadName + &quot; - &quot; + i);
            try {
                Thread.sleep(500); // 0.5 초 대기
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // ✅ 추가작업
        for (int i = 0; i &lt; 10; i++) {
            System.out.println(&quot;현재 쓰레드: &quot; + threadName + &quot; - &quot; + i);
            try {
                Thread.sleep(500); // 0.5 초 대기
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(&quot;::: 작업 끝 :::&quot;);
    }
}</code></pre>
<hr>
<h1 id="📖-멀티-쓰레드">📖 멀티 쓰레드</h1>
<ul>
<li>스레드 실행시에는 <code>run()</code> 호출하면 안돼고, <code>start()</code> 를 사용하자</li>
<li>결과가 병렬로, 스레드1,2를 번갈아가면서 작업이 처리된다</li>
</ul>
<pre><code class="language-java">// ✅ Thread 클래스 상속으로 쓰레드 구현
public class MyThread extends Thread {

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(&quot;::: &quot; + threadName + &quot;쓰레드 시작 :::&quot;);
        for (int i = 0; i &lt; 10; i++) {
            System.out.println(&quot;현재 쓰레드: &quot; + threadName + &quot; - &quot; + i);
            try {
                Thread.sleep(500); // 딜레이 0.5 초
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(&quot;::: &quot; + threadName + &quot;쓰레드 종료 :::&quot;);
    }
}


--------------------

public class Main {

    public static void main(String[] args) {
        System.out.println(&quot;::: main 쓰레드 시작&quot;);

        MyThread thread0 = new MyThread();
        MyThread thread1 = new MyThread();

        // 1. thread0 실행
        System.out.println(&quot;::: main 이 thread0 을 실행&quot;);
        thread0.start();

        // 2. thread1 실행
        System.out.println(&quot;::: main 이 thread1 을 실행&quot;);
        thread1.start();

        System.out.println(&quot;::: main 쓰레드 종료&quot;);
    }
}
</code></pre>
<h2 id="📌-join">📌 join</h2>
<ul>
<li>위 예시에서는 메인스레드가 스레드를 실행시키자마자 끝나버렸음</li>
<li>조인 사용시 메인스레드가 다른 스레드가 작업을 끝낼때까지 기다리게 되기 때문에</li>
<li><blockquote>
<p>총 작업시간을 측정할 수 있다</p>
</blockquote>
</li>
</ul>
<pre><code class="language-java">public class Main {

    public static void main(String[] args) {
        System.out.println(&quot;::: main 쓰레드 시작&quot;);
        MyThread thread0 = new MyThread();
        MyThread thread1 = new MyThread();

        // 시작시간 기록
        long startTime = System.currentTimeMillis();

        // 1. thread0 시작
        System.out.println(&quot;thread0 시작&quot;);
        thread0.start();

        // 2. thread1 시작
        System.out.println(&quot;thread1 시작&quot;);
        thread1.start();

        // ⌛️ main 쓰레드 대기 시키기
        try {
            thread0.join();
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        long totalTime = endTime - startTime;
        System.out.println(&quot;총 작업 시간: &quot; + totalTime + &quot;ms&quot;);
        System.out.println(&quot;::: main 쓰레드 종료&quot;);
    }
}</code></pre>
<hr>
<h1 id="📖-runnable-인터페이스-활용">📖 Runnable 인터페이스 활용</h1>
<blockquote>
<ol>
<li>유시보수성, 재사용성 향상</li>
<li>기존 클래스를 확장가능</li>
</ol>
</blockquote>
<ul>
<li><p>지금처럼 Thread를 상속받아서 MyThread를 구현하면?</p>
</li>
<li><blockquote>
<p><code>실행로직</code>(내가 만든) + <code>제어로직</code>(start(),join(),,등) 이렇게 두가지 역할임</p>
</blockquote>
</li>
<li><p>Runnable을 활용하면? <code>실행로직</code>을 별도 구현체로 분리 가능</p>
</li>
<li><blockquote>
<p><code>Thread</code> : 제어로직 / <code>Runnable 구현체</code> : 실행로직</p>
</blockquote>
</li>
</ul>
<h2 id="1️⃣-기본-사용-예시">1️⃣ 기본 사용 예시</h2>
<pre><code class="language-java">public class MyRunnable implements Runnable {
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        for (int i = 0; i &lt; 10; i++) {
            System.out.println(&quot;현재 쓰레드: &quot; + threadName + &quot; - &quot; + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

----------------------------------

public class Main {

    public static void main(String[] args) {

        MyRunnable task = new MyRunnable(); // ✅ 하나의 작업 객체 선언

                // ✅ 하나의 작업을 여러 쓰레드에서 공유
        Thread thread0 = new Thread(task); // 작업 객체 공유
        Thread thread1 = new Thread(task); // 작업 객체 공유

                // 실행
        thread0.start();
        thread1.start();
    }
}
</code></pre>
<hr>
<h2 id="2️⃣-기존-클래스를-유지하면서-확장하는-예시">2️⃣ 기존 클래스를 유지하면서 확장하는 예시</h2>
<ul>
<li>Thread를 상속하면 Java는 다중상속을 지원하지 않기에 다른 클래스 상속은 불가능!</li>
<li>Runnable은 인터페이스이므로 상속까지 해서 확장이 가능</li>
</ul>
<pre><code class="language-java">public class MyNewClass { // ✅ 새로운 클래스 

    public void printMessage() {
        System.out.println(&quot;MyClass 기능 실행&quot;);
    }
}
----------

public class MyRunnable extends MyNewClass implements Runnable { // ✅ 다중 상속

...

----------

public class Main {

    public static void main(String[] args) {

        MyRunnable task = new MyRunnable();

        // ✅ 기존 클래스를 유지하면서 확장해서 활용
        task.printMessage(); 

...</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래밍 3주차] 키오스크 과제 Level 2~4 / TIL day 15]]></title>
            <link>https://velog.io/@luv_lyn/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-3%EC%A3%BC%EC%B0%A8-%ED%82%A4%EC%98%A4%EC%8A%A4%ED%81%AC-%EA%B3%BC%EC%A0%9C-Lv1-TIL-day-15</link>
            <guid>https://velog.io/@luv_lyn/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-3%EC%A3%BC%EC%B0%A8-%ED%82%A4%EC%98%A4%EC%8A%A4%ED%81%AC-%EA%B3%BC%EC%A0%9C-Lv1-TIL-day-15</guid>
            <pubDate>Tue, 11 Mar 2025 13:08:09 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="📋-필수-요구사항">📋 필수 요구사항</h1>
<hr>
<ul>
<li><input checked="" disabled="" type="checkbox"> Lv 1. 기본적인 키오스크를 프로그래밍해보자</li>
<li><input checked="" disabled="" type="checkbox"> Lv 2. 객체 지향 설계를 적용해 햄버거 메뉴를 클래스로 관리하기</li>
<li><input checked="" disabled="" type="checkbox"> Lv 3. 객체 지향 설계를 적용해 순서 제어를 클래스로 관리하기</li>
<li><input checked="" disabled="" type="checkbox"> Lv 4. 객체 지향 설계를 적용해 음식 메뉴와 주문 내역을 클래스 기반으로 관리하기</li>
<li><input disabled="" type="checkbox"> Lv 5. 캡슐화 적용하기</li>
</ul>
<blockquote>
</blockquote>
<p><strong>이번 과제에서 신경쓸 부분🥲</strong>
<em>저번 과제 이후 클래스간의 역할을 정리하고 분리하는 작업이 얼마나 중요한지를 깨달았다.
각 레벨을 구현하기 전에는 항상 클래스의 역할을 정리하고 시작하기로 했다 !
요구 사항을 잘 읽고 잘 지키자..</em></p>
<blockquote>
</blockquote>
<br>

<hr>
<h1 id="📝-나의-코드-리뷰하기">📝 나의 코드 리뷰하기</h1>
<hr>
<h2 id="🌟--level2-">🌟 [ Level2 ]</h2>
<blockquote>
</blockquote>
<h3 id="📍-요구사항">📍 요구사항</h3>
<ul>
<li>*<em><code>MenuItem</code> *</em><ul>
<li>클래스 생성 후 속성 입력</li>
</ul>
</li>
<li>*<em><code>Main</code> *</em><ul>
<li>MenuItem을 통해 객체 생성 / 이름, 가격, 설명을 세팅</li>
<li>Array list로 아이템들을 추가 <pre><code class="language-java">Main (메뉴의 객체 생성)
 ├── MenuItem (메뉴 속성 등록)
&gt;   ```
</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="📍-주요-코드">📍 주요 코드</h3>
<pre><code class="language-java">public class MenuItem  {

//    속성
    String name;
    double price;
    String detail;

//    생성자
    public MenuItem(String name, double price, String detail) {
        this.name = name;
        this.price = price;
        this.detail = detail;  

 // 개별 속성을 가져올 getter 메서드들
 ...

    }
}      

-------------------------------------

public class Main {
    public static void main(String[] args) {
        ...

        List&lt;Menuitem&gt; menuitems = new ArrayList&lt;&gt;();

        menuitems.add (new Menuitem(&quot;ShackBurger&quot;, 6.9, &quot;토마토, 양상추, 쉑소스가 토핑된 치즈버거&quot;));
        menuitems.add (new Menuitem(&quot;SmokeBurger&quot;, 8.9, &quot;베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거&quot;));
        menuitems.add (new Menuitem(&quot;CheeseBurger&quot;, 6.9, &quot;포테이토 번과 비프패티, 치즈가 토핑된 치즈버거&quot;));
        menuitems.add (new Menuitem(&quot;HamBurger&quot;, 5.4, &quot;비프패티를 기반으로 야채가 들어간 기본버거&quot;));


        for (Menuitem item : menuitems) {
            System.out.println( item.getName() + &quot; | &quot; + item.getPrice() + &quot; | &quot; + item.getDetail());
        }

        System.out.print(&quot;메뉴를 선택하세요 : &quot;);
        int num = scanner.nextInt();

        // switch문으로 입력한 번호에 따라 선택한 버거 보여주기
        ...
</code></pre>
<h3 id="💡-어려웠던-점">💡 어려웠던 점</h3>
<ul>
<li>객체를 생성하고 <code>List</code>에 담아야 할때, 저렇게 객체 생성과 동시에 하는게 깔끔한듯 하다</li>
<li><code>List</code>의 데이터를 탐색할 때는 <strong><code>향상된 for문</code></strong> 을 사용하는 것 잊지말자..^^</li>
</ul>
<br>


<hr>
<h2 id="🌟--level3-">🌟 [ Level3 ]</h2>
<blockquote>
</blockquote>
<h3 id="📍-요구사항-1">📍 요구사항</h3>
<ul>
<li><strong><code>Kiosk</code></strong> <ul>
<li>클래스 생성하기</li>
<li><code>start</code> 함수로 입력,반복문 로직 만들기 (메인에서 하지말고)<ul>
<li>0 을 입력하면 뒤로가기되거나 종료</li>
</ul>
</li>
<li><code>menuitem</code>을 관리하는 리스트가 존재한다</li>
<li>List는 <code>kiosk</code>의 생성자를 이용하기  <pre><code class="language-java">Main 
      ├── Kiosk (시작-반복 로직, 객체 관리하기)
     ├── MenuItem (객체 등록하기)
&gt; 
</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="주요코드">주요코드</h3>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        Kiosk kiosk = new Kiosk ();
        kiosk.start();
    }
}
----------------------------------------------

public class Kiosk {
    Scanner scanner = new Scanner(System.in);
    List&lt;Menuitem&gt; menuitem = new ArrayList&lt;&gt;();

    void wholeMenu () {
        menuitem.add(new Menuitem(&quot;ShackBurger&quot;, 6.9, &quot;토마토, 양상추, 쉑소스가 토핑된 치즈버거&quot;));
        menuitem.add(new Menuitem(&quot;SmokeBurger&quot;, 8.9, &quot;베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거&quot;));
        menuitem.add(new Menuitem(&quot;CheeseBurger&quot;, 6.9, &quot;포테이토 번과 비프패티, 치즈가 토핑된 치즈버거&quot;));
        menuitem.add(new Menuitem(&quot;HamBurger&quot;, 5.4, &quot;비프패티를 기반으로 야채가 들어간 기본버거&quot;));
    }

    void start () {
        System.out.println(&quot;키오스크를 시작하려면 아무거나 입력하세요&quot;);
        scanner.nextLine();
        System.out.println(&quot;키오스크를 시작합니다&quot;);
        wholeMenu();

        for (Menuitem item : menuitem) {
            System.out.println(item.getName() + &quot; | &quot; + item.getPrice() + &quot; | &quot; + item.getDetail());
        }
        System.out.print(&quot;종료는 0 | &quot;);
        System.out.print(&quot;메뉴를 선택하세요 : &quot;);
        int num = scanner.nextInt();

        // 기존 main에서 쓰던 switch문 옮겨오기
        ...
        }
    }
}
</code></pre>
<h3 id="💡-어려웠던-점-1">💡 어려웠던 점</h3>
<ul>
<li>main이 깔끔하다 못해 휑해졌다(ㅋㅋ)</li>
<li>키오스크로 객체,리스트를 옮겨와야 하는데 시작점이 아니라 그런지 바로 List에 넣을 수 없었다.
그래서 그냥 냅다 메서드로 만들어 버리고 start함수 실행시 메뉴를 쭉 불러오는 것으로 변경,,,</li>
<li>switch 문에서 예외처리 다시 하기</li>
<li>카테고리가 추가되면 어쩌지..?</li>
</ul>
<hr>
<h2 id="🌟--level4-">🌟 [ Level4 ]</h2>
<blockquote>
</blockquote>
<h3 id="📍-요구사항-2">📍 요구사항</h3>
<ul>
<li>*<em><code>Menu</code> *</em><ul>
<li>클래스 생성 후 list 옮기기</li>
<li><code>Menuitem</code>을 관리한다</li>
<li>(상위개념-&quot;버거&quot;같은) 카테고리 이름 필드를 만들자</li>
<li>카테고리 이름을 반환하는 메서드가 구현되어야 한다<pre><code class="language-java">  Main 
      ├── Kiosk (입력,실행)
        ├── Menu (객체 등록하기)
          ├── MenuItem (객체 등록하기)
&gt;   ```
</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="📍-주요-코드-1">📍 주요 코드</h3>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/4e6cf53d-b02b-4d8d-84b3-8719ace8ef55/image.png' width=50%>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/425819e1-8132-43b2-9693-411d5bba3537/image.png' width=50%>

<ul>
<li>일단 카테고리를 나눠야 하기 때문에 카테고리 별 리스트를 새로 선언했다</li>
</ul>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/97b13ab8-e0c7-4615-b89a-63202dbfbc65/image.png' width=50%>

<ul>
<li>출력은 <code>Kiosk</code>에서 하고 리스트 관리는 <code>Menu</code>에서 하기 때문에 접근할 수 있도록 <code>getter</code>를 추가했다</li>
</ul>
<img src='https://velog.velcdn.com/images/luv_lyn/post/515062a9-85aa-45c1-aeed-de6deef4967a/image.png' width=50%>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/ff421be2-7e4e-4a9a-8487-d993f7ee4227/image.png' width=100%>

<ul>
<li>이제 카테고리 선택 후에!! 출력을 해야하기 때문에  MenuItem의 getter와 List의 index을 활용해서
메뉴 출력 방식도 변경했다 (그러나 카테고리 출력은 아직도 수기 느낌,,ㅎ)</li>
</ul>
<h3 id="💡-어려웠던-점-2">💡 어려웠던 점</h3>
<ul>
<li>처음에 kiosk에 생성했던 객체를 왜인지 menuitem으로 옮겨야 한다고 생각했다 (요구사항 다 까먹은듯)
아무튼 그래서 이상한데 사로잡혀 몇시간을 고민하다가,, 튜터님께 갔고 피드백 후에 생각을 고쳐먹을 수 ? 있었다 ^^</li>
<li>아직까지 요구사항이 전부 구현된지 모르겠다 뭘 모르는데 뭘 모르는지 모르는 이 기분,,</li>
<li>아직까지 코드가 지저분한 느낌이다. kiosk에 출력방식도 뿌듯하게 캡쳐해뒀는데 지금보니 맘에 안들고, list에 추가는 왜 저렇게 했으며... 카테고리 메뉴는 아직도 sout으로만 출력하고 있다..!</li>
</ul>
<hr>
<h1 id="🥹-나의-회고">🥹 나의 회고</h1>
<hr>
<p>난 글렀다..... 뭘 모르는지 몰라서 질문도 못한다 코드가 뒤죽박죽 동물원이다...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[☕️ Java 문법 종합반 (3) - ①예외처리,  Optional  / TIL - day 14]]></title>
            <link>https://velog.io/@luv_lyn/Java-%EB%AC%B8%EB%B2%95-%EC%A2%85%ED%95%A9%EB%B0%98-3-TIL-day-14</link>
            <guid>https://velog.io/@luv_lyn/Java-%EB%AC%B8%EB%B2%95-%EC%A2%85%ED%95%A9%EB%B0%98-3-TIL-day-14</guid>
            <pubDate>Mon, 10 Mar 2025 08:03:18 GMT</pubDate>
            <description><![CDATA[<h1 id="▪️-예외와-예외처리-▪️">▪️ 예외와 예외처리 ▪️</h1>
<hr>
<h1 id="📖-예외">📖 예외</h1>
<blockquote>
<p><em>프로그램 실행중 예상하지 못한 상황</em></p>
</blockquote>
<h2 id="📌-의도하지-않은-예외-예상하지-못한-예외">📌 의도하지 않은 예외 (예상하지 못한 예외)</h2>
<ul>
<li>개발자가 예측하지 못했거나, 예외 처리를 해두지 않아서 프로그램이 비정상 종료되는 경우</li>
<li>사용자가 입력한 값이나 실행 중 상황에 따라 우연히 발생할 수도 있음
➡ 예를 들어 <code>10/0</code> 같은 경우, (내가 해당 예외를 예측하지 않았다는 가정하에)
이 연산으로 인해 발생하는 예외는 내가 의도하지 않은 것임</li>
</ul>
<h2 id="📌-의도적인-예외-내가-특정-조건에서-일부러-발생시킨-예외">📌 의도적인 예외 (내가 특정 조건에서 일부러 발생시킨 예외)</h2>
<ul>
<li>개발자가 특정한 조건에서 예외를 발생시키도록 <code>throw</code> 를 사용한 경우
➡ 예를 들어, <code>미성년자의 접근</code>등 비즈니스 규칙에 대한 예외상황을 정의해두고 제어하는 것 !</li>
</ul>
<h3 id="✔️-throw---예외-발생시키기">✔️ throw - 예외 발생시키기</h3>
<ul>
<li><strong><code>throw new IllegalArgumentException</code></strong> 사용하기</li>
<li>예시 ) 나이 제한이 있는 경우<pre><code class="language-java">public class Main {
  public static void main(String[] args) {
      int age = 10;
      if (age &lt; 18) {
              // ✅ 의도적으로 예외를 발생시키는 부분
          throw new IllegalArgumentException(&quot;미성년자는 접근할 수 없습니다!&quot;);
      }
      System.out.println(&quot;....&quot;);
  }
}</code></pre>
<ul>
<li>예외 발생 내용을 콘솔에서 알려준다 (<strong>가독성</strong>)<img src = 'https://velog.velcdn.com/images/luv_lyn/post/f3fe93bb-d1e3-4693-b841-c66449c715f5/image.png' width=60%>

</li>
</ul>
</li>
</ul>
<br>

<hr>
<h1 id="📖-예외의-구조와-종류">📖 예외의 구조와 종류</h1>
<ul>
<li><strong>예외의 구조</strong><img src = 'https://velog.velcdn.com/images/luv_lyn/post/f0b5ee3a-5ddc-445b-b1d9-342c620f3993/image.png' width=70%>

</li>
</ul>
<h2 id="❎-언체크드---runtimeexception">❎ <strong>언체크드 - RuntimeException</strong></h2>
<ul>
<li><strong>예외처리를 컴파일러가 확인하지 않는다</strong> ➝ 예외처리 하지 않아도 컴파일 가능<h2 id="✅-체크드---exception">✅ <strong>체크드 - Exception</strong></h2>
</li>
<li><strong>예외처리를 컴파일러가 확인한다</strong> ➝ 예외처리하지 않으면 컴파일 오류 발생</li>
<li><em>🌟 반드시 예외 처리를 해야한다 🌟*</em></li>
</ul>
<blockquote>
<h3 id="💡-예외의-전파">💡 예외의 전파</h3>
<p>상위 메서드로 계속 퍼져서 프로그램 시작지점 <strong>메인까지 도달하게된다</strong>!
끝내 처리만 되면 프로그램은 종료되지 않을 것. (메인에서 처리해도 된다는 말)</p>
</blockquote>
<hr>
<h1 id="📖-예외-처리">📖 예외 처리</h1>
<h2 id="❎-uncheckedexception">❎ UncheckedException</h2>
<ul>
<li><code>RuntimeException</code> 을 상속받는 모든 예외는 <strong>UncheckedException</strong> 이다</li>
<li>예외처리를 하지 않으면 비정상적인 결과가 나오거나 프로그램이 종료될 수 있다<blockquote>
</blockquote>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/260c461d-6038-4a3d-ba38-fa239fa079ce/image.png' width=70%>
>
1️⃣ `RuntimeException()` 으로 `ExceptionPractice`를 예외 상황으로 가정.
>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/f84ad5f0-4597-4841-9212-c8a4f30f8f16/image.png' width=70%>
>
2️⃣ **처리하지 않는 경우** - 언체크 예외가 호출되고 나면 예외가 발생하고, 실행되지 않는다
>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/c0d48d66-17d6-4103-a0ed-dd33af731aff/image.png' width=70%>
>
3️⃣ **예외 처리** - 이 예시에서는 main에서 `try-catch`로 모든 예외를 처리해주고 있다
>- 예외는 전파되기에 메인에서 잡아줘도 된다!
>- try문 안에서 if문을 사용하고, `throw new RuntimeException();`을 통해 특정 조건에서 예외를 발생시킬 수 있다



</li>
</ul>
<h2 id="✅-checkedexception">✅ CheckedException</h2>
<ul>
<li><code>Exception</code> 클래스를 직접 상속받는 모든 예외는 <strong>CheckedException</strong>이다
( <code>RuntimeException</code>을 상속받는 예외 제외 )</li>
<li>예외처리는 필수, 하지 않으면 컴파일 오류
① 바로 <code>try-catch</code>를 사용한다
② <code>throws</code>로 다른 클래스에 책임을 전가 후에 처리한다<blockquote>
</blockquote>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/94c0a4a0-87ba-4192-b975-6feffe3f6800/image.png' width=70%>
>
1️⃣ `Exception()` 으로 `callCheckedException`를 예외 상황으로 가정.
> 아직 처리되기 전이므로 컴파일 오류(빨간줄) 발생 !
>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/af58a11d-df6e-4f9d-89a6-a1723180ddc1/image.png' width=70%>
>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/1ecdd9c4-d6bf-4ccf-b053-331edb6aea8d/image.png' width=70%>
>
2️⃣ **예외 처리** - 이번에는 예외 객체를 생성후에 처리했다!


</li>
</ul>
<h2 id="📌-throw와-throws의-차이">📌 throw와 throws의 차이</h2>
<h3 id="1️⃣-throw-➡-예외를-직접-발생시키는-키워드"><strong>1️⃣ <code>throw</code></strong> ➡ <strong>예외를 &quot;직접&quot; 발생시키는 키워드</strong></h3>
<p>✔ <strong>개발자가 특정한 조건에서 예외를 발생시킬 때 사용</strong><br>✔ <code>throw new 예외클래스명(&quot;예외 메시지&quot;)</code> 형태로 사용  </p>
<p>📌 <strong>예제 (<code>throw</code>로 직접 예외 발생)</strong></p>
<pre><code class="language-java">public class Example {
    public static void main(String[] args) {
        int age = 10;
        if (age &lt; 18) {
            throw new IllegalArgumentException(&quot;미성년자는 접근할 수 없습니다!&quot;); // 예외 발생
        }
        System.out.println(&quot;정상 실행됨&quot;);
    }
}</code></pre>
<p>✔ <code>throw</code>는 <strong>즉시 예외를 발생시키고 프로그램을 중단시킴</strong><br>✔ 특정한 조건에서 <strong>&quot;잘못된 입력이 들어왔다!&quot;</strong> 하고 예외를 던지는 역할  </p>
<hr>
<h3 id="2️⃣-throws-➡-메서드에서-예외를-던질-수-있다고-선언"><strong>2️⃣ <code>throws</code></strong> ➡ <strong>메서드에서 &quot;예외를 던질 수 있다&quot;고 선언</strong></h3>
<p>✔ <strong>메서드가 예외를 직접 처리하지 않고, 호출한 곳에서 처리하도록 예외를 넘기는 역할</strong><br>✔ <code>throws 예외클래스명</code> 형태로 메서드 선언부에 사용  </p>
<p>📌 <strong>예제 (<code>throws</code>를 사용해 예외를 호출한 곳에서 처리)</strong></p>
<pre><code class="language-java">public class Example {
    // 메서드에서 예외를 던질 수 있다고 선언
    public static void checkAge(int age) throws IllegalArgumentException {
        if (age &lt; 18) {
            throw new IllegalArgumentException(&quot;미성년자는 접근할 수 없습니다!&quot;);
        }
    }

    public static void main(String[] args) {
        try {
            checkAge(10); // 메서드 호출 → 예외 발생 가능
        } catch (IllegalArgumentException e) {
            System.out.println(&quot;예외 발생: &quot; + e.getMessage());
        }
    }
}</code></pre>
<p>✔ <code>throws</code>는 <strong>메서드가 예외를 직접 처리하지 않고, 예외를 던질 수 있다고 알리는 것</strong><br>✔ 호출한 <code>main()</code> 메서드에서 <code>try-catch</code>로 예외를 처리  </p>
<hr>
<h3 id="📍-throw와-throws-같이-쓰는-예제">📍 <strong><code>throw</code>와 <code>throws</code> 같이 쓰는 예제</strong></h3>
<p><code>throws</code>로 예외를 넘기고, <code>throw</code>로 예외를 발생시키는 코드  </p>
<pre><code class="language-java">public class Example {
    // throws로 예외를 넘길 수 있다고 선언
    public static void checkNumber(int num) throws IllegalArgumentException {
        if (num &lt; 0) {
            throw new IllegalArgumentException(&quot;음수는 허용되지 않습니다!&quot;); // throw로 예외 발생
        }
        System.out.println(&quot;정상적인 숫자입니다: &quot; + num);
    }

    public static void main(String[] args) {
        try {
            checkNumber(-5); // 메서드 호출 → 예외 발생 가능
        } catch (IllegalArgumentException e) {
            System.out.println(&quot;예외 처리: &quot; + e.getMessage());
        }
    }
}</code></pre>
<p>✔ <code>throw</code>는 <code>IllegalArgumentException</code>을 <strong>실제 발생</strong>시키는 역할<br>✔ <code>throws</code>는 <code>checkNumber(int num)</code> 메서드가 예외를 <strong>던질 수 있다는 걸 선언</strong>하는 역할  </p>
<hr>
<h2 id="throw-🆚-throws-비교-요약"><strong><code>throw</code> 🆚 <code>throws</code> 비교 요약</strong></h2>
<table>
<thead>
<tr>
<th>구분</th>
<th><code>throw</code></th>
<th><code>throws</code></th>
</tr>
</thead>
<tbody><tr>
<td>역할</td>
<td>예외를 &quot;직접&quot; 발생시킴</td>
<td>메서드에서 예외를 &quot;던질 수 있음&quot;을 선언</td>
</tr>
<tr>
<td>사용 위치</td>
<td><strong>메서드 내부</strong>에서 사용</td>
<td><strong>메서드 선언부</strong>에서 사용</td>
</tr>
<tr>
<td>사용 방식</td>
<td><code>throw new 예외클래스명(&quot;메시지&quot;);</code></td>
<td><code>메서드명() throws 예외클래스명</code></td>
</tr>
<tr>
<td>처리 방식</td>
<td>예외를 즉시 발생시키고 프로그램 중단</td>
<td>호출한 곳에서 예외를 처리해야 함</td>
</tr>
</tbody></table>
<ul>
<li>예외를 던질 때 👉  <strong><code>throw</code></strong> </li>
<li>메서드에서 넘길때는 👉 <strong><code>throws</code></strong> </li>
</ul>
<hr>
<h1 id="▪️-optional-▪️">▪️ Optional ▪️</h1>
<hr>
<h1 id="📖-optional">📖 Optional</h1>
<ul>
<li><strong><code>null</code>을 안전하게 다루게 해주는 객체</strong>이다! ➝ 관련 예외인 *<code>NullPointerException</code> 을 방지</li>
<li><code>Optional</code> 객체는 값이 있을 수도 있고 없을 수도 있는 컨테이너다!</li>
</ul>
<blockquote>
<h4 id="💡-nullpointerexception-언체크드-예외">💡 <code>NullPointerException</code> (언체크드 예외)</h4>
</blockquote>
<ul>
<li>단순 값이 없을 때보다도, 없는 객체의 메서드를 사용할 때 발생!</li>
<li>컴파일러가 확인해주지 않기 때문에 미리 예외 처리 해둬야함</li>
<li><blockquote>
<p><code>if문</code> 을 활용해 논리적으로 예외를 잡을 수도 있지만,,! 하나씩 언제 다해?????</p>
</blockquote>
</li>
</ul>
<h2 id="📌-optional-활용하기">📌 Optional 활용하기</h2>
<h3 id="1️⃣-ofnullable">1️⃣ ofnullable</h3>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/012df7ba-8efe-488d-a22c-e90128e00387/image.png' width=50%>

<ul>
<li>🌟<strong><code>ofnullable</code>로 null이 담길 가능성이 있는 데이터를 감싸서 메서드에 넣자!</strong>🌟</li>
</ul>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/519e43fa-5b91-44e4-aa74-e1c925de6717/image.png' width=50%>

<ul>
<li><p>이 메서드를 사용하게 되면 <code>Optional 객체</code>가 생성되므로, &quot;어쩌면 null 값이 있을 수도 있겠다!&quot;
➝ 가독성 높아짐</p>
<hr>
</li>
</ul>
<h3 id="2️⃣-ispresent">2️⃣ isPresent()</h3>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/230fe870-df64-4f13-9480-bfa63f4abc8d/image.png' width=50%>

<ul>
<li><p>Optional이 비었는지 아닌지 알 수 있다!</p>
</li>
<li><p><strong><code>True</code> = 안 비었음 , <code>False</code> = 비어있음</strong></p>
<hr>
</li>
</ul>
<h3 id="3️⃣-get">3️⃣ get()</h3>
<img src ='https://velog.velcdn.com/images/luv_lyn/post/f6889a96-4d2a-4430-b816-090f9cf6e58a/image.png' width=50%>

<ul>
<li>Optional 안의 값들을 꺼내준다</li>
</ul>
<br>

<blockquote>
<p>💡 <strong>Optional 사용 예시</strong></p>
</blockquote>
<ul>
<li><code>student</code>가 null이 아닌 경우 안전하게 가져오고, 게터 사용</li>
<li><code>student</code>가 null인 경우 메세지 출력<blockquote>
</blockquote>
<pre><code class="language-java">public class Main {
  public static void main(String[] args) {
      Camp camp = new Camp();
&gt;
      //  Optional 객체 반환받음
      Optional&lt;Student&gt; studentOptional = camp.getStudent();✅
&gt;
      // Optional 객체의 기능 활용
      boolean flag = studentOptional.isPresent(); // ✅false 반환
      if (flag) {
          // 존재할 경우
          Student student = studentOptional.get(); // ✅ 안전하게 Student 객체 가져오기
          String studentName = student.getName();
          System.out.println(&quot;studentName = &quot; + studentName);
&gt;
      } else {
          // null 일 경우
          System.out.println(&quot;학생이 없습니다.&quot;);
      }
  }
}</code></pre>
<blockquote>
</blockquote>
</li>
</ul>
<br>



]]></description>
        </item>
        <item>
            <title><![CDATA[☕️ Java 문법 종합반 (2) - ② 객체지향 / TIL - day 13]]></title>
            <link>https://velog.io/@luv_lyn/Java-%EB%AC%B8%EB%B2%95-%EC%A2%85%ED%95%A9%EB%B0%98-TIL-day-13</link>
            <guid>https://velog.io/@luv_lyn/Java-%EB%AC%B8%EB%B2%95-%EC%A2%85%ED%95%A9%EB%B0%98-TIL-day-13</guid>
            <pubDate>Mon, 10 Mar 2025 03:11:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em>객체지향의 4가지 특징을 정리했다 ! - 개인적으로 <a href="https://velog.io/@luv_lyn/Java-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC">기초 개념정리</a>를 더했다,,,</em></p>
</blockquote>
<hr>
<h1 id="⬛️--객체지향-part-❶---캡슐화">⬛️ &lt; 객체지향 PART ❶ - 캡슐화 &gt;</h1>
<hr>
<h1 id="📖-캡슐화">📖 캡슐화</h1>
<ul>
<li><p>객체의 정보를 외부에서 직접 접근하지 못하게 한다</p>
</li>
<li><p><code>접근제어자</code>로 구현할 수 있다</p>
<hr>
</li>
</ul>
<h1 id="📖-접근제어자">📖 접근제어자</h1>
<p>-
| <strong>접근제어자</strong> | 클래스 내부 | 패키지 내부 | 상속한 클래스 | 전체 공개 |
| --- | --- | --- | --- | --- |
| <strong>public</strong> | ✅ | ✅ | ✅ | ✅ |
| <strong>protected</strong> | ✅ | ✅ | ✅ | ❌ |
| <strong>default</strong> | ✅ | ✅ | ❌ | ❌ |
| <strong>private</strong> | ✅ | ❌ | ❌ | ❌ |</p>
<h2 id="📌-접근제어자-활용-예시">📌 접근제어자 활용 예시</h2>
<ul>
<li><p><strong>1. 생성자 호출</strong></p>
<table>
<thead>
<tr>
<th align="center">잘못 사용한 예시</th>
<th align="center">올바르게 사용한 예시</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src='https://velog.velcdn.com/images/luv_lyn/post/0fdfc34e-975c-41b0-956a-6614c44323cb/image.png' width=100%></td>
<td align="center"><img src='https://velog.velcdn.com/images/luv_lyn/post/479f8e88-fb7f-4111-9938-e39af710d17f/image.png' width=100%></td>
</tr>
<tr>
<td align="center"><img src='https://velog.velcdn.com/images/luv_lyn/post/47e2d528-b988-4641-9437-ffb389d460b2/image.png' width=100%></td>
<td align="center"><img src='https://velog.velcdn.com/images/luv_lyn/post/78d0237b-63b2-44f9-8399-4934743ce105/image.png' width=100%></td>
</tr>
<tr>
<td align="center"><strong>❌ 오류 발생</strong></td>
<td align="center"><strong>문제없음 😁</strong></td>
</tr>
<tr>
<td align="center"><code>private</code> 같은 클래스 내부에서만 사용가능하기 때문</td>
<td align="center"><code>public</code> = 전체공개이기 때문에</td>
</tr>
</tbody></table>
</li>
<li><p><strong>2. 인스턴스 변수</strong></p>
<table>
<thead>
<tr>
<th align="center">✔️클래스</th>
<th align="center">✔️메인</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src = 'https://velog.velcdn.com/images/luv_lyn/post/3043f56a-a37a-431a-8ce8-b1e7ae9981f2/image.png' width=80%></td>
<td align="center"><img src = 'https://velog.velcdn.com/images/luv_lyn/post/ba6ec8d5-339b-4639-acab-74cfde0a6deb/image.png' width=80%></td>
</tr>
</tbody></table>
</li>
</ul>
<ul>
<li><p><strong>3. 인스턴스 메서드 접근</strong></p>
<table>
<thead>
<tr>
<th align="center">✔️클래스</th>
<th align="center">✔️메인</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src = 'https://velog.velcdn.com/images/luv_lyn/post/df39f7af-00c6-4a1e-bf59-3ba9fd8729b0/image.png' width=80%></td>
<td align="center"><img src = 'https://velog.velcdn.com/images/luv_lyn/post/6eb58980-ee6e-47cd-83fd-452196eb5261/image.png' width=80%></td>
</tr>
</tbody></table>
</li>
</ul>
<blockquote>
<p><strong>💡 Tip</strong>
속성은 <code>private</code>로 사용하고, 한단계씩 풀어가자
생성자는 <code>public</code>으로 열어두면 매개변수를 활용해 객체를 생성하고 나면
속성은 변경이 안된다는 것 <del>!</del>! 기억하자</p>
</blockquote>
<hr>
<h1 id="📖-데이터-접근---getter-setter">📖 데이터 접근 - getter, setter</h1>
<ul>
<li>캡슐화 때문에 데이터를 찾거나 접근이 곤란할 때 사용한다</li>
<li>당연 각 메서드를 만드는게 먼저임</li>
</ul>
<h2 id="📌getter--데이터-찾을때">📌<strong><code>getter</code></strong> : <strong>데이터 찾을때</strong></h2>
<ul>
<li>예시 ) 걔 이름이 머였더라? 너무 코드가 길어서 찾을 수 없어🥲<pre><code class="language-java">String name = person.getName();
System.out.println(&quot;이름:&quot;+name); // 출력에 person의 이름 나옴</code></pre>
</li>
</ul>
<h2 id="📌setter--캡슐화-된-데이터를-바꿀때">📌<strong><code>setter</code> *<em>: *</em>캡슐화 된 데이터를 바꿀때</strong></h2>
<ul>
<li><p>예시 ) 데이터를 보호했지만 ! 이름을 private으로 보호해뒀지만 바꿀래😉</p>
<pre><code class="language-java">person.setName(&quot;steve&quot;); // 원래 이름 &quot;surin&quot;
String name2 = person.getName();
System.out.println(&quot;이름:&quot;+name2); // &quot;steve&quot;로 출력됨
</code></pre>
<blockquote>
<h3 id="💡-세터-활용의-예시-핵시설의-관리자">💡 세터 활용의 예시 (핵시설의 관리자)</h3>
<ul>
<li><strong>❌ 무분별한 세터 사용</strong> <img src = 'https://velog.velcdn.com/images/luv_lyn/post/a13c448e-5389-43cb-abc8-cdb58e87fe7f/image.png' width=100%>
- B를 입력하지 못하게 `String store`를 `private`으로 보호 ➡︎ `store`를 사용할 수 없음
- `setter`를 이용해 접근 가능하도록 함 ➡︎ 다시 "B"입력 가능해짐..🤔
</li>
</ul>
<hr>
<ul>
<li>*<em>⭕️ 올바른 사용 *</em><img src = 'https://velog.velcdn.com/images/luv_lyn/post/4244c931-38de-4ffe-9e88-aa5ac23cef23/image.png' width=80%>
- 이 경우 "B"를 입력할 수 없도록, **`if문`을 함께 사용**한다
- 이외에도 세터를 남발대신 `private`으로 묶인 것들을 모아서 별도의 기능으로 만들어 제공하기 등등,, 다양한 방법이 있다
</li>
</ul>
</blockquote>
</li>
</ul>
<hr>
<h1 id="⬛️--객체지향-part-❷---상속">⬛️ &lt; 객체지향 PART ❷ - 상속 &gt;</h1>
<hr>
<h1 id="📖-상속inheritance">📖 상속(Inheritance)</h1>
<ul>
<li>클래스간의 관계를 <strong><code>부모(상위)</code> ,<code>자식(하위)</code></strong> 관계로 바라본다
(부모의 속성과 기능을 자식이 물려받는다)</li>
<li><strong>재사용성, 확장</strong>이 가능하다</li>
<li><strong><code>extends</code></strong> 로 상속 관계 표현 가능<img src='https://velog.velcdn.com/images/luv_lyn/post/acf230e4-5a6a-4116-82f0-c6e8c9049489/image.png' width=50%>


</li>
</ul>
<br>


<h2 id="📌-장점-1️⃣---재사용성">📌 장점 1️⃣ - 재사용성</h2>
<ul>
<li>상속을 통해 부모 클래스의 인스턴스 멤버들에 접근할 수 있다</li>
<li><strong>모든 부모의 요소를 자식이 활용할 수 있다</strong><h4 id="💡-예시">💡 예시</h4>
</li>
<li>이렇게 <code>child</code> 에는 아무것도 작성하지 않았으나, <code>parent</code> 의 모든 요소를 물려받고 있다<img src = 'https://velog.velcdn.com/images/luv_lyn/post/42fe9dc8-fe71-4a58-8783-31bd1dbdffea/image.png' width=80%>

</li>
</ul>
<blockquote>
<h3 id="✔️super---부모-인스턴스-호출">✔️super - 부모 인스턴스 호출</h3>
</blockquote>
<ul>
<li>자식 클래스에서 <strong>명확하게 부모의 인스턴스를 호출</strong>하고 싶을 때</li>
<li><code>super</code>로 구현<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th align="center">✔️자식클래스</th>
<th align="center">✔️메인클래스</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src='https://velog.velcdn.com/images/luv_lyn/post/a5958151-fed0-49fa-a04c-32074a2ee212/image.png' width=100%></td>
<td align="center"><img src='https://velog.velcdn.com/images/luv_lyn/post/d05d8362-2686-4fa4-ab41-177624f0f8c0/image.png' width=100%></td>
</tr>
<tr>
<td align="center">부모클래스의 familyName = &quot;스파르탄&quot;임</td>
<td align="center"><strong>부모 클래스 무시, 자식 클래스에서 다시 할당한 값이 나온다</strong></td>
</tr>
<tr>
<td align="center"><img src = 'https://velog.velcdn.com/images/luv_lyn/post/e41a6d10-4780-4dac-aeb4-b2045f49da4d/image.png' width=100%></td>
<td align="center"><img src = 'https://velog.velcdn.com/images/luv_lyn/post/5fcbe20f-1bcd-4763-af9c-de5005c1e8d2/image.png' width=80%></td>
</tr>
<tr>
<td align="center"><strong><code>super</code></strong>를 통해 부모의 인스턴스를 호출한다</td>
<td align="center"><strong>부모 인스턴스가 명확하게 호출됨</strong></td>
</tr>
</tbody></table>
<blockquote>
<hr>
<h3 id="✔️super---부모-인스턴스의-생성자">✔️super() - 부모 인스턴스의 생성자</h3>
<ul>
<li>부모의 생성자를 물려받았지만 *<em>추가적으로 자식 클래스에서 생성자를 입력하고 싶을 때 *</em>!</li>
</ul>
</blockquote>
</li>
<li>무조건 <code>super()</code>를 위에 두고 아래에 생성자를 만든다 
(부모 생성자가 있어야 자식 생성자도 만들수 있음)<img src = 'https://velog.velcdn.com/images/luv_lyn/post/3f24a4c5-4f87-4b48-9117-2fdb47c93df4/image.png' width=50%>

</li>
</ul>
<br>


<h2 id="📌-장점2️⃣---확장">📌 장점2️⃣ - 확장</h2>
<ul>
<li>부모 클래스의 기능 유지 + 자식 클래스에서 기능을 더 확장
<img src="https://velog.velcdn.com/images/luv_lyn/post/80390e85-9e69-4fc6-8071-de7e4bca2b5c/image.png" alt=""></li>
</ul>
<br>

<hr>
<h1 id="📖-메서드-오버라이딩overriding">📖 메서드 오버라이딩(overriding)</h1>
<ul>
<li>부모 클래스의 <strong>메서드를</strong> 자식 클래스에서 <strong>변경하고 재정의</strong><ul>
<li><strong>메서드 이름, 매개변수, 반환타입이 일치해야</strong>한다</li>
<li><code>접근제어자</code>는 부모보다 강한 수준으로 변경가능</li>
</ul>
</li>
<li><strong><code>@Override</code></strong> 로 구현</li>
</ul>
<table>
<thead>
<tr>
<th align="center">✔️부모클래스의 메서드</th>
<th align="center">✔️자식클래스에서 재정의</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src='https://velog.velcdn.com/images/luv_lyn/post/718ca332-daba-4c5a-b595-dd9e038f3316/image.png' width=90%></td>
<td align="center"><img src='https://velog.velcdn.com/images/luv_lyn/post/ea99482a-ee61-49c0-9f92-e1cd09b8a919/image.png' width=120%></td>
</tr>
<tr>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<hr>
<h1 id="📖-추상-클래스">📖 추상 클래스</h1>
<ul>
<li><p>계층 구조를 활용해서 하위 클래스에 <strong>특정 메서드 구현을 강제</strong>하기 위해 사용한다</p>
<ul>
<li><p>1️⃣ <strong><code>abstract</code> 로 클래스 선언</strong></p>
<img src = 'https://velog.velcdn.com/images/luv_lyn/post/83d387d3-37f2-4114-a17d-bbeddfdfb8f8/image.png' width =50%></li>
<li><p>2️⃣ <strong><code>abstract</code> 로 메서드 자식에게 강제 구현시키기</strong></p>
<table>
<thead>
<tr>
<th align="center">부모 클래스</th>
<th align="center">자식 클래스</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src = 'https://velog.velcdn.com/images/luv_lyn/post/91a3910a-6448-401c-9448-fe271a22e70f/image.png' width =100%></td>
<td align="center"><img src = 'https://velog.velcdn.com/images/luv_lyn/post/a6d30fdc-478a-4052-bd18-61ff7b569056/image.png' width =100%></td>
</tr>
<tr>
<td align="center"></td>
<td align="center"><code>@Override</code>를 쓰고 작성하기</td>
</tr>
</tbody></table>
</li>
</ul>
</li>
<li><p><strong>주의 ‼️ 추상클래스는 인스턴스화 할 수 없다!!!</strong></p>
</li>
</ul>
<blockquote>
<p><strong>추상클래스 vs 인터페이스 ? 🤔</strong></p>
</blockquote>
<ul>
<li><strong>인터페이스</strong> : 표준을 제공 / 인스턴스 변수 사용불가</li>
<li><strong>상속</strong> : 계층적 구조 표현 / 속성이나 기능 재사용시 용이<blockquote>
</blockquote>
</li>
</ul>
<hr>
<h1 id="⬛️--객체지향-part-❸---추상화">⬛️ &lt; 객체지향 PART ❸ - 추상화 &gt;</h1>
<hr>
<h1 id="📖-추상화">📖 추상화</h1>
<ul>
<li>불필요한 정보를 제거하고 <strong>본질적인 특징</strong>만 남기는 것
(예를 들어, 고양이의 특성, 동물의 특성을 제외해주면 생명체라는 본질적 특성에 집중할 수 있다)
➡︎ 덕분에 유지보수성이 좋아진다</li>
</ul>
<h2 id="📌-인터페이스를-사용한-추상-계층-표현">📌 인터페이스를 사용한 추상 계층 표현</h2>
<ul>
<li>나는 막연히 클래스끼리만 상속할 수 있다고 생각했는데,, <del>왜그랬지</del>
인터페이스 &gt; 인터페이스 &gt; 클래스 이렇게도 상속이 된다
<img src="https://velog.velcdn.com/images/luv_lyn/post/c9d832ba-ea96-4849-97ce-018093831036/image.png" alt="">
이런식으로 ! 주석에 달아놓은 것들이** 원래 해당 메서드가 선언된 클래스의 위치**다</li>
</ul>
<h2 id="📌-클래스를-사용한-추상-계층-표현">📌 클래스를 사용한 추상 계층 표현</h2>
<ul>
<li>동일하게 상속을 통해 계층을 표현할 수 있다</li>
<li>추가로!! <strong>메서드 오버라이딩</strong>을 통해 부모의 메서드를 재정의 할 수 있다
<img src="https://velog.velcdn.com/images/luv_lyn/post/56396402-1a63-4053-aa63-8c1b390fc63c/image.png" alt=""></li>
</ul>
<hr>
<h1 id="⬛️--객체지향-part-❹---다형성">⬛️ &lt; 객체지향 PART ❹ - 다형성 &gt;</h1>
<hr>
<h1 id="📖-다형성">📖 다형성</h1>
<ul>
<li><strong>상위 타입을 통해 여러 객체를 다룰 수 있다</strong>
(예시 - 동물을 통해 고양이 강아지를 다룬다) <del>약간 카테고리 느낌, 대분류 소분류 느낌</del></li>
<li>상속 구조 : Lifeform &gt; Animal &gt; Cat,Dog<img src = 'https://velog.velcdn.com/images/luv_lyn/post/94942f57-7cc9-44ce-ab9a-a775eaf880c6/image.png' width=50%>
➡︎ 다형성을 활용해서 Cat, Dog를 Animal 자료형에 넣어줄 수 있다!

</li>
</ul>
<hr>
<h1 id="📖-형변환">📖 형변환</h1>
<h2 id="📌-업캐스팅">📌 업캐스팅</h2>
<ul>
<li>부모 타입으로 자식타입을 받을 수 있다 (스몰박스 &gt; 큰 박스)<h3 id="💡-주의사항">💡 주의사항</h3>
</li>
<li><strong>자식 클래스의 고유 기능은 사용할 수 없다</strong> ➡︎ 사용하려면 다운캐스팅
<img src="https://velog.velcdn.com/images/luv_lyn/post/3faad854-b89c-4156-80ed-92f181db97f4/image.png" alt=""></li>
</ul>
<hr>
<h2 id="📌-다운캐스팅">📌 다운캐스팅</h2>
<ul>
<li>부모데이터 타입을 다시 자식 데이터 타입에 넣어준다
(데이터 손실이 일어나기 때문에 <strong>자료형 명시</strong>해야함)
<img src="https://velog.velcdn.com/images/luv_lyn/post/63873fe8-dcbd-4a62-be10-0db7a1fa6469/image.png" alt=""><h3 id="💡-주의사항-1">💡 주의사항</h3>
</li>
<li>자료형을 잘못써도 컴파일러가 감지하지 못하기 때문에 <strong><code>instanceof</code>를 사용</strong>
<img src="https://velog.velcdn.com/images/luv_lyn/post/8124fe67-a2b7-4c83-b50c-b662ee342ce9/image.png" alt=""></li>
</ul>
<hr>
<h1 id="📖-다형성-장점">📖 다형성 장점</h1>
<ul>
<li><strong>한가지 타입으로 여러 데이터타입을 다룰 수 있다</strong></li>
</ul>
<p><img src="https://velog.velcdn.com/images/luv_lyn/post/dec847b9-4916-45ab-82b7-68536e7684f0/image.png" alt=""></p>
<hr>
]]></description>
        </item>
    </channel>
</rss>