<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발스토리</title>
    <link>https://codingram.tistory.com/</link>
    <description>개발 이야기</description>
    <language>ko</language>
    <pubDate>Tue, 7 Jul 2026 05:00:20 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>무루뭉</managingEditor>
    <item>
      <title>2021 회고</title>
      <link>https://codingram.tistory.com/109</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2021년은 많은 경험을 하고 스스로의 길을 정하게 된 한 해가 된 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2021 학교 활동&lt;/b&gt;&lt;/h2&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학년의 시간을 보냈다. 아무래도 2학기는 인턴 활동으로 학교 생활을 하지 않게 되면서 1학기가 나의 마지막 학교 생활이었다. 사실 비대면이 계속 되면서 제대로 된 학교 생활을 하지는 못해서 아쉽지만 이제는 끝이니까 뭐,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 1학기에는 졸업작품(캡스톤)을 만들었던 게 제일 기억에 남았다. 뜻이 맞는 친구들 + 나까지 5명에서 영어 단어 암기를 도와주는 안드로이드 어플을 만들었다. 결과는 A+로 1등을 했던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 과정은 다사다난했던 것 같다. 배포를 목적으로 기획을 진행했고 1개월반-2개월 정도 흐른 상황에서 저작권에 늪에 빠지게 되었다.. 한영사전 API를 당연히 제공해줄 것이라고 생각했던 불찰이었다. 예문이 저작권에 걸려서 API 제공을 안해준단다... 예문이 저작권에 걸릴지는 생각도 못했다. 그래서 다른 API 제공 사이트도 찾아봤지만 베타 버전이라 정확도가 너무 떨어지고, 캠브리지 대학에 메일까지 보내봤다. 하지만 원하는 대로 흘러가지 않아 급히 크롤링으로 대처하기로 했다. 그래도 결과물은 만족스럽게 나왔다.&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;심심하면 시연 영상 한 번 봐주3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtube.com/watch?v=SCJ3Xw6QmJk&amp;amp;feature=share&quot;&gt;https://youtube.com/watch?v=SCJ3Xw6QmJk&amp;amp;feature=share&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=SCJ3Xw6QmJk&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/hiYGO/hyMXPQt0DG/kaNcuYYQVLh7AimzYBKbB1/img.jpg?width=480&amp;amp;height=360&amp;amp;face=0_0_480_360&quot; data-video-width=&quot;480&quot; data-video-height=&quot;360&quot; data-video-origin-width=&quot;480&quot; data-video-origin-height=&quot;360&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/SCJ3Xw6QmJk&quot; width=&quot;480&quot; height=&quot;360&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2021 동아리 활동&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2020년부터 많은 외부 활동에 지원을 했지만 모두 떨어졌었다. 오로지 혼자 강의를 듣고 클론 코딩을 하고 있었다. 개발에 대한 방향성을 전혀 잡지 못한 상태에서 마음만 조급했었던 것 같다. 그래서 github 내역도 꾸준히 올렸고, 지금 작성하는 블로그도 시작했다. 그러고 나서 2020 하반기에 동아리 공고에 지원을 다시 시작했다. 내가 지원한 동아리는 모두 붙었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021년 상반기에 IT 동아리에 들어가서 노드팀으로 활동을 시작했다. 지금 생각해보면 걱정을 많이 했었던 것 같다.&amp;nbsp; 스스로에게 자신감이 너무 없어서 이게 맞을까..?라는 생각으로 시간을 많이 낭비했던 것 같다. 처음으로 다양한 개발자들과 디자이너들과의 협업을 했었다. 더 열심히 참여했으면 어땠을까라는 아쉬움이 많이 남는 기간이었다. 또 이 기간동안 느낀 것은 아는 것이 있어야 그만큼의 설득력이 있다는 것이었다. 지금 생각해보면 아는 것이 없었기 때문에 용기가 없었던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 얻는 것은 많았다. 처음으로 AWS 비용도 많이 나와서 환불도 받아보고(내 실수로,,ㅎ) 처음으로 앱 스토어에 배포도 해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2021년 하반기에도 노드팀으로 활동을 이어갔다. express를 통해 개발을 하다가 nest라는 프레임워크를 통해 개발을 했다. 상반기 활동에 아쉬움이 많이 남아 하반기에는 최대한 활발하게 활동하려고 노력했다. 동아리 면접관으로도 참여했다. 면접관으로 참여할 능력은 많이 부족한 것은 알지만 다양한 사람들과 대화를 해보고 싶었다. 그리고 프로젝트 서버팀에서도 리드를 맡아보겠다고 이야기 했다. AWS도 다양한 경험을 해보고 싶어서 API 별개로 역할을 맡게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;aws, docker, 새로운 프레임워크 등등 조금 알거나 처음 해보는 것들을 도전해보는 과정에서 시간을 많이 투자했던 것 같다. 별개로 더 restful하게 설계하기 위해 다양한 레퍼런스도 찾아보고 적용해봤다. 아쉬웠던 것은 초반에는 코드리뷰가 어느정도 활발히 이루어졌지만 후반 갈수록 느슨해졌다는 것이다.&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;a href=&quot;https://apps.apple.com/kr/app/rrrr/id1558135806&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://apps.apple.com/kr/app/rrrr/id1558135806&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1641231488761&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;&amp;lrm;Rrrr&quot; data-og-description=&quot;&amp;lrm;일상속 지겨운 알람, Rrrr를 통해 일상을 즐겁게! 단순한 기본 알람이 지겨우셨나요? 오늘부터 Rrrr를 통해 나만의 커스텀 알람을 가져보세요. 다양한 문구, 링크를 추가하여 중요한 순간을 인상&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/kr/app/rrrr/id1558135806&quot; data-og-url=&quot;https://apps.apple.com/kr/app/rrrr/id1558135806&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cgamkO/hyMXPppsAi/yOy9OSGXE4502aCTEL2J0k/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/AxQAS/hyMXHETJU7/XqcWIIwgfKujVzbTCGBkK0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/rrrr/id1558135806&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/kr/app/rrrr/id1558135806&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cgamkO/hyMXPppsAi/yOy9OSGXE4502aCTEL2J0k/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/AxQAS/hyMXHETJU7/XqcWIIwgfKujVzbTCGBkK0/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;&amp;lrm;Rrrr&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lrm;일상속 지겨운 알람, Rrrr를 통해 일상을 즐겁게! 단순한 기본 알람이 지겨우셨나요? 오늘부터 Rrrr를 통해 나만의 커스텀 알람을 가져보세요. 다양한 문구, 링크를 추가하여 중요한 순간을 인상&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.apple.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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/meetu-%EB%AF%B8%EC%B8%84/id1598412305&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://apps.apple.com/kr/app/meetu-%EB%AF%B8%EC%B8%84/id1598412305&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1641231509219&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;&amp;lrm;MEETU (미츄)&quot; data-og-description=&quot;&amp;lrm;미츄는 다양한 내 모습을 쉽고 재미있게 소개할 수 있는 온라인 명함 제작 서비스입니다. 명함 제작 템플릿을 이용하여 쉽고 빠르게 나만의 미츄 카드를 제작할 수 있어요. 나의 부캐를 보여&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/kr/app/meetu-%EB%AF%B8%EC%B8%84/id1598412305&quot; data-og-url=&quot;https://apps.apple.com/kr/app/meetu-%EB%AF%B8%EC%B8%84/id1598412305&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/vzMOs/hyMXNkNVpX/2E1QprJj2b6Xr07HCb81k1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/Q9hKf/hyMXOcWF0I/dKuomO7Ncbqu3N7TUS4Kfk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/meetu-%EB%AF%B8%EC%B8%84/id1598412305&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/kr/app/meetu-%EB%AF%B8%EC%B8%84/id1598412305&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/vzMOs/hyMXNkNVpX/2E1QprJj2b6Xr07HCb81k1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/Q9hKf/hyMXOcWF0I/dKuomO7Ncbqu3N7TUS4Kfk/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;&amp;lrm;MEETU (미츄)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lrm;미츄는 다양한 내 모습을 쉽고 재미있게 소개할 수 있는 온라인 명함 제작 서비스입니다. 명함 제작 템플릿을 이용하여 쉽고 빠르게 나만의 미츄 카드를 제작할 수 있어요. 나의 부캐를 보여&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2021 인턴 활동&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8월부터 12월까지 인턴 활동을 마쳤다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 나는 node를 사용해서 서버 개발을 주로 해왔다. 하지만 spring을 사용해 볼 기회가 생겨서 인턴 활동을 결심했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 정합성, 무결성 등을 보장해주기 위한 솔루션을 개발하는 팀에서 서버 개발자로 활동을 했다. 자바를 오랜만에 보고 사용해서 그런 지 처음에는 조금 힘들었다. 또한 springboot + jpa를 사용해서 개발을 했는데 jpa가 여태까지 써왔던 orm과는 많이 달랐다. 매커니즘을 이해하기에 어려웠던 것 같다. 퇴근하고 강의듣고 주말에도 개인 공부를 통해 회사가서 적용해보고 했던 것 같다.&amp;nbsp;&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;그래도 내 의견을 많이 이야기 했던 것 같다. 초반에는 세미나도 매 주 진행하고 REST API를 설계하면서 의견 제시도 많이하고 개발 방향성에 대해서도 의견을 많이 제시했다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 것 말고도 2021년에는 허리디스크가 터져서 구급차에 실려가기도 하고 고생을 좀 많이 했다.&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;/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;</description>
      <category>Etc</category>
      <category>2021회고</category>
      <category>개발자 회고</category>
      <author>무루뭉</author>
      <guid isPermaLink="true">https://codingram.tistory.com/109</guid>
      <comments>https://codingram.tistory.com/109#entry109comment</comments>
      <pubDate>Thu, 6 Jan 2022 02:08:13 +0900</pubDate>
    </item>
    <item>
      <title>단 건 조회 시 데이터가 없다면..?부터 시작된 이야기</title>
      <link>https://codingram.tistory.com/108</link>
      <description>&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 명의 유저를 조회하는 GET test.com/users/{userId}라는 API가 있다고 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적으로 조회가 완료가 됐다면, 아래와 같이 response를 보내주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1639034939750&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;success&quot;: true,
  &quot;response&quot;: { &quot;id&quot;: 1, &quot;nickname&quot;: &quot;king&quot; },
  &quot;error&quot;: null
}&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;하지만, GET test.com/users/100 즉, 100번의 유저를 조회하려하는 데 100번의 유저가 없다면...??&lt;/p&gt;
&lt;pre id=&quot;code_1639035117323&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;success&quot;: true,
  &quot;response&quot;: { &quot;id&quot;: null, &quot;nickname&quot;: null },
  &quot;error&quot;: null
}&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;하지만 프론트 쪽에서 판단하기에는 매번 (프론트에서 선언한 변수).response.id가 null인 지를 체크하는 비효율적인 코드가 나왔다.&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;pre id=&quot;code_1639035339650&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;success&quot;: true,
  &quot;response&quot;: {},
  &quot;error&quot;: null
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;왜 이런 결정을 하였나??&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 모든 api에서 단 건 조회시 데이터가 없을 때 위와 같은 응답을 내려준다면 JS 메서드로 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;background-color: #f3c000;&quot;&gt;대안으로는 어떤게 제안되었는가??&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 별도의 필드로 count를 내려주어 0이라면 데이터가 없다는 걸로 판별하자는 방법도 나왔었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 아래와 같은 응답이 내려간다.&lt;/p&gt;
&lt;pre id=&quot;code_1639035678179&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;success&quot;: true,
  &quot;response&quot;: { &quot;id&quot;: null, &quot;nickname&quot;: null },
  &quot;error&quot;: null,
  &quot;count&quot; : 0
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;리스트 조회가 아닌 단건 조회에서 0아니면 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;그래서 결국에는 빈 객체로 내려주기로 하였다.&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;background-color: #f3c000;&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;/p&gt;
&lt;pre id=&quot;code_1639036037900&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Transactional(readOnly = true)
    @Override
    public UserDto findOneUser(Long userId) {
        Optional&amp;lt;User&amp;gt; user = userRepository.findByUserId(userId);
        return user.map(UserDto::new).orElseGet(UserDto::new);
    }&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;하지만 이미 타입이 Optional이 걸려있고, UserDto가 걸려있어 빈 객체랑 타입 불일치가 있다.&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마다 다 있고 데이터가 없는 상황일 때 동일한 처리를 해줘야 한다.&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;그래서 Exception Handler를 따로 생성해서 처리하기로 했다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1639036313005&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.meta.common.exception;


public class NoDataException extends RuntimeException {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따로 메시지는 날리지 않을 거기 때문에 간단하게 Exception class를 만들었다.&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;그 다음 ExceptionHandler를 모아둔 파일에 다음과 같이 작성했다.&lt;/p&gt;
&lt;pre id=&quot;code_1639092539356&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestControllerAdvice(annotations = {RestController.class, Controller.class})
public class ExceptionHandler {
  @ExceptionHandler
    public ResponseEntity&amp;lt;ApiResult&amp;lt;?&amp;gt;&amp;gt; noDataException(NoDataException e) {
        Object result = new Object();
        return new ResponseEntity&amp;lt;&amp;gt;(ApiUtils.success(result), HttpStatus.OK);
    }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;&quot;No serializer found for class ~ and no properties discovered to create BeanSerializer&quot;&lt;/i&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;i&gt;&amp;nbsp;&lt;/i&gt;&lt;/b&gt;직렬화를 해야 하는 데 지금은 가장 원초적인 Object 타입을 두었기 때문에 이 에러를 해결할 방법이 떠오르지 않았다.&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;그래서 EmptyDto를 따로두었다.&lt;/p&gt;
&lt;pre id=&quot;code_1639092989853&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.Serializable;

public class EmptyDto implements Serializable {}&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;하지만 같은 에러가 나왔다..! 검색해보니 보통 private 필드를 두고 getter를 안두었거나 하는 등에 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 어떤 필드도 없다..! 그래서 필드가 없으니..!? 직렬화 시켜줄 게 없다? 라는 생각을 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 검색,,,검색,,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbhyq3/btrnvArIICP/nRKvJYVlWKRAFkVaAvEFkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbhyq3/btrnvArIICP/nRKvJYVlWKRAFkVaAvEFkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbhyq3/btrnvArIICP/nRKvJYVlWKRAFkVaAvEFkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdbhyq3%2FbtrnvArIICP%2FnRKvJYVlWKRAFkVaAvEFkk%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;915&quot; height=&quot;175&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㄷㄷㄷㅈ!!&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;background-color: #ffffff; color: #666666;&quot;&gt;spring.jackson.serialization.FAIL_ON_EMPTY_BEANS 요 설정의 디폴트가 true인 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #666666;&quot;&gt;기본값이 유형에 대한 접근자를 찾을 수 없을 때 직렬화를 할 수 없는 유형으로 나타내기 위해 예외가 발생되도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #666666;&quot;&gt;값을 false로 바꿔주던가...!하면 된다.&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 style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;또한, 다양한 어노테이션들로 해결이 가능했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1639094132660&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.io.Serializable;

@JsonSerialize
public class EmptyDto implements Serializable {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1639094239871&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.codehaus.jackson.annotate.JsonIgnoreProperties;

import java.io.Serializable;

@JsonIgnoreProperties(ignoreUnknown = true)
public class EmptyDto implements Serializable {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1639094285934&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.codehaus.jackson.annotate.JsonIgnoreProperties;

import java.io.Serializable;

@JsonIgnoreProperties({&quot;hibernateLazyInitializer&quot;, &quot;handler&quot;})
public class EmptyDto implements Serializable {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등등... 다양한 어노테이션들로 테스트가 다 통과된다...!!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;325&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vuW0V/btrnqj5xZmz/ATK8RFkKC3RI4rfP6jzDP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vuW0V/btrnqj5xZmz/ATK8RFkKC3RI4rfP6jzDP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vuW0V/btrnqj5xZmz/ATK8RFkKC3RI4rfP6jzDP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvuW0V%2Fbtrnqj5xZmz%2FATK8RFkKC3RI4rfP6jzDP1%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;325&quot; height=&quot;95&quot; data-origin-width=&quot;325&quot; data-origin-height=&quot;95&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 나의 서비스 파일이다.&lt;/p&gt;
&lt;pre id=&quot;code_1639095798274&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Transactional(readOnly = true)
    @Override
    public UserDto findOneUser(Long userId) {
        Optional&amp;lt;User&amp;gt; user = userRepository.findByUserId(userId);
        return user.map(UserDto::new).orElseThrow(NoDataException::new);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;느낀점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 진짜 다양한 해결 방법이 있다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; Jackson 라이브러리를 정독해봐야겠다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; Optional을 다루는 것이 아직 너무 미숙하다..!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 이슈를 통해 배운게 많은듯..!!!&lt;/p&gt;</description>
      <category>삽질 기록</category>
      <category>{}</category>
      <category>빈객체</category>
      <category>스프링부트</category>
      <category>직렬화</category>
      <author>무루뭉</author>
      <guid isPermaLink="true">https://codingram.tistory.com/108</guid>
      <comments>https://codingram.tistory.com/108#entry108comment</comments>
      <pubDate>Fri, 10 Dec 2021 09:02:57 +0900</pubDate>
    </item>
    <item>
      <title>[REST docs error] java.lang.IllegalArgumentException: urlTemplate not found. If you are using MockMvc did you use RestDocumentationRequestBuilders to build the request?</title>
      <link>https://codingram.tistory.com/107</link>
      <description>&lt;pre id=&quot;code_1637045305323&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.lang.IllegalArgumentException: urlTemplate not found. If you are using MockMvc did you use RestDocumentationRequestBuilders to build the request?&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;Rest Docs를 적용을 하고 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;api 문서를 만들려면 당연히 클라이언트에게 api를 사용하기 위한 모든 정보를 적어서 줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 user를 조회하기 위한 api url이 domain/user/{username}이라고 치자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;domain/user는 변하지 않지만 username은 동적이다. joo라는 사용자를 조회할 수도 있고 kim이라는 사용자를 조회할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1637045894577&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MvcResult mvcResult = requestGet(baseUrl + &quot;/user/{username}&quot;, &quot;joo&quot;)
                .andExpect(status().isOk())
                .andDo(document(&quot;user-select&quot;,
                        pathParameters(
                                parameterWithName(&quot;username&quot;).description(&quot;유저 이름&quot;)
                        )&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;에러 로그 보면 너무 친절하게 알려준다. 그대로 번역해보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;tw-target-text&quot; class=&quot;java&quot; data-placeholder=&quot;번역&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;java.lang.IllegalArgumentException: urlTemplate을 찾을 수 없습니다. MockMvc를 사용하는 경우 RestDocumentationRequestBuilders를 사용하여 요청을 작성했습니까?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와우,,, 그냥 &lt;span style=&quot;background-color: #f89009;&quot;&gt;MockMvc로 썼으면 RestDocumentationRequestBuilders를 써봐&lt;/span&gt;라고 알려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서를 내용을 가져와 보겠다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRyptR/btrk9tvdehA/OZn0sKKvczmu9HGr9dvwK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRyptR/btrk9tvdehA/OZn0sKKvczmu9HGr9dvwK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRyptR/btrk9tvdehA/OZn0sKKvczmu9HGr9dvwK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRyptR%2Fbtrk9tvdehA%2FOZn0sKKvczmu9HGr9dvwK1%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;989&quot; height=&quot;99&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더욱 친절하게 알려준다. path parameters를 사용하고 싶으면 RestDocumentationRequestBuilders를 쓰라고!!&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ENHaU/btrk7tWNSdL/HiA0qsTQ86KVGiADpjhQb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ENHaU/btrk7tWNSdL/HiA0qsTQ86KVGiADpjhQb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ENHaU/btrk7tWNSdL/HiA0qsTQ86KVGiADpjhQb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FENHaU%2Fbtrk7tWNSdL%2FHiA0qsTQ86KVGiADpjhQb0%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;736&quot; height=&quot;93&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 나는 위와 같은 코드에 변경을 줘서 해결했다..!&lt;/p&gt;</description>
      <category>Spring &amp;amp; SpringBoot</category>
      <category>API</category>
      <category>API문서</category>
      <category>rest docs</category>
      <category>springboot</category>
      <author>무루뭉</author>
      <guid isPermaLink="true">https://codingram.tistory.com/107</guid>
      <comments>https://codingram.tistory.com/107#entry107comment</comments>
      <pubDate>Tue, 16 Nov 2021 16:05:26 +0900</pubDate>
    </item>
    <item>
      <title>Spring REST Docs 적용</title>
      <link>https://codingram.tistory.com/106</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;전 포스팅에 이어서 이번에는 spring 프로젝트에 적용한 포스팅을 쓴다.&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;- spring boot 2.x.x&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- gradle 6.x&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- junit 5&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;build.gradle&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1636597357594&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
    id 'org.springframework.boot' version '2.x.x'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
    // asciidoc 파일을 변환해서, build 폴더에 복사해주는 플러그인!
    id 'org.asciidoctor.convert' version &quot;1.5.6&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1636597471542&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;asciidoctor {
    dependsOn test    // gradle build 시 test -&amp;gt; asciidoctor 순으로 수행
}

bootJar {
    dependsOn asciidoctor    // gradle build 시 asciidoctor 순으로 수행
    from(&quot;${asciidoctor.outputDir}/html5&quot;) {    // gradle build 시 ./build/asciidoc/html5/ 에 html 파일생성
        into 'static/docs'                        // html 파일이 jar 안의 /static/docs/ 폴더로 복사됨
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1636597525900&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
	...
	testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;테스트 코드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레퍼런스 들을 찾다보면 @AutoConfigureRestDocs를 사용해서 설정을 하는 데 설정이 적용이 안돼서 보니까 junit5에서는 안먹힌다고 한다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;@ExtendWith(RestDocumentationExtension.class)&lt;/span&gt;를 필수로 넣어줘야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1636597732913&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ExtendWith(RestDocumentationExtension.class)
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest {}&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;- RestDocumentationExtension&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 이 녀석이 자동으로 빌드 툴에 맞는 output 디렉터리를 설정한다. 나는 gradle을 사용하므로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;build/generated-snippets 경로에 snippet들이 생성된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1636603904275&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@BeforeEach
    void setup(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
        this.mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
                .apply(documentationConfiguration(restDocumentation))
                .apply(springSecurity()).build(); // spring secutiry 쓰고있어서 추가함
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 코드를 작성하는 방법은 공식문서를 참조하기 바란다. 다양한 메서드들이 있으므로~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-restdocs/docs/2.0.5.RELEASE/reference/html5/#getting-started-using-the-snippets&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-restdocs/docs/2.0.5.RELEASE/reference/html5/#getting-started-using-the-snippets&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1636604888113&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Spring REST Docs&quot; data-og-description=&quot;Document RESTful services by combining hand-written documentation with auto-generated snippets produced with Spring MVC Test.&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-restdocs/docs/2.0.5.RELEASE/reference/html5/#getting-started-using-the-snippets&quot; data-og-url=&quot;https://docs.spring.io/spring-restdocs/docs/2.0.5.RELEASE/reference/html5/#getting-started-using-the-snippets&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-restdocs/docs/2.0.5.RELEASE/reference/html5/#getting-started-using-the-snippets&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-restdocs/docs/2.0.5.RELEASE/reference/html5/#getting-started-using-the-snippets&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Spring REST Docs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Document RESTful services by combining hand-written documentation with auto-generated snippets produced with Spring MVC Test.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.spring.io&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트에 성공하면 &lt;span style=&quot;background-color: #f89009;&quot;&gt;build/generated-snippets&lt;/span&gt;에 .adoc 파일들이 생기는 걸 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;371&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vfhI2/btrkz3Jq0At/MDJjbztnLJaUSzbh7Kfs70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vfhI2/btrkz3Jq0At/MDJjbztnLJaUSzbh7Kfs70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vfhI2/btrkz3Jq0At/MDJjbztnLJaUSzbh7Kfs70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvfhI2%2Fbtrkz3Jq0At%2FMDJjbztnLJaUSzbh7Kfs70%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;371&quot; height=&quot;248&quot; data-origin-width=&quot;371&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 생성된 스니펫들을 사용하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gradle의 경우 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;src/docs/asciidoc/*.adoc&lt;/span&gt; 을 생성해준다. 경로는 지켜주어야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;346&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PG9o0/btrkxOeDMMl/MwQiJP6du7sG95THNbDDM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PG9o0/btrkxOeDMMl/MwQiJP6du7sG95THNbDDM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PG9o0/btrkxOeDMMl/MwQiJP6du7sG95THNbDDM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPG9o0%2FbtrkxOeDMMl%2FMwQiJP6du7sG95THNbDDM0%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;346&quot; height=&quot;90&quot; data-origin-width=&quot;346&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, 문서 기능을 하기 위해 조합해주면 되는데 공식문서 참조바란다. 나는 참고로 마크다운이랑 비슷한 느낌을 받았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 전에 생성한 스니펫들을 사용하려면 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;include::{snippets}/~경로~/*.adoc[]&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 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;background-color: #f3c000;&quot;&gt;./gradlew build 하면 build/asciidoc/html5&lt;/span&gt; 경로에 api.html이 생성된다. 본인이 생성한 .adoc 파일의 이름으로 .html이 생성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;356&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhz28N/btrktdm7Dvv/HIxQW0TGebr7b5zMt2Qmj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhz28N/btrktdm7Dvv/HIxQW0TGebr7b5zMt2Qmj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhz28N/btrktdm7Dvv/HIxQW0TGebr7b5zMt2Qmj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhz28N%2Fbtrktdm7Dvv%2FHIxQW0TGebr7b5zMt2Qmj1%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;356&quot; height=&quot;88&quot; data-origin-width=&quot;356&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러고 프로젝트 경로로 들어가 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;cd build/libs로 가서 java -jar *.jar&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;그러고 localhost:8080/docs/api.html 접속하면 생성한 문서가 짠! 하고 반겨 줄 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;느낀 점&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정이 복잡하다. 그리고 확실히 테스트 코드를 짜는 능력이 있어야 한다. 테스트 코드를 짜지 않으면 사용 자체를 못하기 때문이다. 아직은 설정하고 간단한 api로 적용만 해봤지만 스웨거가 그립달까? 쉽긴 쉬웠는데...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 스스로의 레벨업을 위해서라면 가면 좋은 방향인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+++&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 gradle 6버전을 썼다,. 7버전을 쓰면 build error가 뜨는 경우가 있다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gaemi606.tistory.com/entry/Spring-Boot-REST-Docs-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gaemi606.tistory.com/entry/Spring-Boot-REST-Docs-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1636604596600&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring Boot | REST Docs 적용하기 ( + build failed 해결.. )&quot; data-og-description=&quot;Spring REST Docs? Asciidoctor를 사용해 RESTful 서비스에 대한 문서를 생성하도록 돕는다. Swagger vs REST Docs Swagger UI Spring REST Docs 실제 코드에 애노테이션을 추가하여 문서가 작성되는 방식 -&amp;gt; 가독..&quot; data-og-host=&quot;gaemi606.tistory.com&quot; data-og-source-url=&quot;https://gaemi606.tistory.com/entry/Spring-Boot-REST-Docs-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://gaemi606.tistory.com/entry/Spring-Boot-REST-Docs-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dD9GSe/hyMjwEls2o/C2sEYjCnfdb7DNU0Cekpak/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/b5ASKs/hyMjsoq9DG/UqJBOEUoED1jWT7bouHY61/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/bPVhON/hyMjDwJB0x/5hVQXZVcVgMKCEhfYTtUD1/img.png?width=2126&amp;amp;height=1102&amp;amp;face=0_0_2126_1102&quot;&gt;&lt;a href=&quot;https://gaemi606.tistory.com/entry/Spring-Boot-REST-Docs-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gaemi606.tistory.com/entry/Spring-Boot-REST-Docs-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dD9GSe/hyMjwEls2o/C2sEYjCnfdb7DNU0Cekpak/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/b5ASKs/hyMjsoq9DG/UqJBOEUoED1jWT7bouHY61/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/bPVhON/hyMjDwJB0x/5hVQXZVcVgMKCEhfYTtUD1/img.png?width=2126&amp;amp;height=1102&amp;amp;face=0_0_2126_1102');&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;Spring Boot | REST Docs 적용하기 ( + build failed 해결.. )&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring REST Docs? Asciidoctor를 사용해 RESTful 서비스에 대한 문서를 생성하도록 돕는다. Swagger vs REST Docs Swagger UI Spring REST Docs 실제 코드에 애노테이션을 추가하여 문서가 작성되는 방식 -&amp;gt; 가독..&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gaemi606.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 글 참조하면 될 듯하다. 나도 많이 참조했다.&lt;/p&gt;</description>
      <category>Spring &amp;amp; SpringBoot</category>
      <category>API</category>
      <category>API문서</category>
      <category>rest docs</category>
      <author>무루뭉</author>
      <guid isPermaLink="true">https://codingram.tistory.com/106</guid>
      <comments>https://codingram.tistory.com/106#entry106comment</comments>
      <pubDate>Thu, 11 Nov 2021 13:28:14 +0900</pubDate>
    </item>
    <item>
      <title>Spring REST Docs 도입 이유</title>
      <link>https://codingram.tistory.com/105</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 새로운 프로젝트를 시작하는데 REST API 설계부터 하고 api 문서를 뽑아내기 위해 REST Docs를 적용하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Spring REST Docs?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Asciidoctor를 사용해서 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;나는 여러 사이드 프로젝트를 하면서 API 문서를 전달하기 위해 swagger, postman, notion 등을 사용했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 REST Docs를 적용해야 해서 오랜만에 글을 써본다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;swagger&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-&amp;nbsp;&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;- 하지만 많이 쓰이는 이유는 쉽기 때문이라고 생각한다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;postman&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 테스트 코드를 많이 사용하기 전에는 api 테스트를 postman을 통해서 했었다. ( 테스트 코드 작성이 미숙해 아직도 많이 사용 중이긴 함...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 직접 테스트를 하면서 api doc을 생성해서 문서를 뽑아내 줄 수 있다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;notion&lt;/b&gt;&lt;/h3&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그래서 왜 REST Docs를 채택했나!&lt;/h4&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;하는 것을 원했다. 또한, 깔끔한 api 문서를 원했으며 깔끔한 코드를 작성하기로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 test code를 중요시하여 test coverage를 높이기로 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유는 REST Docs를 가리켰다. &lt;b&gt;test code를 작성해야 문서를 자동화시켜주기 때문이다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 생산성 저하로 이루어질 수 있지만 test code를 열심히 공부하고 적용할거니까..ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 실제로 문서와 함께 테스트 해볼 수 있는 환경을 원했지만 결국은 api &quot;문서&quot;이기 때문에 여러가지 이유로&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;다음에는 REST Docs를 적용하는 포스팅으로 와보겠다! 빠잉~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring &amp;amp; SpringBoot</category>
      <category>API문서</category>
      <category>rest docs</category>
      <category>Spring</category>
      <author>무루뭉</author>
      <guid isPermaLink="true">https://codingram.tistory.com/105</guid>
      <comments>https://codingram.tistory.com/105#entry105comment</comments>
      <pubDate>Thu, 11 Nov 2021 11:16:10 +0900</pubDate>
    </item>
    <item>
      <title>TestContainer</title>
      <link>https://codingram.tistory.com/104</link>
      <description>&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;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;&quot;멱등성&quot;&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 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;HTTP method에서 보자면 POST를 제외하고 나머지는 멱등성을 만족한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;POST&lt;/span&gt; 요청을 반복한다면, 데이터들은 계속해서 추가가 되고, 서버의 응답은 다 다른 응답을 나타낸다. 같은 내용이더라도 서로 다른 데이터이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;PUT&lt;/span&gt; 요청으로 2번 데이터를 수정한다고 치면, 2번 데이터가 없는 경우에는 데이터가 생성될 수 있지만, 이미 존재하면 데이터는 수정이 된다. 계속해서 PUT 요청을 날려도 2번 데이터는 우리가 요청한 데이터 값을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;DELETE&lt;/span&gt;는 데이터가 존재하든, 존재하지 않든 DELETE 요청을 날린 시점에서 사라지게 되므로 우리가 요청한 사항은 이루어졌음을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;GET&lt;/span&gt;은 같은 데이터를 계속해서 응답하므로 이해가 쉬울 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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;또한 잘 실행되던 것들이 갑자기 에러 천국으로 변할 수도 있다.&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;TestContainer로 멱등성을 유지해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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라고 볼 수 있다.&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;background-color: #ffc9af;&quot;&gt;1. 로컬 환경에서 실제 DB를 띄우는 방법&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 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;background-color: #ffc9af;&quot;&gt;2. in-memory DB를 활용하는 방법&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- H2 등을 사용하고 ORM을 통해서 특정 DB와의 종속성을 해결하는 방법이다. 빠르고, 간단하지만 특정 DB에 특화된 기능을 테스트하거나 DDL을 명시할 때 &lt;b&gt;실제 동작과는 다른 SQL을 작성해야한다는 문제가 있다. ( 실제 쿼리 수행과 완벽하게 동작하지 않을 수 있다 -&amp;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;- 개발 사항에 따라 스키마 초기 구성을 최신화해줘야하는데 프로젝트가 커지면 데이터 양이 너무 많다. -&amp;gt; 추가 관리 항목&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;3. Docker-Compose 활용하는 방법&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 위에 1 , 2번 방법이 아니라도 도커 이미지만 가지고 있다면 테스트 환경을 구축할 수 있다. 이 것도 간편한 방법이지만 docker-compose 파일을 관리해야하는 번거로움이 있다. 또한, random port를 통해 local 포트 충돌 방지 및 병렬 테스트를 어렵게 만든다. 또한, 도커를 올렸다가 내렸다가 하는 과정도 귀찮고, 자칫하면 백그라운드에 남아있어 리소스를 잡아먹을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;4. TestContainer 활용하는 방법&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 테스트 전에 H2, Oracle(예시), Docker Container를 따로 띄우지 않아도 자동으로 테스트할 때 DB Container를 띄어주고 테스트가 종료되면 컨테이너도 같이 종료시켜주는 역할을 한다. 3번 방법과 동작 원리는 같지만, 실제로 설정하는 것은 확 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+++&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 방식과 다른 점은 test container가 실행된 random port로 접근해서 local port간의 충돌을 방지하고 병렬 test를 가능하게 한다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+++&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커를 사용하면 변화하지 않는 실행환경으로 멱등성을 확보할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 변경을 가하고 싶은 경우에는 그냥 새로운 서버를 구축하고 이미지로 빌드하고 컨테이너를 띄어버리면 된다.&lt;/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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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;TestContainer를 도입하는 것도 괜찮을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 : &lt;a href=&quot;https://riiidtechblog.medium.com/testcontainer-%EB%A1%9C-%EB%A9%B1%EB%93%B1%EC%84%B1%EC%9E%88%EB%8A%94-integration-test-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-4a6287551a31&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://riiidtechblog.medium.com/testcontainer-%EB%A1%9C-%EB%A9%B1%EB%93%B1%EC%84%B1%EC%9E%88%EB%8A%94-integration-test-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-4a6287551a31&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Etc</category>
      <category>docker</category>
      <category>TestContainer</category>
      <category>멱등성</category>
      <author>무루뭉</author>
      <guid isPermaLink="true">https://codingram.tistory.com/104</guid>
      <comments>https://codingram.tistory.com/104#entry104comment</comments>
      <pubDate>Tue, 14 Sep 2021 13:11:21 +0900</pubDate>
    </item>
    <item>
      <title>java.lang.illegalargumentexception at least one jpa metamodel must be present</title>
      <link>https://codingram.tistory.com/103</link>
      <description>&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 data-ke-size=&quot;size16&quot;&gt;에러 로그를 살펴보면 적어도 하나의 jpa metamodel이 필요한 데 없어서 그런것 같다.&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;우선 JPA에서는 엔티티들의 생성 및 수정 시간을 자동으로 관리해주는 Auditing 기능이 있다. 나도 이기능을 사용하기 위해 아래와 같은 코드를 작성해둔 상태였다.&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 alignCenter&quot; data-origin-width=&quot;703&quot; data-origin-height=&quot;264&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mmByK/btreCggPwvQ/t7jBDhHlY7MWSRXwdOiHJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mmByK/btreCggPwvQ/t7jBDhHlY7MWSRXwdOiHJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mmByK/btreCggPwvQ/t7jBDhHlY7MWSRXwdOiHJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmmByK%2FbtreCggPwvQ%2Ft7jBDhHlY7MWSRXwdOiHJ1%2Fimg.png&quot; data-origin-width=&quot;703&quot; data-origin-height=&quot;264&quot; data-ke-mobilestyle=&quot;widthOrigin&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 @EnableJpaAuditing 어노테이션을 테스트들의 가장 기본이 되는 Application 클래스에 등록을 해두면 모든 테스트들이 항상 JPA 관련 Bean들을 필요로 하는 상태가 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, @EnableJpaAuditing을 사용하기 위해서는 최소 하나의 @Entity 클래스가 필요하다는 뜻이다.&amp;nbsp;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;그럼 본 코드에서는 Auditing을 사용하고 테스트 코드에서는 이 설정을 제거하려면 어떻게 하면 될까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각하면 간단했다. 나는 테스트 코드에서 @WebMvcTest를 하고 있었고, 이것이 @SpringbootApplication을 스캔하는 데 @EnableJpaAuditing이 함께 있다 보니 같이 스캔되기 때문에 발생하는 것이니 둘을 분리하면 된다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@WebMvcTest는 일반적인 @Configuration은 스캔하지 않기 때문이다.&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 alignCenter&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;170&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FsJos/btrevXQvcDa/tWaYdBc8R1XfoC1kqMvYgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FsJos/btrevXQvcDa/tWaYdBc8R1XfoC1kqMvYgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FsJos/btrevXQvcDa/tWaYdBc8R1XfoC1kqMvYgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFsJos%2FbtrevXQvcDa%2FtWaYdBc8R1XfoC1kqMvYgK%2Fimg.png&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;170&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;224&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rvJaA/btreu9RqA0W/ZJBlMImc02DSpxjgnrFLz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rvJaA/btreu9RqA0W/ZJBlMImc02DSpxjgnrFLz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rvJaA/btreu9RqA0W/ZJBlMImc02DSpxjgnrFLz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrvJaA%2Fbtreu9RqA0W%2FZJBlMImc02DSpxjgnrFLz1%2Fimg.png&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;224&quot; data-ke-mobilestyle=&quot;widthOrigin&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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;( 예를 들어, 무엇을 자동으로 스캔하고 어떠한 어노테이션을 쓰면 무엇을 필수 구현 해야 하는지..? )&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 찾아보니까 위에 방법으로 해도 되지만 @MockBean으로 테스트 클래스에 JpaMetaModelMappingContext를 추가 하는 방법도 있다.&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 alignCenter&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;98&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tGZGS/btreB9CkIB6/1i4Y6HnfVihKd1U1seh2xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tGZGS/btreB9CkIB6/1i4Y6HnfVihKd1U1seh2xk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tGZGS/btreB9CkIB6/1i4Y6HnfVihKd1U1seh2xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtGZGS%2FbtreB9CkIB6%2F1i4Y6HnfVihKd1U1seh2xk%2Fimg.png&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;98&quot; data-ke-mobilestyle=&quot;widthOrigin&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 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;하지만, 테스트를 추가할 때 마다 저런 설정을 해주는 것은 좋지 않은 방법이다. 그래서 나는 위에서 말한 첫 번째 방법으로 적용했다.&lt;/p&gt;</description>
      <category>삽질 기록</category>
      <category>@WebMvcTest</category>
      <category>Auditing</category>
      <category>JPA</category>
      <author>무루뭉</author>
      <guid isPermaLink="true">https://codingram.tistory.com/103</guid>
      <comments>https://codingram.tistory.com/103#entry103comment</comments>
      <pubDate>Thu, 9 Sep 2021 14:06:49 +0900</pubDate>
    </item>
    <item>
      <title>DefaultSerializer requires a Serializable payload but received an object of type [...]</title>
      <link>https://codingram.tistory.com/102</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;137&quot; width=&quot;896&quot; height=&quot;74&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqQDlP/btrec4nROK3/oIxYd8uelLGjcVg7gh7KIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqQDlP/btrec4nROK3/oIxYd8uelLGjcVg7gh7KIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqQDlP/btrec4nROK3/oIxYd8uelLGjcVg7gh7KIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqQDlP%2Fbtrec4nROK3%2FoIxYd8uelLGjcVg7gh7KIk%2Fimg.png&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;137&quot; width=&quot;896&quot; height=&quot;74&quot; data-ke-mobilestyle=&quot;widthOrigin&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 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;b&gt;&lt;span&gt;&quot;DefaultSerializer에는 직렬화 가능한 페이로드가 필요하지만 [...] 유형의 개체를 수신했습니다&quot;&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;[...]에 내가 사용한 dto 폴더를 나타내 주는데 그 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;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;직렬화란?&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;■ 자바에서 직렬화란 자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 자바 시스템에서도 사용할 수 있도록 바이트 형태로 데이터 변환하는 기술과 바이트로 변환된 데이터를 다시 객체로 변환하는 기술(역직렬화)를 아우른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;■ 시스템적으로는, JVM의 메모리에 상주되어 있는 객체 데이터를 바이트 형태로 변환하는 기술과 직렬화된 바이트 형태의 데이터를 객체로 변환해서 JVM으로 상주시키는 형태를 이야기 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;어떻게 사용할까?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;java.io.Serializable&lt;/span&gt; 인터페이스를 상속받고, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;java.io.ObjectOutputStream&lt;/span&gt; 객체를 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;왜 사용할까?&lt;/b&gt;&lt;/h4&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;- api나 데이터 변환 시 많이 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다량의 데이터 직렬화시 csv, json 등으로 많이 사용한다. csv, 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;참조 블로그 : &lt;a href=&quot;https://velog.io/@redcarrot01/TroubleShooting-springboot-DefaultSerializer-requires-a-Serializable-payload&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@redcarrot01/TroubleShooting-springboot-DefaultSerializer-requires-a-Serializable-payload, &lt;/a&gt;&lt;a href=&quot;https://techblog.woowahan.com/2551/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://techblog.woowahan.com/2551/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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;우선 &lt;b&gt;User 클래스가 엔티티일 경우 User 클래스에 직렬화 코드를 넣으면 안된다.&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이유가 뭘까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 엔티티 클래스에는 언제 다른 엔티티와 관계가 형성될 지 모른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, @ManyToMany 등 자식 엔티티를 가지고 있다고 보면, 직렬화 대상에 자식들까지 포함되어야 하니 성능 이슈, 부수 효과가 발생할 확률이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그렇다면 어떻게 할까?&lt;/h4&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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;user 클래스(엔티티)는 냅두고 userDto(예시) 파일을 하나 생성해서 클래스에 implements Serializable을 붙여서 해결했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 자바 라이브러리만 사용해도 직렬화와 역직렬화를 할 수 있지만, 그에 따른 장단점이 존재하는 듯 보였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 더 알아보다가 직렬화의 필요성에 관련한 블로그 남긴다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hub1234.tistory.com/26&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hub1234.tistory.com/26&lt;/a&gt;&lt;/p&gt;</description>
      <category>삽질 기록</category>
      <category>스프링붙</category>
      <category>직렬화</category>
      <author>무루뭉</author>
      <guid isPermaLink="true">https://codingram.tistory.com/102</guid>
      <comments>https://codingram.tistory.com/102#entry102comment</comments>
      <pubDate>Mon, 6 Sep 2021 12:27:25 +0900</pubDate>
    </item>
    <item>
      <title>@Bean, @Component, @Configuration</title>
      <link>https://codingram.tistory.com/101</link>
      <description>&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Bean&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;234&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wDiIv/btrdFnPvudZ/dmHgLD44B5Dy19GrwQnoIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wDiIv/btrdFnPvudZ/dmHgLD44B5Dy19GrwQnoIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wDiIv/btrdFnPvudZ/dmHgLD44B5Dy19GrwQnoIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwDiIv%2FbtrdFnPvudZ%2FdmHgLD44B5Dy19GrwQnoIK%2Fimg.png&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;234&quot; data-ke-mobilestyle=&quot;widthOrigin&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 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;background-color: #f6e199;&quot;&gt;Bean은 Spring IoC Container가 관리하는 자바 객체, Spring Bean Container에 존재하는 객체를 말한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bean 컨테이너는 DI를 통해 Bean 객체를 사용할 수 있도록 해주는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링에서 Bean은 보통 Singleton으로 존재한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;* Singleton : 어떠한 클래스가 최초로 한번만 메모리에 할당되고 그 메모리에 객체를 만들어 사용하는 패턴&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 스프링의 DI Container에 의해 관리되는 POJO를 Bean이라고 부르고 이러한 Bean들이 스프링을 구성하는 핵심 요소이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 빈 정리해보자잇~!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Spring IoC 컨테이너(또는 DI 컨테이너)에 의해 생성 및 관리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● POJO로써 Spring 애플리케이션을 구성하는 핵심 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Spring Bean 구성요소를 알아보자&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;class: Bean으로 등록할 Java 클래스&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;constructor-arg: Bean 생성 시 생성자에 전달할 파라미터&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;scope : Bean을 생성하기 위한 방법&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;id : Bean 고유 식별자&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;property : Bean 생성 시 setter에 전달할 인수&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;다른 Spring Bean 어노테이션 종류&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 하다보면 상당히 많은 클래스를 Bean으로 등록하게 된다. 옛날에는 이런것들을 모두 xml을 통해 Bean으로 등록하면서 상당히 시간이 소요되는 번거로운 작업을 했었다. 이러한 번거로움을 덜기 위해서 &lt;b&gt;스프링에서는 어노테이션을 활용해 Bean을 등록해서 사용하는 방법을 제공하고 있다.&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;이제 스프링 Bean을 등록하는 방법에서 포스팅 제목의 세 가지 어노테이션을 사용한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@Bean 어노테이션과 @Configuration 어노테이션&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bean으로 등록하고자 한다면 @Bean 어노테이션을 활용하자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1630389092105&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import com.example.HeaderFilter; 
import com.example.SessionInterceptor; 
import org.springframework.boot.web.servlet.FilterRegistrationBean; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.core.Ordered; 
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter; 
import java.util.Properties; 

// 1개 이상 Bean을 등록하고 있음을 명시하는 어노테이션 
@Configuration 
public class WebMvcConfig implements WebMvcConfigurer {
... 

@Bean 
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() { 
return new ResourceUrlEncodingFilter(); 
 } 

@Bean 
public HeaderFilter createHeaderFilter() { 
return new HeaderFilter(); 
 } 

@Bean 
public FilterRegistrationBean&amp;lt;HeaderFilter&amp;gt; getFilterRegistrationBean() { 
FilterRegistrationBean&amp;lt;HeaderFilter&amp;gt; registrationBean = new FilterRegistrationBean&amp;lt;&amp;gt;(createHeaderFilter()); 
registrationBean.setOrder(Integer.MIN_VALUE); 
registrationBean.addUrlPatterns(&quot;/*&quot;); 
return registrationBean; 
 } 

@Bean 
public BCryptPasswordEncoder createBCryptPasswordEncoder(){ 
return new BCryptPasswordEncoder(); 
 }  
}&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;위 코드에서 볼 수 있듯이 많은 클래스들이 Bean으로 등록된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 클래스들을 xml에 일일이 적어서 등록하는 것은 생산력에 큰 영향을 미친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그니까 편리하고 빠른 어노테이션을 통해 Bean을 등록하자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;하지만, 임의의 클래스를 만들어서 @Bean 어노테이션을 붙인다고 되는 것이 아니다. @Bean을 사용하는 클래스에는 반드시 @Configuration 어노테이션을 활용해서 해당 클래스에서 Bean을 등록하고자 함을 명시해주어야 한다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제도 클래스 위에 @Configuration 어노테이션을 명시해서 해당 클래스에서 1개 이상의 Bean을 생성하고 있음을 명시하고 있다. 그렇기 때문에 @Bean 어노테이션을 사용하는 클래스의 경우 반드시 @Configuration과 함께 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 @Configuration 없이도 @Bean만 사용해서 스프링 빈으로 등록이 가능하다. 대신에 메서드 호출을 통해 객체를 생성할 때 싱글톤을 보장하지는 않는다. 그렇기 때문에 스프링 설정 정보를 위해서는 반드시 @Configuration을 사용해주어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, &lt;b&gt;빈의 설정을 담당하는 @Configuration 어노테이션도 내부적으로 @Component를 가지고 있어&lt;/b&gt; @Configuration이 붙은 클래스도 스프링의 빈으로 등록이 되는 것 역시 알아두면 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@Component 어노테이션&lt;/b&gt;&lt;/h3&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;개발자가 직접 개발한 클래스를 Bean으로 등록하고자 하는 경우에는 @Component 어노테이션을 활용한다.&lt;/b&gt; 예를 들어, 파일 업로드 등을 처리하기 위해 직접 개발한 코드를 Bean으로 등록하고자 한다면 @Component 어노테이션을 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Bean으로 등록할 수도 있지만 직접 개발한 클래스의 단위를 빈에 등록할 때는 @Component를 통해 해당 클래스를 빈으로 등록함을 노출하는 것이 좋다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면, @Component만 보면 해당 빈이 등록되도록 잘 설정되었는 지를 찾이 않고 확인할 수 있기 때문이다. 반면에 @Bean을 사용하면 해당 메서드를 찾는 번거로움이 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 @Component를 이용한다면 메인 또는 앱 클래스에서 @ComponentScan으로 컴포넌트를 찾는 탐색 범위를 지정해주어야 한다. 하지만 스프링 부트를 쓴다면 &lt;span style=&quot;color: #000000;&quot;&gt;@SpringBootConfiguration 하위에 포함되어 있어 별도의 설정이 필요 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;생각&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 신경안쓰고 쓰던 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;@SpringBootApplication이 사실 @ComponentScan, @Component를 상속받고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스프링에서는 신경써서 설정을 해야 하는 번거로움이 있었지만 스프링 부트는 신경 안쓰고 어노테이션을 쓸 수 있어서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;편리했었던 것이다. 스프링부트가 편리하지만 그냥 사용하는 것보다 스프링에서 무엇이 편리해졌고 무엇을 내재하고&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;있는 지 더 알아봐야 겠다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Spring &amp;amp; SpringBoot</category>
      <category>@Bean</category>
      <category>@Component</category>
      <category>@configuration</category>
      <category>bean</category>
      <category>스프링</category>
      <author>무루뭉</author>
      <guid isPermaLink="true">https://codingram.tistory.com/101</guid>
      <comments>https://codingram.tistory.com/101#entry101comment</comments>
      <pubDate>Tue, 31 Aug 2021 15:26:06 +0900</pubDate>
    </item>
    <item>
      <title>Service와 ServiceImpl</title>
      <link>https://codingram.tistory.com/100</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;240&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEehTk/btrdDycDBxE/tniXipbuDUJ5qnCQdzRY00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEehTk/btrdDycDBxE/tniXipbuDUJ5qnCQdzRY00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEehTk/btrdDycDBxE/tniXipbuDUJ5qnCQdzRY00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEehTk%2FbtrdDycDBxE%2FtniXipbuDUJ5qnCQdzRY00%2Fimg.png&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;240&quot; data-ke-mobilestyle=&quot;widthOrigin&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 프로젝트들을 구경하면 &lt;b&gt;Service 인터페이스와 ServiceImpl 클래스(구현부)를 따로 두어&lt;/b&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;우선 두 가지로 왜 나누어놓는 지를 알기 위해서는 OOP(객체 지향 프로그래밍)에 대한 이해가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OOP를 완전히 이해하는 것은 어려운 일이지만, 짧게 말하면 '객체'들의 모임이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OOP는 프로그램을 &lt;b&gt;유연하고 변경이 용이&lt;/b&gt;하게 만들기 때문에 SW개발에 많이 사용된다.&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;Service와 SeviceImpl은 다형성과 연관이 있다.&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를 부모로 인터페이스로 만들고, 자식으로는 ServiceImpl 클래스가 부모를 상속받아 구현하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;~Impl이 붙는 것은 개발자들이 따르는 naming convention이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래서 대체 왜 사용할까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;의존 관계를 줄여&lt;/b&gt;&lt;/span&gt; 결합도를 낮춰주는데도 좋은 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;느슨한 결합을 뜻하는데 loose coupling이라고 한다. 의존 관계를 최소화해서 기능 변화에도 최소한의 수정으로 개발할 수 있다. 모듈화를 통해 어디서든 사용할 수 있도록 재사용성이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링의 IOC/DI 기능을 이용해 빈 관리 기능이 사용이 가능하다고 하는데 이 부분은 조금 더 공부해봐야 할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래서 이런 구조를 그냥 따르면 될까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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 인터페이스와 ServiceImpl이 1:1로 매핑이 된다면 굳이 필요할까? 였다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;괜히 하나의 인터페이스가 더 늘어나는 셈인 것 같았다. 그래서 1:1이라면 굳이 인터페이스를 둘 필요가 없다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;단, 1:N 구조에서는 사용하는 것이 좋을 것 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 작업으로 인해 N(N&amp;gt;=2)개 이상의 작업이 수행되거나, 추가될 예정인 경우에는 인터페이스를 따로 두어 관리하는 것이 좋을 것 같다. 추가될 예정인 경우가 애매하다고 보지만 그만큼 자신이 수행중인 업무를 제대로 이해하는 능력 또한 중요한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;생각&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 남들도 이렇게 하니까~ 라는 생각보다 왜 쓰게 되었는 지 왜 이렇게 많이들 쓰는 지를 이해하고 부정적인 시각도 많이 보면서 사용법을 제대로 익혀가는 것이 좋은 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring &amp;amp; SpringBoot</category>
      <category>Service</category>
      <category>serviceImpl</category>
      <category>다형성</category>
      <author>무루뭉</author>
      <guid isPermaLink="true">https://codingram.tistory.com/100</guid>
      <comments>https://codingram.tistory.com/100#entry100comment</comments>
      <pubDate>Tue, 31 Aug 2021 13:31:49 +0900</pubDate>
    </item>
  </channel>
</rss>