<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Sean :: Dev</title>
    <link>https://os94.tistory.com/</link>
    <description>개발개발  </description>
    <language>ko</language>
    <pubDate>Tue, 7 Jul 2026 11:08:28 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>os94</managingEditor>
    <image>
      <title>Sean :: Dev</title>
      <url>https://tistory1.daumcdn.net/tistory/3239021/attach/a76a0fc0d0d3458db39a0bb003af8f34</url>
      <link>https://os94.tistory.com</link>
    </image>
    <item>
      <title>21.04.15) 오늘의 기록 - 날뛰는 팬소음과 Google Chrome Helper</title>
      <link>https://os94.tistory.com/204</link>
      <description>&lt;p&gt;작업을 할때 이것저것 많이 켜두긴 하지만, 그래도 특별히 빌드 돌릴때 아니면 팬이 미쳐 날뛰지 않는데,&lt;/p&gt;
&lt;p&gt;오늘은 유난히 계속해서 맥이 이륙하려 날뛰었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LNJjd/btq2HPPTL9w/enKbZSRcGIKgCzj4OJ5ka1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LNJjd/btq2HPPTL9w/enKbZSRcGIKgCzj4OJ5ka1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LNJjd/btq2HPPTL9w/enKbZSRcGIKgCzj4OJ5ka1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLNJjd%2Fbtq2HPPTL9w%2FenKbZSRcGIKgCzj4OJ5ka1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;해결후 캡쳐한거라 보이지 않지만, 문제의 Google Chrome Helper (Renderer)가 CPU를 미친듯이 먹고있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;516&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mtdo7/btq2HxopAYR/eSZrQhTWCjFgabeuHbgLP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mtdo7/btq2HxopAYR/eSZrQhTWCjFgabeuHbgLP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mtdo7/btq2HxopAYR/eSZrQhTWCjFgabeuHbgLP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmtdo7%2Fbtq2HxopAYR%2FeSZrQhTWCjFgabeuHbgLP0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;516&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Chrome Menu &amp;rarr; Window &amp;rarr; Task Manager 로 진입해 확인해보니,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;보통 일부 크롬 플러그인이 리소스를 잡아먹기도 한다는데,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;난 크롬 탭중에 특정 Grafana 페이지가 무슨 비트코인 채굴마냥 리소스를 다 먹고있었다. (그래프가 좀 많긴 하더만은...)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;종료시켜주니 이제야 맥이 좀 쉰다,, 고생시켜서 미안  &lt;/span&gt;&lt;/p&gt;</description>
      <category>Daily.log</category>
      <category>Google Chrome Helper</category>
      <category>맥 CPU</category>
      <author>os94</author>
      <guid isPermaLink="true">https://os94.tistory.com/204</guid>
      <comments>https://os94.tistory.com/204#entry204comment</comments>
      <pubDate>Thu, 15 Apr 2021 21:58:46 +0900</pubDate>
    </item>
    <item>
      <title>비전공자를 위한 개발 입문 정보</title>
      <link>https://os94.tistory.com/203</link>
      <description>&lt;h3&gt;배경&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;아직 내 앞가림도 제대로 못하는 병아리 신입 개발자이지만, 고등학교 3년동안 희망직업이 교사일 만큼 나는 지식 전파와 학습법 공유 등에 꽤나 재미를 느낀다&lt;/li&gt;
&lt;li&gt;나도 출발이 비전공자라서 주변에 인맥도 없고 정보의 가뭄 속에서 오래 고생했던 경험이 있기에, 이런 부분들에 더 집착하는 경향이 있는것 같기도 하다
&lt;ul&gt;
&lt;li&gt;개발에 어떤 분야들이 있는지, 각각은 어떤 특징과 장단점을 갖는지, 어떤 공부를 어떻게 해야하는지 등 열심히 인터넷 서칭을 하지만 유용한 정보를 얻긴 힘들고 (주로 나오는건 학원 광고들뿐..), 인강을 단순무식하게 외우는 등 비효율적으로 학습을 삽질했던 때를 기억하면, 지금도 속 쓰리곤 한다&lt;/li&gt;
&lt;li&gt;지금도 가끔가다 Collection 프레임워크 메서드 종류를 종이에 써가며 학습하시는 분을 보면, 마음 한켠이 먹먹하다 ㅜ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;요즘 타 전공을 하다가 코딩쪽으로 넘어오는 경우가 많기에, 나 역시도 주변의 친구/지인들에게 내가 아는 것을 공유하고 개발자가 되려는 노력을 직간접적으로 돕곤한다. 겨우 일이년 빠른 것 밖에 없지만, 내 과거의 답답함을 생각하며 과한 오지람으로 친구들에게 이리저리 간섭한다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;내가 주변 친구들에게 공유하는 내용들이 반복되다 보니, 이참에 아예 이렇게 글로 정리해보았다.&lt;/b&gt; &lt;del&gt;중복 제거는 개발자의 숙명..&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이 글은 개발을 아예! 모르는 내 주변의 입문자 친구들을 대상으로 쓴 글&lt;/b&gt;이기에, 대부분의 개발자분들은 물론 git을 써봤다 하시는 분들은 뒤로가기를 눌러 소중한 시간을 절약하셔야 한다! (early return plz.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;왕초보 :: 가장 먼저 해야할 것&lt;/h3&gt;
&lt;ol start=&quot;0&quot;&gt;
&lt;li&gt;아마도 가장 처음에 할 고민, &lt;b&gt;내가 개발자에 맞을까 ?&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;여러 방법이 있지만, 보통 파이썬 기본서를 사서 문법을 익히고 알고리즘 문제를 풀어보며 코딩을 맛보기도 하는데 이런 방법보단, 생활코딩(이외에도 기초수준의 컨텐츠를 제공하는 많은 부트캠프, 사이트, 인강들이 있다) 같은 사이트를 이용해 기초적인 수준의 HTML+JS 홈페이지를 만들어보며 내가 개발에 흥미를 갖고있는지 확인해보는 것을 추천한다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;아주 rough하게 요약해본 개발자의 이미지&lt;/b&gt;
&lt;ul&gt;
&lt;li&gt;장점
&lt;ul&gt;
&lt;li&gt;기술직이라서 어디가도 굶어죽진 않겠다는 안심(?)&lt;/li&gt;
&lt;li&gt;워라밸은 사실 회사/팀마다 편차가 큰 것 같다. 그치만 전반적인 회사 자체의 분위기는 IT 서비스 기업 기준, 대부분 수평적이고 자유로운 문화라서 스트레스가 덜하다&lt;/li&gt;
&lt;li&gt;내가 만든 결과물이 서비스/동작되는 것을 보며 느끼는 성취감&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점
&lt;ul&gt;
&lt;li&gt;평생 공부할 자신이 있는가 (퇴근후, 주말에도 스스로 공부하는 삶)&lt;/li&gt;
&lt;li&gt;&lt;s&gt;나도 모르게 개발/코딩 관련 밈에 중독되며, 친구들에게 너드 취급을 받을 수 있다...&lt;/s&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사견
&lt;ul&gt;
&lt;li&gt;난 우연히 접한 코딩이 너무 재밌어서 넘어온 케이스지만, 요즘 타 전공에서 많이 넘어오며 기대했을 일자리/채용공고 자체는 확실히 많은 편이다. 하지만 개발은 무엇보다도 흥미를 많이 타는 직종이라 한번 경험해보고 자신에게 맞는지 확인해보는게 좋을 것 같다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;Github 계정부터 만들기&lt;/b&gt;
&lt;ul&gt;
&lt;li&gt;TIL 추천 (초반 공부습관 형성 &amp;amp; 문서지만 눈에 보이는 결과물의 성취감)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;notion, 블로그 등 공부한 것을 정리할 수 있는 공간 만들기&lt;/b&gt;
&lt;ul&gt;
&lt;li&gt;티스토리, 깃헙 블로그, velog 등 (굳이 네이버 블로그로만 시작하지 않으면 될 것 같다)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개발자 내에서도 진로 정하기&lt;/b&gt;
&lt;ul&gt;
&lt;li&gt;주의 - 인터넷 서핑과 머리속 고민만으론 1년을 고민해도 절대 결정 못한다
&lt;ul&gt;
&lt;li&gt;다양한 분야를 공부하는걸 언어도/지식도/프레임워크도 모두 다르기 때문에 시간이 아깝다고 느껴서, 실제로 손은 안 대보고 &quot;뭐하지&quot; 고민만 하는 경우가 있는데 이래선 절대 고민이 해결 안된다&lt;/li&gt;
&lt;li&gt;&amp;rarr; 결국 분야별로 한번씩 직접 경험해보면서 자신의 흥미를 찾아야한다. 따라서 결정하는데 최소 6개월은 걸린다고 본다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;주요 분야
&lt;ul&gt;
&lt;li&gt;백엔드&lt;/li&gt;
&lt;li&gt;프론트엔드&lt;/li&gt;
&lt;li&gt;모바일&amp;amp;앱&lt;/li&gt;
&lt;li&gt;그 외 (AI, 데이터 쪽은 일반 서비스개발과는 별개의 분야로 보자)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;개인적인 의견
&lt;ul&gt;
&lt;li&gt;분야별로 한번씩 경험해보는 것을 추천했는데, 프론트의 경우 어짜피 JS를 공부해야하고, ios에 관심이 있다면 스위프트를 해볼 것이다.&lt;/li&gt;
&lt;li&gt;백엔드는 상대적으로 선택지가 다양한데 JS로 Node.js를 해볼수도 or Java로 Spring을 해볼수도 or Python으로 Django를 해볼수도 있다.&lt;/li&gt;
&lt;li&gt;이때 만약 자바로 백엔드를 경험해본다면, 자바에 익숙하므로 안드로이드도 경험해볼수있다(물론 제대로 하겠다면 코틀린을 공부해야겠지만) (백엔드 프레임워크 점유율이 자바X스프링이 가장 많은건 덤!)&lt;/li&gt;
&lt;li&gt;물론 JS로 프론트/서버/앱 전부 체험해볼 순 있지만, JS와 Java는 언어체계가 다르므로 둘 다 경험해보는걸 추천한다. &lt;del&gt;내가 자바개발자라 자바를 추천하는건 아님&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;결론
&lt;ul&gt;
&lt;li&gt;프론트 개발을 체험해보고싶다면, &lt;code&gt;TODO 리스트 만들기&lt;/code&gt; 를 해보자. 이 부분과 관련된 컨텐츠는 넘쳐날것&lt;/li&gt;
&lt;li&gt;백엔드 개발을 체험해보고싶다면, &lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&amp;amp;mallGb=KOR&amp;amp;barcode=9788965402602&amp;amp;orderClick=LEa&amp;amp;Kc=&quot;&gt;스프링 부트와 AWS로 혼자 구현하는 웹 서비스&lt;/a&gt; 책을 참고해보자&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;분야를 정했으면, 이제 그쪽에 관련된 공부만 올인하면 된다&lt;/b&gt;
&lt;ul&gt;
&lt;li&gt;개발자 취준은 다른 분야 - 대표적으로 고시류 시험(몇년이 걸려도 된다는 보장이 없다)과 달리,&lt;br /&gt;신입 개발자로 취직하기 위한 어느정도의 &quot;지식의 총량&quot;이 정해져 있어서, 사람마다 소요 시간은 다르겠지만 어쨌든 바른 방향으로 공부를 계속 하다보면 언젠가는 도달할 수 있다고 생각한다&lt;/li&gt;
&lt;li&gt;무작정 양치기로 공부하지말고 유명한 로드맵을 찾아보자&lt;/li&gt;
&lt;li&gt;프레임워크 학습도 물론 어느정도 필요하지만 매몰되지 말자. CS나 이론적인 내용들에도 갈증을 느끼자&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;TIP&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.notion.so/desktop&quot;&gt;notion&lt;/a&gt; 활용
&lt;ul&gt;
&lt;li&gt;문서&amp;amp;필기 등 공부한 내용을 정리하는 용도로 사용. 에버노트의 업그레이드 버전이라고 보면 된다&lt;/li&gt;
&lt;li&gt;원래 유료였으나 무료로 바뀌어 강추. 모바일 앱도 있음&lt;/li&gt;
&lt;li&gt;더이상 로컬 파일 지옥에서 벗어나자&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Github 꾸미기
&lt;ul&gt;
&lt;li&gt;깃헙은 신입개발자의 이력서&lt;/li&gt;
&lt;li&gt;깃헙을 꾸미고 포장하다보면 포장할 꺼리를 채우기 위해, 학습을 더 열심히 하게 된다&lt;/li&gt;
&lt;li&gt;예시 : TIL, Github Profile, Github Blog, 알고리즘 문제풀이같은 각종 공부 내용 정리 Repo 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;채용 공고 수시로 확인
&lt;ul&gt;
&lt;li&gt;네이버&amp;amp;카카오 상시 채용 공고가 항상 올라와있으니, 자주 눈팅하면서 어느 직군을 많이 뽑고 어느 기술셋을 필요로 하는지 동향을 파악하자 &amp;rarr; 무엇을 공부해야하는지로 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;목표 기업 선정
&lt;ul&gt;
&lt;li&gt;&lt;i&gt;의외로 처음엔 회사들의 categorizing도 힘들어하는 경우도 있어 일단 나열해보았다&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;서비스 기업 : 네이버, 카카오, 라인, 쿠팡, 배민, 토스, 당근마켓, 야놀자 등&lt;/li&gt;
&lt;li&gt;대기업&lt;/li&gt;
&lt;li&gt;SI (삼성 SDS, LG CNS, SK C&amp;amp;C ...)&lt;/li&gt;
&lt;li&gt;통신사&lt;/li&gt;
&lt;li&gt;게임사&lt;/li&gt;
&lt;li&gt;등등 ..&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;교육과정/부트캠프&lt;/h3&gt;
&lt;p&gt;검색하다보면 정말 많은 사설학원, 교육기관들이 존재한다. 본인의 소중한 시간과 돈을 투자하는 선택인데, 부디 충분하고 신중한 고민을 거쳤으면 좋겠다. 언급할 순 없지만, 주변에 안타까운 케이스도 봤어서..&lt;/p&gt;
&lt;p&gt;아래는 어느정도 신뢰성이 보장된 과정들이며, 경쟁이 세지만 도전해보자. 물론 각 과정들마다 배울수있는 부분들이 다르므로 알아보고 충분히 이해하자.&lt;/p&gt;
&lt;p&gt;아래의 유료과정들은 비용부담이 있겠지만 자신있게 추천할 수 있다.&lt;/p&gt;
&lt;p&gt;추가로 &lt;b&gt;국비과정&lt;/b&gt;에 대한 의견은 짧게 요약하기 어려워 생략하며, 영향력있는 두 분의 글을 첨부해본다.&lt;br /&gt;&lt;a href=&quot;https://jojoldu.tistory.com/277&quot;&gt;jojoldu님&lt;/a&gt; &lt;a href=&quot;https://ryan-han.com/post/essay/memoirs2018/&quot;&gt;체대생개발자님&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;우아한테크코스/캠프 (우아한형제들)&lt;/li&gt;
&lt;li&gt;부스트캠프 (NAVER)&lt;/li&gt;
&lt;li&gt;이노베이션 아카데미_42 Seoul (정부)&lt;/li&gt;
&lt;li&gt;SAFFY (Samsung)&lt;/li&gt;
&lt;li&gt;SW사관학교 정글 (KAIST) [유료]&lt;/li&gt;
&lt;li&gt;nextstep [유료]&lt;/li&gt;
&lt;li&gt;코드스쿼드 [유료]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;인터넷 강의&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.inflearn.com/&quot;&gt;인프런&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://opentutorials.org/course/1&quot;&gt;생활코딩&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.boostcourse.org/&quot;&gt;부스트코스&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;추천 자료 &amp;amp; 읽을거리&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%97%85-%ED%86%B5%ED%95%A9%ED%8E%B8#&quot;&gt;인프런 - 한정수님 강의&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;굉장히 내 생각&amp;amp;가치관과 똑같아서 놀랬다. 잘 정리된 좋은 자료이며, 이쪽 시장 상황을 잘 아는 사람이라면 괜찮지만, 과거의 나처럼 제한적인 정보의 환경에 있는 분들이라면 많은 인사이트를 얻고, 갖고 있던 궁금증들을 어느정도 해소할 수 있을 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jojoldu.tistory.com/505&quot;&gt;비전공자로 자바 백엔드 개발자 시작하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ryan-han.com/post/memoirs/memoirs2020/&quot;&gt;체대 출신 개발자의 2020년 회고&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://junilhwang.github.io/TIL/Review/2020-year/end/#%E1%84%8B%E1%85%A5%E1%86%B8%E1%84%86%E1%85%AE&quot;&gt;주니어 개발자의 2020년 회고&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jojoldu/junior-recruit-scheduler&quot;&gt;https://github.com/jojoldu/junior-recruit-scheduler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/devJang/developer-roadmap&quot;&gt;https://github.com/devJang/developer-roadmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.slideshare.net/LuckyYoWu/2018-for&quot;&gt;https://www.slideshare.net/LuckyYoWu/2018-for&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;학부때 듣고 졸업하길 추천하는 CS 과목&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;네트워크, OS, DB&lt;/li&gt;
&lt;li&gt;알고리즘, 자료구조&lt;/li&gt;
&lt;li&gt;컴퓨터구조&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;개발 동아리&lt;/h3&gt;
&lt;p&gt;대학생때 해보려고 리스팅 해둔 목록인데, 관심있는 사람에겐 인터넷 서칭 시간이라도 아낄수있겠지하며 적어보았다.&lt;/p&gt;
&lt;p&gt;&lt;del&gt;경험해본적은 없으므로 내부사정은 잘 모른다. 유지가 잘 안되는 곳도 있는 듯..?&lt;/del&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;멋쟁이사자처럼&lt;/li&gt;
&lt;li&gt;Nexters&lt;/li&gt;
&lt;li&gt;SOPT&lt;/li&gt;
&lt;li&gt;YAPP&lt;/li&gt;
&lt;li&gt;CADI&lt;/li&gt;
&lt;li&gt;REAL&lt;/li&gt;
&lt;li&gt;프로그라피&lt;/li&gt;
&lt;li&gt;디프만&lt;/li&gt;
&lt;li&gt;Mash Up&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;다 쓰고 나니 드는 생각은 역시.. 내 앞가림이나 잘 해야겠다...&lt;/i&gt;&lt;/span&gt;  &lt;/p&gt;</description>
      <category>Etc</category>
      <category>개발자</category>
      <category>비전공자</category>
      <category>입문</category>
      <author>os94</author>
      <guid isPermaLink="true">https://os94.tistory.com/203</guid>
      <comments>https://os94.tistory.com/203#entry203comment</comments>
      <pubDate>Mon, 15 Feb 2021 21:00:55 +0900</pubDate>
    </item>
    <item>
      <title>JPA의 사실과 오해 (사실은 JPQL과 Fetch Join의 오해)</title>
      <link>https://os94.tistory.com/201</link>
      <description>&lt;h1&gt;JPA의 사실과 오해 (사실은 JPQL과 Fetch Join의 오해)&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;20.05.25 JPA 스터디 하며 깨닫게 된 오해&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;JPA를 꽤나 오래 써왔는데(그래봤자 1년이긴 하지만), 이번에 스터디를 하며 크게 잘못 알고 있던 개념 몇가지를 정정하고, 스스로 부끄럽기도 하고 나름의 컬쳐쇼크를 받았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;1. N+1은 연관 엔티티가 Collection인지가 중요한게 아니다.&lt;/h1&gt;
&lt;p&gt;( 주의 - JPQL 기준, SQL X )&lt;/p&gt;
&lt;p&gt;사실 왜 (당시 스터디하던) 우리 모두가 N+1 문제를 연관 엔티티의 컬렉션 여부와 관련지어 생각해왔던 건지 모르겠다. 대부분의 교재나 링크들에서 &lt;code&gt;Member -* Orders&lt;/code&gt; 와 같은 일대다의 관계를 예시로 들며 설명하기 때문일까.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;나는 이제까지 N+1 이 member.getOrders() 의 컬렉션 프록시의 초기화와 관련해, member.getOrders() 혹은 member.getOrders().get(0) 을 호출할 때, orders 컬렉션의 size 만큼 쿼리가 더 나가는, 그래서 그걸 방지하기 위해 Fetch Join을 걸어야하는 그런 걸로 생각하고 있었다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(이부분은 좀더 내 개인적인 부분인데, 개인적으로 N+1 이라는 용어를 초반에 패치 조인을 안걸어서 비즈니스 로직 중간에 의도치 않은 쿼리가 나가는 현상 전체를 가리키는, 좀더 넓은 범위의 용어로 써오기도 했었다. 내 머리속에선)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;결론은 틀렸다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Member -* Orders&lt;/code&gt; 의 관계 예시에서, Member의 Order 필드가 컬렉션인지는 중요한게 아니다. 즉, order 인지 orders 인지가 중요한게 아니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;초점은 오히려&lt;/b&gt; Order 엔티티가 아닌 &lt;b&gt;Member 엔티티&lt;/b&gt;다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;혼란을 방지하기 위해, &lt;code&gt;Member - Order&lt;/code&gt; 의 일대일 관계로 예시를 수정해보자.&lt;/p&gt;
&lt;p&gt;member1 인스턴스를 가져오는 상황이라고 가정하자.&lt;/p&gt;
&lt;p&gt;추가로 &lt;code&gt;N+1 은 Eager인지 Lazy인지도 중요하지 않다&lt;/code&gt;는 사실도 안다고 가정. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(지연 로딩이어도, 나중에 해당 데이터가 사용되는 시점에 프록시가 초기화되며 쿼리가 나간다)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;member1 을 가져오는데, 패치 조인을 걸지 않으면 member1.order 값도 가져오기 위해 추가로 쿼리를 날려야 한다. 이 경우, 날리는 쿼리의 횟수는 1+1 = 2회다.&lt;/p&gt;
&lt;p&gt;이 때, 애초에 member1을 가져올 때 패치 조인을 걸면 member1.order 값도 함께 (한 쿼리로) 가져올 수 있다. 그렇게 되면, 날리는 쿼리의 횟수는 1+1 = 2회에서 1회로 줄어든다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이게 그렇게 유명한 &lt;code&gt;패치 조인&lt;/code&gt; 의 효과다.&lt;/p&gt;
&lt;p&gt;겨우 2&amp;rarr;1 회 줄어들었는데, 무슨 차이일까?&lt;/p&gt;
&lt;p&gt;만약, 애초에 상황이 member1 인스턴스만 가져오는게 아니라, member1, member2, member3 ... member5000 까지, 5000명의 멤버를 가져오는 상황이였다면 달라진다.&lt;/p&gt;
&lt;p&gt;이 경우, 1+5000 &amp;rarr; 1 로 크게 줄어드는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1+5000 &amp;rarr; 1 을 부연 설명하면,&lt;/p&gt;
&lt;p&gt;1 (member1~member5000 가져오기) + 5000 (member1.order 가져오기, ... , member5000.order 가져오기) &amp;rarr; 1 (앞의 데이터 모두 한번에 가져오기. 물론 리턴값은 List&amp;lt;Member&amp;gt; 즉 member1~member5000 그대로, 대신 member1.order~member5000.order의 데이터는 영속성 컨텍스트 안에 존재)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이게 패치 조인의 효과다. 괜히 여기저기서 패치조인, 패치조인하며 자주 사용되던게 아니었다.&lt;/p&gt;
&lt;p&gt;그렇다면, 이걸 다 이해했다고 치고 평소에 편하게 개발할 땐, 무엇을 생각하고 기준으로 삼으며 판단하면 될까 ?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[ 결론 ]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;가져오려는 엔티티의 연관 엔티티가 컬렉션인지 or 단일 객체인지에 상관없이,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(물론 연관 엔티티 데이터를 쓴다는 전제하에)&lt;/span&gt; 리턴 타입이 List&amp;lt;?&amp;gt;면 패치 조인을 거의 무조건 걸어야 한다고 생각하자.&lt;/p&gt;
&lt;p&gt;왜냐면 리턴 타입이 단일 객체라면 패치 조인을 써봤자 겨우 2&amp;rarr;1로 1회 줄어들 뿐이다. 반면 리턴 타입이 리스트라면, 리턴된 리스트의 개수가 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;적으면 어쩔 수 없지만&lt;/span&gt; 얼마나 많을지도 모르는 것이기 때문에, 아까 1+5000 처럼 성능 저하의 위험성을 방지하기 위해 패치 조인을 걸어주는 것이 맞다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;물론 리턴 타입이 단일 객체라고 굳이 패치 조인을 안 쓸 이유는 없는 것 같다.&lt;/p&gt;
&lt;p&gt;더 바깥 비즈니스 로직에서, 반복문을 돌면서 member1 부터 member5000 을 차례로 가져온다면,&lt;/p&gt;
&lt;p&gt;(1+1)*5000 회의 쿼리를 1*5000 회로 줄일수는 있는거니까.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;2. JPQL) OneToMany 패치 조인 에서 distinct를 걸지 않으면, 값이 중복되어 가져와진다.&lt;/h1&gt;
&lt;p&gt;믿고 싶지도 않고 믿기지도 않지만 사실이다. 이 사실을 믿는데 어느정도의 시간이 걸린 이유는,&lt;/p&gt;
&lt;p&gt;우리가 JPQL이 당연히 해줄것이라고 생각했던 것, 다시 말하면 JPQL이 생각처럼 좋진 않고 원시적이라는 것이다.&lt;/p&gt;
&lt;p&gt;(현재 논점과 다른 이야기긴 하지만, 사실 JPQL이 SQL을 만들때 연관 관계를 고려하지 않고 막 만든다는 사실도, 우리의 JPQL에 대한 환상?을 깨뜨리기도 했다)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 중복 문제의 근본적인 원인을 먼저 말하면, &lt;b&gt;SQL과 JPQL이라는 근본적 차이&lt;/b&gt; 때문이기도 하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이슈를 먼저 이야기 해보자.&lt;/p&gt;
&lt;p&gt;member1 은 order1 과 order2 를 가진 객체이다.&lt;/p&gt;
&lt;p&gt;이때 member1을 조회하는 JPQL 쿼리에서 패치 조인으로 member.orders 까지 같이 가져온다고 상황을 가정해보자.&lt;/p&gt;
&lt;p&gt;그저 member.orders를 패치조인으로 함께 가져왔을 뿐인데, 놀랍게도 결과는 똑같은! member1 이 2개 들어있다. 믿기지 않다면 직접 테스트 해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이유는 JPQL이 그리 똑똑하진 않아서, 자동으로 중복 제거를 해주지 않기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;사실 SQL이라면 위의 결과는 당연한거다 !&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;SQL 에선 SELECT 절에 단일 객체, 즉 엔티티를 넣을 수 없다. &lt;code&gt;select m.id, m.name ... from Member m where ~&lt;/code&gt; 와 같이 써야한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;member1.id - member1.name - order1.id - order1.name&lt;/li&gt;
&lt;li&gt;member1.id - member1.name - order2.id - order2.name&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;와 같은 테이블을 결과로 가져오는 것이 당연한거다. (마크다운 테이블 만들기 귀찮음)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;JPQL은 위 테이블에서 좌측 멤버 부분을 객체로 SELECT 하기 때문에,&lt;/p&gt;
&lt;p&gt;JPQL이 내부적으로 중복 제거를 하는게 아니라면(그렇다), 첫번째 row의 member1 인스턴스와 두번째 row의 member1 인스턴스가 (똑같은 놈임에도 불구하고) 중복되어 반환된다.&lt;/p&gt;
&lt;p&gt;&lt;i&gt;어찌보면 중복되는게 당연한거다.&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;무튼, 따라서 애플리케이션 레이어의 우리 개발자 입장에선 distinct를 매번 걸어줘야 하는 수 밖에 없다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;인정하자&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;다행히도 JPQL의 distinct는 (저 테이블, 즉 DB단의 distinct로 해결되지 않는) 애플리케이션단의 distinct 효과를 내준다고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[ 결론 ]&lt;/b&gt; 이 부분도 평소에 개발할 땐, 어떤 기준을 가지고 있으면 될까 ?&lt;/p&gt;
&lt;p&gt;JPQL을 쓰면서 패치 조인을 하는데, (일대일도 다대일도 아닌) 일대다를 패치 조인한다면,&lt;/p&gt;
&lt;p&gt;꼭 distinct 를 써야 한다 !&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(어짜피 안쓰면 비즈니스 로직상 에러날 것.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[ 오해 주의 ]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;distinct 는 SELECT 대상(Member)에 대해서 중복제거를 하는 것이다.&lt;/p&gt;
&lt;p&gt;패치 조인하는 대상(Order)에 대해 하는 게 아니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lMkby/btqEoT95NAa/Tva0sDpHPCIq9nVFfvK4f0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lMkby/btqEoT95NAa/Tva0sDpHPCIq9nVFfvK4f0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lMkby/btqEoT95NAa/Tva0sDpHPCIq9nVFfvK4f0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlMkby%2FbtqEoT95NAa%2FTva0sDpHPCIq9nVFfvK4f0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;-&amp;gt; member1 한개지만, size 2로 나온다. distinct 붙이면 해결.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;3. JPQL) OneToMany 패치 조인을 한번에 2개이상 할 수 없다&lt;/h1&gt;
&lt;p&gt;아까 2번에서 꼭 distinct를 써줘야 했다던, JPQL에서 일대다 패치 조인 - 한번에 2개 걸어서 가져올 수 없다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(책에선 JPQ 구현체에 따라 되는 놈도 있다는데, Hibernate는 안되는 걸로.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이유는&lt;/p&gt;
&lt;p&gt;컬렉션 * 컬렉션이라는 &lt;b&gt;Cartesian 곱&lt;/b&gt;이 만들어지기 때문.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;org.hibernate.loader.MultipleBagFetchException&lt;/code&gt;을 만나게 될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DB, JPA, SQL</category>
      <category>Fetch Join</category>
      <category>hibernate</category>
      <category>JPA</category>
      <category>JPQL</category>
      <category>N+1</category>
      <category>OneToMany</category>
      <author>os94</author>
      <guid isPermaLink="true">https://os94.tistory.com/201</guid>
      <comments>https://os94.tistory.com/201#entry201comment</comments>
      <pubDate>Mon, 25 May 2020 23:12:08 +0900</pubDate>
    </item>
    <item>
      <title>인수테스트 디렉토리 분리 (with gradle)</title>
      <link>https://os94.tistory.com/200</link>
      <description>&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://github.com/eattogether/hey-together/wiki/%EC%9D%B8%EC%88%98%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC-%EB%B6%84%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;우테코 레벨4 프로젝트 과정에서 정리한 Wiki 문서&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;배경&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Pobi 강의 - 개발 방법 중에 단위 테스트는 기존의 main/test 구조로 test 디렉토리에서 돌리고, 오래 걸리는 인수테스트는 별도의 디렉토리로 분리해 CI 환경에서만 돌리는 방법도 존재한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;참고자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.thinkcode.se/blog/2015/04/29/separating-acceptance-tests&quot;&gt;Separating acceptance tests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.thinkcode.se/blog/2017/08/14/gradle-settestclassesdirfile-method-has-been-deprecated&quot;&gt;Seperating acceptance tests 과정의 Trouble shooting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/8791977/is-there-a-way-to-specify-dependencies-for-a-newly-created-sourceset-in-gradle&quot;&gt;Is there a way to specify dependencies for a newly created sourceset in gradle?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kwonnam.pe.kr/wiki/gradle/java&quot;&gt;Gradle Java Plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;과정&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;기존의 &lt;code&gt;src - main/test&lt;/code&gt; 구조에서 &lt;code&gt;src - main/test/acceptanceTest&lt;/code&gt; 구조로 바꿔본다. (acceptanceTest는 우리가 임의로 지정한 디렉토리 이름)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;build.gradle&lt;/code&gt; 설정 추가&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/g2fDiT4.png&quot; alt=&quot;Untitled/Untitled.png&quot; width=&quot;639&quot; height=&quot;559&quot; /&gt;&lt;img src=&quot;https://i.imgur.com/51hewLD.png&quot; alt=&quot;Untitled/Untitled%201.png&quot; width=&quot;481&quot; height=&quot;273&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;build.gradle&lt;/code&gt;의 task 설정하는 부분 코드 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/qMKOeUu.png&quot; alt=&quot;Untitled/Untitled%202.png&quot; width=&quot;651&quot; height=&quot;202&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;생성한 디렉토리에 필요한 의존성이 있으면 다음과 같이 의존성을 추가한다
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;testImplementation&lt;/code&gt;의 test가 test directory를 가리킨다는 것은 개인적으로 컬쳐쇼크였다. 우리의 경우, &lt;code&gt;acceptanceTest&lt;/code&gt;라는 디렉토리를 만들었으므로 &lt;code&gt;configurations { .. }&lt;/code&gt; 에 선언하고 &lt;code&gt;acceptanceTestImplementation&lt;/code&gt;을 이용해 의존성 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/KTNY8ym.png&quot; alt=&quot;Untitled/Untitled%203.png&quot; width=&quot;644&quot; height=&quot;437&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;이슈&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;인텔리제이 문제인지 생성한 acceptanceTest 디렉토리가 테스트 디렉토리로 인식되지 않았다.
&lt;ul&gt;
&lt;li&gt;해당 폴더 우클릭후 mark as - test로 명시적 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;프로젝트 진행과정에서 본 '테스트 디렉토리 생성 및 분리' 작업과 'Spring Rest Docs' 작업을 동시에 진행해서, 각 필요한 의존성을 추가하는데 헷깔렸다..&lt;/li&gt;
&lt;li&gt;main에 추가된 의존성들을 자동으로 분리한 디렉토리에도 추가하는 방법이 있을 것 같은데 아직은 모르겠다..!&lt;/li&gt;
&lt;li&gt;이젠 gradle 문법도 알아야하는구나 =.=&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Server</category>
      <category>Acceptance Test</category>
      <category>gradle</category>
      <category>분리</category>
      <category>인수테스트</category>
      <author>os94</author>
      <guid isPermaLink="true">https://os94.tistory.com/200</guid>
      <comments>https://os94.tistory.com/200#entry200comment</comments>
      <pubDate>Sun, 12 Jan 2020 17:55:34 +0900</pubDate>
    </item>
    <item>
      <title>Spring Rest Docs로 API 문서화하기</title>
      <link>https://os94.tistory.com/199</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://github.com/eattogether/hey-together/wiki/Spring-Rest-Docs%EB%A1%9C-API-%EB%AC%B8%EC%84%9C%ED%99%94%ED%95%98%EA%B8%B0&quot;&gt;우테코 레벨4 프로젝트 과정에서 정리한 Wiki 문서&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;본 문서는 Spring Rest Docs + @SpringBootTest + WebTestClient 버전의 예제로 설명&lt;/h4&gt;
&lt;h2&gt;참고자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-restdocs/docs/2.0.4.RELEASE/reference/html5/&quot;&gt;https://docs.spring.io/spring-restdocs/docs/2.0.4.RELEASE/reference/html5/&lt;/a&gt; ★&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring/docs/current/spring-framework-reference/pdf/testing-webtestclient.pdf&quot;&gt;https://docs.spring.io/spring/docs/current/spring-framework-reference/pdf/testing-webtestclient.pdf&lt;/a&gt; ★&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://woowabros.github.io/experience/2018/12/28/spring-rest-docs.html&quot;&gt;http://woowabros.github.io/experience/2018/12/28/spring-rest-docs.html&lt;/a&gt; ★&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cheese10yun.github.io/spring-rest-docs/&quot;&gt;https://cheese10yun.github.io/spring-rest-docs/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jojoldu.tistory.com/294&quot;&gt;https://jojoldu.tistory.com/294&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Rest Docs 도입 배경&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://boorownie.github.io/2019-11-27/atdd_spring_boot&quot;&gt;https://boorownie.github.io/2019-11-27/atdd_spring_boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://boorownie.github.io/2019-11-27/agail_and_atdd&quot;&gt;https://boorownie.github.io/2019-11-27/agail_and_atdd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;세부 기능 구현에 앞서, Rest Docs를 이용해 전체적인 API를 먼저 만듬으로써 큰 흐름을 이해하고, 프론트와 병행 개발도 가능하게 해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1&gt;과정&lt;/h1&gt;
&lt;h4&gt;&lt;code&gt;build.gradle&lt;/code&gt;에 Configuration 추가&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Spring Boot라면 버전 생략 가능&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testCompile 'org.springframework.restdocs:spring-restdocs-mockmvc:{project-version}'&lt;/code&gt; 부분 사용하는 테스트 객체에 맞게 변경 (MockMvc, WebTestClient, REST Assured)&lt;/li&gt;
&lt;li&gt;아래는 필요한 의존성. (test 디렉토리가 아닌, 별도로 생성한 acceptanceTest라는 디렉토리에서 구현중이라서 acceptanceTestImplementation으로 되어있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Bo7wBnS.png&quot; alt=&quot;Spring%20Rest%20Docs%20API/Untitled.png&quot; /&gt;&lt;img src=&quot;https://i.imgur.com/CQPUwiJ.png&quot; alt=&quot;Spring%20Rest%20Docs%20API/Untitled%201.png&quot; width=&quot;649&quot; height=&quot;453&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;&lt;code&gt;build.gradle&lt;/code&gt;에 bootJar 추가&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/KHJdER7.png&quot; alt=&quot;Spring%20Rest%20Docs%20API/Untitled%202.png&quot; width=&quot;645&quot; height=&quot;391&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;JUnit 5 기준&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/GmlWfTA.png&quot; alt=&quot;Spring%20Rest%20Docs%20API/Untitled%203.png&quot; width=&quot;646&quot; height=&quot;457&quot; /&gt;&lt;img src=&quot;https://i.imgur.com/NnHbHIQ.png&quot; alt=&quot;Spring%20Rest%20Docs%20API/Untitled%204.png&quot; width=&quot;642&quot; height=&quot;404&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;API에 맞게 테스트 작성 (ArticeController 예제)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;docs처럼 &lt;code&gt;bindToApplicationContext()&lt;/code&gt;가 아닌 &lt;code&gt;bindToServer()&lt;/code&gt;를 이용해 webtestclient를 초기화해야 spring mvc의 bean 인식 가능&lt;/li&gt;
&lt;li&gt;&lt;code&gt;responseFields&lt;/code&gt;안에 필드 명시하는 방법들
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fieldWithPath&lt;/code&gt; : 기본적으로 필드 명시&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subsectionWithPath&lt;/code&gt; : 깊이 들어가지않고 high-level로 표현가능. 보통 dto같은 직접만든 객체 표현할때 유용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fieldWithPath(&quot;exampleField&quot;).description(&quot;Text&quot;).optional()&lt;/code&gt; : 값이 null이거나 없을수도있으면 optional() 등..&lt;/li&gt;
&lt;li&gt;다양한 방법이 있다. &lt;a href=&quot;https://docs.spring.io/spring-restdocs/docs/2.0.4.RELEASE/reference/html5/#documenting-your-api-request-response-payloads-subsections&quot;&gt;docs 해당 부분&lt;/a&gt; 참고&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ItF68HR.png&quot; alt=&quot;Spring%20Rest%20Docs%20API/Untitled%205.png&quot; width=&quot;641&quot; height=&quot;453&quot; /&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
@AutoConfigureRestDocs(uriScheme = &quot;https&quot;, uriHost = &quot;docs.api.com&quot;)
public class ArticleControllerTest {

    private WebTestClient webTestClient;

//    @BeforeEach
//    public void setUp(ApplicationContext applicationContext, RestDocumentationContextProvider restDocumentation) {
//        this.webTestClient = WebTestClient.bindToApplicationContext(applicationContext)
//                .configureClient()
//                .filter(documentationConfiguration(restDocumentation))
//                .build();
//    }

    @BeforeEach
    public void setUp(RestDocumentationContextProvider restDocumentation) {
        webTestClient = WebTestClient.bindToServer()
                .baseUrl(&quot;http://localhost:8080&quot;)
                .filter(documentationConfiguration(restDocumentation))
                .build();
    }

    @Test
    void example() {
        this.webTestClient.get().uri(&quot;/&quot;).accept(MediaType.APPLICATION_JSON)
                .exchange().expectStatus().isOk().expectBody()
                .consumeWith(document(&quot;index&quot;,
                        responseFields(
                                fieldWithPath(&quot;title&quot;).description(&quot;this is title.&quot;),
                                fieldWithPath(&quot;contents&quot;).description(&quot;this is contents.&quot;))));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@RestController
public class ArticleController {
    @GetMapping(&quot;/&quot;)
    public ArticleDto test() {
        ArticleDto articleDto = new ArticleDto(&quot;ex-title&quot;, &quot;ex-contents&quot;);
        return articleDto;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;public class ArticleDto {
    private String title;
    private String contents;

    public ArticleDto() {
    }

    public ArticleDto(String title, String contents) {
        this.title = title;
        this.contents = contents;
    }

    // 생략
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;&lt;code&gt;src/docs/asciidoc/api-docs.adoc&lt;/code&gt; 위치로 문서 작성&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/lBYFQpN.png&quot; alt=&quot;Spring%20Rest%20Docs%20API/Untitled%206.png&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;테스트를 돌리면 &lt;code&gt;build/generated-snippets&lt;/code&gt;에서 생성된 스니펫 확인 가능&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/C8aBBNG.png&quot; alt=&quot;Spring%20Rest%20Docs%20API/Untitled%207.png&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;&lt;code&gt;build.gradle&lt;/code&gt;의 bootJar을 실행시키면 &lt;code&gt;build/asciidoc/html5&lt;/code&gt; (build.gradle에서 우리가 설정한 경로) 에서 생성된 HTML파일 확인가능&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/NsqNn9N.png&quot; alt=&quot;Spring%20Rest%20Docs%20API/Untitled%208.png&quot; width=&quot;640&quot; height=&quot;177&quot; /&gt;&lt;img src=&quot;https://i.imgur.com/W7t39H8.png&quot; alt=&quot;Spring%20Rest%20Docs%20API/Untitled%209.png&quot; width=&quot;651&quot; height=&quot;287&quot; /&gt;&lt;/p&gt;</description>
      <category>Server</category>
      <category>Spring REST Docs</category>
      <author>os94</author>
      <guid isPermaLink="true">https://os94.tistory.com/199</guid>
      <comments>https://os94.tistory.com/199#entry199comment</comments>
      <pubDate>Sun, 12 Jan 2020 17:41:59 +0900</pubDate>
    </item>
    <item>
      <title>우아한 테크코스) Lv4 [팀 프로젝트] 정리, 후기</title>
      <link>https://os94.tistory.com/198</link>
      <description>&lt;h1&gt;Lv4 : 팀 프로젝트&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/eattogether/hey-together&quot;&gt;-&amp;gt; https://github.com/eattogether/hey-together&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/eattogether/hey-together/wiki/&quot;&gt;-&amp;gt; 팀 위키&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;요구사항&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;지속적 통합 (모든 테스트 통과할때만 배포, 테스트 커버리지 80%, 품질 기준 통과(정적 분석 도구 이용))을 적용해 일정 기준이 넘는 환경으로 배포&lt;/li&gt;
&lt;li&gt;Spring Rest Docs를 이용해 API 문서화&lt;/li&gt;
&lt;li&gt;프론트는 프레임워크를 사용하기보다 바닐라 JS 기반으로 개발하는게 어떨까?&lt;/li&gt;
&lt;li&gt;인증은 OAuth2를 기반으로 하면 어떨까? 단 Spring Security 사용X&lt;/li&gt;
&lt;li&gt;인증을 제외하고 최소 하나 이상의 외부 API와 연동&lt;/li&gt;
&lt;li&gt;성능을 높이기 위해 자주 사용하지(변하지) 않는 데이터에 대해 (백엔드 부분에) Cache를 적용&lt;/li&gt;
&lt;li&gt;REST 원칙을 지키며 개발 (참고링크)&lt;/li&gt;
&lt;li&gt;Docker 경험&lt;/li&gt;
&lt;li&gt;기능 구현보다 전체적인 프로세스를 경험해보자. 팀끼리 규칙을 정하고. CI 경험 등&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;팀 문화&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이전 레벨2 프로젝트 때와 다른건, 모닝 데일리 미팅 뿐만 아니라 이브닝 데일리도 진행해봤다. 안그래도 기능구현만으로 부족한 시간이라 걱정이 되긴했지만, 지나고보니 꽤 괜찮았다. 모닝 데일리는 가볍게 오늘 뭐할지 계획 정도, 이브닝은 각종 코드와 설계 관련 논의 등 실질적인 기능을 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;개발 문화&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이슈번호를 커밋 메시지 앞에 작성해, 이슈 추적을 좀더 쉽게 할 수 있었다.&lt;/li&gt;
&lt;li&gt;레벨2때 못써봐서 아쉬웠던 ZenHub을 이번엔 써봤고, 이슈들을 에픽 단위로 묶으니 관리하기 더 좋았다 :)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/eattogether/hey-together/wiki/%EC%84%B8%EB%B6%80-%EA%B0%9C%EB%B0%9C-%EC%BB%A8%EB%B2%A4%EC%85%98&quot;&gt;세부 개발 컨벤션&lt;/a&gt; : 다들 레벨2때 나와 비슷한 점을 느꼈는지, 최대한 컨벤션을 세부적으로 정하려 다들 노력했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;팀 목표&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r7D6V/btqA0NFISYV/OCzHFeEYDXJem51BnE8ni1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r7D6V/btqA0NFISYV/OCzHFeEYDXJem51BnE8ni1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r7D6V/btqA0NFISYV/OCzHFeEYDXJem51BnE8ni1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr7D6V%2FbtqA0NFISYV%2FOCzHFeEYDXJem51BnE8ni1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;250&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;다들 계속 해왔던 스프링 서버 기능 구현 외에, 부가적으로 경험해보고 싶은 목표들로 위와 같이 리스팅했다.&lt;br /&gt;하지만 프로젝트 진척이 더뎌지며 이들 중 실제로 사용해본 것은 절반정도, 그것도 제대로 써봤다고 하려면 목표의 20%도 안되는 것 같아 아쉬움이 있다.&lt;/p&gt;
&lt;h3&gt;프로젝트에서 개인적인 목표&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;인프라 기술 경험해보기 (실패)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;프로젝트를 맨 처음 시작할 때, 내 욕심은 그저 도커와 젠킨스를 경험해보는 것이었다. 코스동안 이제까진 인프라보다 거의 코드쪽을 만져왔기 때문에.&lt;/li&gt;
&lt;li&gt;결과적으로 이번에도 인프라 기술을 경험해보진 못했다. 표면적인 이유는 당시 개인 역할 분배할 때, 내가 페어와 함께 도메인쪽을 한창 하고있어서 다른 팀원이 인프라 역할을 가져갔다.&lt;/li&gt;
&lt;li&gt;좀더 원론적으론, 프로젝트 전반적으로 계획 수립과 진행을 제대로 하지 못했고, 때문에 필수 기능을 구현하는 것만으로 시간이 지연되어 인프라 등의 부가 기술을 적용할 시간이 부족했다. 이와 관련해선 뒤에서 다시 기술.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;기술 문서 공유 활발히 하기 (성공)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이 부분은 레벨2 프로젝트때에 비해 좀더 만족스럽다. 초반부터 적극적으로 기술 문서를 작성하자고 어필했고, 먼저 작성해서 위키에 공유하니 다들 감사하게도 따라주시더라.&lt;/li&gt;
&lt;li&gt;결국 기술 문서를 공유하는 이유는, 남이 긴 시간을 들여 학습한 내용을 다른 사람은 좀더 빠른 시간에 흡수하기 위함이다. 프로젝트 기간이 좀더 충분했다면, 기술 문서 공유 뿐 아니라 '매일 코드리뷰 or Merge'하며 서로 구두로 이야기하는 시간이 더 많았으면 하는 아쉬움이 있긴하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;그래서 경험한건?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;구현하며 인상깊었던 부분들 + 삽질&lt;/h3&gt;
&lt;h4&gt;인수테스트 디렉토리 분리 &lt;a href=&quot;https://github.com/eattogether/hey-together/wiki/%EC%9D%B8%EC%88%98%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC-%EB%B6%84%EB%A6%AC&quot;&gt;위키&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;foo&lt;/code&gt; (커스텀 테스트 디렉토리)를 생성했다면 &lt;code&gt;gradle.build&lt;/code&gt;에서 &lt;code&gt;fooImplementation&lt;/code&gt;으로 dependency 추가 가능하다. &amp;lt;- 처음알았을때 컬쳐쇼크였다, testImplementation의 test가 그 test였다니..&lt;/li&gt;
&lt;li&gt;의존성 주입을 깔끔하게 못했다. gradle 문법도 알아야하는듯..&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Spring Rest Docs &lt;a href=&quot;https://github.com/eattogether/hey-together/wiki/Spring-Rest-Docs%EB%A1%9C-API-%EB%AC%B8%EC%84%9C%ED%99%94%ED%95%98%EA%B8%B0&quot;&gt;위키&lt;/a&gt; &lt;a href=&quot;https://os94.tistory.com/192&quot;&gt;블로그&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;인수테스트 디렉토리 분리와 함께, 첫 주차에 다른 조들 사이에서 가장 먼저 적용하고 이를 기술문서로 공유해, 다른 팀들에게 도움을 줄 수 있어 좋았다.&lt;/li&gt;
&lt;li&gt;Spring MVC + WebTestClient 조합에서 &lt;code&gt;bindToApplicationContext()&lt;/code&gt; or &lt;code&gt;bindToServer()&lt;/code&gt;의 이슈가 발생했다.
&lt;ul&gt;
&lt;li&gt;여전히 의문. &lt;code&gt;bindToApplicationContext()&lt;/code&gt;가 안되야하는데 왜 됬던 걸까 ?_?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;ATDD와 섣부른 관계 매핑 &amp;amp; 복잡한 도메인 &lt;a href=&quot;https://os94.tistory.com/192&quot;&gt;블로그&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;문제점 1. 가장 크고 복잡한 도메인 Article을 가장 먼저 구현하려 했던 점&lt;/li&gt;
&lt;li&gt;문제점 2. Article과 관련한 엔티티들을 너무 빨리 관계매핑하려했던 점&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;@OneToMany List&amp;lt;?&amp;gt; 상황에서 LazyInitializationException 예외와 해결방안 &lt;a href=&quot;https://os94.tistory.com/193&quot;&gt;블로그&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;OSIV 설정을 꺼놔서 터진것이었다. 다시 스프링부트 기본값인 true로 바꿔 해결할수있었고, 추가로 OSIV를 꺼도 문제가 발생하지않도록 DTO를 사용해 노출하기로 결정했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;JPA 양방향 순환참조 문제 &lt;a href=&quot;https://os94.tistory.com/192&quot;&gt;블로그&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;당시엔 Json관련 어노테이션을 붙여 순환참조를 끊어 문제를 해결했다가, 이후 DTO를 사용하는 방식으로 변경해 애초의 원인 해결.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;OneToMany와 양방향 남용 &lt;a href=&quot;https://os94.tistory.com/195&quot;&gt;블로그&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;도메인 관계매핑의 복잡성과 함께 프로젝트를 지연시켰던 주된 이슈들 중 다수는 일대다 관계와 양방향에서 비롯된 경우가 많았다. 프로젝트 중반을 넘긴 시점에 이들을 모두 해제하고 구조를 변경했더니, 이후로 이슈가 줄어들었고 코드도 깔끔해질 수 있었다.&lt;/li&gt;
&lt;li&gt;쓰지말라는건 왜 쓰지말아야하는지, best practice가 왜 존재하는지 몸으로 느낄 수 있었다.;;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;정리 : 그래서 우리는 왜 실패했는가?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;팀원들의 개인사정으로 인한 초반 부재
&lt;ul&gt;
&lt;li&gt;초반에 각자 개인사로 너무 많이 (돌아가며) 빠졌다. (사실상 5주중에서 2주가 날아가버렸다) 아쉽지만 어쩔수없었던 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;특정 기능에 대해 한 사람이 담당했던 것
&lt;ul&gt;
&lt;li&gt;프론트를 한사람에게 맡겼더니, 그 사람에게 일이 생겨 작업이 지연될 때 다른사람이 대체하기 어려웠다.&lt;/li&gt;
&lt;li&gt;다음엔 한 부분에 최소한 2명이상 분배해, 백업 멤버를 두자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;핵심 도메인 모델 구축의 지연
&lt;ul&gt;
&lt;li&gt;ATDD와 익숙하지 않은 점 + JPA 관계매핑의 복잡도로, 가장 핵심이 되는 도메인 모델들이 삽질과 변경을 계속하다 뒤늦게 정착됬다.&lt;/li&gt;
&lt;li&gt;핵심이 되는 부분인 만큼, 다들 각자 역할이 있어도 이를 우선순위로 올리고 초반에 다함께 해결했어야 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;불분명한 요구사항 확립 + 마일스톤을 적절히 세우지 못했다
&lt;ul&gt;
&lt;li&gt;처음에 개발주제와 세부 요구사항에 대한 이야기를 충분히 하지 못했다. 이를 문서화하지도 못했다. -&amp;gt; 레벨2 프로젝트때와 비교해, 애초에 주제가 불분명해 충분한 이야기를 나눠야 했었음&lt;/li&gt;
&lt;li&gt;초반에 팀원들이 모이지 못한것과 관련&lt;/li&gt;
&lt;li&gt;그리고 애초에 짧은 기간에 과한 목표를 잡았다. 기본 서비스 기능 구현 외에, 팀원들의 목표였던 비기능 기술들까지 다루려면, 처음써보는 뷰는 포기해야했음. 차라리 슬랙봇 프로젝트처럼.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;의욕 불충분
&lt;ul&gt;
&lt;li&gt;취업과 프로젝트 지속성X으로 팀원들의 사기 저하&lt;/li&gt;
&lt;li&gt;개인에게 있어서 프로젝트의 우선순위가 밀렸다 -&amp;gt; 규율 위의 자율이 필요했음, 리더의 부재로 각자의 책임이 불분명&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;기타 : 사소한 것들&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;인텔리제이 문제로 커스텀 테스트 디렉토리를 인식하지못해, mark as로 명시적으로 테스트 폴더로 바꿔줘야 했다.&lt;/li&gt;
&lt;li&gt;공식 Docs가 시키는대로 하자. 다른 경로가 아닌 &lt;code&gt;src/docs/asciidoc/example.adoc&lt;/code&gt;의 경로에 adoc 파일을 생성해야한다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://os94.tistory.com/194&quot;&gt;블로그 : TIL - JPA 'GenerationType.AUTO'와 Mockito 등&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;회고의 중요성 : 프로젝트가 끝날 즈음, 최종 회고를 했다. 서로 마음을 털어놓고 감정을 공유하며, 서로를 좀더 이해할 수 있었다. 속도 시원했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;팀 차원에서 새롭게 도입해본 기술들&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;인수테스트 디렉토리 분리&lt;/li&gt;
&lt;li&gt;Spring Rest Docs&lt;/li&gt;
&lt;li&gt;JWT 인증&lt;/li&gt;
&lt;li&gt;Vue.js&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;우리도 도입해볼만했던 다른 팀의 기술 및 정책&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;AOP 로깅&lt;/li&gt;
&lt;li&gt;프로젝트 모듈화&lt;/li&gt;
&lt;li&gt;SonarQube&lt;/li&gt;
&lt;li&gt;로젠트리, &lt;a style=&quot;letter-spacing: 0px;&quot; href=&quot;https://docs.sentry.io/&quot;&gt;https://docs.sentry.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;DbUnit&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>우아한 테크코스</category>
      <category>우아한 테크코스</category>
      <author>os94</author>
      <guid isPermaLink="true">https://os94.tistory.com/198</guid>
      <comments>https://os94.tistory.com/198#entry198comment</comments>
      <pubDate>Thu, 9 Jan 2020 21:22:00 +0900</pubDate>
    </item>
    <item>
      <title>2020) 2019년 회고 + 2020년 계획</title>
      <link>https://os94.tistory.com/197</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;2019년 회고&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;~3월&lt;br /&gt;학교 막학기를 앞두고 방향을 무척 고민했던 시기. 방학이라 시간은 많고 &quot;무엇을 어떻게 공부해야할지&quot;는 몰라서, 무식한 방법으로 책이나 인강을 들어대며 시간을 보냈다. (물론 나중에보니 다 도움됬지만) 원인은 개발로 전향한지 얼마 안된 시기였고, 제대로 경험해본건 그나마 안드로이드, 프론트는 취향아닌것같고, 백엔드는 경험해보지못한 분야에 대한 막연한 두려움. 어느길로 나아가야할지 방향을 못 잡고 있었다.&lt;br /&gt;개발 프로그램/동아리나 인턴도 몇군데 넣었다. 모두 면접에서 탈락. 최탈의 아픔에 대해 알게됬다. 쉽게보고 경험삼아 지원했던 회사들에 광탈하니 자존감이 무척 하락했다.&lt;/li&gt;
&lt;li&gt;4~11월&lt;br /&gt;지원한 몇군데에 탈락하고 조용히 막학기 수업이나 들으며 공부를 병행하기로 했다. 그러던중 우테코 공고가 떴고 열심히 선발과정에 임했다. 더이상 갈곳이 없어서였던지, 그렇게 절박한 마음과 노력은 편입때이후 오랜만이었다.&lt;br /&gt;감격스럽게도 선발되어서 8개월간 과정을 진행했다. 삶의 전환점인 우테코를 기점으로 180도 달라졌다. 객관적으로 얻은 개발관련 지식과 정보들 외에도, 이상적인 개발자의 자세나 가치관, 또 &quot;무엇을 어떻게 학습해야할지&quot;에 대한 시야를 얻어, 우테코에 대한 큰 감사와 만족을 했다.&lt;br /&gt;8개월간 빡센 일정에 쉽지 않았지만, 주변의 모두가 열심히 해서 함께 동반 성장할 수 있었다. 중간에 지칠즈음, 학교에서 운좋게 미국 컨퍼런스에 보내줘서 refresh할 수 있었다. 그렇게 좋은 흐름을 타서 지금까지 이르렀다.&lt;/li&gt;
&lt;li&gt;11~12월&lt;br /&gt;레벨3와 회사 선발일정이 겹쳤다. 육체적으론 괜찮았는데 정신적으로 가장 지쳤던 시기이고, 어찌저찌 잘 통과해서 다행히 첫 회사를 들어가게 되었다.&lt;/li&gt;
&lt;li&gt;회고&lt;br /&gt;개인적으로 내 인생에 있어서 그냥 '2019년=우테코'였다. 그만큼 이부분을 삶의 메인 중심으로 가져가며 생활했다. 이 과정에서 주변을 잘 돌아보지 못했는데, 정신없는 나를 잘 챙겨준 주변사람들에게 감사하다.&lt;br /&gt;20년엔 주구장창 개발만 하고 싶진 않다. 물론 첫 회사 생활과 새로운 환경, 적응에 바쁘고 정신없을 것이다. 하지만 무조건 시간을 투자하고 달려든다고 일의 능률이 오르진 않음을 작년에 경험했고, 이에 따라 올해엔 회사와 내 개인적인 삶의 균형을 맞춰서 나만의 라이프사이클을 만들어 지켜가고 싶다.&lt;b&gt; &lt;/b&gt;레벨3 글쓰기했던 것처럼, 무엇보다 내가 즐겼으면 좋겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2020년 계획&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthContent&quot; data-origin-width=&quot;1783&quot; data-origin-height=&quot;961&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKXcSS/btqA8jXBn7J/sApHufcn8kblZX9AKKBvt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKXcSS/btqA8jXBn7J/sApHufcn8kblZX9AKKBvt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKXcSS/btqA8jXBn7J/sApHufcn8kblZX9AKKBvt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKXcSS%2FbtqA8jXBn7J%2FsApHufcn8kblZX9AKKBvt0%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;1783&quot; height=&quot;961&quot; data-origin-width=&quot;1783&quot; data-origin-height=&quot;961&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 CU코치와 면담했던 기억을 떠올려, 만다라트를 처음으로 만들어봤다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Hobby
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서울살이 처음으로 학교근처를 벗어나 잠실로 이사한다. 석촌호수와 올림픽공원, 한강공원의 세 입지를 가진 최고의 조건이다. 20년엔 자연과 함께 운동도 많이 하기로..!&lt;/li&gt;
&lt;li&gt;18년에 비해 19년은 사진을 너무 못찍었다. 20년엔 가끔 기분전환이 필요할때 카메라 챙겨서 출사나갈것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Backend
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세부 기술이나 학습 목표를 나열하긴 애매해서, 학습할때 중점으로 둘 큼지막한 주제들을 적어봤다.&lt;/li&gt;
&lt;li&gt;작년에 비해 새로운건, (잠깐이지만 회사 1주일 다녀보니) &quot;성능&quot;에 대한 고민이 많은 것 같아 중점적으로 가져가야 할 것 같았다. 클린 아키텍쳐도 이젠 슬슬 뭔지 고민해보고 싶다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Category
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학습 주제들.&lt;/li&gt;
&lt;li&gt;우리 부서에선 작년에 공부한 기술들과 비슷한 기술셋을 사용해서 좋았다. 다만 이런 메인이 되는 부분들외에 연한색인 리눅스나 CS에 대한 공부도 소홀히 하지 않았으면 좋겠다. 무엇보다 테스트에 대한 고민은 꼭 진지하게 해볼거다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Study
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내가 지식을 얻는 채널들&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Activity
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러가지 활동을 하고싶지만, 올해는 첫 1년인만큼 공부하고싶은게 많아서 그 부분들에 먼저 집중해야겠다. (1년뒤에 나도 지금의 내 사수들처럼 될수있을까?! 이렇게 보니 코치들과 리뷰어분들과의 거리는 참 멀게 느껴진다)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Business
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 회사생활이라 얼떨떨하다. 빠르게 적응해서 퍼포먼스를 내야겠다.&lt;/li&gt;
&lt;li&gt;다만 회사일에 끌려다니진 않고, Backend에서 언급한 주제들에 대해 고민할수있는 일년에 되었으면 하는 바람.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Health
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공부는 더 하고싶은데 체력의 한계를 느낀적이 많았다. 이제 운동은 선택이 아닌 필수인듯하다. 규칙적인 수면과 스트레스 관리도 삶의 질에 큰 영향을 미치는 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2019년(우테코) 학습 부채&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;레벨2&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;JPA 관련&lt;/s&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;cascade, orphanRemoval, onDeleteCascade&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;paging, sorting&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;JPQL&lt;/s&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;s&gt;Spring Rest Docs vs Swagger&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;다양한 테스트 방식들 : notion/개념정리 참고&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;Jenkins, Docker&lt;/s&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;레벨3&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://os94.tistory.com/176&quot;&gt;링크&lt;/a&gt; 참고&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;레벨4&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;sonarqube&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;OAuth2, JWT&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;Caching&lt;/del&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2020년 공부 계획&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세부 학습 계획은 notion에 작성.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1순위
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회사 업무 관련&lt;/li&gt;
&lt;li&gt;Spring, Spring Boot&lt;/li&gt;
&lt;li&gt;JPA&lt;/li&gt;
&lt;li&gt;개발서적s&lt;/li&gt;
&lt;li&gt;작년에 남긴 학습거리와 아카이빙한 북마크 자료들&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2순위
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CS : DB / Network / OS&lt;/li&gt;
&lt;li&gt;Jenkins, Docker&lt;/li&gt;
&lt;li&gt;Etc
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Test 관련&lt;/li&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;sonarqube + logging관련 툴들&lt;/li&gt;
&lt;li&gt;OAuth2 + JWT&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;3순위
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Algorithm&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;분기별 목표&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1년만에 될진 모르겠지만, 전체적인 학습 방향을 다음과 같은 크게 3단계로 계획해봤다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Spring, JPA&lt;br /&gt;입사 초반 기간에 최대한 빠르게 끝낸다.&lt;/li&gt;
&lt;li&gt;업무 관련 세부 기술 및 DB&lt;br /&gt;업무에서 사용하는, 보다 세부적이고 깊이있는 기술들에 집중.&lt;/li&gt;
&lt;li&gt;아키텍쳐, 설계&lt;br /&gt;입사 1년째 정도의 시기를 목표로, 아키텍쳐 관련 학습을 시작할 수 있었으면 좋겠다. (아키텍쳐, 클린코드, ddd 등 ~..)&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;보안을 보내며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정신없는 2019년이 지나갔다. 인생의 큰 변화가 있었다.&lt;br /&gt;이젠 오히려 어색한 보안/해킹쪽은 뒤로 하고, 익숙한 개발의 삶을 신입 개발자로서 시작한다.&lt;br /&gt;노트북도 드뎌 그램에서 맥북으로 바꾼다. ㅎㅎ&lt;br /&gt;설치되있던 많은 툴들아 안녕  &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthContent&quot; data-origin-width=&quot;1717&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2cXEU/btqBil947JE/50S76EGk1rVpXHPhAuCc1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2cXEU/btqBil947JE/50S76EGk1rVpXHPhAuCc1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2cXEU/btqBil947JE/50S76EGk1rVpXHPhAuCc1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2cXEU%2FbtqBil947JE%2F50S76EGk1rVpXHPhAuCc1k%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;1717&quot; height=&quot;616&quot; data-origin-width=&quot;1717&quot; data-origin-height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2Ehzz/btqBilhY3uv/F9rdwDfOrwtVyVVaNM3KKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2Ehzz/btqBilhY3uv/F9rdwDfOrwtVyVVaNM3KKK/img.png&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;626&quot; style=&quot;width: 37.6171%; margin-right: 10px;&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2Ehzz/btqBilhY3uv/F9rdwDfOrwtVyVVaNM3KKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2Ehzz%2FbtqBilhY3uv%2FF9rdwDfOrwtVyVVaNM3KKK%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;785&quot; height=&quot;626&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ap16q/btqBj65fRZ1/2EBzeyVDzXqr82Ru6Bp3w0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ap16q/btqBj65fRZ1/2EBzeyVDzXqr82Ru6Bp3w0/img.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;294&quot; style=&quot;width: 61.2201%;&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ap16q/btqBj65fRZ1/2EBzeyVDzXqr82Ru6Bp3w0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAp16q%2FbtqBj65fRZ1%2F2EBzeyVDzXqr82Ru6Bp3w0%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;294&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Daily.log</category>
      <category>2019</category>
      <category>2020</category>
      <category>개발자</category>
      <category>계획</category>
      <category>만다라트</category>
      <category>회고</category>
      <author>os94</author>
      <guid isPermaLink="true">https://os94.tistory.com/197</guid>
      <comments>https://os94.tistory.com/197#entry197comment</comments>
      <pubDate>Wed, 8 Jan 2020 22:49:09 +0900</pubDate>
    </item>
    <item>
      <title>JPA 양방향 연관관계 Entity 값 저장</title>
      <link>https://os94.tistory.com/195</link>
      <description>&lt;p&gt;오늘 프로젝트에서 주요 도메인 모델들의 구조를 잡고 연관관계를 매핑하는 과정에서 삽질로 많은 시간을 소모해,&lt;br /&gt;&quot;당연하지만&quot; 다시한번 깨달은 사실을 정리하고 기록해보기로 했다.&lt;/p&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2&gt;배경&lt;/h2&gt;
&lt;p&gt;상황은 비슷한 예시로 들어보면, 가장 이해하기 쉬운 게시글(Article) - 댓글(Comment)의 관계를 설정해보자.&lt;br /&gt;&lt;b&gt;Article(일) - Comment(다)의 일대다/다대일 양방향 연관관계 상태&lt;/b&gt;이다.&lt;br /&gt;(물론 &lt;a href=&quot;https://www.notion.so/bd7d486cc314477a8b55d70e08f55093?v=9b5e24ca49ff455b908c00952172ddf6&amp;amp;p=a4f778e82f2444719a2a49d35d89ea56&quot;&gt;일대다는 가급적 자제하고 다대일만 매핑하는게 best practice&lt;/a&gt;이지만, 양방향이 꼭 필요한 상황이라고 가정해보자)&lt;/p&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2&gt;상황에 따른, 양방향 연관관계 Entity 값 저장 방식&lt;/h2&gt;
&lt;p&gt;양방향 연관관계 Entity를 save하면서, 당연한건데 큰 그림와 방향을 놓치고 있었다.&lt;br /&gt;정리해보면 다음과 같다.&lt;br /&gt;Article은 둘째치고, &lt;b&gt;Comment의 저장에 초점을 맞춰보자.&lt;/b&gt;&lt;br /&gt;이때, 물론 구현방법은 다양하지만, 가장 기본적이고 심플해보이는 방법으로 작성해봤다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;코드 : &lt;a href=&quot;https://github.com/os94/Server-Study/tree/add-bidirection-entity-value&quot;&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;1. Article과 Comment를 한번에(동시에) 생성하고 저장하는 경우&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;(CommentService가 아닌) ArticleService를 통해 Comment를 저장한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;주의사항&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;(당연한것들인데, JPA가 처음이라면 삽질을 유발할 수 있다)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Article 생성자에 List 필드에 대한건 제외해야한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;평소처럼 인텔리제이 단축키를 이용해 constructor를 생성하면 모든 필드에 대해 생성되니 주의.&lt;/li&gt;
&lt;li&gt;따라서 필드에선 바로 List comments = new ArrayList&amp;lt;&amp;gt;(); 처럼 초기화해줘 버리자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@OneToMany List에 CascadeType.ALL이 필요하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cascade 타입을 지정하지않으면 Article만 저장되고 Comment는 저장되지 않는것을 확인할 수 있을 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;당연히 Article.addComment(#) 메소드가 필요하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;우리의 목적은 Comment를 저장하는거였는데, 이런 add 메서드를 이용해 Article을 통해서 Comment를 저장한다.&lt;/li&gt;
&lt;li&gt;이때 추가할 comment의 Article필드가 채워져있어야한다.&lt;/li&gt;
&lt;li&gt;좀 못생겨도 article.addComment(new Comment(arg1, article)); 처럼 채우거나, add 메서드엔 인자만 넘기고 안에서 this.comment = new Comment(arg1, this); 처럼 정리할 수 도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4Omqf/btqAxNmaoDS/yWS2LjEyOXhGzmjMBeYKp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4Omqf/btqAxNmaoDS/yWS2LjEyOXhGzmjMBeYKp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4Omqf/btqAxNmaoDS/yWS2LjEyOXhGzmjMBeYKp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4Omqf%2FbtqAxNmaoDS%2FyWS2LjEyOXhGzmjMBeYKp0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;500&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// github 첫번째 커밋 참고&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;2. (Article은 이미 만들어져있고) Comment만 따로 저장하는 경우&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;CommentService를 통해 바로 저장한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;(예전 jwp-blog 미션에서 우리가 Comment 저장하던 일반적인 방식. 보통 @OneToMany를 통한 리스트를 들고있는건 자제하므로 보통 이렇게 많이 저장. RESTful하기도하고)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;주의사항&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;new Comment()로 새로 만들어 save할건데, 이때 생성자 인자로 Article을 채워야한다. Article은 이미 만들어진 상황이므로 보통 articleId로 DB에서 Article을 꺼내 넣어준다.&lt;/li&gt;
&lt;li&gt;즉 CommentService에는 ArticleService가 필요해짐. 동시에 순환참조를 막기위해 ArticleService는 CommentService를 몰라야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qMfdf/btqAzxo7dBZ/IB1XJuVpbfb5wbgaz8oF00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qMfdf/btqAzxo7dBZ/IB1XJuVpbfb5wbgaz8oF00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qMfdf/btqAzxo7dBZ/IB1XJuVpbfb5wbgaz8oF00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqMfdf%2FbtqAzxo7dBZ%2FIB1XJuVpbfb5wbgaz8oF00%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;500&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// github 두번째 커밋 참고&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;결론&lt;/h3&gt;
&lt;p&gt;결론은 Comment의 Article 필드가 반드시 채워져있어야한다.&lt;br /&gt;그래야 안빠지고 제대로 들어간다. 왜냐면 Article(일) - Comment(다)의 관계에서 보통 키는 다쪽인 Comment가 가질테고, 당연히 Comment의 Article 필드가 채워져야한다. 그렇지않고 비운채로 Comment를 save하는 경우, article_id 필드가 비어있는것을 확인할 수 있었다.&lt;/p&gt;
&lt;p&gt;첨언) 어쩌면 진짜 결론은, 결국 양방향을 쓰지 않는 것이다. 당시엔 프로젝트에서 위의 방식들로 값이 저장되도록 구현했지만, 나중에 결국 악의 근원인 OneToMany를 없애고 양방향을 풀어버렸더니 훨씬 코드로 깔끔해지고 머리도 안아파지더라.&lt;/p&gt;</description>
      <category>DB, JPA, SQL</category>
      <category>Entity</category>
      <category>JPA</category>
      <category>ManyToOne</category>
      <category>OneToMany</category>
      <category>관계매핑</category>
      <category>양방향</category>
      <author>os94</author>
      <guid isPermaLink="true">https://os94.tistory.com/195</guid>
      <comments>https://os94.tistory.com/195#entry195comment</comments>
      <pubDate>Thu, 19 Dec 2019 00:46:42 +0900</pubDate>
    </item>
    <item>
      <title>19.12.12) TIL - JPA 'GenerationType.AUTO'와 Mockito 등</title>
      <link>https://os94.tistory.com/194</link>
      <description>&lt;h1&gt;Spring Data JPA에서 Id의 @GeneratedValue 이슈&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;위의 코드로 익숙한 Entity에서 Id 부분, 우리는 &lt;code&gt;GenerationType.AUTO&lt;/code&gt;가 DB 벤더에 알맞은 전략을 선택해주기 때문에 DB 종속성이 약해져 &lt;code&gt;GenerationType.Identity&lt;/code&gt;(MySQL의 auto increment 역할)보다 적절하다고 알고있었다.&lt;/li&gt;
&lt;li&gt;그러다 리팩토링을 진행하며, &lt;code&gt;GenerationType.Identity&lt;/code&gt; 일땐 잘 작동하던게 &lt;code&gt;GenerationType.AUTO&lt;/code&gt;로 바꾸니 에러가 나기 시작했다.&lt;/li&gt;
&lt;li&gt;검색해보니 오늘도 jojoldu님에게 도움을 받을수있었다. &lt;a href=&quot;https://jojoldu.tistory.com/295&quot;&gt;https://jojoldu.tistory.com/295&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;링크에 정리 잘 되어있어 읽으면 되지만, 간략히 요약해보면 다음과 같다.&lt;/li&gt;
&lt;li&gt;(rough한 요약) Hibernate 예전 버전은 알던대로 &lt;code&gt;AUTO&lt;/code&gt;가 &lt;code&gt;Identity&lt;/code&gt;였다. 하지만 Hibernate 버전이 올라가며 특정 버전부터 &lt;code&gt;AUTO&lt;/code&gt;가 &lt;code&gt;Table&lt;/code&gt;로 바뀌었다. (MySQL의 경우)&lt;/li&gt;
&lt;li&gt;이에 대한 해결책은 2가지를 제시해주셨다. 단순히 &lt;code&gt;Identity&lt;/code&gt;로 바꾸거나, &lt;a href=&quot;http://application.properties&quot;&gt;application.properties&lt;/a&gt; 또는 yml 파일에 해당 Hibernate 옵션 설정을 끄는 방법.&lt;/li&gt;
&lt;li&gt;우린 첫 문단에 언급한대로 &lt;code&gt;Identity&lt;/code&gt;는 피하고싶어서 후자의 방법을 택해, 설정파일을 수정해 해당 이슈를 해결했다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.4-Release-Notes#generator-mappings&quot;&gt;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.4-Release-Notes#generator-mappings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Mockito를 사용하기 위한 @ExtendWith 추가&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;서비스 테스트에 &lt;code&gt;Mockito&lt;/code&gt;를 사용하는 과정에서, &lt;code&gt;Mockito&lt;/code&gt;는 기본 스프링 부트 test 의존성에 들어있어서 바로 사용할 수 있었다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testImplementation &amp;#39;org.springframework.boot:spring-boot-starter-test&amp;#39;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;테스트를 짜고 돌리니 이번에도 터지기 시작했다. 찾아보니 Spring의 빈들과 Mockito는 서로 모른다, 이들을 통합시켜주기위해 &lt;code&gt;@ExtendWith(SpringExtension.class)&lt;/code&gt; 를 테스트 클래스에 추가해줘야했다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@ExtendWith(SpringExtension.class)&lt;/code&gt; 도 되고, &lt;code&gt;@ExtendWith(MockitoExtension.class)&lt;/code&gt; 도 된다.&lt;/li&gt;
&lt;li&gt;이 과정에서 추가로 &lt;code&gt;@RunWith&lt;/code&gt; 와 &lt;code&gt;@ExtendWith&lt;/code&gt; 의 차이도 덤으로 알게되었다. JUnit 버전 차이였다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/55276555/when-to-use-runwith-and-when-extendwith&quot;&gt;https://stackoverflow.com/questions/55276555/when-to-use-runwith-and-when-extendwith&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Mockito를 활용한 서비스 레이어 테스트&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;오랜만에 서비스 테스트를 짜며 Mockito를 활용했다. 기억이 하나씩 되살아났다.&lt;/li&gt;
&lt;li&gt;우리 프로젝트에선 서비스 메소드의 로직이 여전히 너무나 간단해서, 서비스 테스트의 의미가 크진 않은것 같다. 동시에 모킹하며 테스트하는게 &amp;#39;짜고치는 고스톱&amp;#39;이라는 느낌을 여전히 벗을수없었다.&lt;/li&gt;
&lt;li&gt;하지만 나중에 서비스 로직이 복잡해지면 이는 해결될거라 생각.&lt;/li&gt;
&lt;li&gt;이 과정에서 하나 오해했던걸 풀수있었다. &lt;code&gt;given&lt;/code&gt;은 &lt;code&gt;BDDMockito&lt;/code&gt;꺼, &lt;code&gt;when&lt;/code&gt;은 &lt;code&gt;Mockito&lt;/code&gt;꺼다. 별개다. given-when-then이 given()-when()-verify()로 매칭되는거란건 착각이엇다. 각각 상황에 맞게 사용하자. 재밌는 메소드들도 많이 발견했다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Daily.log</category>
      <category>@ExtendWith</category>
      <category>@RunWith</category>
      <category>BDDMockito</category>
      <category>GenerationType</category>
      <category>hibernate</category>
      <category>Mockito</category>
      <category>Spring Data JPA</category>
      <author>os94</author>
      <guid isPermaLink="true">https://os94.tistory.com/194</guid>
      <comments>https://os94.tistory.com/194#entry194comment</comments>
      <pubDate>Thu, 12 Dec 2019 18:26:48 +0900</pubDate>
    </item>
    <item>
      <title>JPA) LazyInitializationException 이슈</title>
      <link>https://os94.tistory.com/193</link>
      <description>&lt;h3&gt;상황&lt;/h3&gt;
&lt;p&gt;19.12.10 우테코 레벨4 eat-together 프로젝트의 Article/Shop/Menu 관련 구현 중 발생한 이슈&lt;/p&gt;
&lt;p&gt;Shop은 @OneToMany로 List를 갖고있다.&lt;br&gt;Menu도 양방향으로 @ManyToOne Shop을 갖고있다.&lt;/p&gt;
&lt;p&gt;가게 조회 기능을 구현하는 과정에서, 테스트에서 Shop을 리턴하니 List때문에 터졌다.&lt;/p&gt;
&lt;p&gt;발생한 &lt;code&gt;LazyInitializationException&lt;/code&gt; 예외를 검색해보니 관련 논의가 많았다.&lt;/p&gt;
&lt;h3&gt;참고자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kingbbode.tistory.com/27&quot;&gt;킹뽀대님 글&lt;/a&gt; , 나중에 정독해보고 OSIV에 대해 고민해보자&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://feco.tistory.com/10&quot;&gt;feco.tistory.com - 3가지 해결방안 제시&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bebong.tistory.com/entry/JPA-Lazy-Evaluation-LazyInitializationException-could-not-initialize-proxy-%E2%80%93-no-Session&quot;&gt;bebong.tistory.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;문제 해결&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;application.properties&lt;/code&gt;에 &lt;code&gt;#spring.jpa.open-in-view=false&lt;/code&gt;로 설정되있었다.&lt;/li&gt;
&lt;li&gt;해당 설정을 지우고 true로 켜니 해결. (fetchType은 Lazy인 상태)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;+ Nick&amp;#39;s Help&lt;/h3&gt;
&lt;p&gt;이 부분을 한창 고민하던 와중에, 지나가던 Nick선생님께서 첨언해주셨다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OSIV는 스프링부트에서 기본적으로 true로 설정되어있음.&lt;/li&gt;
&lt;li&gt;OSIV를 켜면, Lazy가 서비스에서 나갈때 Proxy로 나가는데 컨트롤러에서도 조회가능해진다(조회만). 값에 접근할때 select문 날려 db에서 데이터 가져오고.&lt;/li&gt;
&lt;li&gt;우리는 OSIV 설정이 꺼져있어 터졌던 것. 반대로 말하면 이제까지 우린 대부분 이 설정을 기본값으로 켜놔서, 이 예외를 만날기회가 적었던 것&lt;/li&gt;
&lt;li&gt;원래 트랜잭션과 세션의 주기가 함께 가야 좋은데, OSIV를 켜면 (서비스를 나가며 트랜잭션이 끝나도 컨트롤러에서 접근가능하게) 세션 주기를 늘리는 역할&lt;/li&gt;
&lt;li&gt;그래서 현업에선 OSIV를 끄고 개발시작하기도 한다고한다. 켜면 select문 나가며 예상치못한 성능저하 발생할 수 있으니. OSIV에 관해선 말이 많다고 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;-&amp;gt; &lt;code&gt;LazyInitializationException&lt;/code&gt;을 검색하니 관련한 이슈와 논의가 활발했다. 오늘은 이정도로 문제해결하고 나중에 제대로 이부분을 파보자.&lt;/p&gt;
&lt;hr&gt;
&lt;h4&gt;12.12 추가&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Controller와 Service 사이엔 Dto를 활용해서 주고받기로 팀내 컨벤션을 정했다. 이를 통해, 더이상 엔티티가 서비스밖으로 노출되지않아 OSIV 문제가 더이상 발생하지않았고 도메인 모델을 더욱 보호할수있을것으로 기대할수있었다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DB, JPA, SQL</category>
      <category>JPA</category>
      <category>LazyInitializationException</category>
      <category>ManyToOne</category>
      <category>OneToMany</category>
      <category>Open Session In View</category>
      <category>OSIV</category>
      <category>닉선생님 짱</category>
      <author>os94</author>
      <guid isPermaLink="true">https://os94.tistory.com/193</guid>
      <comments>https://os94.tistory.com/193#entry193comment</comments>
      <pubDate>Tue, 10 Dec 2019 22:09:31 +0900</pubDate>
    </item>
  </channel>
</rss>