<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>코드네임 JY</title>
    <link>https://codenamejy.tistory.com/</link>
    <description>매일매일 발전하는 사람</description>
    <language>ko</language>
    <pubDate>Tue, 18 Aug 2026 15:27:44 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>영재임재영</managingEditor>
    <image>
      <title>코드네임 JY</title>
      <url>https://tistory1.daumcdn.net/tistory/3492337/attach/f94c988276c74993925fe79c87d5526e</url>
      <link>https://codenamejy.tistory.com</link>
    </image>
    <item>
      <title>[UMC] 4th UMC 해커톤 후기</title>
      <link>https://codenamejy.tistory.com/74</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;[UMC] 4th 해커톤 후기.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xDrsT/btsmKAegIAJ/O5CFBz8o9F5KQEtSZVgkd0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xDrsT/btsmKAegIAJ/O5CFBz8o9F5KQEtSZVgkd0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xDrsT/btsmKAegIAJ/O5CFBz8o9F5KQEtSZVgkd0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxDrsT%2FbtsmKAegIAJ%2FO5CFBz8o9F5KQEtSZVgkd0%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;2940&quot; height=&quot;1656&quot; data-filename=&quot;[UMC] 4th 해커톤 후기.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1656&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  해커톤 과정&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baWVQQ/btsmOJIn2U0/V5qsJdZp4bGPFtku9BBIz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baWVQQ/btsmOJIn2U0/V5qsJdZp4bGPFtku9BBIz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baWVQQ/btsmOJIn2U0/V5qsJdZp4bGPFtku9BBIz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaWVQQ%2FbtsmOJIn2U0%2FV5qsJdZp4bGPFtku9BBIz1%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;300&quot; height=&quot;400&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1706&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;날짜&lt;/b&gt; : 2023년 7월 3일 (월) 13:00 ~ 7월 4일 (화) 10:00 (무박 2일)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;/span&gt; &lt;b&gt;주제&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;UMC는 대학교 연합 IT 동아리인데, 이번 해커톤은 UMC에 참여하는 모든 대학교가 참여해 상당히 큰 규모로 열린 대회였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태어나서 처음 경험해 본 해커톤이었다! 과연 내가 밤을 새서 코딩할 수 있을지.. (고민하며 전날 일부러 4시에 잠을 잤다 ㅋㅋㅋ)&lt;b&gt;&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;팀은 랜덤 매칭이었다! 총 10개의 팀으로 나누어졌고 각 팀마다 일정한 비율로 정해진 직군이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각자 &lt;u&gt;PM / 디자이너 / 프론트(Web, Android, iOS) / 서버(Spring, Node)&lt;/u&gt; 직군 중 하나를 가지는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀은 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;nbsp;PM 1 + 디자이너 1 (디자이너가 없는 팀도 있었다) + 프론트(iOS) 3 + 서버(Spring) 3&amp;nbsp;&lt;/span&gt;&lt;/b&gt; 으로 구성되었다.&lt;/p&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;입장은 13시, 본격적인 해커톤 시작은 14시부터 시작되었다. 14시부터 바로 &quot;시작!&quot; 하면서 타이머가 굴러갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 팀 프로젝트 경험이 적지는 않지만, 이렇게 &quot;시작!&quot; 하고 바로 맨땅에 건물을 지으라고 하는 것은 조금 당황스럽기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀은 먼저 &lt;u&gt;'아이디어 도출'&lt;/u&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;p data-ke-size=&quot;size16&quot;&gt;아이디어 구체화하고 확정하기까지는 대략 2시간이 걸렸는데, 중점적으로 고려한 부분은 &lt;u&gt;'공통 관심사를 고려하자'&lt;/u&gt; 였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원 대부분이 야구를 좋아했는데, 좌석을 예매하고 이에 대한 정보를 일일이 찾아보는 것이 불편하다고 생각하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;협업 방식에 있어서는 API 명세서나 기능 명세서와 같은 문서를 만들어 적극 활용하자고 내가 먼저 제안하였다.&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;3학년 1학기 때 '소프트웨어공학' 수업을 들었는데, 소프트웨어를 개발하는데 있어 문서가 매우 중요하다는 사실을 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀플을 진행하며 요구사항 명세를 통해 기능 명세서를 정리했고, API 명세서를 작성해 프론트 연동 시 원활한 소통이 가능했다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;✅ 요구사항 및 기능 명세서 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://bead-help-a4f.notion.site/abf032abdd4b44d589b1822946674582?pvs=4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[링크  ️]&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ API 명세서 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://bead-help-a4f.notion.site/API-e9ec64495f304027a177361b4dd1b14f?pvs=4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[링크  ️]&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 백엔드 설계 문서 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://bead-help-a4f.notion.site/85773cc343964b1a9c90497b5f8c211b?pvs=4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[링크  ️]&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✨ 수상&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_8412.jpg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFCXsA/btsmNVwiCre/6QhoSEGGRPXvs1vG4jy841/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFCXsA/btsmNVwiCre/6QhoSEGGRPXvs1vG4jy841/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFCXsA/btsmNVwiCre/6QhoSEGGRPXvs1vG4jy841/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFCXsA%2FbtsmNVwiCre%2F6QhoSEGGRPXvs1vG4jy841%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;400&quot; height=&quot;400&quot; data-filename=&quot;IMG_8412.jpg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다들 열심히 고생한 결과는..! 총 10팀 중 &lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #9d9d9d;&quot;&gt;&amp;nbsp;최우수상(2등) &lt;/span&gt;&lt;/b&gt;&lt;/span&gt;을 수상했다!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉬지 않고 밤을 새었기 때문에 시상식 때는 비몽사몽했다. 그래서 최우수상에 우리 팀 이름이 불렸을 때 믿기지 않았다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_8399.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAA5mZ/btsmNVC3kEJ/rg0MayZ6j0ueOek111eo51/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAA5mZ/btsmNVC3kEJ/rg0MayZ6j0ueOek111eo51/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAA5mZ/btsmNVC3kEJ/rg0MayZ6j0ueOek111eo51/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAA5mZ%2FbtsmNVC3kEJ%2Frg0MayZ6j0ueOek111eo51%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;400&quot; height=&quot;416&quot; data-filename=&quot;IMG_8399.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해커톤 명찰도 여름여름한 계절감이 느껴져서 엄청 예뻤다  ️ ️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_IMG_8402.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/reS5M/btsmOUXEL4c/xLYulaP08YRvkWEh0ZJfxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/reS5M/btsmOUXEL4c/xLYulaP08YRvkWEh0ZJfxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/reS5M/btsmOUXEL4c/xLYulaP08YRvkWEh0ZJfxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FreS5M%2FbtsmOUXEL4c%2FxLYulaP08YRvkWEh0ZJfxK%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;550&quot; height=&quot;436&quot; data-filename=&quot;edited_IMG_8402.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 나도 그렇고 다들 코딩하는데 바쁘고 정신도 없어서 사진을 많이 찍어놓지 못해 좀 아쉽긴하다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 TMI지만 팀원 대부분이 맥북이었고.. 신기하게도 모두 스페이스 그레이 컬러였다.. &lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;(맥북은 역시 스그가 국룰!?)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-03 오후 9.38.56.png&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1912&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVS96X/btsnxk39cyt/wslIVJF8jQyxMn5SdVy0j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVS96X/btsnxk39cyt/wslIVJF8jQyxMn5SdVy0j0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVS96X/btsnxk39cyt/wslIVJF8jQyxMn5SdVy0j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVS96X%2Fbtsnxk39cyt%2FwslIVJF8jQyxMn5SdVy0j0%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;700&quot; height=&quot;1912&quot; data-filename=&quot;스크린샷 2023-07-03 오후 9.38.56.png&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1912&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해커톤 진행 중에 ERD 설계와 API 명세서를 작성하고 있을 때 캡쳐한 사진이다! 문서를 잘 남겨놓으려고 노력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이제 가장 중요한..! 해커톤을 통해 성장한 내용과 아쉬웠던 내용을 정리하겠다!&lt;/p&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;span style=&quot;color: #f3c000;&quot;&gt;&lt;b&gt;[기술 관련 1] API Response 구조에 대한 고민&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안 혼자 백엔드를 공부했을 때는, API Response 구조에 대해 크게 고민하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 &quot;데이터만 넘기면 되는 것이 아닌가?&quot; 라는 생각에 리턴 타입이 어떤 자료형일지만 생각했는데, 그게 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-13 오후 3.43.58.png&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZINDL/btsnvEz8kRs/LPX1q6l4kTxonLlLD4Aj7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZINDL/btsnvEz8kRs/LPX1q6l4kTxonLlLD4Aj7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZINDL/btsnvEz8kRs/LPX1q6l4kTxonLlLD4Aj7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZINDL%2FbtsnvEz8kRs%2FLPX1q6l4kTxonLlLD4Aj7K%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;300&quot; height=&quot;200&quot; data-filename=&quot;스크린샷 2023-07-13 오후 3.43.58.png&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 해커톤에서는 같은 백엔드 팀원이 알려주신 방법으로 위와 같이 API Response를 작성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 데이터만 넘기는 것인줄 알았는데! &lt;u&gt;상태 코드, 결과, 메시지&lt;/u&gt;를 json 형식으로 같이 반환하는 것이었다!&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;결국 서버의 Response는 처리 결과를 간략하게 요약하고(code), 처리 결과를 보여주고(result),&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 마주하는 사람에게 메시지를 주는(message) 형식을 가지는 것이 의무라고 한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 프로젝트를 진행하며 API Response 구조를 적극 활용하고, 어떻게 디자인하면 좋을지 생각해 볼 것이다!&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: #f3c000;&quot;&gt;&lt;b&gt;[기술 관련 2]&amp;nbsp; AWS 활용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현 기능 중 사진을 올리고 조회하는 기능이 있는데, 이를 어떻게 구현할지 생각해보다가 AWS S3를 생각하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS S3은 'AWS에서 제공하는 온라인 스토리지 웹 서비스' 인데, 여기에 사진을 올리고 받은 링크를 DB에 저장하여 구현하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 해커톤 진행 중 AWS RDS 서비스도 사용해보았다! AWS 관련하여 이해도를 조금은 높일 수 있는 경험이었다!&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: #f3c000;&quot;&gt;&lt;b&gt;[기술 관련 3]&amp;nbsp;&lt;span style=&quot;text-align: start;&quot;&gt; 클라우드타입 배포&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소 배포에 익숙하지 않았기에, 짧은 시간안에 배포까지 진행하기에는 한계가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이번에는 몇 달 전에 전공 팀 프로젝트 팀원분께 배운 '클라우드타입' 을 사용하여 간단한 배포를 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깃허브에는 application.yml 파일을 올리지 않기 때문에, 환경 변수 관련하여 애를 많이 먹었지만 숱한 삽질 끝에 배포도 성공했다!&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: #009a87;&quot;&gt;&lt;b&gt;[협업 관련 1]&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt; 타 파트와 커뮤니케이션&lt;/span&gt;&lt;/b&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;/p&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;또한 프론트엔드에서 ERD 관련 논의가 들어왔는데, 같이 협의하여 효율적인 ERD 설계를 위해 노력하였다.&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 data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;[협업 관련 2] &lt;span style=&quot;text-align: start;&quot;&gt; 깃허브 Branch, Issues, Pull Requests 활용&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; 브랜치를 나누어 작업하는 것은 몇 번 해보았지만, Issues나 Pull Requests를 사용해본 적은 없었다.&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-13 오후 4.20.46.png&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nXuQZ/btsnwltpJrT/Xhue8xqdISHoccbC4rDtrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nXuQZ/btsnwltpJrT/Xhue8xqdISHoccbC4rDtrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nXuQZ/btsnwltpJrT/Xhue8xqdISHoccbC4rDtrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnXuQZ%2FbtsnwltpJrT%2FXhue8xqdISHoccbC4rDtrK%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;500&quot; height=&quot;642&quot; data-filename=&quot;스크린샷 2023-07-13 오후 4.20.46.png&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;642&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;Issues에는 간단하게 어떤 기능을 포함하여 커밋하였는지 작성하였다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를 main (혹은 master) 브랜치에 merge 하기 위해서는 Pull Requests가 필요했고, 이를 사용하여 merge 하였다.&lt;/p&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;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;[협업 관련 3]&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt; 협업을 위한 다양한 툴 사용&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;온라인 디자인 툴인 'Figma' 를 처음 마주하였다. 디자이너님께서 손수 Figma에 화면을 그려주셨고, 잘 활용할 수 있었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 ERD 설계 단계에서는 'ERDCloud' 를 사용하였다. 온라인에서 동시에 ERD를 설계할 수 있어서 편리했다.&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 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; 일단 정식 배포까지는 진행하지 못 했다! AWS, 도커 등을 활용하여 배포하고 싶었지만, 능력이 많이 부족하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 여름 방학 때 도커를 학습해보고 싶고, 실제 서비스까지 배포하려면 보안도 생각해야하기 때문에 VPC도 공부해야 한다!&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;어쨌든 짧은 시간 안에 협업을 진행하며  많은 것을 배울 수 있었던 해커톤이었다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나에게 많은 것을 알려준 팀원들에게 너무 고맙다! 이를 통해 꾸준한 성장을 이루고 싶다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 좋은 기회를 잡은 것 같아서 뿌듯하고, 무엇보다 수상까지 이어져서 좋았다!  &lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>영재임재영</author>
      <guid isPermaLink="true">https://codenamejy.tistory.com/74</guid>
      <comments>https://codenamejy.tistory.com/74#entry74comment</comments>
      <pubDate>Thu, 13 Jul 2023 16:42:47 +0900</pubDate>
    </item>
    <item>
      <title>[프로젝트] 넌 못 지나간다</title>
      <link>https://codenamejy.tistory.com/72</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;#4 넌 못 지나간다.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnMOPg/btrZ658fbHk/lLua8jj0rcqWNTRTMy3CVK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnMOPg/btrZ658fbHk/lLua8jj0rcqWNTRTMy3CVK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnMOPg/btrZ658fbHk/lLua8jj0rcqWNTRTMy3CVK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnMOPg%2FbtrZ658fbHk%2FlLua8jj0rcqWNTRTMy3CVK%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;2940&quot; height=&quot;1645&quot; data-filename=&quot;#4 넌 못 지나간다.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1645&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;가끔 유튜브나 트위치 방송 보면 도네로 보이는 유명한 밈인거 같아서 함 써봤다 (&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://youtu.be/qrNzC5NWro0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://youtu.be/qrNzC5NWro0&lt;/a&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;나는 절대로 포스팅 주제랑 관련 없는 제목은 들고 오지 않는다는 사실 내용을 보면 왜 이 제목을 썼는지 알게 될 것이에요 호호&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  DTO (Data Transfer Object)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO의 사전적인 의미는 '계층 간 데이터 교환을 위해 사용하는 객체' 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 말하는 계층은 &lt;u&gt;Presentation Layer / Business Layer / Data Access Layer&lt;/u&gt; 정도로 알려져 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 단순화해서 보면 이는 각각 &lt;u&gt;Controller / Service / Repository&lt;/u&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;데이터는 엔티티에 담겨 있다. 그렇다면 엔티티를 계층마다 전달해가며 사용하면 되지 않을까? 정답은.. 안 된다!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔티티 클래스는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 데이터베이스와 맞닿은 핵심 클래스 &lt;/span&gt;&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;엔티티 클래스를 웹 화면에서 바로 사용한다고 할 때, 요구사항이 복잡해지면 엔티티 클래스에 화면 처리를 위한 기능이 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 엔티티는 화면에 종속적으로 변하게 되고, 지저분해진 엔티티는 유지보수에 어려움이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 핵심 비즈니스 로직만 가지고 있어야 하고, 화면을 위한 로직을 처리하려면 DTO가 필요 &lt;/span&gt;&lt;/b&gt;하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설계할 때 좋은 방향은&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; View Layer와 DB Layer의 역할을 철저하게 분리하는 것 &lt;/span&gt;&lt;/b&gt;이다!&lt;/p&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;그렇다면 DTO를 어떤 계층에서 사용하는 것이 좋을까? 일단 혼자서 생각해 본 내 생각을 말해보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Repository는 엔티티를 저장하는 계층이라고 생각한다. 따라서 이 곳에서는 엔티티 자체를 다뤄야하므로 여기서는 별로..?&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;한 계층 올라와서 생각해보자. Controller에서 DTO를 다루고, Repository에서는 순수한 엔티티를 다룬다고 하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service가 중간에서 DTO &amp;rarr; Entity 혹은 Entity&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;rarr; DTO 로 변환해주는 역할을 한다면? 이게 가장 깔끔하다고 생각한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2884&quot; data-origin-height=&quot;1206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/skBXq/btrZ71qSUbK/o5EftxKebemuL56mfiUs51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/skBXq/btrZ71qSUbK/o5EftxKebemuL56mfiUs51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/skBXq/btrZ71qSUbK/o5EftxKebemuL56mfiUs51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FskBXq%2FbtrZ71qSUbK%2Fo5EftxKebemuL56mfiUs51%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;860&quot; height=&quot;360&quot; data-origin-width=&quot;2884&quot; data-origin-height=&quot;1206&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레이리스트 프로젝트에서 간단하게 DTO를 어떻게 사용했는지 그림을 그려보았다.&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;u&gt;&lt;b&gt;아티스트 등록 로직&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1️⃣ Controller&lt;/b&gt; : SaveDto에 데이터를 받아서 Service로 넘김&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2️⃣ Service&lt;/b&gt; : Repository에서는 Entity만 다루므로, DTO를 Entity로 매핑해서 Repository로 넘김&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3️⃣ Repository&lt;/b&gt; : Entity 저장(save) 가능&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;u&gt;&lt;b&gt;아티스트 조회 로직&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1️⃣ Repository&lt;/b&gt; : Entity 조회(find) 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2️⃣ Service&lt;/b&gt; : Repository에서 넘어온 Entity를 ResponseDto 객체를 생성하고 매핑해서 Controller로 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3️⃣ Controller&lt;/b&gt; : 웹 화면에서는 Entity가 보여지지 않고, ResponseDto에 담겨진 데이터가 보임&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 Service 계층에서 필요에 따라 순수한 엔티티를 반환하는 로직도 있다! 몇몇 기능들을 수행하기 위해서!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 중요한 건..? (꺾이지 않는 마음도 맞고) 웹 화면에서는 절대로 엔티티를 보여주지 말자!  &lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>영재임재영</author>
      <guid isPermaLink="true">https://codenamejy.tistory.com/72</guid>
      <comments>https://codenamejy.tistory.com/72#entry72comment</comments>
      <pubDate>Mon, 20 Feb 2023 16:39:55 +0900</pubDate>
    </item>
    <item>
      <title>[프로젝트] 즉시 로딩과 지연 로딩</title>
      <link>https://codenamejy.tistory.com/71</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;#3 즉시 로딩과 지연 로딩.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1651&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3N0hZ/btrZLVsqyGX/nKqKUsiLXBc2Ldp08wZFk1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3N0hZ/btrZLVsqyGX/nKqKUsiLXBc2Ldp08wZFk1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3N0hZ/btrZLVsqyGX/nKqKUsiLXBc2Ldp08wZFk1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3N0hZ%2FbtrZLVsqyGX%2FnKqKUsiLXBc2Ldp08wZFk1%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;2940&quot; height=&quot;1651&quot; data-filename=&quot;#3 즉시 로딩과 지연 로딩.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1651&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'프록시' 라는 단어의 의미는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; '무언가를 대신한다'&amp;nbsp;&lt;/span&gt;&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;Hibernate는 '프록시 클래스' 라는 가짜 클래스를 만들 수 있다. 이는 실제 클래스를 상속 받아서 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 우리가 직접 코드를 짜거나.. 등의 행위로 프록시를 직접 만들어서 사용할 일은 없다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q3FBT/btrZR3pYhzj/UCDMyk1ZSrIr7ohcYTcKNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q3FBT/btrZR3pYhzj/UCDMyk1ZSrIr7ohcYTcKNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q3FBT/btrZR3pYhzj/UCDMyk1ZSrIr7ohcYTcKNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq3FBT%2FbtrZR3pYhzj%2FUCDMyk1ZSrIr7ohcYTcKNK%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;500&quot; height=&quot;161&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;526&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프록시 클래스는 실제 클래스를 상속 받아서 만들어지기 때문에, 실제 클래스와 겉 모양은 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 내부에는 실제 객체의&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 참조(target) &lt;/span&gt;&lt;/b&gt;를 보관한다! 따라서 프록시 객체는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 특정 시점에 실제 객체를 호출 &lt;/span&gt;&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;프록시 클래스는 em.getReference( ) 를 통해서 생성할 수 있다. 다음 코드의 동작 과정을 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1676865712900&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Student student = em.getReference(Student.class, 1L);
student.getName();  // name 필드를 가져올 때&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2164&quot; data-origin-height=&quot;1486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgqEwE/btrZYRP5wAU/Zw2mJbnQzdlQkW9FDrYjTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgqEwE/btrZYRP5wAU/Zw2mJbnQzdlQkW9FDrYjTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgqEwE/btrZYRP5wAU/Zw2mJbnQzdlQkW9FDrYjTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgqEwE%2FbtrZYRP5wAU%2FZw2mJbnQzdlQkW9FDrYjTk%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;600&quot; height=&quot;412&quot; data-origin-width=&quot;2164&quot; data-origin-height=&quot;1486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;em.getReference( ) 를 통해 프록시 클래스를 만들면, 실제 엔티티를 가져오는 것이 아닌 프록시 클래스가 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 참조(target)를 통해 실제 엔티티에 접근 &lt;/span&gt;&lt;/b&gt;할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 영속성 컨텍스트에게 초기화 요청으로 DB에서 엔티티를 조회한 후, 실제 엔티티를 생성해놓는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #dddddd;&quot;&gt;(여기서 '초기화' 는 '데이터베이스를 통해서 실제 엔티티를 만들어내는 과정' 을 말함)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 만들어진 실제 엔티티에 참조(target)를 통해 접근할 수 있는 것이다. 실제 엔티티로 바뀌는 것은 절대 아니다!&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;/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;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 생성한 시점(getReference)에는 데이터베이스에 쿼리를 날리지 않음&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;엔티티의 값이&amp;nbsp;&lt;span style=&quot;background-color: #8ca9e3;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt; 실제로 사용되는 시점(getName)에 데이터베이스에 쿼리를 날려 조회&lt;/b&gt;&lt;/span&gt;&amp;nbsp;&lt;/span&gt; (SELECT name FROM Student..)&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연관관계로 이어진 엔티티를 하나가 로딩될 때 나머지도&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;즉시&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;가져오냐, 아니면 실제 필요한 시점에서만&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;지연&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;해서 가져오냐!&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  즉시 로딩 (EAGER)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레이리스트 프로젝트에서 사용하는 Music 엔티티와 Artist 엔티티는 다대일 관계를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Music 엔티티에 Artist 엔티티를 가지도록 설계하였다! 그림으로 표현하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1638&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/27fZp/btrZQdGvKnp/MFTXcgryCjIJoQDKIb7QR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/27fZp/btrZQdGvKnp/MFTXcgryCjIJoQDKIb7QR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/27fZp/btrZQdGvKnp/MFTXcgryCjIJoQDKIb7QR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F27fZp%2FbtrZQdGvKnp%2FMFTXcgryCjIJoQDKIb7QR1%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;550&quot; height=&quot;193&quot; data-origin-width=&quot;1638&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아티스트를 등록하면, 음악을 등록할 때 아티스트를 선택한 후 나머지 정보들을 입력하고 음악을 등록할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록한 음악을 조회하는 화면을 간단하게 보여주면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mD3RC/btrZQdsX4r2/CkeVFKmx4ccz9qPya02K11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mD3RC/btrZQdsX4r2/CkeVFKmx4ccz9qPya02K11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mD3RC/btrZQdsX4r2/CkeVFKmx4ccz9qPya02K11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmD3RC%2FbtrZQdsX4r2%2FCkeVFKmx4ccz9qPya02K11%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;300&quot; height=&quot;128&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 화면을 보면,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 음악을 조회할 때 아티스트도 같이 조회 &lt;/span&gt;&lt;/b&gt;해야 한다. 음악이 어떤 아티스트의 노래인지 알아야하기 때문이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt;&amp;nbsp;음악을 조회할 때 JOIN 명령어를 통해서 아티스트까지 한 번에 조회 &lt;/span&gt;&lt;/b&gt;하는 방법을 &lt;u&gt;&lt;b&gt;'즉시(EAGER) 로딩'&lt;/b&gt;&lt;/u&gt; 이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MZg8z/btrZ0J5FCjr/Z9NmUfhOLDSbsc8XvXf0Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MZg8z/btrZ0J5FCjr/Z9NmUfhOLDSbsc8XvXf0Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MZg8z/btrZ0J5FCjr/Z9NmUfhOLDSbsc8XvXf0Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMZg8z%2FbtrZ0J5FCjr%2FZ9NmUfhOLDSbsc8XvXf0Jk%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;500&quot; height=&quot;446&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드는 @ManyToOne 어노테이션에서 EAGER 옵션을 설정해주면 된다. (참고로 @____ToOne의 default 옵션은 EAGER)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음악 조회 시, 즉시 로딩 방법을 사용할 때 실제로 Hibernate가 DB에 쿼리를 보낸 로그를 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1676868078240&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- MusicController.findMusic (음악 조회 시 발생하는 쿼리)
Hibernate: 
    select
        music0_.music_id as music_id1_2_0_,
        music0_.artist_id as artist_i6_2_0_,
        music0_.cover_img as cover_im2_2_0_,
        music0_.link as link3_2_0_,
        music0_.lyrics as lyrics4_2_0_,
        music0_.title as title5_2_0_,
        artist1_.artist_id as artist_i1_0_1_,
        artist1_.genre as genre2_0_1_,
        artist1_.name as name3_0_1_,
        artist1_.profile_img as profile_4_0_1_,
        artist1_.type as type5_0_1_ 
    from
        music music0_ 
    inner join
        artist artist1_ 
            on music0_.artist_id=artist1_.artist_id 
    where
        music0_.music_id=?

-- MusicController.getCoverImg (음악 앨범 커버 이미지 조회 시 발생하는 쿼리)
Hibernate: 
    select
        music0_.music_id as music_id1_2_0_,
        music0_.artist_id as artist_i6_2_0_,
        music0_.cover_img as cover_im2_2_0_,
        music0_.link as link3_2_0_,
        music0_.lyrics as lyrics4_2_0_,
        music0_.title as title5_2_0_,
        artist1_.artist_id as artist_i1_0_1_,
        artist1_.genre as genre2_0_1_,
        artist1_.name as name3_0_1_,
        artist1_.profile_img as profile_4_0_1_,
        artist1_.type as type5_0_1_ 
    from
        music music0_ 
    inner join
        artist artist1_ 
            on music0_.artist_id=artist1_.artist_id 
    where
        music0_.music_id=?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;앗 참고로 음악 등록할 때 앨범 커버 이미지도 올리는데, &lt;u&gt;조회할 때 음악의 id를 조회한 후&lt;/u&gt; GET 방식으로 파일 이미지를 가져온다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;핵심은! &lt;u&gt;음악 정보를 가져올 때도 음악의 id를 조회&lt;/u&gt;하지만, &lt;u&gt;앨범 커버 이미지를 불러올 때도 음악의 id를 통해 조회&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;음악을 조회할 때 MUSIC 테이블과 ARTIST 테이블이 JOIN 명령어로 묶여졌다. 왜 그렇게 동작할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉시 로딩은&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; JOIN 명령어를 사용해서 쿼리 하나로 여러 테이블을 한꺼번에 조회 &lt;/span&gt;&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;그리고 앨범 커버 이미지를 조회할 때 발생한 쿼리를 보자. 음..? 여기에도 JOIN 명령어를 사용해서 쿼리가 날아갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약간 띠용(?)한 느낌이 든다.. 왜냐면&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 음악의 앨범 커버를 조회할 때는 아티스트 정보는 필요없기 때문 &lt;/span&gt;&lt;/b&gt;이다..!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그것도 그렇고 사실 JOIN이 포함된 쿼리가 간단하게 짧은 쿼리에 비해 이해하기 조금 더 어렵지 않나 ㅎㅎ..)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  지연 로딩 (LAZY)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 지연 로딩으로 엔티티를 연결하면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sr78X/btrZ2AgzS1J/Hrz6QZGga3SluI0isCaSB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sr78X/btrZ2AgzS1J/Hrz6QZGga3SluI0isCaSB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sr78X/btrZ2AgzS1J/Hrz6QZGga3SluI0isCaSB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsr78X%2FbtrZ2AgzS1J%2FHrz6QZGga3SluI0isCaSB1%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;500&quot; height=&quot;446&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 코드는 @ManyToOne 어노테이션에서 LAZY 옵션을 설정해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음악 조회 시, 이번에는 지연 로딩 방법을 사용할 때 Hibernate가 DB에 쿼리를 보낸 로그를 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1676869030961&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- MusicController.findMusic (음악 조회 시 발생하는 쿼리)
Hibernate: 
    select
        music0_.music_id as music_id1_2_0_,
        music0_.artist_id as artist_i6_2_0_,
        music0_.cover_img as cover_im2_2_0_,
        music0_.link as link3_2_0_,
        music0_.lyrics as lyrics4_2_0_,
        music0_.title as title5_2_0_ 
    from
        music music0_ 
    where
        music0_.music_id=?
Hibernate: 
    select
        artist0_.artist_id as artist_i1_0_0_,
        artist0_.genre as genre2_0_0_,
        artist0_.name as name3_0_0_,
        artist0_.profile_img as profile_4_0_0_,
        artist0_.type as type5_0_0_ 
    from
        artist artist0_ 
    where
        artist0_.artist_id=?

-- MusicController.getCoverImg (음악 앨범 커버 이미지 조회 시 발생하는 쿼리)
Hibernate: 
    select
        music0_.music_id as music_id1_2_0_,
        music0_.artist_id as artist_i6_2_0_,
        music0_.cover_img as cover_im2_2_0_,
        music0_.link as link3_2_0_,
        music0_.lyrics as lyrics4_2_0_,
        music0_.title as title5_2_0_ 
    from
        music music0_ 
    where
        music0_.music_id=?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;일단 음악 조회 시 발생하는 쿼리에서 즉시 로딩에서는 하나의 쿼리로 JOIN 명령어를 통해 테이블이 합쳐졌던 것과 다르게,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지연 로딩에서는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; MUSIC 테이블에 대한 쿼리와 ARTIST 테이블에 대한 쿼리가 둘 다 나갔다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676869407399&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
public class MusicController {

    @GetMapping(&quot;/music/{id}&quot;)
    public String findMusic(@PathVariable Long id, Model model) {
        MusicResponseDto music = musicService.findOneMusicById(id);
        model.addAttribute(&quot;music&quot;, music);
        return &quot;music/selectMusic&quot;;
    }
}

@Service
public class MusicService {

    public MusicResponseDto findOneMusicById(Long id) {
        Music findMusic = musicRepository.findOneById(id);  // MUSIC 테이블에 쿼리가 나가는 시점
        byte[] fileImage = ImageUtils.decompressImage(findMusic.getCoverImg());

        return new MusicResponseDto(findMusic.getId(), findMusic.getTitle(), 
        	findMusic.getArtist(), findMusic.getLyrics(), findMusic.getLink(), fileImage);
    		// ARTIST 테이블에 쿼리가 나가는 시점 (getArtist)
    }
}

@Repository
public class MusicRepository {

    public Music findOneById(Long id) {
        return em.find(Music.class, id);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음악 조회 로직을 간단하게 보면 위 코드와 같은데, &lt;u&gt;&lt;b&gt;MUSIC 테이블에 쿼리가 나가는 시점&lt;/b&gt;&lt;/u&gt;은&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; id를 통해 엔티티를 조회할 때 &lt;/span&gt;&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;ARTIST 테이블에 쿼리가 나가는 시점&lt;/b&gt;&lt;/u&gt;은&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; Artist 엔티티의 데이터가 실제로 필요한 시점(getArtist) &lt;/span&gt;&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;그래서 지연 로딩을 사용하면 ARTIST 테이블 입장에서,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 실제로 ARTIST 테이블 내 데이터가 사용될 때 쿼리가 나가는 것 &lt;/span&gt;&lt;/b&gt;이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명한 프록시의 매커니즘과 비슷하지 않나? 그렇다! 이는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 프록시 매커니즘을 사용해서 필요한 시점에 쿼리가 나간다!&amp;nbsp;&lt;/span&gt;&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;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 오직 MUSIC 테이블에만 쿼리가 나간 것 &lt;/span&gt;&lt;/b&gt;을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 연관 관계가&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 즉시 로딩 &lt;/span&gt;&lt;/b&gt;으로 묶여 있을 때는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 어쩔 수 없이 JOIN을 통해 테이블을 묶어 한 번에 조회 &lt;/span&gt;&lt;/b&gt;해야 했지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt;&amp;nbsp;지연 로딩 &lt;/span&gt;&lt;/b&gt;에서 ARTIST 테이블은 필요할 때만 찾으면 되고,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 이미지는 MUSIC 테이블에만 있으니 여기에만 쿼리가 나간 것 &lt;/span&gt;&lt;/b&gt;이다!&lt;/p&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;그렇다면 이번 프로젝트에서 MUSIC 테이블과 ARTIST 테이블은 즉시 로딩과 지연 로딩 중 어떤 것으로 연결되어야 할까?&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;음악 조회할 때는 JOIN으로 묶어서 조회하던, 따로 쿼리를 2개 써서 조회하던 큰 상관은 없다고 생각한다! 이건 내 맘이지~  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 커버 이미지를 불러올 때&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 즉시 로딩은 애꿎은(?) ARTIST 테이블까지 불렀다. 지연 로딩은 MUSIC 테이블만 부른 반면에!&amp;nbsp;&lt;/span&gt;&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;따라서 MUSIC 테이블과 ARTIST 테이블 간의 관계는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 지연(LAZY) 로딩 &lt;/span&gt;&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;&lt;u&gt;&lt;b&gt;즉시 로딩과 지연 로딩&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;즉시 로딩&lt;/b&gt; : JOIN 명령어를 통해서 하나의 쿼리로 여러 테이블을 동시에 조회&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;음악 조회 시 발생하는 쿼리&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;즉시 로딩&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: MUSIC 테이블과 ARTIST 테이블이 JOIN 되어 한꺼번에 조회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;지연 로딩&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 일단 MUSIC 테이블에 쿼리를 날려 조회하고, 아티스트가 필요한 시점에 ARTIST 테이블에 쿼리를 날려 조회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;➡️ &quot;두 테이블이 모두 필요한 것은 맞아..! 그래서 JOIN으로 묶여서 들어오던 따로 두 개의 쿼리를 날려 들어오던 노상!&quot;&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;u&gt;&lt;b&gt;음악 앨범 커버 이미지 조회 시 발생하는 쿼리&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;즉시 로딩&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: MUSIC 테이블과 ARTIST 테이블이 JOIN 되어 한꺼번에 조회 (사실 ARTIST는 불필요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;지연 로딩&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 음악에 이미지 데이터가 존재하므로, MUSIC 테이블에만 쿼리를 날려 조회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;➡️ &quot;ARTIST 테이블까지 불러올 필요는 없으므로, 지연 로딩으로 설정하는 것이 좋겠네!&quot;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N+1 문제(EAGER 관계로 묶여있는 것을 가져오기 위해 추가적인 쿼리가 계속 나가야 함)를 야기한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가급적이면 지연 로딩만 사용하는 것을 권장한다!&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>영재임재영</author>
      <guid isPermaLink="true">https://codenamejy.tistory.com/71</guid>
      <comments>https://codenamejy.tistory.com/71#entry71comment</comments>
      <pubDate>Mon, 20 Feb 2023 14:49:43 +0900</pubDate>
    </item>
    <item>
      <title>[프로젝트] 테이블 설계 &amp;amp; 엔티티 세팅</title>
      <link>https://codenamejy.tistory.com/70</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;#2 테이블 설계 &amp;amp;amp;amp;amp;amp; 엔티티 세팅.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3xwno/btrZewsl9T0/9KTmEn9kc3DigHDTzpnYIk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3xwno/btrZewsl9T0/9KTmEn9kc3DigHDTzpnYIk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3xwno/btrZewsl9T0/9KTmEn9kc3DigHDTzpnYIk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3xwno%2FbtrZewsl9T0%2F9KTmEn9kc3DigHDTzpnYIk%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;2940&quot; height=&quot;1648&quot; data-filename=&quot;#2 테이블 설계 &amp;amp;amp;amp;amp; 엔티티 세팅.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 연관관계를 생각할 때, 나는 엔티티부터 생각하고 테이블 설계로 넘어갔다. 하지만 생각하지 못한 부분이 너무나도 많았다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬럼 안에서는 리스트 형태로 데이터를 가질 수 없지 않나! 자바로 먼저 코드 짤 생각부터 하니 데이터베이스 설계가 망가졌다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 테이블을 먼저 설계한 후에, 엔티티를 세팅 &lt;/span&gt;&lt;/b&gt;하도록 하자! 앞으로도!&lt;/p&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;현재 프로젝트에서 사용할 테이블은 총 4개이다.&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;✅ 회원 정보, USER&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 음악 플레이리스트, PLAYLIST&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 음악 그 잡채, MUSIC&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 아티스트 정보, ARTIST&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;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;MUSIC &amp;amp; ARTIST&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Rxw0w/btrZbq72RdQ/e31elEnIQwPpY3ZW26Fg9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Rxw0w/btrZbq72RdQ/e31elEnIQwPpY3ZW26Fg9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Rxw0w/btrZbq72RdQ/e31elEnIQwPpY3ZW26Fg9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRxw0w%2FbtrZbq72RdQ%2Fe31elEnIQwPpY3ZW26Fg9K%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;600&quot; height=&quot;211&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 ARTIST는 다수의 MUSIC을 가질 수 있다! 그래서 둘은 (MUSIC 입장)&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 다대일 관계 &lt;/span&gt;&lt;/b&gt;이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;b&gt;MUSIC (N : 1) ARTIST&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;MUSIC &amp;amp; PLAYLIST&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 좀 생각할 부분이 많았다.. 생각해보면! 하나의 PLAYLIST는 다수의 MUSIC을 가질 수 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 둘은 (MUSIC 입장) 다대일 관계구나! 라고 생각해서 다음과 같이 테이블 관계를 그렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwD4JH/btrZg8kMwa7/H7odeMhUh9sqKvFMZgpsaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwD4JH/btrZg8kMwa7/H7odeMhUh9sqKvFMZgpsaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwD4JH/btrZg8kMwa7/H7odeMhUh9sqKvFMZgpsaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwD4JH%2FbtrZg8kMwa7%2FH7odeMhUh9sqKvFMZgpsaK%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;600&quot; height=&quot;536&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 PLAYLIST에서 MUSIC을 삭제하는 상황을 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 playlist_id가 14번이라면, MUSIC 테이블 내에서 14 값을 가진 playlist_id를 지워야한다! 근데 이게 뭐 어때서?&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;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; PLAYLIST 테이블에 DELETE 쿼리가 나가는 것이 아니라 MUSIC 테이블에 쿼리가 나가야 한다..!&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt;&amp;nbsp;반대 테이블에 쿼리가 나가야하는 문제 &lt;/span&gt;&lt;/b&gt;가 발생할 뿐더러, MUSIC 테이블 자체는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 고유한 성격 &lt;/span&gt;&lt;/b&gt;을 가져야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로,   이나   뮤직에서도&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 음악 데이터 자체에 사용자의 플레이리스트 정보를 넣지는 않을 것 &lt;/span&gt;&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;그렇다면 어떤 관계로 구현해야하지? 잠시 간과한 사실이 하나 있다. 하나의 MUSIC도 여러 PLAYLIST에 포함될 수 있다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 다대다 관계는 직접 데이터베이스 상에서 풀어낼 수 없다. 중간 테이블을 놓고, 일대다 + 다대일 관계로 풀어야한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2374&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yvJkE/btrZi6Um0EU/FsnoHSl3zDVKdLsiLw4T4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yvJkE/btrZi6Um0EU/FsnoHSl3zDVKdLsiLw4T4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yvJkE/btrZi6Um0EU/FsnoHSl3zDVKdLsiLw4T4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyvJkE%2FbtrZi6Um0EU%2FFsnoHSl3zDVKdLsiLw4T4k%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;860&quot; height=&quot;542&quot; data-origin-width=&quot;2374&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINKED_TABLE 이라는 이름으로 중간 테이블을 하나 놓고, 둘의&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 다대다 관계를 일대다 + 다대일 관계 &lt;/span&gt;&lt;/b&gt;로 풀어내었다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;b&gt;PLAYLIST (1 : N) &lt;span style=&quot;color: #dddddd;&quot;&gt;LINKED_TABLE&lt;/span&gt; (N : 1) MUSIC&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;PLAYLIST &amp;amp; USER&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chWTHe/btrZclyto8g/7jIor96h97Kj3KWQt6YP3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chWTHe/btrZclyto8g/7jIor96h97Kj3KWQt6YP3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chWTHe/btrZclyto8g/7jIor96h97Kj3KWQt6YP3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchWTHe%2FbtrZclyto8g%2F7jIor96h97Kj3KWQt6YP3k%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;600&quot; height=&quot;524&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;524&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 USER는 다수의 PLAYLIST를 가질 수 있다! 그래서 둘은 (PLAYLIST 입장)&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #8ca9e3; color: #ffffff;&quot;&gt; 다대일 관계 &lt;/span&gt;&lt;/b&gt;이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;PLAYLIST (N : 1) USER&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;모든 테이블 간의 관계를 정리했다! MySQL Workbench를 사용해서 그려본 ERD는 다음과 같다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(참고로 ERD는 'Entity Relationship Diagram' 을 의미한다)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNc6wg/btrZi7Fw7R7/pKTbG6Muz1uuPOch9FAk01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNc6wg/btrZi7Fw7R7/pKTbG6Muz1uuPOch9FAk01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNc6wg/btrZi7Fw7R7/pKTbG6Muz1uuPOch9FAk01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNc6wg%2FbtrZi7Fw7R7%2FpKTbG6Muz1uuPOch9FAk01%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;700&quot; height=&quot;463&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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;모든 테이블의 연관관계를 매핑하고 ERD까지 완성했으니, 이제 엔티티로 옮길 차례이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2362&quot; data-origin-height=&quot;1144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuwP3R/btrZei9cuQ6/vLkkbTSrSNvKEAabqFLjc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuwP3R/btrZei9cuQ6/vLkkbTSrSNvKEAabqFLjc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuwP3R/btrZei9cuQ6/vLkkbTSrSNvKEAabqFLjc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuwP3R%2FbtrZei9cuQ6%2FvLkkbTSrSNvKEAabqFLjc0%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;700&quot; height=&quot;339&quot; data-origin-width=&quot;2362&quot; data-origin-height=&quot;1144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 엔티티 간 관계를 그릴 수 있다! 데이터베이스 테이블을 먼저 설계하고, 그 다음에 엔티티를 생각하자!  &lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>영재임재영</author>
      <guid isPermaLink="true">https://codenamejy.tistory.com/70</guid>
      <comments>https://codenamejy.tistory.com/70#entry70comment</comments>
      <pubDate>Tue, 14 Feb 2023 14:48:37 +0900</pubDate>
    </item>
    <item>
      <title>[프로젝트] 요구사항 분석 및 구현 단계</title>
      <link>https://codenamejy.tistory.com/68</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;#1 요구사항 분석 및 구현 단계.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvSgLu/btrZc8lEvgy/W5kGULM02agBc7PnMygA80/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvSgLu/btrZc8lEvgy/W5kGULM02agBc7PnMygA80/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvSgLu/btrZc8lEvgy/W5kGULM02agBc7PnMygA80/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvSgLu%2FbtrZc8lEvgy%2FW5kGULM02agBc7PnMygA80%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;2940&quot; height=&quot;1648&quot; data-filename=&quot;#1 요구사항 분석 및 구현 단계.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;⚾️ 요구사항 분석&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;1240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r0rsA/btrZj9C3Cl0/K2H6tbNKCZ9jAnLB8PzHx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r0rsA/btrZj9C3Cl0/K2H6tbNKCZ9jAnLB8PzHx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r0rsA/btrZj9C3Cl0/K2H6tbNKCZ9jAnLB8PzHx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr0rsA%2FbtrZj9C3Cl0%2FK2H6tbNKCZ9jAnLB8PzHx1%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;600&quot; height=&quot;440&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;1240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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;[STEP 1] 엔티티 세팅 및 CRUD 기능 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp; ✔️&lt;span&gt; 기본적인 엔티티 &amp;amp; 연관관계 세팅&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;&lt;span&gt;&amp;nbsp; ✔️&lt;span&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;&lt;b&gt;[STEP 2] 회원 관련 처리 및 플레이리스트 기능 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp; ✔️&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;&lt;span&gt;&amp;nbsp; ✔️&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;플레이리스트 기능 구현&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;---------------- 백엔드 구현 완료 ----------------&lt;/span&gt;&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;&lt;b&gt;[STEP 3] 웹 화면과 연동&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp; ✔️&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;프론트엔드 구현 및 연동 (리액트 생각 중)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; ✔️ 웹 화면 UI 구성하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;--------------- 프론트엔드 구현 완료&amp;nbsp;---------------&lt;/span&gt;&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;&lt;b&gt;[STEP 4] 로컬 환경에서 외부로 확장 및 배포&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp; ✔️&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;외부 데이터베이스 연결&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; ✔️ 배포 (Github Actions 말고 새로운 툴 고민 중)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;--------------- 배포 및 프로젝트 완료&amp;nbsp;---------------&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1676339852620&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - limjustin/PLAYLIST: 플레이리스트 구현 프로젝트&quot; data-og-description=&quot;플레이리스트 구현 프로젝트. Contribute to limjustin/PLAYLIST development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/limjustin/PLAYLIST&quot; data-og-url=&quot;https://github.com/limjustin/PLAYLIST&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Mel8K/hyRC3vR2GA/LowYVvKg4CxEdKek88Sh9k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/limjustin/PLAYLIST&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/limjustin/PLAYLIST&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Mel8K/hyRC3vR2GA/LowYVvKg4CxEdKek88Sh9k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - limjustin/PLAYLIST: 플레이리스트 구현 프로젝트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;플레이리스트 구현 프로젝트. Contribute to limjustin/PLAYLIST development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>영재임재영</author>
      <guid isPermaLink="true">https://codenamejy.tistory.com/68</guid>
      <comments>https://codenamejy.tistory.com/68#entry68comment</comments>
      <pubDate>Tue, 14 Feb 2023 10:57:01 +0900</pubDate>
    </item>
    <item>
      <title>[스프링 JPA] 연관관계 매핑 (2)</title>
      <link>https://codenamejy.tistory.com/69</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;#4 연관관계 매핑 (2).jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6yNlc/btrY1nXdZKF/lwLoxGReXNmNQkNxVE6n01/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6yNlc/btrY1nXdZKF/lwLoxGReXNmNQkNxVE6n01/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6yNlc/btrY1nXdZKF/lwLoxGReXNmNQkNxVE6n01/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6yNlc%2FbtrY1nXdZKF%2FlwLoxGReXNmNQkNxVE6n01%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;2940&quot; height=&quot;1645&quot; data-filename=&quot;#4 연관관계 매핑 (2).jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1645&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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;u&gt;&lt;b&gt;다대일 (N : 1)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwmGhN/btrY1lSQhxJ/Qy0UDfoKkcGkyfWsN5O5lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwmGhN/btrY1lSQhxJ/Qy0UDfoKkcGkyfWsN5O5lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwmGhN/btrY1lSQhxJ/Qy0UDfoKkcGkyfWsN5O5lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwmGhN%2FbtrY1lSQhxJ%2FQy0UDfoKkcGkyfWsN5O5lk%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;650&quot; height=&quot;984&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;984&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'다대일' 관계는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; '다' 방향에서 항상 연관관계의 주인 &lt;/span&gt;&lt;/b&gt;을 가진다! 생각을 조금만 해보면 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'일' 방향에서 여러 데이터를 가지고 있어야하는데,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; DB 테이블에는 한 컬럼에 리스트 형태로 데이터를 담을 수 없다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 '다' 방향에서&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 어떤 PK 값과 연결되어야 하는지 알려주는 FK 값을 가져야&amp;nbsp;&lt;/span&gt;&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;위에서 말했던 것! &quot;DB 테이블에는 한 컬럼에 리스트 형태로 데이터를 담을 수 없다&quot; 라는 사실이다.&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;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; musics 라는 리스트 &lt;/span&gt;&lt;/b&gt;가 추가되었지만 실제&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 데이터베이스와는 전혀 상관없는 필드 &lt;/span&gt;&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 단순히 조회용 리스트 &lt;/span&gt;&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;&lt;b&gt;✅ 다대일 관계에서는 '다' 방향이 FK 값을 가진다! '일' 방향에서는 리스트로 단순 조회만 가능하다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;✅&lt;span&gt; 데이터베이스 테이블 구조를 먼저 작성하고, 그 다음에 엔티티 구조를 작성하도록 하자! 그래야 코드 작성할 때 안 헷갈린다!&lt;/span&gt;&lt;/span&gt;&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;&lt;u&gt;&lt;b&gt;일대다 (1 : N)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;1142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CdAeU/btrY4l59Z4b/IXR4I9J5oljw2v1KfleK20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CdAeU/btrY4l59Z4b/IXR4I9J5oljw2v1KfleK20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CdAeU/btrY4l59Z4b/IXR4I9J5oljw2v1KfleK20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCdAeU%2FbtrY4l59Z4b%2FIXR4I9J5oljw2v1KfleK20%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;650&quot; height=&quot;1142&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;1142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서 플레이리스트(이하 '플리')는 음악 리스트를 담을 수 있게 구현했다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 실제 데이터베이스 테이블을 보면 FK 값은 MUSIC 테이블에 위치 &lt;/span&gt;&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;기능 구현 단계에서, 플리에서 음악을 삭제해야하는 로직을 짜고 싶다고 가정하자. 자바에서는 쉽게 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 데이터베이스 테이블 입장에서 볼 때는?&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 엔티티가 관리하는 FK 값이 반대 테이블에 있는 상황 &lt;/span&gt;&lt;/b&gt;이다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 PLAYLIST만 UPDATE 쿼리를 날리고 싶은데, FK가 반대 테이블에 있는 관계로&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; MUSIC에도 쿼리를 날려야한다..&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&lt;b&gt;✅ 단점이 있다면? '일' 방향이 연관관계의 주인이 되므로, FK가 반대 테이블에 위치하게 된다.&lt;/b&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;일대일 (1 : 1)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MwpXo/btrY5k0vAGk/Ih0MkecC4ctXKLZNCAWJP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MwpXo/btrY5k0vAGk/Ih0MkecC4ctXKLZNCAWJP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MwpXo/btrY5k0vAGk/Ih0MkecC4ctXKLZNCAWJP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMwpXo%2FbtrY5k0vAGk%2FIh0MkecC4ctXKLZNCAWJP1%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;650&quot; height=&quot;1238&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일대일 관계는 쉽다! 다대일 관계랑 비슷한데 바뀐 것이 있다면, 한쪽 방향이 '다' 가 아닌 '일' 이라는 점이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 연관관계의 주인이 어느 쪽이던 상관없다! 즉,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; FK를 어느 쪽에서 가지던 상관이 없는데..!&amp;nbsp;&lt;/span&gt;&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;MUSIC 테이블을 주 테이블이라고 하고, RATING 테이블을 대상 테이블이라고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 프로젝트의 정서(?) ㅋㅋㅋ 주제 상!! RATING 테이블 보다는 MUSIC 테이블이 더 핵심이라고 생각해서 그런다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt;&amp;nbsp;주 테이블만 조회해도 대상 테이블의 데이터를 확인 &lt;/span&gt;&lt;/b&gt;할 수 있지만, 만약&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 값이 없다면 null 값이 들어가야 하는 것 &lt;/span&gt;&lt;/b&gt;이 단점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대상 테이블은&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; '다' 관계를 갖게 되었을 때 FK가 있으므로 구조 변경이 없어도&amp;nbsp;&lt;/span&gt;&lt;/b&gt; 되지만,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 항상 지연 로딩된다 &lt;/span&gt;&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;&lt;b&gt;✅ 누가 연관관계의 주인을 하던 큰 상관이 없다!&lt;/b&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;다대다 (N : M)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2446&quot; data-origin-height=&quot;1348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zwCWE/btrYWaEqO8y/LMtKqa5IKdFbzKhQO14Fb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zwCWE/btrYWaEqO8y/LMtKqa5IKdFbzKhQO14Fb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zwCWE/btrYWaEqO8y/LMtKqa5IKdFbzKhQO14Fb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzwCWE%2FbtrYWaEqO8y%2FLMtKqa5IKdFbzKhQO14Fb0%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;750&quot; height=&quot;413&quot; data-origin-width=&quot;2446&quot; data-origin-height=&quot;1348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 일대다 부분에서 설명한 예제는 약간 틀렸다..! 하나의 음악도 여러 플리에 공유될 수 있지 않나!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 PLAYLIST와 MUSIC은 서로 다대다 관계이다! 다대다 관계는 어떻게 구현할 수 있을까?&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;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 연결 테이블 &lt;/span&gt;&lt;/b&gt;을 하나 만들고, 자바에서는 이를&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 엔티티로 승격 &lt;/span&gt;&lt;/b&gt;시켜서 다대다 관계를 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 절대로 실무에서 사용하면 안 된다 &lt;/span&gt;&lt;/b&gt;고 한다! 여러 데이터들이 중간에 포함될 경우 복잡해질 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&lt;b&gt;✅ 다대다 방식은 실무에서 사용하면 안 된다고 한다..!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 구현해야하는 상황이라면, 일대다 + 다대일 관계로 풀어내서 구현해야 한다!&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  상속관계 매핑&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjtm98/btrYY7t0A3N/KYXqdh3d3bPv6kH7vlh7A0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjtm98/btrYY7t0A3N/KYXqdh3d3bPv6kH7vlh7A0/img.png&quot; data-alt=&quot;(개인 프로젝트에서는 상속관계 매핑을 사용하지 않아서 김영한님의 자료 사진을 쓰겠습니다    )&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjtm98/btrYY7t0A3N/KYXqdh3d3bPv6kH7vlh7A0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcjtm98%2FbtrYY7t0A3N%2FKYXqdh3d3bPv6kH7vlh7A0%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;600&quot; height=&quot;171&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(개인 프로젝트에서는 상속관계 매핑을 사용하지 않아서 김영한님의 자료 사진을 쓰겠습니다    )&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에는 '상속' 개념이 존재한다. 하지만 관계형 데이터베이스에서는 '상속' 개념이 존재하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 '슈퍼타입' 과 '서브타입' 이라는 개념이 존재한다. 슈퍼타입은 '공통 속성' 서브타입은 '고유 속성' 이라고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;위 사진의 왼쪽 구조도를 '논리 모델' 이라고 하고, 아래처럼 실제 구현하는 것을 '물리 모델' 이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 3개의 물리 모델이 있다.&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;1. 조인 전략 (Identity)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cENowF/btrZahbdl91/oi2fXviUCNEHlYwHm6cC11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cENowF/btrZahbdl91/oi2fXviUCNEHlYwHm6cC11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cENowF/btrZahbdl91/oi2fXviUCNEHlYwHm6cC11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcENowF%2FbtrZahbdl91%2Foi2fXviUCNEHlYwHm6cC11%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;615&quot; height=&quot;191&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 슈퍼 타입은 공통이고, 각각의 서브타입만 분리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;span&gt; 유용한 경우 : 예시로 나머지는 조회 아직 필요 없고, Book의 Author 정보만 먼저 필요한 경우!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@Inheritance(strategy = InheritanceType.JOINED) 어노테이션 사용&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;/b&gt; : JOIN 쿼리가 많이 필요해서 쿼리 자체가 조금 복잡함&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. 단일 테이블 전략 (Rollup)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B0OUX/btrY2nceECT/inEKKeExFeBSKHdgZ40fQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B0OUX/btrY2nceECT/inEKKeExFeBSKHdgZ40fQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B0OUX/btrY2nceECT/inEKKeExFeBSKHdgZ40fQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB0OUX%2FbtrY2nceECT%2FinEKKeExFeBSKHdgZ40fQK%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;525&quot; height=&quot;194&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 하나의 테이블로 통합&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&lt;span&gt; 유용한 경우 : 예시로 Album, Movie, Book이 모두 통합되어 함께 처리되어야 하는 경우!&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 어노테이션 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 장점&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: JOIN 쿼리가 필요 없어서 쿼리가 단순하고 조회 성능이 빠름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 단점&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 관련 없는 필드일 경우 null 값이 들어가고, 테이블의 크기가 급성장함 &lt;span style=&quot;color: #9d9d9d;&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;&lt;b&gt;3. 구현 클래스마다 테이블 전략 (Rolldown)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z4Qjw/btrZbPEYudR/2k9IWwV4LURMrUcWHPq6e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z4Qjw/btrZbPEYudR/2k9IWwV4LURMrUcWHPq6e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z4Qjw/btrZbPEYudR/2k9IWwV4LURMrUcWHPq6e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ4Qjw%2FbtrZbPEYudR%2F2k9IWwV4LURMrUcWHPq6e0%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;600&quot; height=&quot;171&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 슈퍼 타입을 각각의 서브타입 테이블에 분리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;유용한 경우 : Movie, Book 객체가 Album 객체에 비해서 엄청나게 많은데 Album만 조회하고 싶은 경우!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 어노테이션 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 장점&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 서브 타입의 구분이 명확해짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 단점&lt;/b&gt;&lt;span&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;이제는 전체적인 클래스에 대해서 공통 매핑 정보가 필요하다면? 그때 사용할 수 있는 어노테이션이 하나 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WCK8G/btrZchH3gug/xBkZHjY8Q60klTM5TjBE6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WCK8G/btrZchH3gug/xBkZHjY8Q60klTM5TjBE6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WCK8G/btrZchH3gug/xBkZHjY8Q60klTM5TjBE6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWCK8G%2FbtrZchH3gug%2FxBkZHjY8Q60klTM5TjBE6K%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;550&quot; height=&quot;321&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;u&gt;&lt;b&gt;@MappedSuperclass&lt;/b&gt;&lt;/u&gt; 라는 어노테이션이다. 이는 테이블과 전혀 관계 없다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;가끔 책이나 다른 사람의 프로젝트를 보면 BaseTimeEntity 클래스가 있는데 이 방식으로 구현한 것이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 게시판 작성 프로젝트일 경우, 작성 시간.. 작성한 사람.. 등의 정보를 이 클래스에 적을 수 있는 것이다!&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>영재임재영</author>
      <guid isPermaLink="true">https://codenamejy.tistory.com/69</guid>
      <comments>https://codenamejy.tistory.com/69#entry69comment</comments>
      <pubDate>Mon, 13 Feb 2023 16:35:32 +0900</pubDate>
    </item>
    <item>
      <title>[스프링 JPA] 연관관계 매핑 (1)</title>
      <link>https://codenamejy.tistory.com/67</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;#3 연관관계 매핑 (1).jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dc3jsY/btrYT9yU4Vm/S9vsHRiJpHylU6K5hcKW11/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dc3jsY/btrYT9yU4Vm/S9vsHRiJpHylU6K5hcKW11/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dc3jsY/btrYT9yU4Vm/S9vsHRiJpHylU6K5hcKW11/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdc3jsY%2FbtrYT9yU4Vm%2FS9vsHRiJpHylU6K5hcKW11%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;2940&quot; height=&quot;1643&quot; data-filename=&quot;#3 연관관계 매핑 (1).jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1643&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  객체 vs 관계형 데이터베이스 패러다임 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형 데이터베이스에서는 다음과 같은 방식으로 테이블 간 관계를 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYd2rL/btrYH1130zC/GRd7lMRKpCmqVv00YhZOy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYd2rL/btrYH1130zC/GRd7lMRKpCmqVv00YhZOy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYd2rL/btrYH1130zC/GRd7lMRKpCmqVv00YhZOy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYd2rL%2FbtrYH1130zC%2FGRd7lMRKpCmqVv00YhZOy0%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;650&quot; height=&quot;468&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, MUSIC 테이블은 어떤 가수의 노래인지에 대한 정보가 필요하므로 ARTIST 테이블과 연결되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 MUSIC 테이블 내에서 ARTIST 테이블에 있는 컬럼들의 PK 값을 가지는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 외래 키 방식 &lt;/span&gt;&lt;/b&gt;으로 구현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675999721399&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Music {
    private Long id;
    private String title;
    private Long artist_id;  // Music &amp;rarr; Artist
}

public class Artist {
    private Long id;
    private String name;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 객체를 테이블에 맞추어 모델링한 결과이다. 하지만 이는 전혀 '객체지향' 스럽지 않다. 왜냐고?&lt;/p&gt;
&lt;pre id=&quot;code_1675999838814&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 찾은 Music을 통해 Artist를 가져오고 싶을 때
Music findMusic = em.find(Music.class, 1L);
Long findId = findMusic.getArtistId();  // Artist의 id 값을 찾아야 함
Artist findArtist = em.find(Artist.class, findId);  // 찾은 값을 토대로 결과 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾은 Music 객체에서 Artist 객체를 가져오고 싶다면? Music 객체 내에서 Artist의 id 값을 일단 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 해당 id 값을 활용해서 Artist 객체를 최종적으로 가져오는.. 꽤나 복잡한 구성을 가지고 있다..  &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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 관계형 데이터베이스&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 외래 키(FK)를 매핑 &lt;/span&gt;&lt;/b&gt;하는 방식으로 연관성을 정의&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 객체(자바)&lt;/b&gt; :&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 객체 참조(Reference) &lt;/span&gt;&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;그래서 관계형 데이터베이스의 패러다임을 그대로 자바에서 객체로 구현한다면? 객체지향적인 코드를 작성할 수 없다..  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 객체지향적인 방식으로 테이블 간의 관계를 구현할 수는 없을까? 객체를 어떻게 사용한다면 되지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답은!&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 객체를 사용하면서, 서로 간 연관관계를 지정 &lt;/span&gt;&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;+ 추가적으로, 이런 식의 객체와 관계형 데이터베이스 간의 패러다임 차이를 해결하고자 나온 것이 ORM 기술이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔티티와 테이블은 서로 모델 형태의 차이도 존재한다. 일례로, 객체를 바로 데이터베이스에 저장할 수는 없지 않나!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 객체를 통해 데이터베이스를 다룰 수 있도록 하여 객체지향적인 코드로 개발자가 로직 부분에 더 집중할 수 있게 해준다.&lt;/p&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;/p&gt;
&lt;pre id=&quot;code_1676001804463&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Music {
    @Id @GeneratedValue
    @Column(name = &quot;music_id&quot;)
    private Long id;
    
    private String title;
    
    @ManyToOne
    @JoinColumn(name = &quot;artist_id&quot;)
    private Artist artist;  // Music &amp;rarr; Artist
}

public class Artist {
    @Id @GeneratedValue
    @Column(name = &quot;artist_id&quot;)
    private Long id;
    
    private String name;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 작성한 코드에 부수적인 것들을 조금 붙이기는 했는데, 중요한 건&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; artist_id가 아닌 Artist 객체를 그대로 가져온 것 &lt;/span&gt;&lt;/b&gt;이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 id 값을 가져와 FK 방식을 직접 구현했는데, 이제는 객체를 직접 가져와서 몇 가지 어노테이션을 설정해주면 된다.&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;u&gt;&lt;b&gt;@ManyToOne 어노테이션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 하나의 Artist는 여러 Music을 가질 수 있다! 따라서 Artist (1 : N) Music 관계가 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ Music 입장에서는 Artist에게 多 : 1 관계이므로, 이를 어노테이션을 나타내면 'ManyToOne' 을 사용하면 된다.&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;u&gt;&lt;b&gt;@JoinColumn 어노테이션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 외래 키가 어떤 정보를 담을건지 생각하면 된다! 직관적으로 해석하면&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; &quot;Join하는 Column은 무엇인가?&quot; &lt;/span&gt;&lt;/b&gt;로 읽자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;span&gt; Artist 객체의 id 값(artist_id)을 외래 키의 값으로 담고 있으므로, 위처럼 써주면 된다!&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;&lt;span&gt;&lt;span&gt;이처럼 객체를 가져오게 되면, 객체지향적인 코드를 구성할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676003007164&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 찾은 Music을 통해 Artist를 가져오고 싶을 때
Music findMusic = em.find(Music.class, 1L);
Artist findArtist = findMusic.getArtist();  // Music에서 바로 Artist 반환 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 바로 Getter를 사용해서 가져올 수 있다! 이는 단방향 연관관계이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면! 키 값을 그대로 사용하는 것이 아닌,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 객체를 가져와 관계를 설정하고 조인해야하는 컬럼을 나타내기 &lt;/span&gt;&lt;/b&gt;만 하면 된다!&lt;/p&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;단방향은 'A &amp;rarr; B' 처럼 한 쪽으로만 진행되는 것을 의미하는데, 양방향은 'A &amp;harr; B' 처럼 서로 왔다갔다 할 수 있음을 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연한거 아니냐고? 초딩도 아는거 아니냐고? ㅋㅋㅋ 맞다! 하지만 지금부터 '방향' 이라는 개념 자체를 들고 한 번 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYd2rL/btrYH1130zC/GRd7lMRKpCmqVv00YhZOy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYd2rL/btrYH1130zC/GRd7lMRKpCmqVv00YhZOy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYd2rL/btrYH1130zC/GRd7lMRKpCmqVv00YhZOy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYd2rL%2FbtrYH1130zC%2FGRd7lMRKpCmqVv00YhZOy0%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;650&quot; height=&quot;468&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시를 다시 한 번 들고 와보면, 관계형 데이터베이스의 경우 위 그림을 봤을 때 무언가 '단방향' 처럼 느껴진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화살표 때문에 그런가? 하지만 엄청난 사실..&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 관계형 데이터베이스는 '뱡향' 이라는 개념이 없다!!&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676003513039&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- MUSIC 테이블에서 ARTIST 테이블 JOIN
SELECT * FROM MUSIC M
JOIN ON ARTIST A WHERE M.artist_id = A.artist_id;

-- ARTIST 테이블에서 MUSIC 테이블 JOIN
SELECT * FROM ARTIST A
JOIN ON MUSIC M WHERE A.artist_id = M.artist_id;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 SQL 쿼리문처럼,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 두 테이블 중 어느 테이블에서도 서로의 테이블을 조회 &lt;/span&gt;&lt;/b&gt;할 수 있다. 따라서&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; '방향성'을 가지고 있지 않다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676003703115&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Music {
    @Id @GeneratedValue
    @Column(name = &quot;music_id&quot;)
    private Long id;
    
    private String title;
    
    @ManyToOne
    @JoinColumn(name = &quot;artist_id&quot;)
    private Artist artist;  // Music &amp;rarr; Artist
}

public class Artist {
    @Id @GeneratedValue
    @Column(name = &quot;artist_id&quot;)
    private Long id;
    
    private String name;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 위의 기존 자바 코드에서는 Music &amp;rarr; Artist 방향의 접근이 가능하지만, Artist &amp;rarr; Music 방향의 접근은 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이를 구현하고 싶다면? 이럴 때 '양방향' 연관관계를 구현하면 된다!&lt;/p&gt;
&lt;pre id=&quot;code_1676003841918&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Music {
    @Id @GeneratedValue
    @Column(name = &quot;music_id&quot;)
    private Long id;
    
    private String title;
    
    @ManyToOne
    @JoinColumn(name = &quot;artist_id&quot;)
    private Artist artist;  // Music &amp;rarr; Artist
}

public class Artist {
    @Id @GeneratedValue
    @Column(name = &quot;artist_id&quot;)
    private Long id;
    
    private String name;
    
    @OneToMany(mappedBy = &quot;artist&quot;)
    private List&amp;lt;Music&amp;gt; musics = new ArrayList&amp;lt;&amp;gt;();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Artist 클래스 내에 자신이 부른 Music에 접근할 수 있는 리스트를 하나 만든다고 해보자. Music에 접근할 필드가 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&lt;u&gt;&lt;b&gt;@OneToMany 어노테이션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ Artist 입장에서는 Music에 1 : 多 관계이므로, 이를 어노테이션을 나타내면 'OneToMany' 을 사용하면 된다.&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;u&gt;&lt;b&gt;mappedBy 옵션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 접근할 반대편 클래스에 어떤 변수로 걸려 있는지&amp;nbsp;&lt;/span&gt;&lt;/b&gt; 체크하면 된다!&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;span&gt; Music 객체가 artist 객체 인스턴스를 가지고 있는 상황이므로,&amp;nbsp;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt;&lt;b&gt; &quot;어떤 필드에 매핑되었나?&quot; &lt;/b&gt;&lt;/span&gt;라고 생각하면 된다!&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&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;&lt;span&gt;&lt;span&gt;어노테이션 사용 관련은, 천천히 코드를 읽어보면서 매칭해나가면 어렵지 않다!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;  연관관계 사용 시 주의점&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;#01. 연관관계의 주인은?&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;객체의 양방향 관계는 서로 다른 방향의 단방향 2개로 풀어낼 수 있다! (A &amp;harr; B) = (A &amp;rarr; B) + (B &amp;rarr; A) 처럼!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&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;&lt;span&gt;&lt;span&gt;결론부터 말하면,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 외래 키(FK)가 있는 곳, 즉 1 : N 관계에서 'N' 의 부분에서 연관관계의 주인을 가지고 있는 것 &lt;/span&gt;&lt;/b&gt;이 좋다!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;예를 들어, Artist (&lt;span&gt;1&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;多) Music 관계에서 Music 필드를 수정한다고 하면, 당연히 Music 필드에 쿼리가 나갈 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;하지만 Artist 필드를 수정한다고 하면, FK는 Music 필드에 있으므로, Music 필드에도 쿼리가 나가야한다.&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;&lt;span&gt;&lt;span&gt;그렇기 때문에 FK가 있는 쪽(FK가 있으면 당연히 'N' 부분임)에 연관관계의 주인을 가지고 있으라는 것이다!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;연관관계의 주인이라면,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 주인만이 외래 키를 관리(등록 및 수정) &lt;/span&gt;&lt;/b&gt;할 수 있고,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 주인이 아니면 조회(읽기)만 가능 &lt;/span&gt;&lt;/b&gt;하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;일례로 Music 클래스는 외래 키를 직접 수정할 수 있으나, Artist 클래스는 Music에 대한 조회만 할 수 있다.&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;&lt;u&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;#02. 양방향 연관관계 사용 시, 양쪽에 모두 값을 세팅하자!&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;이렇게 양방향 연관관계를 가지고 있다면,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 반드시 양쪽에 모두 값을 세팅할 필요 &lt;/span&gt;&lt;/b&gt;가 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;Flush를 통해 DB에 쿼리를 보내주지 않는다면, 객체에 대한 데이터는 DB에는 아직 없고 영속성 컨텍스트에 들어있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676005415244&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Artist artist = new Artist();
artist.setName(&quot;ITZY&quot;);

Music music = new Music();
music.setTitle(&quot;달라달라&quot;);
music.setArtist(artist);

// == 현재 영속성 컨텍스트 내 데이터 상태 (id는 임의로 넣겠다) == //
// artist (id: 20L, name: ITZY, musics: NULL)
// music (id:1L, title: 달라달라, artist: artist 객체)

// 여기서 바로 Artist에 musics를 조회한다면?
System.out.println(artist.getMusics());  // output : (없음)

// 만약 DB에 한번 데이터가 담겼었다면?
em.flush();  // DB에 영속성 컨텍스트 반영
em.clear();
System.out.println(artist.getMusics());  // output : music 리스트 조회 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;DB에 데이터가 반영되지 않은 상태에서 영속성 컨텍스트 내 1차 캐시를 조회하면 artist 내 musics 에는 값이 없다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;하지만 DB에 데이터가 한 번 저장되었다면, 이는 값을 불러올 수 있다. 어떻게 그게 가능하지?  &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;현재 위 코드만 보면 artist 내 musics 에는 값이 들어있지 않는데, 그 상태에서 바로 DB에 데이터를 넣어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB의 입장에서&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; Music 객체와 Artist 객체 사이 관계 &lt;/span&gt;&lt;/b&gt;를 알고 있기 때문에 어떻게든 값을 찾아올 수는 있다!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt;&amp;nbsp;FK 방식으로 연결되어 있는 것 &lt;/span&gt;&lt;/b&gt;을 DB는 알고 있다! &lt;/span&gt;&lt;span&gt;하지만 그 전에는 직접 musics 데이터를 넣지 않았기 때문에 모를 수 밖에..&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;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 결론은 애초부터 데이터를 양쪽에 다 넣어주는 것이 좋다!&amp;nbsp;&lt;/span&gt;&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;&lt;u&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;#03. 단방향만으로 설계하는 것이 좋다!&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;양방향 설계는 단뱡향 2개가 합쳐진 것이라고 했다. 그리고 위에서 말한 주인 문제, 양쪽에 값을 모두 세팅해야하는 문제가 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;그래서 양방향보다는 되도록이면 단방향으로 설계를 진행하는 것이 좋다! 그리고&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 충분히 단방향만으로 설계&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt;&lt;b&gt;를 할 수 있다!&lt;/b&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;일단 단방향 매핑으로 모든 설계를 마무리하고, 그 이후 개발단계에서 필요한 양방향 관계를 정의하는 것으로도 충분하다!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>영재임재영</author>
      <guid isPermaLink="true">https://codenamejy.tistory.com/67</guid>
      <comments>https://codenamejy.tistory.com/67#entry67comment</comments>
      <pubDate>Fri, 10 Feb 2023 14:41:06 +0900</pubDate>
    </item>
    <item>
      <title>[스프링 JPA] 엔티티 매핑</title>
      <link>https://codenamejy.tistory.com/66</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;#2 엔티티 매핑.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1649&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/38MQz/btrYz0afwwZ/ipOUrUECXMaR0jt5U8viN0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/38MQz/btrYz0afwwZ/ipOUrUECXMaR0jt5U8viN0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/38MQz/btrYz0afwwZ/ipOUrUECXMaR0jt5U8viN0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F38MQz%2FbtrYz0afwwZ%2FipOUrUECXMaR0jt5U8viN0%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;2940&quot; height=&quot;1649&quot; data-filename=&quot;#2 엔티티 매핑.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1649&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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;u&gt;&lt;b&gt;@Entity 어노테이션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 객체를 테이블에 매핑할 수 있게 해주고, 이제부터 JPA가 관리해준다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 기본 생성자 반드시 필요하다! (한 달 전에 엔티티 공부할 때 고생했던 기억! &lt;a href=&quot;https://codenamejy.tistory.com/49&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크  ️&lt;/a&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;&lt;u&gt;&lt;b&gt;@Table 어노테이션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;데이터베이스 스키마 자동 생성&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;스프링에서 DB를 사용하다보면, properties 파일이나 yml 파일에서 spring.jpa.hibernate.ddl-auto 옵션을 볼 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&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;&lt;span&gt;&lt;span&gt;&lt;b&gt;✅ create&lt;/b&gt; : IF 기존 테이블 있으면 DROP 진행하고 CREATE&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;rarr;&lt;span&gt; 잘못 사용하면 큰일난다   요즘 개발바닥 유튜브를 자주 보는데 create 관련 실제 겪으신 엄청난 문제가.. (&lt;a href=&quot;https://youtu.be/SWZcrdmmLEU&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크  ️&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ create-drop&lt;/b&gt; : create 옵션과 같으나 어플리케이션 종료시점에 테이블 DROP&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 테스트 케이스에서 다 수행하고 마지막에 깔끔히 삭제하고 싶을 때 사용한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ update&lt;/b&gt; : ALTER 사용해서 변경한 내용만 반영&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 실제 서비스 배포 시에는 create, create-drop, update 사용하면 안 된다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ validate&lt;/b&gt; : 객체 구성(엔티티)과 스키마(테이블)가 다른지 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ none&lt;/b&gt; : 옵션 사용하지 않음&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; &amp;nbsp;필드와 컬럼 매핑&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;@Column 어노테이션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 컬럼(행)의 특성을 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;span&gt; (MySQL 기준으로 쓰면) NOT NULL, UNIQUE, DEFAULT 등의 옵션 지정할 수 있음!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ length도 자주 사용한다! (기본 값 : 255)&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;u&gt;&lt;b&gt;@Enumerated 어노테이션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;span&gt; 자바의 enum 타입을 매핑할 때 사용&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&lt;span&gt; STRING, &lt;span&gt;ORDINAL(비추) &lt;/span&gt;옵션이 있음!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;ORDINAL은 INTEGER 형태로 값을 저장하기 때문에, enum 구성을 수정할 경우 데이터의 의미가 망가질 수 있음 주의!!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;@Temporal 어노테이션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&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&gt;&lt;span&gt; 최신 하이버네이트에서는 LocalDate, LocalDateTime 자료형 객체를 사용하기 때문에 굳이 사용할 필요 없음!&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;&lt;u&gt;&lt;b&gt;@Lob 어노테이션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&lt;span&gt; 오라클 DB에서 대형 데이터를 저장할 때 사용&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;span&gt;&lt;span&gt;&lt;span&gt;문자면 CLOB, 그 외에는 BLOB으로 사용 (그냥 참고)&lt;/span&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;&lt;u&gt;&lt;b&gt;@Transient 어노테이션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;메모리 상에서 임시로 데이터를 저장하고 싶을 때 사용&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&lt;span&gt;&lt;span&gt; 그 말인 즉슨, 실제 DB에는 이 옵션이 붙는다면 해당 필드가 반영되지 않음&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의미는 알고 있었지만, 그동안 무의식적으로 &lt;b&gt;&lt;u&gt;@Id @GeneratedValue(strategy = GenerationType.IDENTITY)&lt;/u&gt;&lt;/b&gt; 로 자주 썼었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 &lt;u&gt;&lt;b&gt;'자동 생성 + MySQL 데이터베이스에 기본 키 생성을 위임한다'&lt;/b&gt;&lt;/u&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;&lt;b&gt;&lt;u&gt;@Id 어노테이션&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&lt;span&gt;&lt;span&gt; 그냥 @Id 어노테이션만 쓰고 Setter 방식으로 ID 값을 넣어줄 수도 있다!&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; 하지만 기본 키는 엄청 중요한 개념이란걸 알고 있지 않나! 사용자가 직접 생성하도록 하는 것은 좋지 않다!&lt;/span&gt;&lt;/span&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;&lt;u&gt;&lt;b&gt;@GeneratedValue 어노테이션&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;b&gt; IDENTITY&lt;/b&gt; : 기본 키 생성을 데이터베이스에 위임 (대표적으로 MySQL에서 사용)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;rarr;&lt;span&gt; MySQL 방식이라면, 이는 AUTO_INCREMENT와 같은 의미이다!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;b&gt; SEQUENCE&lt;/b&gt; : 데이터베이스 오브젝트를 통해 유일한 값을 순서대로 생성 (Oracle에서 사용)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;b&gt; TABLE&lt;/b&gt; : 키를 생성하는 전용 테이블을 따로 만들어서 SEQUENCE 방식으로 동작&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt; AUTO&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 아무것도 쓰지 않고 생략하면 AUTO! DB Dialect에 따라서 타입이 위의 3개 중에서 자동 지정&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>영재임재영</author>
      <guid isPermaLink="true">https://codenamejy.tistory.com/66</guid>
      <comments>https://codenamejy.tistory.com/66#entry66comment</comments>
      <pubDate>Thu, 9 Feb 2023 11:40:58 +0900</pubDate>
    </item>
    <item>
      <title>[스프링 JPA] 영속성 컨텍스트</title>
      <link>https://codenamejy.tistory.com/65</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;#1 영속성 컨텍스트.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qgFDK/btrYszchU2q/pqHcO2d8G8NzdL8AS3whp1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qgFDK/btrYszchU2q/pqHcO2d8G8NzdL8AS3whp1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qgFDK/btrYszchU2q/pqHcO2d8G8NzdL8AS3whp1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqgFDK%2FbtrYszchU2q%2FpqHcO2d8G8NzdL8AS3whp1%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;2940&quot; height=&quot;1650&quot; data-filename=&quot;#1 영속성 컨텍스트.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  EntityManager 그리고 트랜잭션&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EntityManager를 사용하면&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 내부적으로 DB Connection을 사용해 데이터베이스를 사용 &lt;/span&gt;&lt;/b&gt;할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성 단위는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 요청(한 트랜잭션 단위)이 들어올 때마다 새롭게 선언 &lt;/span&gt;&lt;/b&gt;하며,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 하나의 트랜잭션이 완료되면 꼭 close&amp;nbsp;&lt;/span&gt;&lt;/b&gt; 해주어야 한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절대로 쓰레드 간 EntityManager를 공유해서 사용하면 안 된다! 한 트랜잭션 안에서만 사용하고 닫아야 한다!&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;이런 EntityManager를 만들어내는 공장이 따로 있다! 공장의 이름은 EntityManagerFactory 라고 한다!&lt;/p&gt;
&lt;pre id=&quot;code_1675824133332&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName);

EntityManager em = emf.createEntityManager();

em.close();  // 트랜잭션 단위로 EntityManager close 해주기

emf.close();  // 전체 코드가 완료되면 EntityManagerFactory close 해주기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방식으로 EntityManager를 생성할 수 있다.&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; EntityManager는 한 트랜잭션 단위로 동작 &lt;/span&gt;&lt;/b&gt;했다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt;&amp;nbsp;EntityManagerFactory는 코드의 모든 동작이 완료되면 그때 close&amp;nbsp;&lt;/span&gt;&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;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; JPA에서 데이터를 변경하는 모든 작업은 트랜잭션 안에서 진행되어야 한다!&amp;nbsp;&lt;/span&gt;&lt;/b&gt; 엄청나게 중요한 내용이다! ⭐️⭐️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(단, 간단한 데이터 조회는 트랜잭션이 없이도 동작할 수 있다는 사실..!)&lt;/p&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;JPA가 내부에서 어떤 방식으로 동작하는지 알고 싶다면 '영속성 컨텍스트' 라는 개념을 알아야 한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영속성 컨텍스트는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 동작하고 있는 EntityManager 안에서 엔티티를 영구적으로 저장하는 환경&amp;nbsp;&lt;/span&gt;&lt;/b&gt; 이라고 이해하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1675825695921&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Member member = new Member();
member.setId(1L);
member.setName(&quot;memberA&quot;);

em.persist(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;위와 같은 방식으로 Member 라는 엔티티 객체를 영속성 컨텍스트로 저장할 수 있다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 위 코드를 수행하면 데이터베이스에 엔티티가 잘 저장되어 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;334&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y9vBZ/btrYvnvDDO1/759tY1GEPTYKzBRHggTRqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y9vBZ/btrYvnvDDO1/759tY1GEPTYKzBRHggTRqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y9vBZ/btrYvnvDDO1/759tY1GEPTYKzBRHggTRqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy9vBZ%2FbtrYvnvDDO1%2F759tY1GEPTYKzBRHggTRqK%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;210&quot; height=&quot;138&quot; data-origin-width=&quot;334&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지 않다는 것을 확인할 수 있다.&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; '영속성 컨텍스트에 저장'하는 것은 '데이터베이스에 저장'하는 것과는 완전히 다른 개념 &lt;/span&gt;&lt;/b&gt;이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PdjtA/btrYrjHODH1/OPsUtxcgG9SVClWacPYXrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PdjtA/btrYrjHODH1/OPsUtxcgG9SVClWacPYXrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PdjtA/btrYrjHODH1/OPsUtxcgG9SVClWacPYXrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPdjtA%2FbtrYrjHODH1%2FOPsUtxcgG9SVClWacPYXrk%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;600&quot; height=&quot;562&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 공부할 때, 데이터를 최종적으로 DB에 반영하는 과정을 'COMMIT' 이라고 공부했다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영속성 컨텍스트는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; DB와는 다른, 엔티티를 다른 매커니즘으로 활용하기 위한 또 하나의 공간 &lt;/span&gt;&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;그래서 em.persist( ) 를 통해서&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 영속성 컨텍스트로 저장을 했더라도, DB에 데이터가 반영되는 것이 아니다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB에&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 데이터를 저장하고 싶다면 반드시 COMMIT 과정을 넣어주어야&amp;nbsp;&lt;/span&gt;&lt;/b&gt; 하고, COMMIT은 트랜잭션을 통해서 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675827410312&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Member member = new Member();
member.setId(1L);
member.setName(&quot;memberA&quot;);

em.persist(member);  // 영속성 컨텍스트로 저장

em.getTransaction().commit();  // 커밋&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;COMMIT 과정을 추가한 위 코드를 수행한 결과는 다음과 같다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/stfjp/btrYsOUNulB/ZektPvVyLx3XccrUoP6rkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/stfjp/btrYsOUNulB/ZektPvVyLx3XccrUoP6rkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/stfjp/btrYsOUNulB/ZektPvVyLx3XccrUoP6rkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fstfjp%2FbtrYsOUNulB%2FZektPvVyLx3XccrUoP6rkK%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;550&quot; height=&quot;432&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이버네이트가 찍어준 로그를 보면 INSERT 쿼리를 통해 DB에 데이터가 잘 들어간 것을 확인할 수 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; DB에 저장된 데이터에 대한 수정 &lt;/span&gt;&lt;/b&gt;은 어떻게 동작할까?&lt;/p&gt;
&lt;pre id=&quot;code_1675827902744&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Member findMember = em.find(Member.class, 1L);
findMember.setName(&quot;memberZ&quot;);

// em.persist(findMember);  // 영속성 컨텍스트로 저장

em.getTransaction().commit();  // 커밋&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 EntityManager에 저장된 값을 찾으려면 find 메서드를 사용하면 된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 찾아온 값을 setName 메서드를 통해 값을 세팅하고, &lt;u&gt;&lt;b&gt;persist 메서드를 사용해서 내용을 저ㅈ..ㅏㅇ 하지 않아도 된다!!  &lt;/b&gt;&lt;/u&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;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; '변경 감지'&amp;nbsp;&lt;/span&gt;&lt;/b&gt; 라는 특성이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1798&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQqBYD/btrYsAibsv4/59tylFpfiBSkjZKpEHOkV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQqBYD/btrYsAibsv4/59tylFpfiBSkjZKpEHOkV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQqBYD/btrYsAibsv4/59tylFpfiBSkjZKpEHOkV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQqBYD%2FbtrYsAibsv4%2F59tylFpfiBSkjZKpEHOkV1%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;700&quot; height=&quot;770&quot; data-origin-width=&quot;1798&quot; data-origin-height=&quot;770&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 코드에서 find 메서드로 영속성 컨텍스트를 가져왔고, setName 메서드로 필드 내용을 변경하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 영속성 컨텍스트를 가져와서 사용한 덕분 &lt;/span&gt;&lt;/b&gt;에, EntityManager는 다음과 같은 행동을 할 수 있다.&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;1️⃣ 영속성 컨텍스트에 저장된 엔티티의 &lt;u&gt;&lt;b&gt;스냅샷과 변경된 내용을 비교&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ 변경이 있으면 &lt;u&gt;&lt;b&gt;감지하고 UPDATE 쿼리 수행&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ 커밋해서 최종적으로 데이터베이스에 반영&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;s&gt;(세상에 요런일이)&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EntityManager 입장에서,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 본인이 관리하는 영속성 컨텍스트에 대해서 변경 감지 기능을 수행 &lt;/span&gt;&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 기록해놓은&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 스냅샷과 코드에서 변경된 내용을 서로 비교해서 UPDATE 쿼리로 데이터를 변경 &lt;/span&gt;&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;이 과정에서 persist 메서드는 쓰이지 않았다. 그리고 위에서 말하지 않았나!&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; persist는 DB에 데이터를 저장하는 것이 아니라고!&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 위에 있는 수정 예제 코드를 수행하면 다음과 같은 결과가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1084&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0qCfQ/btrYqMYiz8P/Mqi3XSYlXZMVnuxokHlOeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0qCfQ/btrYqMYiz8P/Mqi3XSYlXZMVnuxokHlOeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0qCfQ/btrYqMYiz8P/Mqi3XSYlXZMVnuxokHlOeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0qCfQ%2FbtrYqMYiz8P%2FMqi3XSYlXZMVnuxokHlOeK%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;500&quot; height=&quot;295&quot; data-origin-width=&quot;1084&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;find 메서드에서 SELECT 쿼리를 통해 영속성 컨텍스트를 찾아오고, 변경 감지 기능 통해 UPDATE 쿼리를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스에서 결과를 확인해보면&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #67cb75; color: #ffffff;&quot;&gt; 수정할 때는 persist 메서드가 없어도 내용이 수정될 수 있다는 것 &lt;/span&gt;&lt;/b&gt;을 확인할 수 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적인 데이터의 반영은 COMMIT이 수행하는 것이고, persist 메서드는 영속성 컨텍스트에 올리는 용도니까!&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;u&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 어쨌든 DB에 최종적인 데이터를 반영하는 것은 COMMIT으로 수행되어야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;span&gt; persist 메서드는 영속성 컨텍스트에 올리는 용도&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&lt;span&gt; 변경 감지 기능이 있기 때문에, 영속성 컨텍스트를 가져와서 Setter로만 내용을 바꿔도 데이터를 수정할 수 있음!&lt;/span&gt;&lt;/span&gt;&lt;/p&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&lt;b&gt;1️⃣ &lt;u&gt;1차 캐시&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2010&quot; data-origin-height=&quot;706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkdv7r/btrYq45Gt1T/VIYVKeyvRpAxFoPZwn3cr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkdv7r/btrYq45Gt1T/VIYVKeyvRpAxFoPZwn3cr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkdv7r/btrYq45Gt1T/VIYVKeyvRpAxFoPZwn3cr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdkdv7r%2FbtrYq45Gt1T%2FVIYVKeyvRpAxFoPZwn3cr1%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;650&quot; height=&quot;706&quot; data-origin-width=&quot;2010&quot; data-origin-height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영속성 컨텍스트는 1차 캐시 기능을 통해서 DB에서 엔티티를 꺼내오지 않고도 데이터를 조회할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675832631451&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Member member = new Member();
member.setId(2L);
member.setName(&quot;memberB&quot;);

em.persist(member);

System.out.println(&quot;Find Member ID is 2L&quot;);
Member findMember2 = em.find(Member.class, 2L);

System.out.println(&quot;Find Member ID is 3L&quot;);
Member findMember3 = em.find(Member.class, 3L);&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;DB에서 데이터를 가져오는 것이 아니라 캐시된 데이터를 가져온다면 DB에 쿼리를 날릴 필요가 없지 않나!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 나온 결과는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsEWxe/btrYsAwdjKQ/jetEwQjVBBzn7Qy9LB4zYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsEWxe/btrYsAwdjKQ/jetEwQjVBBzn7Qy9LB4zYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsEWxe/btrYsAwdjKQ/jetEwQjVBBzn7Qy9LB4zYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsEWxe%2FbtrYsAwdjKQ%2FjetEwQjVBBzn7Qy9LB4zYk%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;330&quot; height=&quot;252&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영속성 컨텍스트에 저장된 데이터를 가져올 때는 1차 캐시를 활용하기 때문에 DB에 쿼리를 날릴 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 현재 1차 캐시에 없는 데이터를 가져올 때는 DB에 있는 데이터를 일단 꺼내와야 하기 때문에 SELECT 쿼리가 날라간다!&lt;br /&gt;(DB에는 데이터가 있지만 1차 캐시에는 데이터가 없다고 가정)&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;2️⃣ &lt;u&gt;&lt;b&gt;동일성 보장&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675833214898&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Member a = em.find(Member.class, 2L);
Member b = em.find(Member.class, 2L);

System.out.println(a == b);  // True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영속성 컨텍스트가 1차 캐시에 저장되어 있을 때, 똑같은 엔티티를 꺼내면 그 엔티티는 동일성을 보장할 수 있다는 내용이다!&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;3️⃣ &lt;u&gt;&lt;b&gt;쓰기 지연&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675833407780&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Member memberA = new Member();
memberA.setId(1L);
memberA.setName(&quot;memberA&quot;);

Member memberB = new Member();
memberB.setId(2L);
memberB.setName(&quot;memberB&quot;);

em.persist(memberA);
em.persist(memberB);

em.getTransaction().commit();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안 계속 반복했던 중요한 내용! persist 메서드는 영속성 컨텍스트에 저장하는 것이지 DB에 데이터를 저장하는 것이 아니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위 예제 코드를 수행하면 INSERT 쿼리는 엔티티에서 persist 메서드를 각각 호출하였을 때 나가는 것이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;COMMIT 하기 직전에 INSERT 쿼리가 나간다! 이것을 쓰기 지연이라고 한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(영속성 컨텍스트가 저장되는 공간과 DB 공간이 다른 것을 알고 있기만 한다면! 이해가 매우 쉽다!)&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;4️⃣ &lt;u&gt;&lt;b&gt;변경 감지&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;/u&gt;위에서 언급했다! ㅎㅎ&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>영재임재영</author>
      <guid isPermaLink="true">https://codenamejy.tistory.com/65</guid>
      <comments>https://codenamejy.tistory.com/65#entry65comment</comments>
      <pubDate>Wed, 8 Feb 2023 14:21:48 +0900</pubDate>
    </item>
    <item>
      <title>[MySQL] Data Control Language</title>
      <link>https://codenamejy.tistory.com/64</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;#4 Data Control Language.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1655&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kKr5O/btrYjmdTrTT/fxZdcx0htq226NaH6eqkok/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kKr5O/btrYjmdTrTT/fxZdcx0htq226NaH6eqkok/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kKr5O/btrYjmdTrTT/fxZdcx0htq226NaH6eqkok/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkKr5O%2FbtrYjmdTrTT%2FfxZdcx0htq226NaH6eqkok%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;2940&quot; height=&quot;1655&quot; data-filename=&quot;#4 Data Control Language.jpg&quot; data-origin-width=&quot;2940&quot; data-origin-height=&quot;1655&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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;기본 키(Primary Key)&lt;/b&gt; : 행 데이터에 대한 고유 식별자 (중복 ❌ / NULL 허용 &lt;span&gt;❌)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;span&gt;&lt;b&gt; 고유 키(Unique Key)&lt;/b&gt; : 기본 키로 지정되지 않은 행 데이터에 대한 고유 식별자 (중복 ❌ / &lt;span&gt;NULL 허용&amp;nbsp;&lt;span&gt;⭕️)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅&lt;span&gt;&lt;b&gt; 외래 키(Foreign Key)&lt;/b&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;&lt;span&gt;&lt;span&gt;기본 키와 고유 키는 테이블을 생성할 때 다음과 같이 만들어낼 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675740092665&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE User (
    id INT AUTO_INCREMENT PRIMARY KEY,
    first_name CHAR(2) UNIQUE,
    last_name CHAR(3)
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;id 필드는 기본 키이고, first_name 필드는 고유 키이다! 하지만 외래 키 개념이 조금 더 중요한 것 같다..  &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;JPA에서&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 엔티티 간의 관계 &lt;/span&gt;&lt;/b&gt;를 설정할 때 1:1, 1:多 등의 관계를&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 외래 키를 통해서 관계를 매핑 &lt;/span&gt;&lt;/b&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpaPaL/btrYlAbdJCL/OkXtS9m3yDKH7MM6DCjYa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpaPaL/btrYlAbdJCL/OkXtS9m3yDKH7MM6DCjYa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpaPaL/btrYlAbdJCL/OkXtS9m3yDKH7MM6DCjYa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpaPaL%2FbtrYlAbdJCL%2FOkXtS9m3yDKH7MM6DCjYa0%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;400&quot; height=&quot;203&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외래 키를 추가하고 싶으면, 위 포맷을 따라 추가하면 된다! 실제로 JPA를 통해 엔티티 간 관계를 만든 예제를 보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4rIkQ/btrYlAbeyik/kQQJ8qXJlTWou9pGW46rsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4rIkQ/btrYlAbeyik/kQQJ8qXJlTWou9pGW46rsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4rIkQ/btrYlAbeyik/kQQJ8qXJlTWou9pGW46rsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4rIkQ%2FbtrYlAbeyik%2FkQQJ8qXJlTWou9pGW46rsk%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;400&quot; height=&quot;109&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt;&amp;nbsp;한 명의 회원은 여러 주문 &lt;/span&gt;&lt;/b&gt;을 할 수 있어서, member(회원) 테이블과 orders(주문) 테이블은&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 1:多 관계 &lt;/span&gt;&lt;/b&gt;를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 부모 테이블은 member, 자식 테이블은 orders로&amp;nbsp;&lt;/span&gt;&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;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt;&amp;nbsp;외래 키 &lt;/span&gt;&lt;/b&gt;는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 자식 테이블에서 필드 &lt;/span&gt;&lt;/b&gt;로 가지고 있어야 하기 때문에,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 자식 테이블인 orders에서 외래 키를 생성 &lt;/span&gt;&lt;/b&gt;했고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt;&amp;nbsp;자식 테이블 내 member_id 라는 필드가 부모 테이블의 기본 키에 매핑 &lt;/span&gt;&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;그리고 외래 키를 통한 수정 및 삭제 시 옵션이 2가지 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;NO ACTION, RESTRICT&lt;/b&gt; : 자식 테이블에 외래 키가 존재할 때 부모 테이블에서 행 데이터&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 수정 및 삭제 제한&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;CASCADE&lt;/b&gt; : 부모 테이블에서 행 데이터를 수정 및 삭제할 경우,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 자식 테이블에서도 똑같이 반영&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  트랜잭션 (Transaction)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션의 좋은 예시를 얄코님이 알려주셨다! 친구끼리 계좌를 송금하는 상황을 살펴보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2630&quot; data-origin-height=&quot;1522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBq4F4/btrYpgQ4XDE/fjwYmnym5RtugMnj7Yu2j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBq4F4/btrYpgQ4XDE/fjwYmnym5RtugMnj7Yu2j1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBq4F4/btrYpgQ4XDE/fjwYmnym5RtugMnj7Yu2j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBq4F4%2FbtrYpgQ4XDE%2FfjwYmnym5RtugMnj7Yu2j1%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;700&quot; height=&quot;405&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2630&quot; data-origin-height=&quot;1522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친구에게 돈을 송금할 때, A의 계좌에서 돈이 빠져나가면 그 돈은 그대로 B의 계좌에 들어가야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 A의 계좌에서 돈이 빠져가나지 않았는데 B에게 돈이 들어오거나, (오히려 좋아?)&lt;br /&gt;A의 계좌에서는 돈이 빠져나갔는데 B의 계좌에 돈이 들어오지 않는 상황이라면.. (하이재킹을 당해버렸다)&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;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 모든 동작이 모두 성공하거나 혹은 모두 실패해야 한다!&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; A의 계좌에서 돈이 빠져나가고, B의 계좌에 돈이 들어오는 상황을 하나의 트랜잭션으로&lt;/span&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt;묶어야한다!&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 중간에 문제가 생겨도 다시 처음의 상태로 ROLLBACK 가능하기 때문이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 모든 동작이 정상적으로 수행 가능한 상태라면, COMMIT 명령을 수행해서 최종적인 결과를 확정하면 된다!&lt;/p&gt;
&lt;pre id=&quot;code_1675743347632&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;START TRANSACTION;

INSERT INTO User (name, age, introduce) VALUES ('Seo', 22, 'hi');

SELECT * FROM User;

-- COMMIT;

ROLLBACK;

SELECT * FROM User;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 첫번째 SELECT 문에서는 넣은 데이터를 확인할 수 있지만, 두번째 SELECT 문에서는 넣은 데이터가 없어진 상태이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ROLLBACK으로 인해 데이터를 넣기 이전! 즉,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 처음 상태로 원상 복구 &lt;/span&gt;&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;데이터는 COMMIT 이전까지 DB에 값이 저장되지 않는다!!&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 최종적인 COMMIT을 수행해야 완전히 값이 저장 &lt;/span&gt;&lt;/b&gt;되기 때문이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 중간에 주석 처리된 COMMIT을 수행하고, ROLLBACK을 수행한다면,&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 데이터가 이미 DB에 반영 &lt;/span&gt;&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;✅&lt;b&gt;&lt;span&gt; COMMIT &lt;/span&gt;&lt;/b&gt;&lt;span&gt;: 데이터를&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt; 최종적으로 DB에 반영 &lt;/span&gt;&lt;/b&gt;하는 과정&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;ROLLBACK &lt;/b&gt;:&amp;nbsp;&lt;span style=&quot;background-color: #304562; color: #ffffff;&quot;&gt;&lt;b&gt; 하나의 트랜잭션 안에서 데이터를 원래 상태로 복구&lt;/b&gt; &lt;/span&gt;시키는 과정&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  권한 다루기&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1675744194998&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GRANT UPDATE, DELETE ON Order TO 'user01'@'%';

REVOKE UPDATE, DELETE ON Order TO 'user01'@'%';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;b&gt;&lt;span&gt;&lt;span&gt; GRANT&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;: 테이블 생성 / 수정 / 삭제 권한 부여&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;REVOKE&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;: 테이블 생성 / 수정 / 삭제 권한 박탈&lt;/p&gt;</description>
      <category>기타 공부</category>
      <author>영재임재영</author>
      <guid isPermaLink="true">https://codenamejy.tistory.com/64</guid>
      <comments>https://codenamejy.tistory.com/64#entry64comment</comments>
      <pubDate>Tue, 7 Feb 2023 13:51:41 +0900</pubDate>
    </item>
  </channel>
</rss>