<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>기록하는 습관</title>
    <link>https://flexiblecode.tistory.com/</link>
    <description>기록하는 습관은 큰 힘을 가지고 있다고 생각합니다. 
스스로 정리 안된 부분을 알 수 있고, 작성하는 과정 속에서 한 번 더 정리되기 때문입니다.
제가 알게된 지식과 지나온 생각의 과정들을 공유하는 공간입니다. ^^</description>
    <language>ko</language>
    <pubDate>Tue, 23 Jun 2026 14:45:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>로그뉴</managingEditor>
    <image>
      <title>기록하는 습관</title>
      <url>https://tistory1.daumcdn.net/tistory/3079489/attach/ad1ce8b3fe56452398d330237a600e55</url>
      <link>https://flexiblecode.tistory.com</link>
    </image>
    <item>
      <title>[AI] 대화형 AI와 백엔드 통신 방법 (WebSocket vs. SSE)</title>
      <link>https://flexiblecode.tistory.com/242</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. WebSocket vs SSE 비교&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-4o&quot; data-message-id=&quot;eeea6618-2753-4d66-bf79-0db1510a5b62&quot; data-message-author-role=&quot;assistant&quot;&gt;특징WebSocketSSE (Server-Sent Events)
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;통신 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;양방향 (full-duplex)&lt;/td&gt;
&lt;td&gt;단방향 (server -&amp;gt; client)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;연결 유지&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;지속적인 연결 유지 (클라이언트와 서버가 모두 메시지 전송 가능)&lt;/td&gt;
&lt;td&gt;지속적인 연결 유지 (서버에서 클라이언트로만 데이터 전송 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;브라우저 지원&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;대부분의 최신 브라우저 지원&lt;/td&gt;
&lt;td&gt;대부분의 최신 브라우저 지원 (IE 지원 X)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;재연결&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;직접 구현 필요&lt;/td&gt;
&lt;td&gt;자동 재연결 지원 (EventSource API)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용 사례&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;채팅, 온라인 게임, 금융 거래&lt;/td&gt;
&lt;td&gt;실시간 알림, 데이터 스트리밍, 로그 업데이트&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 대화형 AI와의 통신에서 적합한 방식&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;WebSocket이 더 적합한 경우&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI가 &lt;b&gt;클라이언트의 입력을 실시간으로 받아야 하고&lt;/b&gt;, 결과를 즉시 반환해야 하는 경우 (예: 실시간 채팅 AI, 협업 문서 편집)&lt;/li&gt;
&lt;li&gt;AI가 &lt;b&gt;사용자의 중간 입력을 실시간 반영하여 동적으로 응답을 생성하는 경우&lt;/b&gt; (예: AI 코딩 도우미)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일반적인 챗봇&lt;/b&gt;처럼 &lt;b&gt;AI가 클라이언트의 요청을 받아 응답하는 패턴이 많을 경우&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SSE가 더 적합한 경우&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI가 &lt;b&gt;한 번의 요청에 대해 지속적으로 데이터를 스트리밍하는 경우&lt;/b&gt; (예: ChatGPT처럼 길게 이어지는 답변)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일방적인 데이터 전송이 주된 경우&lt;/b&gt; (예: 뉴스 업데이트, 실시간 알림)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;재연결이 중요한 경우&lt;/b&gt; (SSE는 기본적으로 자동 재연결 기능을 제공)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 대화형 AI 에서의 적용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 유저와의 대화를 기반으로 추천을 해주거나, 지속적인 이벤트 업데이트가 필요하다면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;WebSocket&lt;/b&gt;: 유저가 여러 가지 조건을 변경하면서 AI의 추천이 계속 바뀌는 경우 적합.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SSE&lt;/b&gt;: AI가 특정 조건에 맞는 추천을 한 번 생성한 후 지속적으로 스트리밍할 때 적합.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;실시간 대화 및 쌍방향 커뮤니케이션이 필요한 경우 &amp;rarr; WebSocket&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;AI 응답이 일방향 스트리밍(단방향 푸시) 형태라면 &amp;rarr; SSE&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 &lt;b&gt;실시간 대화형 추천 기능&lt;/b&gt;을 담당한다면 &lt;b&gt;WebSocket&lt;/b&gt;을, 추천 정보를 스트리밍하듯 보여준다면 &lt;b&gt;SSE&lt;/b&gt;를 사용하는 게 좋다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-4o&quot; data-message-id=&quot;8d196b68-38d3-46f8-b190-271a2246015f&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;내가 WebSocket 으로 선택한 이유&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;사용자가 AI 답변 중에 조건을 수정할 수 있음 &amp;rarr; 양방향 통신 필요&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSE는 &lt;b&gt;서버에서 클라이언트로만&lt;/b&gt; 데이터를 보낼 수 있어서, 사용자가 조건을 바꿀 때마다 새로운 HTTP 요청을 보내야 함.&lt;/li&gt;
&lt;li&gt;WebSocket을 쓰면 AI가 답변을 보내는 중에도 &lt;b&gt;사용자가 즉시 조건을 수정하고 다시 요청할 수 있음&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;즉, &lt;b&gt;연속적인 요청-응답 구조&lt;/b&gt;가 필요하므로 WebSocket이 적합!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AI가 실시간으로 업데이트하면서 보내야 할 수도 있음&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI가 &quot;추천을 한 번에 다 생성하는게 아니라, 단계별로 점진적으로 응답을 보내는 방식&quot;이라면?&lt;/li&gt;
&lt;li&gt;WebSocket을 쓰면 &lt;b&gt;한 번의 연결에서 스트리밍 방식으로 응답&lt;/b&gt;을 보낼 수 있음.&lt;/li&gt;
&lt;li&gt;반면 SSE는 &lt;b&gt;서버 &amp;rarr; 클라이언트 방향이므로, 사용자가 중간에 요청을 수정하려면 새로운 SSE 연결을 만들어야 함.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자 경험 (UX) 개선&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WebSocket을 쓰면,
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 조건을 변경하면 기존 응답을 &lt;b&gt;즉시 취소&lt;/b&gt;하고 새로운 플랜을 받을 수 있음.&lt;/li&gt;
&lt;li&gt;AI가 &lt;b&gt;실시간으로 상태 업데이트&lt;/b&gt;할 수 있어서,&lt;br /&gt;&quot;지금 추천 생성 중...  &quot; 같은 UI를 쉽게 만들 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;사용자가 AI 응답 중에도 조건을 수정할 수 있는 경우 &amp;rarr; WebSocket이 더 적합!&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;한 번 요청하면 AI가 일방적으로 최종 결과를 전달하는 경우 &amp;rarr; SSE 가능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  추천 아키텍처 예시 (WebSocket 사용)&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;사용자가 WebSocket 연결 생성&lt;/b&gt; (ws://server/ai-recommend)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자가 추천 요청&lt;/b&gt; ({&quot;action&quot;: &quot;request_plan&quot;, &quot;criteria&quot;: {...}})&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AI가 실시간으로 응답을 보냄&lt;/b&gt; ({&quot;status&quot;: &quot;processing&quot;, &quot;step&quot;: 1, &quot;data&quot;: {...}})&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자가 조건을 변경하면 즉시 새로운 요청을 전송&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버는 기존 추천을 취소하고 새로운 추천을 보내줌&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 &lt;b&gt;빠르게 응답을 주고받으면서 실시간 인터랙션이 가능한 AI 추천 시스템&lt;/b&gt;을 만들 수  있다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>개발</category>
      <author>로그뉴</author>
      <guid isPermaLink="true">https://flexiblecode.tistory.com/242</guid>
      <comments>https://flexiblecode.tistory.com/242#entry242comment</comments>
      <pubDate>Fri, 7 Feb 2025 18:00:47 +0900</pubDate>
    </item>
    <item>
      <title>[AI] WebSocket vs. WebSocket Stream</title>
      <link>https://flexiblecode.tistory.com/243</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  WebSocket Stream 방식이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebSocket Stream 방식은 &lt;b&gt;서버가 데이터를 한 번에 전송하는 게 아니라, 여러 번에 걸쳐 스트리밍(조각)으로 보내는 방식&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;WebSocket 연결을 유지하면서 데이터를 점진적으로 처리하고, 필요한 만큼 실시간으로 전송하는 패턴&lt;/b&gt;을 의미한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ 일반적인 WebSocket vs. WebSocket Stream&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방식설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;일반 WebSocket&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;한 번의 요청으로 한 번의 응답을 받음 (ex. 채팅 메시지)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;WebSocket Stream&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;서버가 데이터를 &lt;b&gt;조각&lt;/b&gt;으로 나눠서 지속적으로 전송 (ex. AI 응답, 실시간 데이터 처리)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;WebSocket Stream이 필요한 경우?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI가 &lt;b&gt;긴 답변&lt;/b&gt;을 한 번에 보내기 어렵고, &lt;b&gt;조금씩 전송&lt;/b&gt;해야 할 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실시간 데이터 업데이트&lt;/b&gt; (예: 주식 가격, 게임 상태, AI 답변)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스트리밍 기반 AI 응답&lt;/b&gt; (ex. ChatGPT API처럼 조금씩 출력)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✅ WebSocket Stream이 좋은 이유&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;실시간 업데이트 가능&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI가 응답을 다 만들 때까지 기다리지 않고, &lt;b&gt;중간 진행 상황도 받을 수 있음&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 응답 속도&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;긴 데이터(예: AI 생성 결과)를 한꺼번에 보내는 것보다, &lt;b&gt;조금씩 보내는 게 UX가 좋음&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자가 즉시 반응 가능&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI가 답변을 생성하는 동안에도, &lt;b&gt;FE에서 취소하거나 조건을 수정할 수 있음&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  최종 정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ &lt;b&gt;WebSocket Stream = 데이터를 한꺼번에 보내지 않고, 필요한 만큼 실시간으로 조각(스트림)으로 전송하는 방식&lt;/b&gt;&lt;br /&gt;✔ &lt;b&gt;AI 추천 플랜 같은 경우, 생성되는 대로 WebSocket으로 보내면 FE에서 실시간 업데이트 가능&lt;/b&gt;&lt;br /&gt;✔ &lt;b&gt;ChatGPT처럼 AI 응답이 천천히 나오는 경우에도 적합&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;즉, WebSocket Stream을 쓰면 AI 응답을 기다리지 않고, 빠르게 처리할 수 있음.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;** 통신 구조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;[FE]&lt;/span&gt; --- WebSocket Stream ---&amp;gt; &lt;span&gt;[BE]&lt;/span&gt; --- WebSocket Stream ---&amp;gt; &lt;span&gt;[AI 서버]&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <author>로그뉴</author>
      <guid isPermaLink="true">https://flexiblecode.tistory.com/243</guid>
      <comments>https://flexiblecode.tistory.com/243#entry243comment</comments>
      <pubDate>Fri, 7 Feb 2025 18:00:14 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] 예외 처리와 엔티티 비교</title>
      <link>https://flexiblecode.tistory.com/240</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 예외 처리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;jpa 표준 예외는 javax.persistence.PersistenceException의 자식 클래스이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;jpa 예외는 &lt;u&gt;모두 unchecked 예외&lt;/u&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;** unchecked 예외란?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;언체크 예외는 RuntimeException의 하위 클래스들을 의미. 체크 예외와는 달리 에러 처리를 강제하지 않는다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;말 그대로&amp;nbsp;실행 중에(runtime)&amp;nbsp;발생할 수 있는 예외를 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;배열의 범위를 벗어난(ArrayIndexOutOfBoundsException)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;값이 null이 참조변수를 참조(NullPointerException)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1503&quot; data-origin-height=&quot;925&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q0re7/btsDSsyg5nZ/5ynkpvAfZRLLswaMkmwQs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q0re7/btsDSsyg5nZ/5ynkpvAfZRLLswaMkmwQs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q0re7/btsDSsyg5nZ/5ynkpvAfZRLLswaMkmwQs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq0re7%2FbtsDSsyg5nZ%2F5ynkpvAfZRLLswaMkmwQs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;925&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1503&quot; data-origin-height=&quot;925&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;JPA 표준 예외 2가지&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 트랜잭션 롤백을 표시하는 예외&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;트랜잭션 강제로 커밋해도 커밋되지 않고, javax.persistence.RollbackException&amp;nbsp;예외 발생&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 234px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 37.093023%;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;트랜잭션 롤백을 표시하는 예외&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 62.790698%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px; width: 37.093023%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.EntityExistsException&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 36px; width: 62.790698%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;EntityManaget.persist(..)&amp;nbsp;호출 시 이미 같은 엔티티가 있으면 발생&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px; width: 37.093023%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.EntityNotFoundException&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 36px; width: 62.790698%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;EntityManager.getReference(..)를 호출했는데 실제 사용 시 엔티티가 존재하지 않으면 발생.&amp;nbsp;refresh(..),&amp;nbsp;lock(..)에서도 발생&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px; width: 37.093023%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.OptimisticLockException&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 36px; width: 62.790698%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;낙관적 락 충돌 시 발생&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px; width: 37.093023%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.PessimisticLockException&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 36px; width: 62.790698%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비관적 락 충돌 시 발생&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 37.093023%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.RollbackException&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 62.790698%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;EntityTransaction.commit()&amp;nbsp;실패 시 발생. 롤백이 표시되어 있는 트랜잭션 커밋 시에도 발생&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px; width: 37.093023%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.TransactionRequiredException&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 36px; width: 62.790698%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;트랜잭션이 필요할 때 트랜잭션 없으면 발생. 트랜잭션 없이 엔티티 변경할 때 주로 발생&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. &lt;span style=&quot;text-align: start;&quot;&gt;트랜잭션 롤백을 표시하지 않는 예외&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;심각한 예외가 아니므로 개발자가 트랜잭션 커밋 롤백할지 판단&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;트랜잭션 롤백을 표시하는 예외&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.NoResultException&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Query.getSingleResult()&amp;nbsp;호출 시 결과가 하나도 없을 때 발생&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.NonUniqueResultException&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Query.getSingleResult()&amp;nbsp;호출 시 겨로가가 둘 이상일 때 발생&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.LockTimeoutException&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비관적 락에서 시간 초과 시 발생&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.QueryTimeoutException&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;쿼리 실행 시간 초과 시 발생&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 스프링 프레임워크의 JPA 예외 반환&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스프링 프레임워크는 데이터 접근 계층에 대한 예외를 추상화해서 제공&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;color: #ececec; text-align: start; border-collapse: collapse; width: 100%; height: 108px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;JPA 예외&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;스프링 변환 예외&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.PersistenceException&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;org.springframework.orm.jpa.JpaSystemException&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.NoResultException&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;org.springframework.dao.Ex=mptyResultDataAccessException&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.NonUniqueResultException&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;org.springframework.dao.IncorrectResultSizeDataAccessException&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javax.persistence.LockTimeoutException&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;org.springframework.CannotAcquireLockException&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;스프링-프레임워크에-jpa-예외-변환기-적용&quot; style=&quot;color: #4a4a4a; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 스프링 프레임워크에 JPA 예외 변환기 적용&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #5d686f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JPA 예외를 스프링 프레임워크가 제공하는 추상화된 예외로 변경하려면&amp;nbsp;PersistenceExceptionTranslationPostProcessor를 스프링 빈으로 등록한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1706157719005&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
    return new PersistenceExceptionTranslationPostProcessor();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;트랜잭션-롤백-시-주의사항&quot; style=&quot;color: #4a4a4a; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;트랜잭션 롤백 시 주의사항&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #5d686f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터베이스의 반영사항만 롤백하였을 경우 영속성 컨택스트 내부 객체는 그대로 남아다. 따라서 트랜잭션이 롤백된 영속성 컨텍스트를 그대로 사용할 때 조심해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #5d686f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;트랜잭션당 영속성 컨택스트 전략&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문제생겼을 때 트랜잭션 AOP 종료시점 트랜잭션 롤백하면서 영속성 컨택스트 함께 종료하므로 문제가 없다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;OSIV 사용 전략(문제됨)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;영속성 컨택스트 범위 &amp;gt; 트랜잭션 범위일 경우 문제가 발생한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;롤백하고 남아있는 영속성 컨택스트에 이상이 발생해도 다른 컨택스트에서 그대로 사용할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해결방법 &amp;rarr;&amp;nbsp;em.clear()&amp;nbsp;영속성 컨텍스트 초기화해 예방한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;엔티티-비교&quot; style=&quot;color: #4a4a4a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 엔티티 비교&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;&lt;b&gt;1) 영속성 컨텍스트가 같을 때 엔티티 비교&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #5d686f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비교 방법 3가지&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동일성(identical): == 비교가 같다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동등성(equivalent):&amp;nbsp;equals()&amp;nbsp;비교가 같다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터베이스 동등성:&amp;nbsp;@Id인 데이터베이스 식별자가 같다.&amp;nbsp;member.getld().equals(findMember.getld())&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccHQVT/btsDUcouXJe/nU0xvh24O0Jd8rz20QaJS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccHQVT/btsDUcouXJe/nU0xvh24O0Jd8rz20QaJS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccHQVT/btsDUcouXJe/nU0xvh24O0Jd8rz20QaJS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccHQVT%2FbtsDUcouXJe%2FnU0xvh24O0Jd8rz20QaJS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;626&quot; height=&quot;330&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 영속성 컨텍스트가 다를 때 엔티티 비교&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tyg4z/btsDWsK73FL/dSCHYRz5QExG2KfkKSNxO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tyg4z/btsDWsK73FL/dSCHYRz5QExG2KfkKSNxO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tyg4z/btsDWsK73FL/dSCHYRz5QExG2KfkKSNxO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftyg4z%2FbtsDWsK73FL%2FdSCHYRz5QExG2KfkKSNxO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;353&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스에만&amp;nbsp;@Transactional이 있는 경우의 트랜잭션 범위와 영속성 컨텍스트의 범위&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;영속성 컨텍스트가 다르면 동일성 비교에 실패&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동일성: == 비교가 실패&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동등성:&amp;nbsp;equals()&amp;nbsp;비교가 만족. 단&amp;nbsp;equals()&amp;nbsp;구현해야 함&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비즈니스 키를 활용한 동등성 비교 권장(예: 주민등록번호)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터베이스 동등성:&amp;nbsp;@Id인 데이터베이스 식별자 같음&lt;/span&gt;
&lt;pre class=&quot;css&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;member.getId().equals(findMember.getId())&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>스터디/자바 ORM 표준 JPA 프로그래밍</category>
      <author>로그뉴</author>
      <guid isPermaLink="true">https://flexiblecode.tistory.com/240</guid>
      <comments>https://flexiblecode.tistory.com/240#entry240comment</comments>
      <pubDate>Thu, 25 Jan 2024 13:21:55 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] 스프링 데이터 JPA (2)</title>
      <link>https://flexiblecode.tistory.com/239</link>
      <description>&lt;h2 id=&quot;JPAprogramming-12JPA-JPAQueryDSL&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스프링 데이터 JPA와 QueryDSL 통합&lt;/span&gt;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. QueryDslPredicateExecutor 사용&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;첫 번째 방법은 다음처럼 리포지토리에서 QueryDslPredicateExecutor를 상속받으면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이제&amp;nbsp;상품&amp;nbsp;리포지토리에서&amp;nbsp;QueryDSL을&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;아래&amp;nbsp;예제는&amp;nbsp;QueryDSL이&amp;nbsp;생성한&amp;nbsp;쿼리&amp;nbsp;타입으로&amp;nbsp;장난감이라는&amp;nbsp;이름을&amp;nbsp;포함하고&amp;nbsp;있으면서&amp;nbsp;가격이&amp;nbsp;10000~20000원인&amp;nbsp;상품을&amp;nbsp;검색한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;public interface ItemRepository extends JpaRepository&amp;lt;Item, Long&amp;gt;, QueryDslPredicateExecutor&amp;lt;Item&amp;gt; {}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;applescript&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;QItem item = QItem.item;
Iterable&amp;lt;Item&amp;gt; result = itemRepository.findAll(
	item.name.contains(&quot;장난감&quot;).and(item.price.between(10000, 20000))
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예제의&amp;nbsp;QueryDslPredicateExecutor&amp;nbsp;인터페이스를&amp;nbsp;보면&amp;nbsp;QueryDSL을&amp;nbsp;검색조건으로&amp;nbsp;사용하면서&amp;nbsp;스프링&amp;nbsp;데이터&amp;nbsp;JPA가&amp;nbsp;제공하는&amp;nbsp;페이징과&amp;nbsp;정렬&amp;nbsp;기능도&amp;nbsp;함께&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;public interface QueryDslPredicateExecutor&amp;lt;T&amp;gt; {
	T findOne(Predicate predicate);
    Iterable&amp;lt;T&amp;gt; findAll(Predicate predicate);
    Iterable&amp;lt;T&amp;gt; findAll(Predicate predicate, OrderSpecifier&amp;lt;?&amp;gt;... orders);
    Page&amp;lt;T&amp;gt; findAll(Predicate predicate, Pageable pageable);
    long count(Predicate predicate);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;지금까지&amp;nbsp;살펴본&amp;nbsp;QueryDslPredicateExecutor는&amp;nbsp;스프링&amp;nbsp;데이터&amp;nbsp;JPA에서&amp;nbsp;편리하게&amp;nbsp;QueryDSL을&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있지만&amp;nbsp;기능에&amp;nbsp;한계가&amp;nbsp;있다.&amp;nbsp;예를&amp;nbsp;들어&amp;nbsp;join,&amp;nbsp;fetch를&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;없다.&amp;nbsp;따라서&amp;nbsp;QueryDSL이&amp;nbsp;제공하는&amp;nbsp;다양한&amp;nbsp;기능을&amp;nbsp;사용하려면&amp;nbsp;JPAQuery를&amp;nbsp;직접&amp;nbsp;사용하거나&amp;nbsp;스프링&amp;nbsp;데이터&amp;nbsp;JPA가&amp;nbsp;제공하는&amp;nbsp;QueryDslRepositorySupport를&amp;nbsp;사용해야&amp;nbsp;한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. &lt;b&gt;QueryDslRepositorySupport &lt;/b&gt;사용&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;QueryDSL의&amp;nbsp;모든&amp;nbsp;기능을&amp;nbsp;사용하려면&amp;nbsp;JPAQuery&amp;nbsp;객체를&amp;nbsp;직접&amp;nbsp;생성해서&amp;nbsp;사용하면&amp;nbsp;된다.&amp;nbsp;이때&amp;nbsp;스프링&amp;nbsp;데이터&amp;nbsp;JPA가&amp;nbsp;제공하는&amp;nbsp;QueryDslRepositorySupport를&amp;nbsp;상속받아&amp;nbsp;사용하면&amp;nbsp;조금&amp;nbsp;더&amp;nbsp;편리하게&amp;nbsp;QueryDSL을&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;public interface CustomOrderRepository {
	public List&amp;lt;Order&amp;gt; search(OrderSearch orderSearch);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #121212; color: #ececec; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;public class OrderRepositoryImpl extends QueryDslRepositorySupport implements CustomOrderRepository {
	public OrderRepositoryImpl() {
    	super(Order.class);
    }
    
    @Override
    public List&amp;lt;Order&amp;gt; search(OrderSearch orderSearch) {
    	QOrder order = QOrder.order;
        QMember member = QMember.member;
        
        JPQLQuery query = from(order);
        
        if(StringUtils.hasText(orderSearch.getMemberName())) {	
        	query.leftJoin(order.member, member).where(member.name.contains(orderSearch.getMemberName()));
        }
        
        if(orderSearch.getOrderStatus() != null) {
        	query.where(order.status.eq(orderSerach.getOrderStatus()));
        }
        
        return query.list(order);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. QuerydslReositorySupport 인터페이스&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. Querydsl 3.x 버전을 대상&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2.&amp;nbsp;Querydsl&amp;nbsp;4.x에&amp;nbsp;나온&amp;nbsp;JPAQueryFactory로&amp;nbsp;시작할&amp;nbsp;수&amp;nbsp;없음&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3.&amp;nbsp;select로&amp;nbsp;시작할&amp;nbsp;수&amp;nbsp;없음&amp;nbsp;(from으로&amp;nbsp;시작해야함)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4.&amp;nbsp;QueryFactory&amp;nbsp;를&amp;nbsp;제공하지&amp;nbsp;않음&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. 스프링 데이터 Sort 기능이 정상 동작하지 않음. 큐 소트를 사용하면 되긴하지만 정렬을 직접해줘야 함&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스터디/자바 ORM 표준 JPA 프로그래밍</category>
      <author>로그뉴</author>
      <guid isPermaLink="true">https://flexiblecode.tistory.com/239</guid>
      <comments>https://flexiblecode.tistory.com/239#entry239comment</comments>
      <pubDate>Thu, 14 Dec 2023 17:00:58 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] 스프링 데이터 JPA</title>
      <link>https://flexiblecode.tistory.com/238</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;쿼리 메소드 기능&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yZ7Vb/btsBzxHdYD9/kzUReRNVlUSzVNsxKnU5a0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yZ7Vb/btsBzxHdYD9/kzUReRNVlUSzVNsxKnU5a0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yZ7Vb/btsBzxHdYD9/kzUReRNVlUSzVNsxKnU5a0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyZ7Vb%2FbtsBzxHdYD9%2FkzUReRNVlUSzVNsxKnU5a0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;674&quot; height=&quot;662&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 메소드 이름으로 쿼리 생성&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1701928015844&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface MemberRepository extends Repository&amp;lt;Member, Long&amp;gt; {
    List&amp;lt;Member&amp;gt; findByEmailAndName(String email, String name);
}

// 실행되는 JPQL
select m from Member m where m.email = ?1 and m.name = ?2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**&lt;u&gt; 엔티티의 필드명이 변경되면 인터페이스에 정의한 메소드 이름도 꼭 함께 변경해야 한다.&lt;/u&gt; 그렇지 않으면 애플리케이션 시작하는 시점에 오류가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. JPA NamedQuery&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1701928116224&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 어노테이션 방식
@Entity
@NamedQuery(
    name=&quot;Member.findByUsername&quot;,
    query=&quot;select m from Member m where m.username = :username&quot;)
public class Member {
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 데이터 jpa를 사용하면 메소드 이름만으로 Named 쿼리를 호출할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1701928244309&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface MemberRepository 
    extends JpaRepository&amp;lt;Member, Long&amp;gt; { // 여기 선언한 Member 도메인 클래스

    List&amp;lt;Member&amp;gt; findByUsername(@Param(&quot;username&quot;) String username);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 데이터 JPA는 선언한 &quot;도메인 클래스 + .(점) + 메소드 이름&quot;으로 Named 쿼리를 찾아서 실행한다. 위의 예제의 경우, Member.findByUsername이라는 Named 쿼리를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. @Query, 리포지토리 메소드에 쿼리 정의&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리포지토리 메소드에 직접 쿼리를 정의하는 방법.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행할 메소드에 정적 쿼리를 직접 작성하므로 이름 없는 Named 쿼리라 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- JPA Named 애플리케이션 실행 시점에 문법 오류를 발견할 수 있는 것이 장점이다.&lt;/p&gt;
&lt;pre id=&quot;code_1701928375506&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface MemberRepository extends JpaRepository&amp;lt;Member, Long&amp;gt; {
    @Query(&quot;select m from Member m where m.username = ?1&quot;)
    Member findByUsername (String username);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native SQL을 사용하려면 @Query 어노테이션에 nativeQuery = true를 설정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1701928383835&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface MemberRepository extends JpaRepository&amp;lt;Member, Long&amp;gt; {
    @Query(value = &quot;SELECT * FROM MEMBER WHERE USERNAME = ?0&amp;rdquo;,
        nativeQuery = true)
    Member findByUsername (String username);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 파라미터 바인딩&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 데이터 JPA는 위치 기반 파라미터 바인딩과 이름 기반 파라미터 바인딩을 모두 지원한다.&lt;/p&gt;
&lt;pre id=&quot;code_1701928642286&quot; class=&quot;sql&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;select m from Member m where m.username = ?1 //위치 기반
select m from Member m where m.username = :name //이름기반&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 벌크성 수정 쿼리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-offset-key=&quot;56e4a46cf3e5493fb941ab3950d75ff1:0&quot;&gt;- 스프링 데이터&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;JPA&lt;span style=&quot;color: #000000; text-align: start;&quot; data-offset-key=&quot;56e4a46cf3e5493fb941ab3950d75ff1:2&quot;&gt;에서 벌크성 수정, 삭제 쿼리는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Modifying&lt;span style=&quot;color: #000000; text-align: start;&quot; data-offset-key=&quot;56e4a46cf3e5493fb941ab3950d75ff1:4&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;어노테이션을 사용하면 된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 벌크성 쿼리&lt;span style=&quot;color: #000000; text-align: start;&quot; data-offset-key=&quot;56e4a46cf3e5493fb941ab3950d75ff1:6&quot;&gt;를 실행하고 나서 영속성 컨텍스트를 초기화하고 싶으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@Modifying(clearAutomatically = true)&lt;span style=&quot;color: #000000; text-align: start;&quot; data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQyU4QSVBNCVFRCU5NCU4NCVFQiVBNyU4MSUyMCVFQiU4RCVCMCVFQyU5RCVCNCVFRCU4NCVCMCUyMCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIySlBBJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJtYXJrJTIyJTJDJTIydHlwZSUyMiUzQSUyMmNvZGUlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTdEJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJUVDJTk3JTkwJUVDJTg0JTlDJTIwJUVCJUIyJThDJUVEJTgxJUFDJUVDJTg0JUIxJTIwJUVDJTg4JTk4JUVDJUEwJTk1JTJDJTIwJUVDJTgyJUFEJUVDJUEwJTlDJTIwJUVDJUJGJUJDJUVCJUE2JUFDJUVCJThBJTk0JTIwJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjJNb2RpZnlpbmclMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlMjAlRUMlOTYlQjQlRUIlODUlQjglRUQlODUlOEMlRUMlOUQlQjQlRUMlODUlOTglRUMlOUQlODQlMjAlRUMlODIlQUMlRUMlOUElQTklRUQlOTUlOTglRUIlQTklQjQlMjAlRUIlOTAlOUMlRUIlOEIlQTQuJTIwJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUIlQjIlOEMlRUQlODElQUMlRUMlODQlQjElMjAlRUMlQkYlQkMlRUIlQTYlQUMlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUIlQTUlQkMlMjAlRUMlOEIlQTQlRUQlOTYlODklRUQlOTUlOTglRUElQjMlQTAlMjAlRUIlODIlOTglRUMlODQlOUMlMjAlRUMlOTglODElRUMlODYlOEQlRUMlODQlQjElMjAlRUMlQkIlQTglRUQlODUlOEQlRUMlOEElQTQlRUQlOEElQjglRUIlQTUlQkMlMjAlRUMlQjQlODglRUElQjglQjAlRUQlOTklOTQlRUQlOTUlOTglRUElQjMlQTAlMjAlRUMlOEIlQjYlRUMlOUMlQkMlRUIlQTklQjQlMjAlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiU0ME1vZGlmeWluZyhjbGVhckF1dG9tYXRpY2FsbHklMjAlM0QlMjB0cnVlKSUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb2RlJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQiVBNSVCQyUyMCVFQyU4MiVBQyVFQyU5QSVBOSVFRCU5NSU5OCVFQiVBOSVCNCUyMCVFQiU5MCU5QyVFQiU4QiVBNC4lMjAlRUElQjglQjAlRUIlQjMlQjglRUElQjAlOTIlRUMlOUQlODAlMjBmYWxzZSVFQyU5RCVCNCVFQiU4QiVBNC4lMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjI5OGRjNzUzMDJmMjU0ZTc0YTIyYjRiOWQwYWEyMDMzMyUyMiU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMjliMDc1M2E0NGFjYzRhMjVhMWViYWExY2YxMWY0OGI2JTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyOWE5YWQ2Zjk5OGQ5NGI2MjgxMzI2Mjg5ZDJiNDhjMGElMjIlN0Q=&quot; data-offset-key=&quot;56e4a46cf3e5493fb941ab3950d75ff1:8&quot;&gt;를 사용하면 된다. (&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQyU4QSVBNCVFRCU5NCU4NCVFQiVBNyU4MSUyMCVFQiU4RCVCMCVFQyU5RCVCNCVFRCU4NCVCMCUyMCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIySlBBJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJtYXJrJTIyJTJDJTIydHlwZSUyMiUzQSUyMmNvZGUlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTdEJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJUVDJTk3JTkwJUVDJTg0JTlDJTIwJUVCJUIyJThDJUVEJTgxJUFDJUVDJTg0JUIxJTIwJUVDJTg4JTk4JUVDJUEwJTk1JTJDJTIwJUVDJTgyJUFEJUVDJUEwJTlDJTIwJUVDJUJGJUJDJUVCJUE2JUFDJUVCJThBJTk0JTIwJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjJNb2RpZnlpbmclMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlMjAlRUMlOTYlQjQlRUIlODUlQjglRUQlODUlOEMlRUMlOUQlQjQlRUMlODUlOTglRUMlOUQlODQlMjAlRUMlODIlQUMlRUMlOUElQTklRUQlOTUlOTglRUIlQTklQjQlMjAlRUIlOTAlOUMlRUIlOEIlQTQuJTIwJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUIlQjIlOEMlRUQlODElQUMlRUMlODQlQjElMjAlRUMlQkYlQkMlRUIlQTYlQUMlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUIlQTUlQkMlMjAlRUMlOEIlQTQlRUQlOTYlODklRUQlOTUlOTglRUElQjMlQTAlMjAlRUIlODIlOTglRUMlODQlOUMlMjAlRUMlOTglODElRUMlODYlOEQlRUMlODQlQjElMjAlRUMlQkIlQTglRUQlODUlOEQlRUMlOEElQTQlRUQlOEElQjglRUIlQTUlQkMlMjAlRUMlQjQlODglRUElQjglQjAlRUQlOTklOTQlRUQlOTUlOTglRUElQjMlQTAlMjAlRUMlOEIlQjYlRUMlOUMlQkMlRUIlQTklQjQlMjAlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiU0ME1vZGlmeWluZyhjbGVhckF1dG9tYXRpY2FsbHklMjAlM0QlMjB0cnVlKSUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb2RlJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQiVBNSVCQyUyMCVFQyU4MiVBQyVFQyU5QSVBOSVFRCU5NSU5OCVFQiVBOSVCNCUyMCVFQiU5MCU5QyVFQiU4QiVBNC4lMjAlRUElQjglQjAlRUIlQjMlQjglRUElQjAlOTIlRUMlOUQlODAlMjBmYWxzZSVFQyU5RCVCNCVFQiU4QiVBNC4lMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjI5OGRjNzUzMDJmMjU0ZTc0YTIyYjRiOWQwYWEyMDMzMyUyMiU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMjliMDc1M2E0NGFjYzRhMjVhMWViYWExY2YxMWY0OGI2JTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyOWE5YWQ2Zjk5OGQ5NGI2MjgxMzI2Mjg5ZDJiNDhjMGElMjIlN0Q=&quot; data-offset-key=&quot;56e4a46cf3e5493fb941ab3950d75ff1:8&quot;&gt;기본값은 false)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701928578480&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Modifying
@Query(&quot;update Product p set p.price = p.price * 1.1 where
    p.stockAmount &amp;lt; :stockAmount&quot;)
int bulkPriceUp(@Param(&quot;stockAmount&quot;) String stockAmount);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;6. 반환 타입&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 한 건 이상이면 컬렉션 인터페이스 사용, 단건이면 반환 타입을 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 조회 결과가 없으면 컬렉션은 빈 컬렉션을 반환하고, 단건은 null을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 단건을 기대하고 반환 타입을 지정했는데 결과가 2건 이상 조회되면 NonUniqueResultException 예외가 발생한다.&lt;/p&gt;
&lt;pre id=&quot;code_1701928638022&quot; class=&quot;java&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;List&amp;lt;Member&amp;gt; findByName (String name); // 컬렉션
Member findByEmail (String email); // 단건&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;7. 페이징과 정렬&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Page를 사용하면 스프링 데이터 JPA는 페이징 기능을 제공하기 위해 검색된 데이터 건수를 조회하는 count 쿼리를 추가로 호출한다.&lt;/p&gt;
&lt;pre id=&quot;code_1701928797930&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//count 쿼리 사용
Page&amp;lt;Member&amp;gt; findByName(String name, Pageable pageable);

//count 쿼리 사용 안 함
List&amp;lt;Member&amp;gt; findByName(String name, Pageable pageable);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701928807503&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//페이징 조건과 정렬 조건 설정
PageRequest pageRequest =
    new PageRequest(0, 10, new Sort(Direction.DESC, &quot;name&quot;));

Page&amp;lt;Member&amp;gt; result =
    memberRepository.findByNameStartingWith(&quot;김&quot;, pageRequest);

List&amp;lt;Member&amp;gt; members = result.getContent (); //조회된 데이터
int totalPages = result.getTotalPages (); //전체 페이지 수
boolean hasNextPage = result .hasNextPage (); //다음 페이지 존재 여부&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고) Page 인터페이스&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701928997966&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface Page&amp;lt;T&amp;gt; extends Iterable&amp;lt;T&amp;gt; {

    int getNumber(); //현재 페이지
    int getSize(); //페이지 크기
    int getTotalPages(); //전체 페이지 수
    int getNumberOfElements(); //현재 페이지에 나올 데이터 수
    long getTotalElements(); //전체 데이터 수
    boolean hasPreviousPage(); //이전 페이지 여부
    boolean isFirstPage(); //현재 페이지가 첫 페이지 인지 여부
    boolean hasNextPage(); //다음 페이지 여부
    boolean isLastPage(); //현재 페이지가 마지막 페이지 인지 여부
    Pageable nextPageable(); //다음 페이지 객체, 다음 페이지가 없으면 null
    Pageable previousPageable(); //다음 페이지 객체, 이전 페이지가 없으면 null
    List&amp;lt;T&amp;gt; getContent(); //조회된 데이터
    boolean hasContent(); //조회된 데이터 존재 여부
    Sort getSort(); //정렬정보

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;명세&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Specification&lt;span style=&quot;color: #000000; text-align: start;&quot; data-offset-key=&quot;36f99bcdd65d48338316af5bb0736809:10&quot;&gt;은 컴포지트 패턴으로 구성되어 있어서 여러&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Specification&lt;span style=&quot;color: #000000; text-align: start;&quot; data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMlNwZWNpZmljYXRpb24lMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUMlOUQlODAlMjAlRUMlQkIlQjQlRUQlOEYlQUMlRUMlQTclODAlRUQlOEElQjglMjAlRUQlOEMlQTglRUQlODQlQjQlRUMlOUMlQkMlRUIlQTElOUMlMjAlRUElQjUlQUMlRUMlODQlQjElRUIlOTAlOTglRUMlOTYlQjQlMjAlRUMlOUUlODglRUMlOTYlQjQlRUMlODQlOUMlMjAlRUMlOTclQUMlRUIlOUYlQUMlMjAlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMlNwZWNpZmljYXRpb24lMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUMlOUQlODQlMjAlRUMlQTElQjAlRUQlOTUlQTklRUQlOTUlQTAlMjAlRUMlODglOTglMjAlRUMlOUUlODglRUIlOEIlQTQuJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyODAwZWRjYTRjYWY0NDQ0YWFkYTVmY2FjOTEzNjk0M2MlMjIlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjIwMmFiZTI3OTkwNzc0MmUzYmFiNDBlZDM4ZWY3N2JiMCUyMiU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMjljYjU1MmI3YzA3NTQ2OGI5OWU3NTJlMWYyMjE3ZTI0JTIyJTdE&quot; data-offset-key=&quot;36f99bcdd65d48338316af5bb0736809:12&quot;&gt;을 조합할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-rnwr700-1777fci=&quot;true&quot; data-rnwr1490-1777fci=&quot;true&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-key=&quot;03284600be3f4b45b5f0f005055e8fff&quot;&gt;
&lt;div data-block-content=&quot;03284600be3f4b45b5f0f005055e8fff&quot;&gt;
&lt;p id=&quot;text-undefined-6&quot; data-rnwr700-gg6oyi-ubezar-135wba7-1kfrs79=&quot;true&quot; data-rnwrdesktop-gg6oyi-adyw6z-135wba7-b88u0q=&quot;true&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-key=&quot;2154367ec7a5440a8df33fc61b92b134&quot;&gt;&lt;span data-offset-key=&quot;2154367ec7a5440a8df33fc61b92b134:0&quot;&gt;사용자 정의 리포지터리 구현&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot; data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJoZWFkaW5nLTIlMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQyU4MiVBQyVFQyU5QSVBOSVFQyU5RSU5MCUyMCVFQyVBMCU5NSVFQyU5RCU5OCUyMCVFQiVBNiVBQyVFRCU4RiVBQyVFQyVBNyU4MCVFRCU4NCVCMCVFQiVBNiVBQyUyMCVFQSVCNSVBQyVFRCU5OCU4NCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMjNjOGE5ZGExZTEwMzRhMGM4MTFhNjBhMTUyZjRjNWY4JTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyMTEzYzM3YWRmZTlmNGViMTk1MzlhYzdhODIyMmQ4ZTglMjIlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQyU4QSVBNCVFRCU5NCU4NCVFQiVBNyU4MSUyMCVFQiU4RCVCMCVFQyU5RCVCNCVFRCU4NCVCMCUyMCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIySlBBJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJtYXJrJTIyJTJDJTIydHlwZSUyMiUzQSUyMmNvZGUlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTdEJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJUVCJUExJTlDJTIwJUVCJUE2JUFDJUVEJThGJUFDJUVDJUE3JTgwJUVEJTg0JUIwJUVCJUE2JUFDJUVCJUE1JUJDJTIwJUVBJUIwJTlDJUVCJUIwJTlDJUVEJTk1JTk4JUVCJUE5JUI0JTIwJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUMlOUQlQjglRUQlODQlQjAlRUQlOEUlOTglRUMlOUQlQjQlRUMlOEElQTQlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUIlQTclOEMlMjAlRUMlQTAlOTUlRUMlOUQlOTglRUQlOTUlOTglRUElQjMlQTAlMjAlRUElQjUlQUMlRUQlOTglODQlRUMlQjIlQjQlRUIlOEElOTQlMjAlRUIlQTclOEMlRUIlOTMlQTQlRUMlQTclODAlMjAlRUMlOTUlOEElRUIlOEElOTQlRUIlOEIlQTQuJTIwJUVEJTk1JTk4JUVDJUE3JTgwJUVCJUE3JThDJTIwJUVCJThCJUE0JUVDJTk2JTkxJUVEJTk1JTlDJTIwJUVDJTlEJUI0JUVDJTlDJUEwJUVCJUExJTlDJTIwJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUIlQTklOTQlRUMlODYlOEMlRUIlOTMlOUMlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUIlQTUlQkMlMjAlRUMlQTclODElRUMlQTAlOTElMjAlRUElQjUlQUMlRUQlOTglODQlRUQlOTUlQjQlRUMlOTUlQkMlMjAlRUQlOTUlQTAlMjAlRUIlOTUlOEMlRUIlOEYlODQlMjAlRUMlOUUlODglRUIlOEIlQTQuJTIwJUVBJUI3JUI4JUVCJUEwJTg3JUVCJThCJUE0JUVBJUIzJUEwJTIwJUVCJUE2JUFDJUVEJThGJUFDJUVDJUE3JTgwJUVEJTg0JUIwJUVCJUE2JUFDJUVCJUE1JUJDJTIwJUVDJUE3JTgxJUVDJUEwJTkxJTIwJUVBJUI1JUFDJUVEJTk4JTg0JUVEJTk1JTk4JUVCJUE5JUI0JTIwJUVBJUIzJUI1JUVEJTg2JUI1JTIwJUVDJTlEJUI4JUVEJTg0JUIwJUVEJThFJTk4JUVDJTlEJUI0JUVDJThBJUE0JUVBJUIwJTgwJTIwJUVDJUEwJTlDJUVBJUIzJUI1JUVEJTk1JTk4JUVCJThBJTk0JTIwJUVBJUI4JUIwJUVCJThBJUE1JUVBJUI5JThDJUVDJUE3JTgwJTIwJUVCJUFBJUE4JUVCJTkxJTkwJTIwJUVBJUI1JUFDJUVEJTk4JTg0JUVEJTk1JUI0JUVDJTk1JUJDJTIwJUVEJTk1JTlDJUVCJThCJUE0LiUyMCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJUVDJThBJUE0JUVEJTk0JTg0JUVCJUE3JTgxJTIwJUVCJThEJUIwJUVDJTlEJUI0JUVEJTg0JUIwJTIwSlBBJUVCJThBJTk0JTIwJUVDJTlEJUI0JUVCJTlGJUIwJTIwJUVCJUFDJUI4JUVDJUEwJTlDJUVCJUE1JUJDJTIwJUVDJTlBJUIwJUVEJTlBJThDJUVEJTk1JUI0JUVDJTg0JTlDJTIwJUVEJTk1JTg0JUVDJTlBJTk0JUVEJTk1JTlDJTIwJUVCJUE5JTk0JUVDJTg2JThDJUVCJTkzJTlDJUVCJUE3JThDJTIwJUVBJUI1JUFDJUVEJTk4JTg0JUVEJTk1JUEwJTIwJUVDJTg4JTk4JTIwJUVDJTlFJTg4JUVCJThBJTk0JTIwJUVCJUIwJUE5JUVCJUIyJTk1JUVDJTlEJTg0JTIwJUVDJUEwJTlDJUVBJUIzJUI1JUVEJTk1JTlDJUVCJThCJUE0LiUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJib2xkJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjIzY2M0NjI0OTJlMDI0MzllYjI3NjZhNWU3OTBlY2YzOCUyMiU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMjQ4NjdiZjc5MDc5NjQ2NTZhNDg4YTkyZWRmOWY1ZDRlJTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyY2M1ODYxMTFjOTJhNDNjOWE2M2ZjZjVlM2Y1ZmJhYWUlMjIlN0Q=&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-rnwr700-1777fci=&quot;true&quot; data-rnwr1490-1777fci=&quot;true&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-key=&quot;4867bf7907964656a488a92edf9f5d4e&quot;&gt;
&lt;div data-block-content=&quot;4867bf7907964656a488a92edf9f5d4e&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-key=&quot;3cc462492e02439eb2766a5e790ecf38&quot;&gt;&lt;span data-offset-key=&quot;3cc462492e02439eb2766a5e790ecf38:0&quot;&gt;- 스프링 데이터&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;JPA&lt;span data-offset-key=&quot;3cc462492e02439eb2766a5e790ecf38:2&quot;&gt;로 리포지터리를 개발하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;인터페이스&lt;span data-offset-key=&quot;3cc462492e02439eb2766a5e790ecf38:4&quot;&gt;만 정의하고 구현체는 만들지 않는다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-key=&quot;3cc462492e02439eb2766a5e790ecf38&quot;&gt;&lt;span data-offset-key=&quot;3cc462492e02439eb2766a5e790ecf38:4&quot;&gt;- &lt;/span&gt;메소드&lt;span data-offset-key=&quot;3cc462492e02439eb2766a5e790ecf38:6&quot;&gt;를 직접 구현하고 싶다면? -&amp;gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span data-key=&quot;3cc462492e02439eb2766a5e790ecf38&quot;&gt;&lt;span data-offset-key=&quot;3cc462492e02439eb2766a5e790ecf38:6&quot;&gt;리포지터리를 직접 구현하면 공통 인터페이스가 제공하는 기능까지 모두 구현해야 하므로,&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;u&gt;&lt;b&gt;스프링 데이터 JPA는 이런 문제를 우회해서 필요한 메소드만 구현할 수 있는 방법을 제공한다.&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701929121899&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface MemberRepositoryCustom {
    public List&amp;lt;Member&amp;gt; findMemberCustom();
}

// 클래스 이름을 짓는 규칙: (repository interface 이름 + Impl)
public class MemberRepositorylmpl implements MemberRepositoryCustom {
    @Override
    public List&amp;lt;Member&amp;gt; findMemberCustom () {
        ... //사용자정의구현
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Java</category>
      <author>로그뉴</author>
      <guid isPermaLink="true">https://flexiblecode.tistory.com/238</guid>
      <comments>https://flexiblecode.tistory.com/238#entry238comment</comments>
      <pubDate>Thu, 7 Dec 2023 14:38:50 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] 객체지향 쿼리와 JPQL</title>
      <link>https://flexiblecode.tistory.com/237</link>
      <description>&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 기본 문법과 쿼리 API&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Java Persistence Query Language(JPQL)은 엔터티 객체를 대상으로 하는 쿼리 언어로, SQL과 유사하지만 테이블이 아닌 엔터티에 대해 작동한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1.1 기본 문법&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JPQL의 기본 문법은 다음과 같다:&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1699508148920&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT &amp;lt;별칭 또는 엔터티명&amp;gt;
FROM &amp;lt;엔터티명&amp;gt;
WHERE &amp;lt;조건&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699508143116&quot; class=&quot;n1ql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;SELECT e FROM Employee e WHERE e.department = 'IT'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 쿼리는 'IT' 부서에 속한 모든 직원을 검색한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1.2 쿼리 API&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JPQL은 EntityManager를 통해 실행된다. EntityManager는 엔터티를 관리하고 영속성 컨텍스트를 제공하는데, 이를 이용해 JPQL을 실행할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699508165752&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EntityManager em = // EntityManager를 얻는 코드

TypedQuery&amp;lt;Employee&amp;gt; query = em.createQuery(&quot;SELECT e FROM Employee e WHERE e.department = 'IT'&quot;, Employee.class);

List&amp;lt;Employee&amp;gt; resultList = query.getResultList();&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 얻은 resultList에는 쿼리 결과가 들어있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 파라미터 바인딩&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파라미터 바인딩은 JPQL 쿼리에 동적인 값들을 전달하는 방법을 의미한다. 정적인 쿼리만으로는 한계가 있음.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2.1 위치 기반 파라미터 바인딩&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가장 간단한 방법은 위치 기반 파라미터 바인딩이다. 쿼리 문자열 안에 ?1, ?2, 등의 플레이스홀더를 사용하고, 파라미터 인덱스에 해당하는 값을 지정한다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1699508234874&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TypedQuery&amp;lt;Employee&amp;gt; query = em.createQuery(&quot;SELECT e FROM Employee e WHERE e.department = ?1&quot;, Employee.class);
query.setParameter(1, &quot;IT&quot;);

List&amp;lt;Employee&amp;gt; resultList = query.getResultList();&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2.2 이름 기반 파라미터 바인딩&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이름 기반 파라미터 바인딩은 :파라미터명 형식을 사용한다. 가독성이 더 높고, 순서에 구애받지 않아 편리하다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1699508245523&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TypedQuery&amp;lt;Employee&amp;gt; query = em.createQuery(&quot;SELECT e FROM Employee e WHERE e.department = :dept&quot;, Employee.class);
query.setParameter(&quot;dept&quot;, &quot;IT&quot;);

List&amp;lt;Employee&amp;gt; resultList = query.getResultList();&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2.3 위치 기반 vs. 이름 기반&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;선택은 개인의 취향이지만, 보통 이름 기반 파라미터 바인딩이 더 가독성이 좋다고 생각함.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 프로젝션&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로젝션은 JPQL에서 쿼리 결과로 가져올 데이터를 선택하는 방법이다. 불필요한 데이터를 가져오지 않고 필요한 부분만 선택하여 성능을 최적화할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3.1 엔터티 프로젝션&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가장 간단한 프로젝션은 엔터티 자체를 선택하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1699508927524&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TypedQuery&amp;lt;Employee&amp;gt; query = em.createQuery(&quot;SELECT e FROM Employee e WHERE e.department = 'IT'&quot;, Employee.class);

List&amp;lt;Employee&amp;gt; resultList = query.getResultList();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 경우에는 Employee 엔터티 전체를 가져오게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3.2 단일 값 프로젝션&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 엔터티의 특정 필드만 필요하다면, 단일 값 프로젝션을 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699508943168&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TypedQuery&amp;lt;String&amp;gt; query = em.createQuery(&quot;SELECT e.name FROM Employee e WHERE e.department = 'IT'&quot;, String.class);

List&amp;lt;String&amp;gt; resultList = query.getResultList();&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 예시에서는 직원의 이름만을 가져오고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3.3 여러 값 프로젝션&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여러 값을 한 번에 가져올 수도 있다. 그러나 이 경우는 Object[]나 Tuple을 사용해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699508969095&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TypedQuery&amp;lt;Object[]&amp;gt; query = em.createQuery(&quot;SELECT e.name, e.salary FROM Employee e WHERE e.department = 'IT'&quot;, Object[].class);

List&amp;lt;Object[]&amp;gt; resultList = query.getResultList();&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3.4 DTO 프로젝션&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가장 권장되는 방법 중 하나는 DTO(Data Transfer Object)를 사용하는 것이다. 엔터티의 일부 필드만 필요한 경우 DTO를 활용하여 필요한 정보만 전달받을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699508984513&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TypedQuery&amp;lt;EmployeeDTO&amp;gt; query = em.createQuery(&quot;SELECT new com.example.EmployeeDTO(e.name, e.salary) FROM Employee e WHERE e.department = 'IT'&quot;, EmployeeDTO.class);

List&amp;lt;EmployeeDTO&amp;gt; resultList = query.getResultList();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. 페이징 API&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;페이징은 대량의 데이터를 작은 일부분씩 나눠서 가져오는 기술로, 성능을 향상시키고 메모리 소비를 줄일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4.1 setFirstResult와 setMaxResults&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;setFirstResult는 가져올 결과의 시작 위치를 지정하고, setMaxResults는 가져올 최대 결과 수를 지정한다. 이 두 메서드를 사용하여 페이징을 구현할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699509247970&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int pageNumber = 1;
int pageSize = 10;

TypedQuery&amp;lt;Employee&amp;gt; query = em.createQuery(&quot;SELECT e FROM Employee e WHERE e.department = 'IT'&quot;, Employee.class);

query.setFirstResult((pageNumber - 1) * pageSize);
query.setMaxResults(pageSize);

List&amp;lt;Employee&amp;gt; resultList = query.getResultList();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 예시에서는 1페이지에서 10개의 결과를 가져오고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4.2 페이징을 위한 쿼리 작성&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;쿼리 자체에서 페이징을 구현할 수도 있다. OFFSET과 LIMIT을 사용하는 방법이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699509262189&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int pageNumber = 1;
int pageSize = 10;

TypedQuery&amp;lt;Employee&amp;gt; query = em.createQuery(&quot;SELECT e FROM Employee e WHERE e.department = 'IT' ORDER BY e.name&quot;)
    .setFirstResult((pageNumber - 1) * pageSize)
    .setMaxResults(pageSize);

List&amp;lt;Employee&amp;gt; resultList = query.getResultList();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 하면 데이터베이스에서 직접 페이징이 이루어진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. 집합과 정렬&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터를 검색하면서 종종 집합(집계) 함수를 사용하여 결과를 합치거나 통계를 내는 경우가 있다. 또한 정렬은 결과를 특정 기준에 따라 정렬하는 것으로, 보기 좋은 결과를 얻기 위해 중요하다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5.1 집합 함수&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JPQL에서는 다양한 집합 함수를 사용할 수 있다. 가장 일반적인 것은 COUNT, SUM, AVG, MIN, MAX 등이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1699509392230&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TypedQuery&amp;lt;Long&amp;gt; countQuery = em.createQuery(&quot;SELECT COUNT(e) FROM Employee e&quot;, Long.class);
Long totalCount = countQuery.getSingleResult();

TypedQuery&amp;lt;Double&amp;gt; avgSalaryQuery = em.createQuery(&quot;SELECT AVG(e.salary) FROM Employee e&quot;, Double.class);
Double averageSalary = avgSalaryQuery.getSingleResult();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 예시에서는 COUNT와 AVG 함수를 사용하여 직원의 총 수와 평균 급여를 구하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5.2 정렬&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결과를 특정 기준에 따라 정렬하려면 ORDER BY 절을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699509405674&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TypedQuery&amp;lt;Employee&amp;gt; query = em.createQuery(&quot;SELECT e FROM Employee e WHERE e.department = 'IT' ORDER BY e.salary DESC&quot;, Employee.class);

List&amp;lt;Employee&amp;gt; resultList = query.getResultList();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 예시에서는 IT 부서에 속한 직원들을 급여 기준으로 내림차순 정렬하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;6. 서브쿼리&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;NOT&amp;nbsp;EXISTS : 서브쿼리에 결과가 존재하면 참&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;NOT&amp;nbsp;IN : 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ALL : 모두 만족하면 참&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ANY, SOME : 같은 의미, 조건을 하나라도 만족하면 참&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JPA는 SELECT, WHERE, HAVING절에서만 서브 쿼리 사용 가능&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FROM절의 서브 쿼리는 현재 JPQL에서 불가능&amp;nbsp;(조인으로 풀 수 있으면 풀어서 해결)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;7. JPQL 타입&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문자: 'HELLO', 'She''s'&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;숫자: 10L(Long), 10D(Double), 10F(Float)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Boolean: TRUE, FALSE&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ENUM: jpabook.MemberType.Admin&amp;nbsp;(패키지명 포함)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;엔티티 타입: TYPE(m) = Member&amp;nbsp;(상속 관계에서 사용)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;8. JPQL 기본 함수&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;CONCAT&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;SUBSTRING&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TRIM&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LOWER, UPPER&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LENGTH&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LOCATE&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ABS, SQRT, MOD&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;SIZE, INDEX&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>스터디/자바 ORM 표준 JPA 프로그래밍</category>
      <author>로그뉴</author>
      <guid isPermaLink="true">https://flexiblecode.tistory.com/237</guid>
      <comments>https://flexiblecode.tistory.com/237#entry237comment</comments>
      <pubDate>Thu, 9 Nov 2023 15:00:37 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] 9장 값 타입</title>
      <link>https://flexiblecode.tistory.com/236</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JPA 의 데이터 타입을 가장 크게 분류하면 엔티티 타입과 값 타입으로 나눌 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;엔티티 타입 : @Entity로 정의하는 객체&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;식별자를 통해 지속적으로 추적할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;엔티티의 속성 값을 변경하더라도 같은 엔티티이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;값 타입 : int, Integer, String 처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체를 말한다.&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;식별자가 없고 숫자나 문자같은 속성만 있으므로 추적할 수 없다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;숫자 값을 100에서 200으로 변경하면 완전히 다른 값으로 대체된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;값 타입은 다음의 3가지로 나눌 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #dddddd; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;기본값 타입(Basic value type)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바 기본 타입(예: int, double)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;래퍼 클래스(예: Integer)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;String&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc; color: #dddddd; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;임베디드 타입(복합 값 타입)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JPA 에서 사용자가 직접 정의한 값 타입&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc; color: #dddddd; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;컬렉션 값 타입&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하나 이상의 값 타입을 저장할 때 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 기본값 타입&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1698214551893&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Entity
public class Member {
	@Id @GeneratedValue
	private Long id;

	private String name;
	private int age;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Member&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;bb1b19bddeac4c7d8a0d7c7463e18e50:1&quot;&gt;&amp;nbsp;엔티티의 값 타입인&amp;nbsp;&lt;/span&gt;name&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;bb1b19bddeac4c7d8a0d7c7463e18e50:3&quot;&gt;,&amp;nbsp;&lt;/span&gt;age&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;bb1b19bddeac4c7d8a0d7c7463e18e50:5&quot;&gt;&amp;nbsp;속성은 식별자 값도 없고&amp;nbsp;&lt;/span&gt;생명 주기&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;bb1b19bddeac4c7d8a0d7c7463e18e50:7&quot;&gt;도 회원 엔티티에 의존한다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;bb1b19bddeac4c7d8a0d7c7463e18e50:7&quot;&gt;따라서 회원 엔티티&amp;nbsp;&lt;/span&gt;인스턴스&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;bb1b19bddeac4c7d8a0d7c7463e18e50:9&quot;&gt;를 제거하면&amp;nbsp;&lt;/span&gt;name&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;bb1b19bddeac4c7d8a0d7c7463e18e50:11&quot;&gt;,&amp;nbsp;&lt;/span&gt;age&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;bb1b19bddeac4c7d8a0d7c7463e18e50:13&quot;&gt;&amp;nbsp;값도 제거된다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;값 타입&lt;span style=&quot;text-align: start;&quot; data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMk1lbWJlciUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb2RlJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiUyMCVFQyU5NyU5NCVFRCU4QiVCMCVFRCU4QiVCMCVFQyU5RCU5OCUyMCVFQSVCMCU5MiUyMCVFRCU4MyU4MCVFQyU5RSU4NSVFQyU5RCVCOCUyMCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIybmFtZSUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb2RlJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiUyQyUyMCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyYWdlJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJtYXJrJTIyJTJDJTIydHlwZSUyMiUzQSUyMmNvZGUlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTdEJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJTIwJUVDJTg2JThEJUVDJTg0JUIxJUVDJTlEJTgwJTIwJUVDJThCJTlEJUVCJUIzJTg0JUVDJTlFJTkwJTIwJUVBJUIwJTkyJUVCJThGJTg0JTIwJUVDJTk3JTg2JUVBJUIzJUEwJTIwJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUMlODMlOUQlRUIlQUElODUlMjAlRUMlQTMlQkMlRUElQjglQjAlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUIlOEYlODQlMjAlRUQlOUElOEMlRUMlOUIlOTAlMjAlRUMlOTclOTQlRUQlOEIlQjAlRUQlOEIlQjAlRUMlOTclOTAlMjAlRUMlOUQlOTglRUMlQTElQjQlRUQlOTUlOUMlRUIlOEIlQTQuJTIwJUVCJTk0JUIwJUVCJTlEJUJDJUVDJTg0JTlDJTIwJUVEJTlBJThDJUVDJTlCJTkwJTIwJUVDJTk3JTk0JUVEJThCJUIwJUVEJThCJUIwJTIwJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUMlOUQlQjglRUMlOEElQTQlRUQlODQlQjQlRUMlOEElQTQlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUIlQTUlQkMlMjAlRUMlQTAlOUMlRUElQjElQjAlRUQlOTUlOTglRUIlQTklQjQlMjAlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMm5hbWUlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlMkMlMjAlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMmFnZSUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb2RlJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiUyMCVFQSVCMCU5MiVFQiU4RiU4NCUyMCVFQyVBMCU5QyVFQSVCMSVCMCVFQiU5MCU5QyVFQiU4QiVBNC4lMjAlRUElQjclQjglRUIlQTYlQUMlRUElQjMlQTAlMjAlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQSVCMCU5MiUyMCVFRCU4MyU4MCVFQyU5RSU4NSUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb2RlJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQyU5RCU4MCUyMCVFQSVCMyVCNSVFQyU5QyVBMCVFRCU5NSU5OCVFQiVBOSVCNCUyMCVFQyU5NSU4OCUyMCVFQiU5MCU5QyVFQiU4QiVBNC4lMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjJhZjdkOGI1MmI0Mjg0MDRlYTdiNjNjZjYyNmUzYWUyNyUyMiU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMmNjOTVlOGE4NmMyMDQzMjU4ZTNjZTE3NGYzZDhjNzBlJTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyZWMzZTkyODhjMDVlNGI3OGI5NDNlMTVmOTNmMWI2MDIlMjIlN0Q=&quot; data-offset-key=&quot;bb1b19bddeac4c7d8a0d7c7463e18e50:15&quot;&gt;은 공유하면 안 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 임베디드 타입(복합 값 타입)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-key=&quot;31c69274f7634cc9b8a0b25dac1753bf&quot;&gt;
&lt;div data-block-content=&quot;31c69274f7634cc9b8a0b25dac1753bf&quot;&gt;
&lt;div&gt;
&lt;div data-key=&quot;c3ce78cf9e5c4c8dae470cb473b01dab&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot; data-key=&quot;768c899a06d44bec859c534c5dcd0f37&quot;&gt;@Embeddable&amp;nbsp;: 값 타입을 정의하는 곳에 표시&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot; data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJsaXN0LWl0ZW0lMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMmJsb2NrJTIyJTJDJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmlzVm9pZCUyMiUzQWZhbHNlJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCUyQyUyMm5vZGVzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIydGV4dCUyMiUyQyUyMmxlYXZlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJTQwRW1iZWRkYWJsZSUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb2RlJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiUyMCUzQSUyMCVFQSVCMCU5MiUyMCVFRCU4MyU4MCVFQyU5RSU4NSVFQyU5RCU4NCUyMCVFQyVBMCU5NSVFQyU5RCU5OCVFRCU5NSU5OCVFQiU4QSU5NCUyMCVFQSVCMyVCMyVFQyU5NyU5MCUyMCVFRCU5MSU5QyVFQyU4QiU5QyUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMjUzZGZhMWUwZDkwZDRhN2RiOTA5ZDExNzI0MjNhMTg0JTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyYWI4MWFkYWViZDlhNDBhZThjMjNiNTQ2OWZlMzQ0NjElMjIlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjJmYTg5MTc3ZGViMmI0M2U4OTFkOWViZGY2MjI0MjhjNiUyMiU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmJsb2NrJTIyJTJDJTIydHlwZSUyMiUzQSUyMmxpc3QtaXRlbSUyMiUyQyUyMmlzVm9pZCUyMiUzQWZhbHNlJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCUyQyUyMm5vZGVzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIyYmxvY2slMjIlMkMlMjJ0eXBlJTIyJTNBJTIycGFyYWdyYXBoJTIyJTJDJTIyaXNWb2lkJTIyJTNBZmFsc2UlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJ0ZXh0JTIyJTJDJTIybGVhdmVzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlNDBFbWJlZGRlZCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb2RlJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiUyMCUzQSUyMCVFQSVCMCU5MiUyMCVFRCU4MyU4MCVFQyU5RSU4NSVFQyU5RCU4NCUyMCVFQyU4MiVBQyVFQyU5QSVBOSVFRCU5NSU5OCVFQiU4QSU5NCUyMCVFQSVCMyVCMyVFQyU5NyU5MCUyMCVFRCU5MSU5QyVFQyU4QiU5QyUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMmRjZjc1YmQ0OGEwZTRhNTFhYjlkMDY2Mzk3NWQyNThmJTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyOTExNWYzNmUxNGFkNGYzNTg4OGZiOWYwZDFjM2EzMTklMjIlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjIwNDcwNmUwNjY3OWI0MDNhODFlYWYyNmI2OTVhMjk2MSUyMiU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMmUxZjU2Y2U4MjM5YTQwNmI5YWE1ZTJiNzA1OGQwZmFiJTIyJTdE&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-key=&quot;04706e06679b403a81eaf26b695a2961&quot;&gt;
&lt;div data-block-content=&quot;04706e06679b403a81eaf26b695a2961&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;@Embedded&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot; data-offset-key=&quot;dcf75bd48a0e4a51ab9d0663975d258f:1&quot;&gt;&amp;nbsp;: 값 타입을 사용하는 곳에 표시&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1698214766278&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
public class Member {
  @Id @GeneratedValue
  private Long id;
  private String name;

  @Embedded 
  Address homeAddress;
  
  @Embedded
  @AttributeOverrides({
    @AttributeOverride(name=&quot;city&quot;, column=@Column(name = &quot;COMPANY_CITY&quot;)),
    @AttributeOverride(name=&quot;street&quot;, column=@Column(name = &quot;COMPANY_STREET&quot;)),
    @AttributeOverride (name=&quot;zipcode&quot;, column=@Column (name = &quot;COMPANY_ZIPCODE&quot;))
  })
  Address companyAddress;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;임베디드 타입이 null이면 매핑한 컬럼 값은 모두 null이 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 값 타입과 불변 객체&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1698215028440&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;member1.setHomeAddress(new Address(&quot;oldCIty&quot;));
Address address = member1.getHomeAddress();

address.setCity(&quot;newCity&quot;);
member2.setHomeAddress(address);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e6edf3; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;영속성 컨텍스트는 회원1의 주소도 newCity로 변경시켜버린다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 부작용을 막으려면 값을 복사해서 사용하면 안된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;값 타입 복사&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #101920; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바는 기본 타입에 값을 대입하면 값을 복사해서 전달하고 객체에 값을 대입하면 참조값을 전달한다. (Call by value, Call by reference)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문제는 임베디드 타입처럼 직접 정의한 값 타입은 자바의 기본타입이 아니라 객체 타입이여서 대입해서 사용하게 되면 공유 참조로 인한 Side Effect가 발생할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;객체의 공유참조는 피할 수 없는데, 따라서 근본적인 해결책이 필요하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가장 단순한 방법은 객체의 값을 수정하지 못하게 막으면 공유 참조해도 값을 변경하지 못하므로 부작용을 막을 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;불변 객체&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e6edf3; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;객체를 불변하게 만들면 값을 수정할 수 없으므로 부작용이 원천 차단 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서 되도록 값 타입은 불변 객체로 설계하자.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;불변 객체도 결국 객체여서 참조 값 공유를 피할 수는 없지만 인스턴스의 값을 수정할 수 없으므로 부작용이 발생하지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;불변 객체를 구현하는 방법은 다양하지만 가장 간단한 방법은 생성자로만 값을 설정하고 수정자를 만들지 않으면 된다. (setter를 제거)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 값 타입의 비교&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e6edf3; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바가 제공하는 객체 비교 2가지&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동일성 : ==&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동등성 : equals()&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;값 타입을 비교할 때는 equlas()를 사용해서 동등성을 비교해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;값 타입의 equals() 메소드를 재정의할 때는 모든 필드값을 비교하도록 구현한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바에서 equals()를 재정의하면 hashCode()도 재정의하는 것이 안전하다. 그렇지 않으면 해시를 사용하는 컬렉션(HashSet, HashMap)이 정상 동작하지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. 값 타입 컬렉션&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;f8ac51373a974805b6ed84408737d0cb:0&quot;&gt;값 타입을 하나 이상 저장 하려면 컬렉션에 보관하고&amp;nbsp;&lt;/span&gt;@ElementCollection&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;f8ac51373a974805b6ed84408737d0cb:2&quot;&gt;,&amp;nbsp;&lt;/span&gt;@CollectionTable&lt;span style=&quot;text-align: start;&quot; data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQSVCMCU5MiUyMCVFRCU4MyU4MCVFQyU5RSU4NSVFQyU5RCU4NCUyMCVFRCU5NSU5OCVFQiU4MiU5OCUyMCVFQyU5RCVCNCVFQyU4MyU4MSUyMCVFQyVBMCU4MCVFQyU5RSVBNSUyMCVFRCU5NSU5OCVFQiVBMCVBNCVFQiVBOSVCNCUyMCVFQyVCQiVBQyVFQiVBMCU4OSVFQyU4NSU5OCVFQyU5NyU5MCUyMCVFQiVCMyVCNCVFQSVCNCU4MCVFRCU5NSU5OCVFQSVCMyVBMCUyMCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJTQwRWxlbWVudENvbGxlY3Rpb24lMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlMkMlMjAlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiU0MENvbGxlY3Rpb25UYWJsZSUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb2RlJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiUyMCVFQyU5NiVCNCVFQiU4NSVCOCVFRCU4NSU4QyVFQyU5RCVCNCVFQyU4NSU5OCVFQyU5RCU4NCUyMCVFQyU4MiVBQyVFQyU5QSVBOSVFRCU5NSU5OCVFQiVBOSVCNCUyMCVFQiU5MCU5QyVFQiU4QiVBNC4lMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjI5NTNkY2ZjNDg3ODc0MjhkODk5ODM0NjQ4N2E2YzJiNyUyMiU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMjE2MTMyNWNiYWNiMzRjNDQ5MzQ2Y2ZjMDBkNWIxNjU0JTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyMTY4MTU4NjA1OGQ3NGMxZDk0ZGY5MWE3ZjYyZmNiMzglMjIlN0Q=&quot; data-offset-key=&quot;f8ac51373a974805b6ed84408737d0cb:4&quot;&gt;&amp;nbsp;어노테이션을 사용하면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1698215485611&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
public class Member {
  @Id @GeneratedValue
  private Long id;
  
  @Embedded
  private Address homeAddress;

  @ElementCollection
  @CollectionTable(
    name = &quot;FAVORITE_FOODS&quot;,
    joinColumns = @JoinColumn(name = &quot;MEMBER_ID&quot;))
  @Column(name=&quot;FOOD_NAME&quot;)
  private Set&amp;lt;String&amp;gt; favor丄teFoods = new HashSet&amp;lt;String&amp;gt;();

  @ElementCollection
  @CollectionTable(
    name = &quot;ADDRESS&amp;rdquo;, 
    joinColumns = @JoinColumn(name = &quot;MEMBER_ID&quot;))
  private List&amp;lt;Address&amp;gt; addressHistory = new ArrayList&amp;lt;Address&amp;gt;();
  //...
}

@Embeddable
public class Address {
  @Column
  private String city;
  private String street;
  private String zipcode
  //...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FavoriteFoods&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;b0387c91164e4e4eb3b6d933e6de4e0d:1&quot;&gt;는 기본값 타입인&amp;nbsp;&lt;/span&gt;String&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;b0387c91164e4e4eb3b6d933e6de4e0d:3&quot;&gt;을&amp;nbsp;&lt;/span&gt;컬렉션&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;b0387c91164e4e4eb3b6d933e6de4e0d:5&quot;&gt;으로 가진다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-offset-key=&quot;b0387c91164e4e4eb3b6d933e6de4e0d:5&quot;&gt;이것은 데이터베이스 테이블로 매핑해야 하는데 관계형 데이터베이스의 테이블은 컬럼 안에 컬렉션을 포함할 수 없다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;b0387c91164e4e4eb3b6d933e6de4e0d:5&quot;&gt;따라서 별도의&amp;nbsp;&lt;/span&gt;테이블&lt;span style=&quot;text-align: start;&quot; data-offset-key=&quot;b0387c91164e4e4eb3b6d933e6de4e0d:7&quot;&gt;을 추가하고&amp;nbsp;&lt;/span&gt;@CollectionTable&lt;span style=&quot;text-align: start;&quot; data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMkZhdm9yaXRlRm9vZHMlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMm1hcmslMjIlMkMlMjJ0eXBlJTIyJTNBJTIyY29kZSUyMiUyQyUyMmRhdGElMjIlM0ElN0IlN0QlN0QlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTJDJTdCJTIyb2JqZWN0JTIyJTNBJTIybGVhZiUyMiUyQyUyMnRleHQlMjIlM0ElMjIlRUIlOEElOTQlMjAlRUElQjglQjAlRUIlQjMlQjglRUElQjAlOTIlMjAlRUQlODMlODAlRUMlOUUlODUlRUMlOUQlQjglMjAlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMlN0cmluZyUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb2RlJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQyU5RCU4NCUyMCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJUVDJUJCJUFDJUVCJUEwJTg5JUVDJTg1JTk4JTIyJTJDJTIybWFya3MlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJtYXJrJTIyJTJDJTIydHlwZSUyMiUzQSUyMmNvZGUlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTdEJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJUVDJTlDJUJDJUVCJUExJTlDJTIwJUVBJUIwJTgwJUVDJUE3JTg0JUVCJThCJUE0LiUyMCVFQyU5RCVCNCVFQSVCMiU4MyVFQyU5RCU4MCUyMCVFQiU4RCVCMCVFQyU5RCVCNCVFRCU4NCVCMCVFQiVCMiVBMCVFQyU5RCVCNCVFQyU4QSVBNCUyMCVFRCU4NSU4QyVFQyU5RCVCNCVFQiVCOCU5NCVFQiVBMSU5QyUyMCVFQiVBNyVBNCVFRCU5NSU5MSVFRCU5NSVCNCVFQyU5NSVCQyUyMCVFRCU5NSU5OCVFQiU4QSU5NCVFQiU4RCVCMCUyMCVFQSVCNCU4MCVFQSVCMyU4NCVFRCU5OCU5NSUyMCVFQiU4RCVCMCVFQyU5RCVCNCVFRCU4NCVCMCVFQiVCMiVBMCVFQyU5RCVCNCVFQyU4QSVBNCVFQyU5RCU5OCUyMCVFRCU4NSU4QyVFQyU5RCVCNCVFQiVCOCU5NCVFQyU5RCU4MCUyMCVFQyVCQiVBQyVFQiU5RiVCQyUyMCVFQyU5NSU4OCVFQyU5NyU5MCUyMCVFQyVCQiVBQyVFQiVBMCU4OSVFQyU4NSU5OCVFQyU5RCU4NCUyMCVFRCU4RiVBQyVFRCU5NSVBOCVFRCU5NSVBMCUyMCVFQyU4OCU5OCUyMCVFQyU5NyU4NiVFQiU4QiVBNC4lMjAlRUIlOTQlQjAlRUIlOUQlQkMlRUMlODQlOUMlMjAlRUIlQjMlODQlRUIlOEYlODQlRUMlOUQlOTglMjAlMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFRCU4NSU4QyVFQyU5RCVCNCVFQiVCOCU5NCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIybWFyayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb2RlJTIyJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCU3RCU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlMkMlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQyU5RCU4NCUyMCVFQyVCNiU5NCVFQSVCMCU4MCVFRCU5NSU5OCVFQSVCMyVBMCUyMCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJTQwQ29sbGVjdGlvblRhYmxlJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJtYXJrJTIyJTJDJTIydHlwZSUyMiUzQSUyMmNvZGUlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTdEJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJUVCJUE1JUJDJTIwJUVDJTgyJUFDJUVDJTlBJUE5JUVEJTk1JUI0JUVDJTg0JTlDJTIwJUVDJUI2JTk0JUVBJUIwJTgwJUVEJTk1JTlDJTIwJUVEJTg1JThDJUVDJTlEJUI0JUVCJUI4JTk0JUVDJTlEJTg0JTIwJUVCJUE3JUE0JUVEJTk1JTkxJTIwJUVEJTk1JUI0JUVDJTk1JUJDJTIwJUVEJTk1JTlDJUVCJThCJUE0LiUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMmIzNDdjN2Q0M2YyODRkMTg4ZmZhMTlhZWYyOGZmNjcxJTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyYTdlZWIwZGU1ZWYzNDQ4YTk3NTYzMTU0MTkwYzNmMzMlMjIlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjJjZDU1MDY4YjRiOTE0ZjYxYjg2MmIwOGIzOWQ4NDAxYSUyMiU3RA==&quot; data-offset-key=&quot;b0387c91164e4e4eb3b6d933e6de4e0d:9&quot;&gt;를 사용해서 추가한 테이블을 매핑 해야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #e6edf3; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;값 타입 컬렉션의 제약사항&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e6edf3; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;값 타입 컬렉션에 변경 사항이 발생하면, 값 타입 컬렉션이 매핑된 테이블의 연관된 모든 데이터를 삭제하고, 현재 값 타입 컬렉션 객체에 있는 모든 값을 데이터베이스에 다시 저장한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;값 타입 컬렉션의 매핑된 테이블에 데이터가 많다면 값 타입 대신 일대다 관계를 고려해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본 키를 구성해야 한다. 따라서 데이터베이스 기본 키 제약조건으로 인해 null을 입력할 수 없고, 같은 값을 중복해서 저장할 수 없는 제약도 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 문제를 해결하려면 값 타입 컬렉션 대신 새로운 엔티티를 만들어서 일대다 관계로 설정하고 cascade + orphan remove 기능을 적용하면 값 타입 컬렉션처럼 사용할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스터디/자바 ORM 표준 JPA 프로그래밍</category>
      <author>로그뉴</author>
      <guid isPermaLink="true">https://flexiblecode.tistory.com/236</guid>
      <comments>https://flexiblecode.tistory.com/236#entry236comment</comments>
      <pubDate>Wed, 25 Oct 2023 15:26:16 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] 복합키에서 equals () 및 hashCode () 구현</title>
      <link>https://flexiblecode.tistory.com/235</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;회사에서 JPA 스터디를 중간에 참여하게 되어 7장부터 글을 쓰게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;앞으로 책을 읽고 알게된 내용이나, 궁금했던 점 위주로 정리할 예정이다. &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;복합 키와 식별관계 매핑&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;식별관계 vs 비식별관계&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;DB 테이블 사이의 관계는 외래 키가 기본 키에 포함되는지 여부에 따라 식별, 비식별로 구분된다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;식별관계&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;식별 관계는 부모 테이블의 기본 키를 내려 받아 자식 테이블의 기본키 + 외래키로 사용하는 관계&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비식별관계&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비식별 관계는 부모 테이블의 기본 키를 받아서 자식 테이블의 외래 키로만 사용하는 관계다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비식별 관계는 외래키에 NULL을 허용하는지에 따라 필수적, 선택적으로 갈린다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #555555; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필수적 비식별 관계&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;외래키에 NULL 허용하지 않음&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;연관관계를 필수적으로 맺어야함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;선택적 비식별 관계&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;외래키에 NULL 허용함&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;연관관계를 맺을지 말지 선택 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;** 비식별 관계를 주로 사용하는 것을 추천한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;복합키를 구성하기 위한 필수조건&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1.&amp;nbsp;@EmbeddedId&amp;nbsp;또는&amp;nbsp;@IdClass&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2.&amp;nbsp;public의&amp;nbsp;no-args&amp;nbsp;constructor&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3.&amp;nbsp;serializable을&amp;nbsp;상속&amp;nbsp;받기&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4.&amp;nbsp;equals(),&amp;nbsp;hashCode()&amp;nbsp;Override&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;복합키 : 비식별 관계 매핑&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;식별자 필드가 2개 이상이면 별도의 식별자 클래스를 만들고 equals와 hashcode를 override해야 한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JPA는 복합키를 지원하기 위해&amp;nbsp;@IdClass,&amp;nbsp;@EmbeddedId&amp;nbsp;2가지 방법을 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;@IdClass&lt;/span&gt;는 관계형 DB에 가까운 방법이고 &lt;span style=&quot;text-align: start;&quot;&gt;@EmbeddedId&lt;/span&gt;는 좀 더 객체지향에 가까운 방법이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1696480916272&quot; class=&quot;bash&quot; style=&quot;background-color: #fafafa; color: #000000; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;ParentId id1 = new ParentId();
id1.setId1(&quot;test1&quot;);
id1.setId2(&quot;test2&quot;);

ParentId id2 = new ParentId();
id2.setId1(&quot;test1&quot;);
id2.setId2(&quot;test2&quot;);

id1.equals(id2) // false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;id1.equals(id2) 의 결과값은 false이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바의 모든 클래스는 기본으로 Object 클래스를 상속받는데 이 클래스가 제공하는 기본 equals()는 인스턴스 참조 값 비교인 ==비교(동일성비교)를 하기 때문이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;식별자 클래스의&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;equals()&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;hashCode()&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;영속성 컨텍스트가 식별자를 비교할때 사용&lt;/span&gt;하기 때문에 반드시 오버라이딩 해야한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;식별자 객체의 동등성(equals)이 지켜지지 않으면 예상과 다른 엔티티가 조회되거나 엔티티를 찾을 수 없는 등 영속성 컨텍스트가 엔티티를 관리하는 데 문제가 발생한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;복합 키를 사용할 때 equals()와 hashCode()를 필수로 구현해야 하는 이유?&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;복합키는 일반적으로 두 개 이상의 필드를 조합하여 하나의 키를 구성하는데, 여러 필드로 구성된 복합키의 경우에는 각 필드의 값이 같더라도 인스턴스가 다를 수 있다. 이때 equals()와 hashCode() 메서드를 적절히 오버라이딩하여 두 객체가 동일한 키를 나타내는지를 정의하면, 해시 기반의 자료구조에서 올바르게 동작하도록 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;equals() 메서드를 오버라이딩하는 이유:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #d1d5db; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;equals() 메서드는 두 객체가 동등한지 여부를 판단한다. 복합키의 경우, 여러 필드를 비교하여 동등성을 판단해야 한다. 따라서 equals() 메서드를 오버라이딩하여 동등성 비교를 정의할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;hashCode() 메서드를 오버라이딩하는 이유:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #d1d5db; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;hashCode() 메서드는 해시 기반 자료구조에서 객체의 해시 코드를 반환한다. 해시 기반 자료구조에서 객체를 저장하고 검색할 때 해시 코드를 사용하여 성능을 향상시킬 수 있다. 복합키의 경우, 각 필드의 해시 코드를 조합하여 객체의 해시 코드를 생성할 수 있도록 hashCode() 메서드를 오버라이딩해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;equals()와 hashCode()를 오버라이딩 하는 예시&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OrderKey 클래스가 있고, 사용자 ID와 주문 번호 두 필드로 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 주문은 사용자 ID와 주문 번호로 식별된다. (= 복합키)&lt;/p&gt;
&lt;pre id=&quot;code_1696483544525&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.util.Objects;

public class OrderKey {
    private Long userId;
    private String orderNumber;

    // 생성자, getter, setter 생략 (필요에 따라 구현)

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        OrderKey orderKey = (OrderKey) o;
        return Objects.equals(userId, orderKey.userId) &amp;amp;&amp;amp;
                Objects.equals(orderNumber, orderKey.orderNumber);
    }

    @Override
    public int hashCode() {
        return Objects.hash(userId, orderNumber);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 코드에서는 OrderKey 클래스를 정의하고, 이 클래스에서 equals()와 hashCode() 메서드를 오버라이딩하여 복합키의 동등성 비교와 해시 코드 생성을 정의했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스터디/자바 ORM 표준 JPA 프로그래밍</category>
      <category>7장 고급매핑</category>
      <category>JPA</category>
      <category>고급매핑</category>
      <category>자바 ORM 표준 JPA 프로그래밍</category>
      <category>자바 ORM 표준 JPA 프로그래밍 7장</category>
      <author>로그뉴</author>
      <guid isPermaLink="true">https://flexiblecode.tistory.com/235</guid>
      <comments>https://flexiblecode.tistory.com/235#entry235comment</comments>
      <pubDate>Thu, 5 Oct 2023 14:17:39 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin In Action]10장 - 애노테이션과 리플렉션</title>
      <link>https://flexiblecode.tistory.com/234</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;** 이 글은 Kotlin In Action을 읽고 정리한 글입니다. **&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;kotlininaction.jpeg&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lrjbi/btshMWmqBAS/58TC26zhHwSUmmFdQ8uvb0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lrjbi/btshMWmqBAS/58TC26zhHwSUmmFdQ8uvb0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lrjbi/btshMWmqBAS/58TC26zhHwSUmmFdQ8uvb0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flrjbi%2FbtshMWmqBAS%2F58TC26zhHwSUmmFdQ8uvb0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;375&quot; data-filename=&quot;kotlininaction.jpeg&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;d29e&quot; style=&quot;color: #292929; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.1 애노테이션 선언과 적용&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 id=&quot;ab01&quot; style=&quot;color: #292929; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.1.1 애노테이션 적용&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;애노테이션 적용을 위해서는 적용하는 대상 앞에 애노테이션을 붙이면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;애노테이션은 '@'과 애노테이션 이름으로 이루어진다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를 들어 JUnit 프레임워크를 사용한다면 테스트 메서드 앞에 '@Test' 애노테이션을 붙이면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685582422403&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyTest{
  @Test fun testTrue(){
    Assert.assertTrue(true)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;애노테이션의&amp;nbsp;인자로는&amp;nbsp;원시&amp;nbsp;타입의&amp;nbsp;값,&amp;nbsp;문자열,&amp;nbsp;enum,&amp;nbsp;클래스&amp;nbsp;참조,&amp;nbsp;다른&amp;nbsp;애노테이션&amp;nbsp;클래스, 배열이&amp;nbsp;들어갈&amp;nbsp;수&amp;nbsp;있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;애노테이션의&amp;nbsp;인자를&amp;nbsp;지정하는&amp;nbsp;문법은 자바와 약간 다르다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스를&amp;nbsp;애노테이션&amp;nbsp;인자로&amp;nbsp;지정할&amp;nbsp;때는&amp;nbsp;@MyAnnotation&amp;nbsp;(MyClass:class)&amp;nbsp;처럼&amp;nbsp;::class&amp;nbsp;를&amp;nbsp;클래스&amp;nbsp;이름&amp;nbsp;뒤에&amp;nbsp;넣어야&amp;nbsp;한다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다른&amp;nbsp;애노테이션을&amp;nbsp;인자로&amp;nbsp;지정할&amp;nbsp;때는&amp;nbsp;인자로&amp;nbsp;들어가는&amp;nbsp;애노테이션의&amp;nbsp;이름&amp;nbsp;앞에&amp;nbsp;@&amp;nbsp;를&amp;nbsp;넣지&amp;nbsp;않아야&amp;nbsp;한다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;배열을&amp;nbsp;인자로&amp;nbsp;지정하려면&amp;nbsp;RequestMapping(path&amp;nbsp;=&amp;nbsp;arrayOf(&quot;/foo&quot;,&amp;nbsp;&quot;/bar&quot;))&amp;nbsp;처럼&amp;nbsp;arrayOf&amp;nbsp;함수를&amp;nbsp;사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;79df&quot; style=&quot;color: #292929; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.1.2 애노테이션 대상&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코틀린&amp;nbsp;소스코드에서&amp;nbsp;한&amp;nbsp;선언을&amp;nbsp;컴파일한&amp;nbsp;결과가&amp;nbsp;여러&amp;nbsp;자바&amp;nbsp;선언과&amp;nbsp;대응하는&amp;nbsp;경우가&amp;nbsp;자주&amp;nbsp;있다.&amp;nbsp;그리고&amp;nbsp;이때&amp;nbsp;코틀린&amp;nbsp;선언과&amp;nbsp;대응하는&amp;nbsp;여러&amp;nbsp;자바&amp;nbsp;선언에&amp;nbsp;각각&amp;nbsp;애노테이션을&amp;nbsp;붙여야&amp;nbsp;할&amp;nbsp;때가&amp;nbsp;있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를&amp;nbsp;들어&amp;nbsp;코틀린&amp;nbsp;프로퍼티는&amp;nbsp;기본적으로&amp;nbsp;자바&amp;nbsp;필드와&amp;nbsp;게터&amp;nbsp;메서드&amp;nbsp;선언과&amp;nbsp;대응한다.&amp;nbsp;프로퍼티가&amp;nbsp;변경&amp;nbsp;가능하면&amp;nbsp;세터에&amp;nbsp;대응하는&amp;nbsp;자바&amp;nbsp;세터&amp;nbsp;메서드와&amp;nbsp;세터&amp;nbsp;파라미터가&amp;nbsp;추가된다.&amp;nbsp;게다가&amp;nbsp;주&amp;nbsp;생성자에서&amp;nbsp;프로퍼티를&amp;nbsp;선언하면&amp;nbsp;이런&amp;nbsp;접근자&amp;nbsp;메서드와&amp;nbsp;파라미터&amp;nbsp;외에&amp;nbsp;자바&amp;nbsp;생성자&amp;nbsp;파라미터와도&amp;nbsp;대응이&amp;nbsp;된다.&amp;nbsp;따라서&amp;nbsp;애노테이션을&amp;nbsp;붙일&amp;nbsp;때&amp;nbsp;이런&amp;nbsp;요소&amp;nbsp;중&amp;nbsp;어떤&amp;nbsp;요소에&amp;nbsp;애노테이션을&amp;nbsp;붙일지&amp;nbsp;표시할&amp;nbsp;필요가&amp;nbsp;있다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;사용&amp;nbsp;시점&amp;nbsp;대상&lt;/b&gt;&amp;nbsp;선언으로&amp;nbsp;애노테이션을&amp;nbsp;붙일&amp;nbsp;요소를&amp;nbsp;정할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;사용&amp;nbsp;시점&amp;nbsp;대상은&amp;nbsp;&lt;b&gt;@&amp;nbsp;기호와&amp;nbsp;애노테이션&amp;nbsp;이름&amp;nbsp;사이에&amp;nbsp;붙으며&lt;/b&gt;,&amp;nbsp;애노테이션&amp;nbsp;이름과는&amp;nbsp;:&amp;nbsp;으로&amp;nbsp;분리된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사용 지점 대상을 지정할 때 지원하는 대상 목록은 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;property&amp;nbsp;프로퍼티 전체. 자바에서 선언된 애노테이션에는 이 사용 지점 대상을 사용할 수 없다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;field&amp;nbsp;프로퍼티에 의해 생성되는 (뒷받침하는) 필드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;get&amp;nbsp;프로퍼티 게터&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;set&amp;nbsp;프로퍼티 세터&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;receiver&amp;nbsp;확장 함수나 프로퍼티의 수신 객체 파라미터&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;param&amp;nbsp;생성자 파라미터&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;setparam&amp;nbsp;세터 파라미터&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;delegate&amp;nbsp;위임 프로퍼티의 위임 인스턴스를 담아둔 필드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;file&amp;nbsp;파일 안에 선언된 최상위 함수와 프로퍼티를 담아두는 클래스&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;79df&quot; style=&quot;color: #292929; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.1.3 애노테이션을 활용한 JSON 직렬화 제어&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;직렬화란?&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;직렬화(serialization)는&amp;nbsp;객체를&amp;nbsp;저장장치에&amp;nbsp;저장하거나&amp;nbsp;네트워크를&amp;nbsp;통해&amp;nbsp;전송하기&amp;nbsp;위해&amp;nbsp;텍스트나&amp;nbsp;이진&amp;nbsp;형식으로&amp;nbsp;변환하는&amp;nbsp;것이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;역직렬화란?&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;역직렬화(deserialization)는&amp;nbsp;텍스트나&amp;nbsp;이진&amp;nbsp;형식으로&amp;nbsp;저장된&amp;nbsp;데이터로부터&amp;nbsp;원래의&amp;nbsp;객체를&amp;nbsp;만들어낸다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Person&amp;nbsp;의 인스턴스를&amp;nbsp;serialize&amp;nbsp;함수에 전달하면 JSON 표현이 담긴 문자열을 돌려받는다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;data class Person(val name: String, val age: Int)

&amp;gt;&amp;gt;&amp;gt; val person = Person(&quot;Alice&quot;, 29)
&amp;gt;&amp;gt;&amp;gt; println(serialize(person))

{&quot;age&quot;: 29, &quot;name&quot;: &quot;Alice&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #ececec; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #ececec; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JSON 표현을 다시 객체로 만들려면&amp;nbsp;deserialize&amp;nbsp;함수를 호출한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;lisp&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; val json = &quot;&quot;&quot;{&quot;name&quot;: &quot;Alice&quot;, &quot;age&quot;: 29}&quot;&quot;&quot;
&amp;gt;&amp;gt;&amp;gt; println(deserialize&amp;lt;Person&amp;gt;(json))

Person(name=Alice, age=29)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #ececec; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JSON에는 객체의 타입이 저장되지 않기 때문에 JSON 데이터로부터 인스턴스를 만들려면 타입 인자로 클래스를 명시해야 한다. 여기서는&amp;nbsp;Person&amp;nbsp;클래스를 타입 인자로 넘겼다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #ececec; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;애노테이션을 활용해 객체를 직렬화하거나 역직렬화하는 방법을 제어할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;객체를 JSON으로 직렬화할 때 제이키드 라이브러리는 기본적으로 모든 프로퍼티를 직렬화하면 프로퍼티 이름을 키로 사용한다. 애노테이션을 사용하면 이런 동작을 변경할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #ececec; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;@JsonExclude&amp;nbsp;애노테이션을 사용하면 직렬화나 역직렬화 시 그 프로퍼티를 무시할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;@JsonName&amp;nbsp;애노테이션을 사용하면 프로퍼티를 표현하는 키/값 쌍의 키로 프로퍼티 이름 대신 애노테이션이 지정한 이름을 쓰게 할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.1.4 애노테이션 선언&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@JsonExclude&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;애노테이션&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; 아무 파라미터도 없는&lt;/b&gt; 가장 단순한 애노테이션&lt;/li&gt;
&lt;li&gt;애노테이션 선언은 일반 클래스 선언처럼 보이지만 일반 클래스와 차이는&amp;nbsp;class앞에 annotation이라는 변경자가 붙어있다는 점이다.&lt;/li&gt;
&lt;li&gt;애노테이션은 오직 선언이나 식과 관련있는 메타데이터의 구조를 정의하기 때문에 내부에 아무 코드도 들어있을 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;pre class=&quot;crystal&quot; style=&quot;background-color: #f7f7f7;&quot;&gt;&lt;code&gt;@Target(AnnotationTarget.PROPERTY)
annotation class JsonExclude&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@JsonName&lt;/b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;애노테이션&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;파라미터가 있는&lt;/b&gt; 애노테이션&lt;/li&gt;
&lt;li&gt;파라미터는 애노테이션 클래스의 주생성자에 파라미터를 선언해야 한다.&lt;/li&gt;
&lt;li&gt;다만 애노테이션 클래스는 모든 파리미터에 앞에&amp;nbsp;&lt;b&gt;val&lt;/b&gt;을 붙여야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;pre class=&quot;crystal&quot; style=&quot;background-color: #f7f7f7;&quot;&gt;&lt;code&gt;@Target(AnnotationTarget.PROPERTY)
annotation class JsonName(val name: String)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.1.5 메타애노테이션: 애노테이션을 처리하는 방법 제어&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;메타애노테이션이란?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애노테이션 클래스에 적용할 수 있는 애노테이션&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;표준라이브러리에서는 몇 가지 메타애노테이션이 있으며, 그런 애노테이션은 컴파일러가 애노테이션을 처리하는 방법을 제어한다. 가장 흔히 쓰는 메타에노테이션은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;@Taget&lt;/b&gt;이다. 제이키드 라이브러리는 프로퍼티 애노테이션만을 사용하므로, 애노테이션 클래스에&lt;span&gt;&amp;nbsp;&lt;/span&gt;@Target을 지정해야 한다. 필요하다면 둘 이상의 대상을 한꺼번에 선언할수 있다.&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;pre class=&quot;crystal&quot; style=&quot;background-color: #f7f7f7;&quot;&gt;&lt;code&gt;@Target(AnnotationTarget.PROPERTY, AnnotationTarget.METHOD)
annotation class JsonExclude&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 메타애노테이션을 직접 만든다면&amp;nbsp;AnnotationTarget.ANNOTATION_CLASS를 대상으로 지정하라.&lt;/p&gt;
&lt;pre id=&quot;code_1685584709884&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class BindingAnnotation

@BindingAnnotation
annotation class MyBinding&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.1.6 애노테이션 파라미터로 클래스 사용&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어떤 클래스를 선언 메타데이터로 참조할 수 있는 기능이 필요할 때에는 클래스 참조를 파라미터로 하는 애노테이션 클래스를 선언하면 그런 기능을 사용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@DeserializeInterface&lt;/b&gt;애노테이션&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;직렬화된&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Person&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;인스턴스를 역직렬화하는 과정에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;company&lt;/b&gt; 프로퍼티를 표현하는 JSON을 읽으면 제이키드는 그 프로퍼티 값에 해당하는 JSON을 역직렬화하면서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;CompanyImpl&lt;/b&gt;의 인스턴스를 만들어 Person 인스턴스의 company 프로퍼티에 설정한다. 이렇게 역직렬화에 사용할 클래스를 지정하기 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;DeserializeInterface&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;애노테이션의 인자로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;CompanyImpl::class&lt;/b&gt;를 넘긴다.&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #f7f7f7;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;interface Company { 
    val name: String
}

data class CompanyImpl(override val name: String) : Company

data class Person(
    val name: String,

    @DeserializeInterface(CompanyImpl::class) 
    val company: Company
)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;KClass&lt;/b&gt;는 자바&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;java.lang.class&lt;/b&gt;타입과 같은 역할을 하는 코틀린 타입이다. 코틀린 클래스에 대한 참조를 저장할 때 KClass 타입을 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.1.7 애노테이션 파라미터로 제네릭 클래스 받기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 제이키드는 원시 타입이 아닌 프로퍼티를 중첩된 객체로 직렬화 한다. 이런 기본동작을 변경하고 싶으면 값을 직렬화하는 로직을 직접 제공하면 된다. 클래스를&amp;nbsp;인자로&amp;nbsp;받아야&amp;nbsp;하면&amp;nbsp;애노테이션&amp;nbsp;파라미터&amp;nbsp;타입에&amp;nbsp;KClass&amp;lt;out&amp;nbsp;허용할&amp;nbsp;클래스&amp;nbsp;이름&amp;gt;&amp;nbsp;을&amp;nbsp;쓴다.&amp;nbsp;&amp;nbsp;제네릭&amp;nbsp;클래스를&amp;nbsp;인자로&amp;nbsp;받아야&amp;nbsp;하면&amp;nbsp;KClass&amp;lt;out&amp;nbsp;허용할&amp;nbsp;클래스&amp;nbsp;이름&amp;lt;*&amp;gt;&amp;gt;&amp;nbsp;처럼&amp;nbsp;허용할&amp;nbsp;클래스&amp;nbsp;이름&amp;nbsp;뒤에&amp;nbsp;스타&amp;nbsp;프로젝션을&amp;nbsp;덧붙인다.&amp;nbsp;이&amp;nbsp;애노테이션이&amp;nbsp;어떤&amp;nbsp;타입에&amp;nbsp;대해&amp;nbsp;쓰일&amp;nbsp;지&amp;nbsp;알&amp;nbsp;수&amp;nbsp;없기&amp;nbsp;때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@CustomSerializer &lt;/b&gt;애노테이션은 커스텀 직렬화 클래스에 대한 참조를 인자로 받는다.&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #f7f7f7;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;data class Person( 
    val name: String,
    @CustomSerializer(DateSerializer::class) val birthDate: Date
)

annotation class CustomSerializer(
	val serializerClass: KClass&amp;lt;out ValueSerializer&amp;lt;*&amp;gt;&amp;gt;
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.2 리플렉션: 실행 시점에 코틀린 객체 내부 관찰&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리플렉션이란?&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 시점에 동적으로 객체의 프로퍼티와 메서드에 접근할 수 있게 해주는 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린에서 리플렉션을 사용하려면 두 가지 서로 다른 리플렉션 API를 다뤄야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #292929; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;java.lang.reflect 패키지를 통해 제공하는 표준 리플렉션&lt;/li&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;코틀린이 kotlin.reflect 패키지를 통해 제공하는 코틀린 리플렉션&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON 직렬화 라이브러리는 어떤 객체든 JSON으로 변환할 수 있어야 하고, 실행 시점이 되기 전까지는 라이브러리가 직렬화할 프로퍼티나 클래스에 대한 정보를 알 수 없다. 이런 경우 리플렉션을 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.2.1 코틀린 리플렉션 API: KClass, KCallable, KFunction, KProperty&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;kClass&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;java.lang.class&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;에 해당하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;KClass&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;를 사용하면 클래스 안에 모든 선언을 열거하고 각 선언에 접근하거나 클래스의 상위 클래스를 얻는 등의 작업이 가능하다. 실행시점에 객체의 클래스를 얻으려면 먼저 객체의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;javaClass&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;프로퍼티를 사용해 객체의 자바 클래스를 얻어야 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;javaClass&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;는 자바의 java.lang.Object.getClass()와 같다. 일단 자바 클래스를 얻었으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;kotlin&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;확장 프로퍼티를 통해 자바에서 코틀린 리플렉션 API로 옮겨올수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #f7f7f7;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Person(val name: String, val age: Int)

&amp;gt;&amp;gt;&amp;gt; import kotlin.reflect.full.*          # memberProperties 확장함수 import
&amp;gt;&amp;gt;&amp;gt; val person = Person(&quot;Alice&quot;, 29)
&amp;gt;&amp;gt;&amp;gt; val kClass = person.javaClass.kotlin  # kClass&amp;lt;Person&amp;gt;의 인스턴스를 반환한다.
&amp;gt;&amp;gt;&amp;gt; println(kClass.simpleName)
Person
&amp;gt;&amp;gt;&amp;gt; kClass.memberProperties.forEach { println(it.name) }
age
name&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;KClass에 대해 사용할 수 있는 다양한 기능은 실제로 &lt;b&gt;kotlin-reflect&lt;/b&gt; 라이브러리 (&lt;span style=&quot;background-color: #dddddd; color: #ef5369; text-align: start;&quot;&gt;implementation &quot;org.jetbrains.kotlin:kotlin-reflect&quot;)&amp;nbsp;&lt;/span&gt;를 통해 제공하는 확장함수다. 이런 확장함수를 사용하기 위해서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;import kotlin.reflect.full.* 로 확장함수를 import해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;kCallable&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 함수와 프로퍼티를 아우르는 공통 상위 인터페이스&lt;/b&gt;이다. 그안에는 call메소드가 있다. call을 사용하면 함수나 프로퍼티의 게터를 호출할 수 있다.&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #f7f7f7;&quot;&gt;&lt;code&gt;interface KClass&amp;lt;T : Any&amp;gt;
{ 
    val simpleName: String?
    val qualifiedName: String?
    val members: Collection&amp;lt;KCallable&amp;lt;*&amp;gt;&amp;gt;
    val constructors: Collection&amp;lt;KFunction&amp;lt;T&amp;gt;&amp;gt;
    val nestedClasses: Collection&amp;lt;KClass&amp;lt;*&amp;gt;&amp;gt;
    ...
}
interface KCallable&amp;lt;out R&amp;gt; {
    fun call(vararg args: Any?): R
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;kFunction&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;리플렉션&lt;span&gt;&amp;nbsp;&lt;/span&gt;call을 사용할 때는 함수 인자를 vararg 리스트로 전달한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;::foo식의 값 타입이 리플렉션 API에 있는 KFunction 클래스의 인스턴스임을 알 수 있다. 이 함수 참조가 가르키는 함수를 호출하려면&lt;span&gt;&amp;nbsp;&lt;/span&gt;KCallable.call 메소드를 호출한다.&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #f7f7f7;&quot;&gt;&lt;code&gt;fun foo(x: Int) = println(x)
&amp;gt;&amp;gt;&amp;gt; val kFunction = ::foo
&amp;gt;&amp;gt;&amp;gt; kFunction.call(42)
42&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;kProperty&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;kProperty의 call 메서드를 호출할 수 있다. kProperty의 call은 프로퍼티의 게터를 호출한다.&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;pre class=&quot;ruby&quot; style=&quot;background-color: #f7f7f7;&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; var counter = 0
&amp;gt;&amp;gt;&amp;gt; val kProperty = ::counter
&amp;gt;&amp;gt;&amp;gt; kProperty.setter.call(21)
&amp;gt;&amp;gt;&amp;gt; println(kProperty.get()) 
21&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;멤버 프로퍼티는 kProperty1 인스턴스로 표현된다.&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;pre class=&quot;ruby&quot; style=&quot;background-color: #f7f7f7;&quot;&gt;&lt;code&gt;class Person(val name:String, val age:Int)
&amp;gt;&amp;gt;&amp;gt; val person = Person(&amp;ldquo;Alice&amp;rdquo;, 29)
&amp;gt;&amp;gt;&amp;gt; val memberProperty = Person::age
&amp;gt;&amp;gt;&amp;gt; println(memberProperty.get(person)) 
&amp;gt;&amp;gt;&amp;gt; 29&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.2.2 리플렉션을 사용한 객체 직렬화 구현&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래 함수는 클래스의 각 프로퍼티를 차례로 직렬화한다. 결과 JSON은 { prop1: value1, prop2: value2 } 같은 형태다.&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #f7f7f7;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;private fun StringBuilder.serializeObject(obj: Any){
    val kClass = obj.javaClass.kotlin
    val properties = kClass.memberProperties
    properties.jointToStringBuilder(
    		this, prefix = &amp;ldquo;{&amp;ldquo;, postfix = &amp;ldquo;}&amp;rdquo;) { prop -&amp;gt; 
        serializeString(prop.name)
        append(&amp;ldquo;: &amp;ldquo;)
        serializePropertyValue(prop.get(obj)) 
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.2.3 애노테이션을 활용한 직렬화 제어&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.2.4 JSON 파싱과 객체 역직렬화&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10.2.5 최종 역직렬화 단계: callBy(), 리플렉션을 사용해 객체 만들기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스터디/Kotlin In Action</category>
      <category>Kotlin</category>
      <category>Kotlin In Action</category>
      <category>Kotlin in action 10장</category>
      <category>kotlin in action 정리</category>
      <category>KotlinInAction</category>
      <category>코틀린</category>
      <category>코틀린인액션</category>
      <author>로그뉴</author>
      <guid isPermaLink="true">https://flexiblecode.tistory.com/234</guid>
      <comments>https://flexiblecode.tistory.com/234#entry234comment</comments>
      <pubDate>Tue, 30 May 2023 10:41:57 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin In Action]6장 - 코틀린 타입 시스템</title>
      <link>https://flexiblecode.tistory.com/233</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;** 이 글은 Kotlin In Action을 읽고 정리한 글입니다. **&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;kotlininaction.jpeg&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5Y9Vl/btr3oe3plZD/gKLKcQTRQyMFjRXj8hc5g1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5Y9Vl/btr3oe3plZD/gKLKcQTRQyMFjRXj8hc5g1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5Y9Vl/btr3oe3plZD/gKLKcQTRQyMFjRXj8hc5g1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5Y9Vl%2Fbtr3oe3plZD%2FgKLKcQTRQyMFjRXj8hc5g1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;375&quot; data-filename=&quot;kotlininaction.jpeg&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 널 가능성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1678714688219&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun strLenSafe(s: String?) = s.length()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;타입 이름 뒤에 물음표(?)를 붙이면 그 타입의 변수나 프로퍼티에 null 참조를 저장할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678714782422&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val x:String? = null
val y:String = x&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;널이 될 수 있는 값을 널이 될 수 없는 타입의 변수에 대입할 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;안전한 호출 연산자 ?.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;null 검사와 메서드 호출을 한 번의 연산으로 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678715260150&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val country = this.company?.address?.country&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;엘비스 ?:연산자&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이항 연산자로 좌항을 계산한 값이 널인지 검사한다. 좌항 값이 널이 아니면 좌항 값을 결과로 하고, 좌항 값이 널이면 우항 값을 결과로 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678715230749&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val info = xxxRepository.findById(id) ?: throw CustomException()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;안전한 캐스트 as?&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;as?는 값을 대상 타입으로 변환할 수 없으면 null을 반환한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678715456045&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val otherPerson = o as? Person ?: return false // 변환 실패시 null 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;널 아님 단언 !!&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어떤 값이든 널이 될 수 없는 타입으로 강제로 바꿀 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여러 !! 단언문을 한 줄에 함께 쓰는 것을 피하라.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678715596003&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val sNotNull: String = s!!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;let 함수&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;null이 될 수 있는 값을 null이 아닌 값만 인자로 받는 함수에 넘기는 경우.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;let 함수는 자신의 수신 객체를 인자로 전달받은 람다에게 넘긴다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;let을 중첩시키는 것보다, if를 사용해 모든 값을 한꺼번에 검사하는 편이 낫다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678715714594&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;email?.let { sendEmailTo(it) }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;lateinit 나중에 초기화&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로퍼티를 나중에 초기화 할 수 있는 키워드로, 나중에 초기화하는 프로퍼티는 항상 var여야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678715853436&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private lateinit var myService: MyService&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Null이 될 수 있는 타입 확장&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;널이 될 수 있는 타입의 확장 함수는 안전한 호출 없이도 호출 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코틀린에서는 널이 될 수 있는 타입의 확장함수 안에서는 this가 널이 될 수 있다는 점이 자바와 다르다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678716080119&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;input.isNullorEmpty()
input.isNullorBlank()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;타입 파라미터의 널 가능성&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모든 타입 파라미터는 기본적으로 널이 될 수 있다.&lt;br /&gt;널 가능 타입과 널 불가 타입 모두 타입 파라미터 인자로 사용 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;타입 파라미터 T를 클래스나 함수 안에서 타입 이름으로 사용하면 이름 끝에 물음표가 없더라도 T가 널이 될 수 있는 타입이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678716644172&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun &amp;lt;T&amp;gt; printHashCode(t: T) { // t는 Any? 타입으로 추론
    println(t?.hashCode()) 
}

fun &amp;lt;T: Any&amp;gt; printHashCode(t: T) { // t는 Any 타입으로 추론
     println(t.hashCode())
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 파라미터가 널이 아님을 확실히 하려면 널이 될 수 없는 타입 상한을 지정해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1679370547859&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun &amp;lt;T: Any&amp;gt; printHashCode(t: T) { // 널이 될 수 없는 타입 T
		println(t.hashCode())
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;자바의 널 가능성 애노테이션&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바 코드에 널 가능성 애노테이션을 사용하면 코틀린은 그 정보를 활용한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;@Nullable String ‐&amp;gt; String?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;@NotNull String ‐&amp;gt; String&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JSR‐305 표준, 안드로이드, 젯브레인 애노테이션 등의 널 가능성 애노테이션 지원한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;플랫폼 타입&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;널 관련 정보를 알 수 없는 자바 타입을 말한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;널 가능 타입으로 처리해도 되고 널 불가 타입으로 처리해도&amp;nbsp;되기 때문&lt;/b&gt;에&amp;nbsp;널 가능성을 코틀린 코드에서 알맞게 처리해야&amp;nbsp;한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코틀린에서 플랫폼 타입을 선언할 수는 없고, &lt;b&gt;자바 코드에서 가져온 타입만 플랫폼 타입이 된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678716886054&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person { 
    private final String name;
    public String getName() {
       return name;
    }   
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678716964327&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun yellAt(p: Person) {
    println(person.name.toUpperCase() + &quot;!!!&quot;) // person.name이 null일 때 예외 발생
}

fun yellAtSafe(p: Person) {
    println((person.name ?: &quot;Anyone&quot;).toUpperCase() + &quot;!!!&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 코틀린의 원시 타입&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코틀린은 원시 타입과 래퍼 타입을 구분하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;
&lt;div style=&quot;background-color: #ffffff;&quot;&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;숫자 변환&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;한 숫자 타입을 다른 타입 숫자로 자동 변환하지 않으므로 명시적으로 변환해야 함&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;숫자 리터럴은 컴파일러가 필요한 변환을 자동으로 처리 연산자를 각 타입에 대해 오버로딩하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678717233194&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val b: Byte = 1
val l = b + 1L
val l1: Long = 42 // Int 타입 리터럴을 Long으로 컴파일러가 변환 
val i: Int = 42 
val l2: Long = i.toLong()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;
&lt;div style=&quot;background-color: #ffffff;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;최상위 타입&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1678717319650&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val answer: Any = 42&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;
&lt;div style=&quot;background-color: #ffffff;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Any 타입은 모든 널이 될 수 없는 타입의 조상 타입니다. 내부에서&amp;nbsp;Any는&amp;nbsp;Object에 대응한다.&amp;nbsp;코틀린에서&amp;nbsp;Any를 사용하면 자바 바이트코드의&amp;nbsp;Object로 컴파일된다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Any 타입에는 널이 들어갈 수 없다! -&amp;gt; Any?를 사용해야 널 허용.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Any: toString, equals, hashCode&amp;nbsp;세 개의 메서드 제공&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;
&lt;div style=&quot;background-color: #ffffff;&quot;&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Unit&amp;nbsp;타입&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Unit&amp;nbsp;타입은 자바의&amp;nbsp;void와 같은 기능을 한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관심을 가질 만한 내용을 리턴하지 않는 함수의 리턴 타입으로&amp;nbsp;Unit&amp;nbsp;사용한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Unit은&amp;nbsp;void와 달리 타입, Unit이라는 단일 값을 가짐. 리턴 타입이&amp;nbsp;Unit인 함수는 묵시적으로&amp;nbsp;Unit을 리턴&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1678717570433&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Processor&amp;lt;T&amp;gt; {
    fun process(): T
}

class NoResultProcessor: Processor&amp;lt;Unit&amp;gt; {
    override fun process() {
        // 업무 처리 코드
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;
&lt;div style=&quot;background-color: #ffffff;&quot;&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Nothing&amp;nbsp;타입&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;fun fail(message: String): Nothing {
     throw IllegalStateException(message)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nothing 타입은 함수가 결코 정상적을 끝나지 않는다는 의미를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Nothing&amp;nbsp;타입은 아무 값도 포함하지 않는다.&amp;nbsp;함수의 반환 타입이나 반환 타입으로 쓰일 타입 파라미터로만 쓸 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 컬렉션과 배열&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;읽기 전용과 변경 가능한 컬렉션&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;
&lt;div style=&quot;background-color: #ffffff;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인터페이스 분리&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;kotlin.collections.Collection :&amp;nbsp;조회 기능만 제공&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;kotlin.collections.MutableCollection :&amp;nbsp;수정 기능도 함께 제공&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;읽기 전용 컬렉션 인터페이스는 읽기 전용이고,&amp;nbsp;실제 구현체는 수정 가능할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코틀린 컬렉션과 자바&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 코틀린 컬렉션은 그에 상응하는 자바 컬렉션 인터페이스의 인스턴스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 전용과 변경 가능 2가지를 제공한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhn9Fm/btr3uD2QUXb/bNxg6y2yFjcAz96tDjaQs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhn9Fm/btr3uD2QUXb/bNxg6y2yFjcAz96tDjaQs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhn9Fm/btr3uD2QUXb/bNxg6y2yFjcAz96tDjaQs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdhn9Fm%2Fbtr3uD2QUXb%2FbNxg6y2yFjcAz96tDjaQs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;659&quot; height=&quot;335&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스터디/Kotlin In Action</category>
      <category>Kotlin</category>
      <category>Kotlin In Action</category>
      <category>Kotlin in action 6장</category>
      <category>kotlin in action 정리</category>
      <category>KotlinInAction</category>
      <category>코틀린</category>
      <category>코틀린인액션</category>
      <author>로그뉴</author>
      <guid isPermaLink="true">https://flexiblecode.tistory.com/233</guid>
      <comments>https://flexiblecode.tistory.com/233#entry233comment</comments>
      <pubDate>Mon, 13 Mar 2023 23:28:07 +0900</pubDate>
    </item>
  </channel>
</rss>