<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Steady and right</title>
    <link>https://maenco.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 6 Jul 2026 17:27:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>MAENCO</managingEditor>
    <item>
      <title>[jQuery] 탐색 선택자 (1) - 위치 탐색 선택자</title>
      <link>https://maenco.tistory.com/entry/jQuery-%ED%83%90%EC%83%89-%EC%84%A0%ED%83%9D%EC%9E%90-1-%EC%9C%84%EC%B9%98-%ED%83%90%EC%83%89-%EC%84%A0%ED%83%9D%EC%9E%90</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;탐색 선택자&lt;/b&gt;&lt;/blockquote&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;위치 탐색 선택자&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 선택자로 요소(태그)를 선택했을 경우 배열에 담기게 된다, 이때 배열의 인덱스(index)를 사용하면 특정 요소를 더 세밀하게 선택할 수 있는데 위치 탐색 선택자는 바로 이 인덱스를 활용하여 특정 요소를 선택하게 된다&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 676px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style6&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 6.97677%; height: 18px; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;종류&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 18px; text-align: center;&quot;&gt;사용법&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 6.97677%; height: 658px; text-align: center;&quot; rowspan=&quot;28&quot;&gt;위치&lt;br /&gt;탐색&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;first 선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 18px; text-align: center;&quot;&gt;$(&quot;선택 요소:first&quot;)&lt;br /&gt;$(&quot;선택 요소&quot;).first()&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;선택된 요소 중 첫번째 요소를 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu li:first&quot;)&lt;br /&gt;$(&quot;#menu li&quot;).first()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;last 선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 18px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #f5f7f5;&quot;&gt;$(&quot;선택 요소:last&quot;)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #f5f7f5;&quot;&gt;$(&quot;선택 요소&quot;).last()&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;선택된 요소 중 마지막 요소를 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu li:last&quot;)&lt;br /&gt;$(&quot;#menu li&quot;).last()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;even 선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 18px; text-align: center;&quot;&gt;$(&quot;선택 요소:even&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;선택한 요소 중 짝수 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu li:even&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;odd 선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 18px; text-align: center;&quot;&gt;$(&quot;선택 요소:odd&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;선택한 요소 중 홀수 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu li:odd&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;eq(index)&lt;br /&gt;탐색 선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 18px; text-align: center;&quot;&gt;$(&quot;선택 요소:eq(index)&quot;)&lt;br /&gt;$(&quot;선택 요소&quot;).eq(index)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;선택한 요소 중 지정한 인덱스가 참조하는 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu li:eq(2)&quot;)&lt;br /&gt;$(&quot;#menu li&quot;).eq(2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 40px; text-align: center;&quot;&gt;it(index)&lt;br /&gt;탐색 선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소:lt(index)&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택한 요소 중 지정한 인덱스보다 &lt;br /&gt;작은 (less than) 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu li:lt(2)&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 40px; text-align: center;&quot;&gt;gt(index)&lt;br /&gt;탐색 선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소:gt(index)&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택한 요소 중 지정한 인덱스보다&lt;br /&gt;큰 (greater than) 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu li:gt(2)&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 40px; text-align: center;&quot;&gt;first-of-type&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소:first-of-type&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택한 요소의 무리 중 첫번째 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;li:first-of-type&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 40px; text-align: center;&quot;&gt;last-of-type&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소:last-of-type&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택한 요소의 무리 중 마지막 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;li:last-of-type&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 40px; text-align: center;&quot;&gt;nth-child(n)&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소:nth-child(n)&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택한 요소의 무리 중 n번째 요소를 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu li:nth-child(2)&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 40px; text-align: center;&quot;&gt;nth-child(Nn)&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소:nth-child(Nn)&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택한 요소의 무리 중 지정한 숫자(N)의 배수 요소를 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu li:nth-child(2n)&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 40px; text-align: center;&quot;&gt;nth-last-of-type(n)&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 31.2788%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소:nth-last-of-type(n)&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택한 요소의 무리 중 마지막에서 지정한 숫자의 요소를 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center; width: 76.2791%;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu li:nth-last-child(2)&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; text-align: center; height: 18px;&quot;&gt;only-child&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 31.2788%; height: 18px;&quot;&gt;$(&quot;선택 요소:only-child&quot;)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 45.0003%; height: 18px;&quot;&gt;선택한 요소의 부모 요소에게 하나뿐인 자식이라면 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; text-align: center; height: 18px;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px; width: 76.2791%;&quot; colspan=&quot;2&quot;&gt;$(&quot;li:only-child&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; text-align: center; height: 18px;&quot;&gt;slice(index)&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px; width: 31.2788%;&quot;&gt;$(&quot;선택 요소&quot;).slice(start idx, end idx)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 45.0003%; height: 18px;&quot;&gt;지정 구간 인덱스(start idx ~ end idx)의 요소를 선택,&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 16.7441%; text-align: center; height: 18px;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 76.2791%; text-align: center; height: 18px;&quot; colspan=&quot;2&quot;&gt;$(&quot;.list&quot;).slice(1, 3)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인 학습을 위해 작성되는 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;제가 잘못 알고 있는 점에 대한 지적 / 더 나은 방향에 대한 댓글을 환영합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참조 서적:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/59461086&quot;&gt;자바스크립트 + 제이쿼리 입문 : 정인용&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Javascript/jQuery</category>
      <category>제이쿼리 위치탐색선택자</category>
      <category>제이쿼리 탐색선택자</category>
      <author>MAENCO</author>
      <guid isPermaLink="true">https://maenco.tistory.com/98</guid>
      <comments>https://maenco.tistory.com/entry/jQuery-%ED%83%90%EC%83%89-%EC%84%A0%ED%83%9D%EC%9E%90-1-%EC%9C%84%EC%B9%98-%ED%83%90%EC%83%89-%EC%84%A0%ED%83%9D%EC%9E%90#entry98comment</comments>
      <pubDate>Thu, 4 Nov 2021 21:56:20 +0900</pubDate>
    </item>
    <item>
      <title>[jQuery] 기본 선택자 (2) - 인접관계 선택자</title>
      <link>https://maenco.tistory.com/entry/jQuery-%EA%B8%B0%EB%B3%B8-%EC%84%A0%ED%83%9D%EC%9E%90-2-%EC%9D%B8%EC%A0%91%EA%B4%80%EA%B3%84-%EC%84%A0%ED%83%9D%EC%9E%90</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;인접 관계 선택자&lt;/b&gt;&lt;/blockquote&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;table style=&quot;border-collapse: collapse; width: 100%; height: 604px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style6&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 6.97677%; height: 18px; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;종류&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 18px; text-align: center;&quot;&gt;사용법&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 6.97677%; height: 586px; text-align: center;&quot; rowspan=&quot;24&quot;&gt;인접 &lt;br /&gt;관계&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;부모 요소 &lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 18px; text-align: center;&quot;&gt;$(&quot;선택 요소&quot;).parent()&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;선택한 요소를 감싸고 있는 부모 요소를 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu&quot;).parent();&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;상위 요소 &lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 18px; text-align: center;&quot;&gt;1. $(&quot;선택 요소&quot;).parents()&lt;br /&gt;2. $(&quot;선택 요소&quot;).parents(&quot;선택 요소&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;1. 선택한 요소 기준으로 모든 상위 요소 선택&lt;br /&gt;2. 선택한 요소 기준으로 상위 요소 중 선택한 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;1. $(&quot;.example1&quot;).parents()&lt;br /&gt;&lt;br /&gt;2. $(&quot;.example2&quot;).parents(&quot;span&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;가장 가까운&lt;br /&gt;상위 요소 &lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 18px; text-align: center;&quot;&gt;$(&quot;선택 요소&quot;).closest(&quot;선택 요소&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;선택한 요소 기준으로 가장 가까운 상위 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;.exampl1&quot;).closest(&quot;div&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;하위 요소 &lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 18px; text-align: center;&quot;&gt;$(&quot;기준 요소 선택1 요소 선택2&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;기준 요소로 선택한 하위 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#wrap h1&quot;) -&amp;gt; #wrap이 포함하는 모든 h1 요소를 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;자식 요소 &lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 18px; text-align: center;&quot;&gt;1. $(&quot;선택 요소 &amp;gt; 자식 선택 요소&quot;)&lt;br /&gt;2. $(&quot;선택 요소&quot;).children(&quot;자식 선택 요소&quot;)&lt;br /&gt;3. $(&quot;선택 요소&quot;).children()&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 18px; text-align: center;&quot;&gt;1&amp;amp;2. 선택한 요소 기준으로 지정한 자식 요소만 선택&lt;br /&gt;3. 선택한 요소 기준으로 모즌 자식 요소를 선택&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;#wrap &amp;gt; h1&quot;) -&amp;gt; wrap 요소 안의 자식 요소 h1 선택&lt;br /&gt;&lt;br /&gt;$(&quot;#wrap &amp;gt; section&quot;) -&amp;gt; wrap 요소 안의 section 요소안의 모든 요소 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 40px; text-align: center;&quot;&gt;이전 요소 &lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소&quot;).prev()&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택한 요소 기준으로 이전에 오는 형제 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;.wrap&quot;).prev()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 40px; text-align: center;&quot;&gt;다음 요소&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 40px; text-align: center;&quot;&gt;1. $(&quot;선택 요소&quot;).next()&lt;br /&gt;&lt;br /&gt;2. $(&quot;선택 요소1 + 선택 요소2&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;1. 선택한 요소 기준으로 다음에 오는 형제 요소만 선택&lt;br /&gt;&lt;br /&gt;2. 선택한 요소 기준으로 바로 다음에 오는 선택한 요소만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;1. $(&quot;.wrap&quot;).next().next() -&amp;gt; 체이닝 형식으로도 가능&lt;br /&gt;&lt;br /&gt;2. $(&quot;.wrap + span&quot;) -&amp;gt; wrap 클래스 바로 다음에 오는 span 태그만 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 40px; text-align: center;&quot;&gt;전체 이전&lt;br /&gt;요소 선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소&quot;).prevAll()&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택 요소 기준으로 이전에 오는 모든 형제 요소를 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;.wrap&quot;).prevAll()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 40px; text-align: center;&quot;&gt;전체 다음&lt;br /&gt;요소 선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소&quot;).nextAll()&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택 요소 기준으로 다음에 오는 모든 형제 요소를 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;.wrap&quot;).nextAll()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 40px; text-align: center;&quot;&gt;전체 형제&lt;br /&gt;요소 선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소&quot;).siblings()&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택한 요소의 모든 형제 요소를 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;.wrap&quot;).siblings()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 40px; text-align: center;&quot;&gt;범위 제한&lt;br /&gt;이전 요소&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소&quot;).prevUntil&lt;br /&gt;(&quot;범위 제한 선택 요소&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택한 요소 기준으로 범위 제한 &lt;br /&gt;요소까지 전체 형(이전)요소 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;.title6&quot;).prevUntil(&quot;.maintitle&quot;) -&amp;gt; title6 이전 형제 요소 중 maintitle 까지 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 40px; text-align: center;&quot;&gt;범위 제한&lt;br /&gt;다음 요소&lt;br /&gt;선택자&lt;/td&gt;
&lt;td style=&quot;width: 35.2323%; height: 40px; text-align: center;&quot;&gt;$(&quot;선택 요소&quot;).nextUntil&lt;br /&gt;(&quot;범위 제한 선택 요소&quot;)&lt;/td&gt;
&lt;td style=&quot;width: 45.0003%; height: 40px; text-align: center;&quot;&gt;선택한 요소 기준으로 범위 제한&lt;br /&gt;요소까지 전체 동생(다음)요소 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 12.7906%; height: 18px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;width: 80.2326%; height: 18px; text-align: center;&quot; colspan=&quot;2&quot;&gt;$(&quot;.title3&quot;).nextUntil(&quot;.title7&quot;) -&amp;gt; title3 이후 형제 요소 중 title7 까지 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인 학습을 위해 작성되는 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;제가 잘못 알고 있는 점에 대한 지적 / 더 나은 방향에 대한 댓글을 환영합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참조 서적:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/59461086&quot;&gt;자바스크립트 + 제이쿼리 입문 : 정인용&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Javascript/jQuery</category>
      <category>제이쿼리 인접 관계 선택자</category>
      <author>MAENCO</author>
      <guid isPermaLink="true">https://maenco.tistory.com/97</guid>
      <comments>https://maenco.tistory.com/entry/jQuery-%EA%B8%B0%EB%B3%B8-%EC%84%A0%ED%83%9D%EC%9E%90-2-%EC%9D%B8%EC%A0%91%EA%B4%80%EA%B3%84-%EC%84%A0%ED%83%9D%EC%9E%90#entry97comment</comments>
      <pubDate>Tue, 2 Nov 2021 21:01:33 +0900</pubDate>
    </item>
    <item>
      <title>[jQuery] 기본 선택자 (1) - 직접 선택자</title>
      <link>https://maenco.tistory.com/entry/jQuery-%EA%B8%B0%EB%B3%B8-%EC%84%A0%ED%83%9D%EC%9E%90-1-%EC%A7%81%EC%A0%91-%EC%84%A0%ED%83%9D%EC%9E%90</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;선택자&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택자는 HTML 요소를 선택해서 가져오는 역할을 한다. jQuery(제이쿼리)는 이 선택자를 통하여 HTML의 요소를 보다 쉽게 선택하여 동적인 CSS, 이벤트, 애니메이션 등을 적용할 수 있다. 매우 많은 선택자가 존재하며 본글에서는 기본선택자 중에 직접 선택자에 대하여 알아보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;직접 선택자&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 340px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style6&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 10.6977%; text-align: center; height: 10px;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 13.6046%; text-align: center; height: 10px;&quot;&gt;&lt;b&gt;종류&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.3023%; text-align: center; height: 10px;&quot;&gt;&lt;b&gt;사용법&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 56.3954%; text-align: center; height: 10px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 10.6977%; height: 330px; text-align: center;&quot; rowspan=&quot;12&quot;&gt;&lt;b&gt;직접 선택자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 13.6046%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;전체 선택자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.3023%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$(&quot;*&quot;)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 56.3954%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;모든 요소를 선택&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 32px;&quot;&gt;
&lt;td style=&quot;width: 13.6046%; text-align: center; height: 32px;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%; text-align: center; height: 32px;&quot; colspan=&quot;2&quot;&gt;$(&quot;*&quot;).css(&quot;border&quot;, &quot;1px solid black&quot;);&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 13.6046%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;아이디 선택자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.3023%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$(&quot;#아이디명&quot;)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 56.3954%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;id 태그에 지정한 값을 선택&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 13.6046%; text-align: center; height: 40px;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%; text-align: center; height: 40px;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu&quot;).css(&quot;border&quot;, &quot;1px solid black&quot;);&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 13.6046%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;클래스 선택자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.3023%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;$(&quot;.클래스명&quot;)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 56.3954%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;class 태그에 지정한 값을 선택&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;width: 13.6046%; text-align: center; height: 35px;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%; text-align: center; height: 35px;&quot; colspan=&quot;2&quot;&gt;$(&quot;.category&quot;).css(&quot;border&quot;, &quot;1px solid black&quot;);&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 13.6046%; text-align: center; height: 18px;&quot;&gt;&lt;b&gt;요소 선택자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.3023%; text-align: center; height: 18px;&quot;&gt;&lt;b&gt;$(&quot;요소명&quot;)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 56.3954%; text-align: center; height: 18px;&quot;&gt;&lt;b&gt;지정한 요소명과 일치하는 요소들을 선택&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 27px;&quot;&gt;
&lt;td style=&quot;width: 13.6046%; text-align: center; height: 27px;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%; text-align: center; height: 27px;&quot; colspan=&quot;2&quot;&gt;$(&quot;h1&quot;).css(&quot;border&quot;, &quot;1px solid black&quot;);&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 13.6046%; text-align: center; height: 18px;&quot;&gt;&lt;b&gt;그룹 선택자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.3023%; text-align: center; height: 18px;&quot;&gt;&lt;b&gt;$(&quot;선택 1, 선택 2, ...&quot;)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 56.3954%; text-align: center; height: 18px;&quot;&gt;&lt;b&gt;선택 1, 선택 2 에 지정된 요소들을 한번에 선택&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 28px;&quot;&gt;
&lt;td style=&quot;width: 13.6046%; text-align: center; height: 28px;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%; text-align: center; height: 28px;&quot; colspan=&quot;2&quot;&gt;$(&quot;#menu, .category, h1&quot;).css(&quot;border&quot;, &quot;1px solid black&quot;);&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 13.6046%; text-align: center; height: 20px;&quot;&gt;&lt;b&gt;종속 선택자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.3023%; text-align: center; height: 20px;&quot;&gt;&lt;b&gt;$(&quot;p.txt_1&quot;)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;$(&quot;p#txt_1&quot;)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 56.3954%; text-align: center; height: 20px;&quot;&gt;&lt;b&gt;&amp;lt;p&amp;gt; 태그 중 class 태그가 txt_1 또는 id 태그가 txt_1 인 요소를 선택&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 58px;&quot;&gt;
&lt;td style=&quot;width: 13.6046%; text-align: center; height: 58px;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.3023%; text-align: center; height: 58px;&quot; colspan=&quot;2&quot;&gt;$(&quot;p.description&quot;).css(&quot;border&quot;, &quot;1px solid black&quot;);&lt;br /&gt;&lt;br /&gt;$(&quot;p#price&quot;).css(&quot;border&quot;, &quot;1px solid black&quot;);&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 선택자를 사용하여 내가 원하는 요소(태그)를 '직접' 선택하여 CSS 를 비롯한 이벤트, 애니메이션을 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인 학습을 위해 작성되는 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;제가 잘못 알고 있는 점에 대한 지적 / 더 나은 방향에 대한 댓글을 환영합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참조 서적:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/59461086&quot;&gt;자바스크립트 + 제이쿼리 입문 : 정인용&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Javascript/jQuery</category>
      <category>jQuery 직접 선택자</category>
      <category>제이쿼리 기본 선택자</category>
      <category>제이쿼리 직접 선택자</category>
      <author>MAENCO</author>
      <guid isPermaLink="true">https://maenco.tistory.com/96</guid>
      <comments>https://maenco.tistory.com/entry/jQuery-%EA%B8%B0%EB%B3%B8-%EC%84%A0%ED%83%9D%EC%9E%90-1-%EC%A7%81%EC%A0%91-%EC%84%A0%ED%83%9D%EC%9E%90#entry96comment</comments>
      <pubDate>Thu, 28 Oct 2021 22:13:36 +0900</pubDate>
    </item>
    <item>
      <title>[jQuery] 제이쿼리(jQuery)란</title>
      <link>https://maenco.tistory.com/entry/jQuery-%EC%A0%9C%EC%9D%B4%EC%BF%BC%EB%A6%ACjQuery%EB%9E%80</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;제이쿼리(jQuery)의 탄생&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 HTML의 Elements(요소)를 접근하는 것이 쉽지 않았다고 한다. 또한 웹브라우저마다 호환성이 떨어져 개발한 내용을 다른 브라우저에 적용하기가 어려웠다, 이에 제이쿼리가 요소의 접근을 물론 각 웹브라우저의 호환 더 나아가서는 애니메이션과 이벤트 등에도 편의 기능을 제공하며 자바스크립트에서 필수적인 존재로 자리 잡았다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(지금은 제이쿼리의 입지가 점점 더 좁아져 가는 상황이라고 한다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;제이쿼리(jQuery)의 장점&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제이쿼리가 왜 자바스크립트에서 많이 사용할 수 밖에 없었는지 장점을 통해 간단하게 알아보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. HTML DOM의 접근성과 조작성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해 각 태그들에 접근하기가 용이하다, 제이쿼리의 선택자를 사용하면 클래스, 아이디 더 나아가서는 해당 요소의 위, 아래 그리고 형제 요소에 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 웹브라우저의 호환성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제이쿼리 이전에는 각 웹브라우저의 호환성이 떨어져 개발자가 각 웹브라우저에 맞는 개발을 했어야 한다고 한다, 이에 제이쿼리는 거의 모든 웹브라우저에 적용할 수 있는 호환성을 갖춰 개발자는 제이쿼리 하나로 각 브라우저에 적용할 수 있었다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 이벤트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트의 특성상 다양한 이벤트에 응답하는데 (클릭, 입력과 같은 사용자와의 상호작용) 이때 사용자가 어떠한 행동을 할 때마다 맞춤으로 이벤트를 이용해 상호작용하여야 한다. 제이쿼리 이전에는 모든 웹브라우저가 동일한 방식으로 이벤트를 구현하지 않았는데 제이쿼리는 브라우저와 상관없이 동일한 이름으로 이벤트 기능을 제공함으로써 이벤트를 좀 더 편리하게 개발할 수 있게 되었다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. Ajax&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기식 통신을 위해 Ajax를 사용할 때 이전에는 웹브라우저마다 그에 맞게 Ajax 코드를 작성했어야 한다고 한다. 제이쿼리의 경우 이벤트와 마찬가지로 이를 동일화 시켜 좀 더 편리하게 다양한 API를 개발할 수 있게 되었다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;핵심 정리&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트의 라이브러리인 제이쿼리가 나오기 이전에는 각 웹브라우저들의 호환성이 낮아 개발자가 브라우저마다 맞춤 개발을 했어야 했으나, 제이쿼리를 통해 HTML 요소에 대한 접근, 상호작용을 위한 이벤트, API 개발을 위한 Ajax 등을 거의 모든 웹브라우저에 한 번에 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인 학습을 위해 작성되는 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;제가 잘못 알고 있는 점에 대한 지적 / 더 나은 방향에 대한 댓글을 환영합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참조 링크:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://opentutorials.org/course/53/45&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://opentutorials.org/course/53/45&lt;/a&gt;&lt;a href=&quot;https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://code.tutsplus.com/tutorials/what-is-jquery--cms-26232&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://code.tutsplus.com/tutorials/what-is-jquery--cms-26232&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참조 서적:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/59461086&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;자바스크립트 + 제이쿼리 입문 : 정인용&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Javascript/jQuery</category>
      <category>jQuery란</category>
      <category>제이쿼리란</category>
      <author>MAENCO</author>
      <guid isPermaLink="true">https://maenco.tistory.com/95</guid>
      <comments>https://maenco.tistory.com/entry/jQuery-%EC%A0%9C%EC%9D%B4%EC%BF%BC%EB%A6%ACjQuery%EB%9E%80#entry95comment</comments>
      <pubDate>Wed, 27 Oct 2021 00:11:52 +0900</pubDate>
    </item>
    <item>
      <title>[WEB] 마크업 언어(Markup Language)</title>
      <link>https://maenco.tistory.com/entry/WEB-%EB%A7%88%ED%81%AC%EC%97%85-%EC%96%B8%EC%96%B4Markup-Language</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;마크업 언어(Markup Language)란&lt;/b&gt;&lt;/blockquote&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;조금 더 풀어서 살펴보기 위해 예를 들어보자면 클라이언트가 잡지를 읽는다고 가정해보자, 이때 1-10페이지 까지는 의류, 11-20 페이지 까지는 액세서리의 내용이 들어있다고 한다면, 이 페이지들은 하나하나 마크업으로 작성이 되어서 존재하는 것이다(인터넷에서는 웹페이지) 이것을 프로그래밍 언어로 각 페이지를 요청에 맞게 방문할 수 있게 하면 그것이 바로 서비스가 된다. 즉 클라이언트가 사용하는 웹서비스는 마크업언어를 기반으로 쌓아 올린 페이지들의 묶음인 것이다.&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_1634088789464&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;TEST TEXT 1&amp;lt;/p&amp;gt;
&amp;lt;span&amp;gt;TEST TEXT 2&amp;lt;/span&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여는 태그와 닫는 태그 그리고 그 안의 내용을 합쳐서 Element라고 하는데 위의 예시에서 &amp;lt;p&amp;gt; 태그로 감싸져 있는 요소 같은 경우는 p element라고 한다.&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;b&gt;∙ 표현적 마크업 (Presentational Markup)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 워드 처리 시스템이 사용하는 마크업, 즉 문서의 형태를 표현하기 위한 마크업이다. 대표적인 예는 HTML의 style태그를 생각하면 된다, 이 외에도 메모장이나 워드의 제목 앞에 몇 칸의 공백을 넣는 등의 행위도 표현적 마크업의 일종이라고 볼 수 있다.&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;∙ 절차적 마크업 (Procedural Markup)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램이 문서를 처리하는 방법을 서술 마크업&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;∙ 기술적 마크업 (Descriptive Markup)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 많이 사용되는 마크업 언어의 종류이며 문서가 표현하는 내용을 기술한다, 대표적으로는 HTML과 마크업이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;MarkDown Language&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 MarkDown 언어도 존재한다 간략하게 알아보자면, 흔히 사용하는 .md 확장자 파일이 바로 대표적인 마크다운 언어인데, 주로 SNS의 태그 기능에 사용한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;핵심정리&lt;/b&gt;&lt;/blockquote&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;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인 학습을 위해 작성되는 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;제가 잘못 알고 있는 점에 대한 지적 / 더 나은 방향에 대한 댓글을 환영합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참조 링크:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%A7%88%ED%81%AC%EC%97%85_%EC%96%B8%EC%96%B4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.wikipedia.org/wiki/%EB%A7%88%ED%81%AC%EC%97%85_%EC%96%B8%EC%96%B4&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://blog.cordelia273.space/15&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.cordelia273.space/15&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://developerjio.tistory.com/entry/%EB%A7%88%ED%81%AC%EC%97%85-%EC%96%B8%EC%96%B4Markup-Language%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80%EC%9A%94&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developerjio.tistory.com/entry/%EB%A7%88%ED%81%AC%EC%97%85-%EC%96%B8%EC%96%B4Markup-Language%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80%EC%9A%94&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://titus94.tistory.com/2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://titus94.tistory.com/2&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>WEB</category>
      <category>markup language</category>
      <category>마크업 언어</category>
      <author>MAENCO</author>
      <guid isPermaLink="true">https://maenco.tistory.com/94</guid>
      <comments>https://maenco.tistory.com/entry/WEB-%EB%A7%88%ED%81%AC%EC%97%85-%EC%96%B8%EC%96%B4Markup-Language#entry94comment</comments>
      <pubDate>Wed, 13 Oct 2021 17:30:53 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] DTO와 VO의 차이</title>
      <link>https://maenco.tistory.com/entry/Java-DTO%EC%99%80-VO%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 위한 객체를 만들다 보면 항상 DTO와 VO를 혼용해서 쓰고 하는데, 어떤 것이 맞는 것인지 항상 헷갈렸다.&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;DTO(Data Transfer Object)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순수하게 데이터를 담아 계층 간으로 전달하는 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;163&quot; data-filename=&quot;Screen Shot 2021-10-01 at 5.52.08 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6QGYT/btrgAUXXKKr/Pr21tqQPPqSuADD6JE0ZoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6QGYT/btrgAUXXKKr/Pr21tqQPPqSuADD6JE0ZoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6QGYT/btrgAUXXKKr/Pr21tqQPPqSuADD6JE0ZoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6QGYT%2FbtrgAUXXKKr%2FPr21tqQPPqSuADD6JE0ZoK%2Fimg.png&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;163&quot; data-filename=&quot;Screen Shot 2021-10-01 at 5.52.08 PM.png&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;로직을 갖고 있지 않은 순수한 데이터 객체이며 메서드로는 getter/setter 만을 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 getter/setter 이외에 다른 로직이 필요 없는 이유를 좀 더 자세히 살펴보자면, 만약 DTO가 데이터 전달 만을 위한 객체라고 한다면 완전히 데이터 '전달' 용도로만 사용하기 때문에 getter/setter로직만이 필요하지 다른 로직이 필요하지 않은 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1633076957499&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//getter와setter 메서드 만을 가진다
public class UserDTO {
    
    private String name;
    private String id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 보내는 쪽에서는 setter를 이용하여 값을 담아 전달하고, 받는 쪽에서는 getter를 이용하여 값을 꺼내 쓰게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 setter의 경우 변조 가능성이 있기 때문에 아래와 같이 생성자로 값을 넣어주도록 하게 하면 전달하는 과정에서 변조가 불가능하다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1633077009000&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//생성자를 이용한 불변 객체
public class UserDTO {

    private String name;
    private String id;

    public UserDTO(String name, String id) {
        this.name = name;
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public String getId() {
        return id;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;VO(Value Object)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VO는 값 그 자체를 나태는 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO와 반대로 로직을 포함할 수 있으며, VO의 경우 특정 값 자체를 표현하기 때문에 불변성의 보장을 위해 생성자를 사용하여야 한다.&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;VO는 서로 다른 이름을 갖는 VO 인스턴스라도 모든 속성 값이 같다면 두 인스턴스는 같은 객체인 것이 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 모두 똑같은 자동차가 색깔만 다르다고 하더라도 이를 별개의 객체로 보는 것이 아니라 하나의 객체(자동차)로 보는것과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1633079500038&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//생성자를 이용한 불변 객체
public class CarVO {

    private final String color;

    public CarVO(String color) {
        this.color = color;
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동차의 색깔의 값을 가지고 있는 VO를 하나 만들어서 객체 두개를 아래와 같이 비교 테스트로 돌려보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1633079206041&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CarVOTest {

    @Test
    void CarVOEqualTest() {
        final String color = &quot;red&quot;;

        CarVO car1 = new CarVO(color);
        CarVO car2 = new CarVO(color);

        assertThat(car1).isEqualTo(car2);
        assertThat(car1).hasSameHashCodeAs(car2);

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;179&quot; data-filename=&quot;Screen Shot 2021-10-01 at 6.08.06 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvHMfd/btrgzIxlibN/0cQVGJRq150iVtBqmDFO11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvHMfd/btrgzIxlibN/0cQVGJRq150iVtBqmDFO11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvHMfd/btrgzIxlibN/0cQVGJRq150iVtBqmDFO11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvHMfd%2FbtrgzIxlibN%2F0cQVGJRq150iVtBqmDFO11%2Fimg.png&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;179&quot; data-filename=&quot;Screen Shot 2021-10-01 at 6.08.06 PM.png&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;이러한 문제가 있기 때문에 값만을 비교하기 위해서 equals()와 hashCode()를 오버 라이딩(재정의) 해주어야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1633079510534&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class CarVO {

    private final String color;

    public CarVO(String color) {
        this.color = color;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        CarVO carVO = (CarVO) o;
        return Objects.equals(color, carVO.color);
    }

    @Override
    public int hashCode() {
        return Objects.hash(color);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 VO는 값 그 자체를 나타내기 때문에 setter 같은 성격의 변조 가능성이 있는 메서드가 존재하면 안되며, 두 객체의 '필드 값이 같다면' 모두 같은 각체로 만드는 것이 핵심이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;DTO vs VO&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO와 VO의 차이점을 정리해보면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;384&quot; data-filename=&quot;Screen Shot 2021-10-01 at 6.26.32 PM.png&quot; width=&quot;669&quot; height=&quot;272&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjJrAC/btrgBIJC8nY/6XIQjkkVVJGeDMgza1RfL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjJrAC/btrgBIJC8nY/6XIQjkkVVJGeDMgza1RfL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjJrAC/btrgBIJC8nY/6XIQjkkVVJGeDMgza1RfL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjJrAC%2FbtrgBIJC8nY%2F6XIQjkkVVJGeDMgza1RfL0%2Fimg.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;384&quot; data-filename=&quot;Screen Shot 2021-10-01 at 6.26.32 PM.png&quot; width=&quot;669&quot; height=&quot;272&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;핵심정리&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO는 계층간의 데이터를 전송하기 위한 객체로써, getter/setter를 사용하여 보내는 사람은 setter를 사용하여 값을 담고, 받는 사람은 getter를 사용하여 값을 꺼내 쓴다. 이때 값의 변조를 막고 싶다면 생성자로 불변 객체로 만들어 버리면 된다. 또한 DTO의 특성상 데이터 전달만은 목적으로 사용하기 때문에 getter/setter를 제외한 다른 로직이 필요가 없다.&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;VO의 경우 값 그 자체를 나타내는 객체로써, 핵심은 필드 값이 같다면 두 객체를 같은 객체로 본다는 것이다. 이때 당연하게도 주소값을 비교하는 경우의 문제점을 위해서 hashcode()와 equals()를 재정의하여 필드 값이 같다면 같은 객체로 인식될 수 있도록 해주어야 한다.&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;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인 학습을 위해 작성되는 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;제가 잘못 알고 있는 점에 대한 지적 / 더 나은 방향에 대한 댓글을 환영합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참조 링크:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=z5fUkck_RZM&amp;amp;ab_channel=%EC%9A%B0%EC%95%84%ED%95%9CTech&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.youtube.com/watch?v=z5fUkck_RZM&amp;amp;ab_channel=%EC%9A%B0%EC%95%84%ED%95%9CTech&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://velog.io/@taehee-kim-dev/DTO-vs-VO&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@taehee-kim-dev/DTO-vs-VO&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-06-11-value-object/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-06-11-value-object/&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Java</category>
      <category>Data Transfer Object</category>
      <category>dto</category>
      <category>DTO와 VO의 차이</category>
      <category>Value Object</category>
      <category>vo</category>
      <author>MAENCO</author>
      <guid isPermaLink="true">https://maenco.tistory.com/93</guid>
      <comments>https://maenco.tistory.com/entry/Java-DTO%EC%99%80-VO%EC%9D%98-%EC%B0%A8%EC%9D%B4#entry93comment</comments>
      <pubDate>Fri, 1 Oct 2021 18:34:03 +0900</pubDate>
    </item>
    <item>
      <title>[Git] Git의 이해</title>
      <link>https://maenco.tistory.com/entry/Git-Git%EC%9D%98-%EC%9D%B4%ED%95%B4</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Git이란&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 할 때 꼭 필요한 것 중에 하나가 바로 버전을 관리하는 시스템인데, Git이 바로 이 작업을 도와주는 버전 관리 시스템 중 하나이다, 마치 게임의 세이브 파일처럼 코드의 작업 중간중간에 세이브 포인트를 만들어 이 포인트들로 자유롭게 옮겨 갈 수 있다 (단순히 ctrl+z를 하는 것과는 다르다는 점)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;230&quot; data-origin-height=&quot;204&quot; data-filename=&quot;Screen Shot 2021-09-25 at 7.51.06 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQW9dG/btrf1hrQris/w8JmcO7nzQ6HwLONKan0w0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQW9dG/btrf1hrQris/w8JmcO7nzQ6HwLONKan0w0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQW9dG/btrf1hrQris/w8JmcO7nzQ6HwLONKan0w0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQW9dG%2Fbtrf1hrQris%2Fw8JmcO7nzQ6HwLONKan0w0%2Fimg.png&quot; data-origin-width=&quot;230&quot; data-origin-height=&quot;204&quot; data-filename=&quot;Screen Shot 2021-09-25 at 7.51.06 PM.png&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;bull; SVN vs Git&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 SVN이라는 버전관리 시스템을 사용하기도 했으나 현재는 대부분 Git을 사용하는 추세라고 한다, SVN의 경우 중앙 서버에 소스코드와 히스토리를 저장하여야 하기 때문에 서버에 장애가 발생하게 되면 접근이 불가능하고 한 곳에서 관리되다 보니 리스크 또한 비교적 컸다, Git의 경우에는 로컬 저장소에 소스코드를 분산해서 저장하기 때문에 이러한 리스크의 문제점을 해결할 수 있고, 또한 서버를 통하지 않아 SVN과 비교해서 훨씬 빠르다는 장점이 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;CLI vs GUI&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git을 사용하여 버전을 관리하는데는 두 가지 방법이 있다, Command Line Interface(CLI)와 Graphic User Interface(GUI)로 구분되는데, 이 둘 중 맞는 방법은 없으며 상황 혹은 기호에 따라 사용하면 된다.&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;bull;CLI&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terminal(윈도우에서는 Git Bash)을 이용하여 직접 명령어를 입력하여 Git 작업을 실행하는 방법이다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;557&quot; data-origin-height=&quot;172&quot; data-filename=&quot;Screen Shot 2021-09-25 at 8.19.24 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi6CUp/btrfVttxEv1/SkVGxjP5F45D22x8SE7MI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi6CUp/btrfVttxEv1/SkVGxjP5F45D22x8SE7MI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi6CUp/btrfVttxEv1/SkVGxjP5F45D22x8SE7MI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi6CUp%2FbtrfVttxEv1%2FSkVGxjP5F45D22x8SE7MI1%2Fimg.png&quot; data-origin-width=&quot;557&quot; data-origin-height=&quot;172&quot; data-filename=&quot;Screen Shot 2021-09-25 at 8.19.24 PM.png&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;bull;GUI&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 통하여 버튼 등을 클릭하여 보다 직관적으로 Git 작업을 실행하는 방법이다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;133&quot; data-filename=&quot;Screen Shot 2021-09-25 at 8.20.34 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ItPWh/btrfU4tZMzR/Vy2PmQ7SziuNSKPh9fuDAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ItPWh/btrfU4tZMzR/Vy2PmQ7SziuNSKPh9fuDAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ItPWh/btrfU4tZMzR/Vy2PmQ7SziuNSKPh9fuDAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FItPWh%2FbtrfU4tZMzR%2FVy2PmQ7SziuNSKPh9fuDAK%2Fimg.png&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;133&quot; data-filename=&quot;Screen Shot 2021-09-25 at 8.20.34 PM.png&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Git Cloud&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;188&quot; data-filename=&quot;Screen Shot 2021-09-25 at 8.32.54 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yYnLy/btrfTLuLyvG/CglAQvHRjRkCuwkKt0z6z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yYnLy/btrfTLuLyvG/CglAQvHRjRkCuwkKt0z6z1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yYnLy/btrfTLuLyvG/CglAQvHRjRkCuwkKt0z6z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyYnLy%2FbtrfTLuLyvG%2FCglAQvHRjRkCuwkKt0z6z1%2Fimg.png&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;188&quot; data-filename=&quot;Screen Shot 2021-09-25 at 8.32.54 PM.png&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;만약 다수의 사람들이 '협업' 으로 코드를 개발한다면, 로컬 저장소에 분산되어 저장한다고 하더라도 결과물을 만들기 위해서는 한 곳에서 각자의 결과물들을 합쳐야 할 것이다, 이런 작업을 위한 서버를 지원하는 GitHub, Bitbuket, Gitlab 등등이 있다. 이때 주의해야 할 점은 나의 경우 GitHub를 주로 사용하는데 처음에는 Git과 GitHub가 비슷한 개념인 줄 알았다, 하지만 GitHub와 같은 Git Cloud의 경우 서버를 제공하는 것일 뿐 Git과는 다른 개념이라는 것&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;이러한 Git Cloud를 이용하여 로컬저장소에 분산되어 저장되어 있던 결과물들을 서버에도 저장할 수도 있고, 이를 합칠 수도 있다. 더 나아가서는 오픈소스 같은 프로젝트를 통째로 가져와 사용할 수도 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;핵심정리&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 진행 중에 세이브 포인트를 저장하는 것처럼, 개발을 하는 작업 중에도 중간 중간에 되돌아갈 수 있는 세이브 포인트를 만드는 작업이 바로 버전 관리의 핵심이다, 이러한 작업의 결과물들을 Git Cloud에 저장하여 공유할 수도, 결과물을 합칠 수도 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git은 직접 명령어를 입력하여 실행하는 CLI와, 프로그램을 통하여 버튼 등을 클릭하는 GUI의 방식으로 나뉜다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인 학습을 위해 작성되는 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;제가 잘못 알고 있는 점에 대한 지적 / 더 나은 방향에 대한 댓글을 환영합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CM/Git</category>
      <category>Git</category>
      <author>MAENCO</author>
      <guid isPermaLink="true">https://maenco.tistory.com/92</guid>
      <comments>https://maenco.tistory.com/entry/Git-Git%EC%9D%98-%EC%9D%B4%ED%95%B4#entry92comment</comments>
      <pubDate>Sat, 25 Sep 2021 20:35:44 +0900</pubDate>
    </item>
    <item>
      <title>[Spring MVC] MultipartFile (File Upload)</title>
      <link>https://maenco.tistory.com/entry/Spring-MVC-MultipartFile-File-Upload</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(참고: Spring Boot와 Thymeleaf를 기준으로 작성된 글입니다)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;HTML Form 전송 방식&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML Form을 통한 파일 업로드를 이해하기 위해서는 두 가지 방식의 차이를 알아야 한다&lt;/p&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;&amp;bull; application/x-www-form-urlencoded&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1632363084637&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;form action=&quot;/save&quot; method=&quot;post&quot;&amp;gt;
    &amp;lt;input type=&quot;text&quot; name=&quot;usernmae&quot;/&amp;gt; //kim
    &amp;lt;input type=&quot;text&quot; name=&quot;age&quot;/&amp;gt; //20
    &amp;lt;button type=&quot;submit&quot;&amp;gt;전송&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application/x-www-form-urlencoded 방식은 HTML 폼 데이터를 서버로 전송하는 가장 기본적인 방법이다, Form 태그에 별도의 enctype옵션이 없다면 웹 브라우저는 요청 HTTP의 헤더에 다음과 같이 추가하여, 폼에 입력한 항목을 HTTP 바디와 함께 전송한다&lt;/p&gt;
&lt;pre id=&quot;code_1632363014629&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST/save HTTP/1.1
Host:localhost:8080
Content-Type:application/x-www-form-urlencoded

username=kim&amp;amp;age=20&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;size18&quot;&gt;&lt;b&gt;&amp;bull; multipart/form-data&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일과 함께 항목을 같이 전송하기 위하여 HTTP는 multipart/form-data라는 전송 방식을 제공한다, multipart라는 이름처럼 다른 종류의 여러 파일과 폼의 내용을 '함께' 전송할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1632363226763&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;form action=&quot;/save&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&amp;gt;
    &amp;lt;input type=&quot;text&quot; name=&quot;usernmae&quot;/&amp;gt; //kim
    &amp;lt;input type=&quot;text&quot; name=&quot;age&quot;/&amp;gt; //20
    &amp;lt;input type=&quot;file&quot; name=&quot;file&quot;/&amp;gt; //example.png
    &amp;lt;button type=&quot;submit&quot;&amp;gt;전송&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시와 같이 enctype을 multipart/form-data로 지정하게 되면 여러 개의 파트로 나누어 각각의 전송 항목을 구분한다&lt;/p&gt;
&lt;pre id=&quot;code_1632363534258&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST/save HTTP/1.1
Host:localhost:8080
Content-Type:multipart/form-data,boundary=------XXX

-------XXX
Content-Disposition: form-data; name=&quot;username&quot;

kim
-------XXX
Content-Disposition: form-data; name=&quot;age&quot;

20
-------XXX
Content-Disposition: form-dta; name=&quot;file&quot;; filename=&quot;example.png&quot;
Content-Type: image/png

(바이너리 코드....)
-------XXX--&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 multipart/form-data는 각각의 항목을 구분해서, 한 번에 전송하는 원리이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;MultipartFile&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 Form 통해 전송해온 파일들을 편리하게 받을 수 있게 스프링에서는 MultipartFille이라는 인터페이스를 지원한다&lt;/p&gt;
&lt;pre id=&quot;code_1632363875938&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
public class FileController {
    
    @PostMapping(&quot;/save&quot;)
    public String saveFile(@RequestParam MultipartFile file) {
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시처럼 @RequestParam을 업로드하는 HTML Form의 name에 맞추어 적용하면 되며, @ModelAttribute에서도 동일하게 사용할 수 있다, MultipartFile인터페이스의 주요 메서드는 아래와 같다&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;bull; file.getOriginalFileName() : 업로드 파일 명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; file.transferTo(...) : 파일 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;UUID Filename + ExtendName&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 클라이언트가 보내는 이미지 파일은 서버에서 UUID 등의 구분자를 부여하여 관리하게 된다, 그 이유는 만약 클라이언트 A가 올린 파일과 클라이언트 B가 올린 파일의 내용은 다르지만 이름이 같은 경우에 덮어쓰기가 되기 때문이다, 또한 확장자 파일명을 따로 붙여줘 더욱 편리하게 파일들을 관리할 수 있도록 해주는 것이 좋다고 한다&lt;/p&gt;
&lt;pre id=&quot;code_1632364411321&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//메서드를 따로 만들어 공용으로 사용

private String extractExt(String originalFilename) {
    int pos = originalFilename.lastIndexOf(&quot;.&quot;);
    return originalFilename.substring(pos + 1);
}

private String createStoreFileName(String originalFilename) {
    String ext = extractExt(originalFilename);
    String uuid = UUID.randomUUID().toString();
    return uuid + &quot;.&quot; + ext;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;핵심정리&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 Form 통하여 파일을 보내는 경우, 보통 폼에 입력한 항목들을 같이 전송하기 때문에 MultipartForm을 사용하여 보내게 된다, 말 그대로 여러 파트로 구분하여 보내어지는데 스프링에서는 서버 측에서 편리하게 받을 수 있도록 MultipartFile이라는 인터페이스를 지원한다&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;서버에서 받게 된 파일은 UUID 등의 구분자를 통하여 중복되어 덮어 써지는 일을 방지하고 확장자명까지 추출해내어 추후에도 파일을 편리하게 관리하는 것이 좋다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인 학습을 위해 작성되는 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;제가 잘못 알고 있는 점에 대한 지적 / 더 나은 방향에 대한 댓글을 환영합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참조 링크:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Spring/Spring MVC</category>
      <category>application/x-www-form-urlencoded</category>
      <category>multipart/form-data</category>
      <category>multipartfile</category>
      <author>MAENCO</author>
      <guid isPermaLink="true">https://maenco.tistory.com/91</guid>
      <comments>https://maenco.tistory.com/entry/Spring-MVC-MultipartFile-File-Upload#entry91comment</comments>
      <pubDate>Thu, 23 Sep 2021 11:39:21 +0900</pubDate>
    </item>
    <item>
      <title>[Spring MVC] TypeConverter &amp;amp; Formatter</title>
      <link>https://maenco.tistory.com/entry/Spring-MVC-TypeConverter-Formatter</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(참고: Spring Boot, Thymeleaf, Gradle을 기준으로 작성된 글입니다.)&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;웹 애플리케이션의 경우 클라이언트가 보내는 데이터와 서버에서 다시 반환하는 데이터 간의 타입이 불일치하는 경우가 빈번하게 발생한다, 예를 들어 String으로 보낸 데이터를(HTTP 요청 파라미터의 경우 모두 문자 타입으로 처리됨) int타입으로 변환해야 한다던지, 보낸 데이터를 개발자가 지정한 객체에 담아서 받는 상황이 대표적인 경우다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Converter Interface&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 변환 과정을 편리하게 사용할 수 있도록 스프링은 기본적으로 컨버터를 적용시키지만 개발자가 직접 원하는 타입으로 변환을 위하여 컨버터 인터페이스의 구현체를 만들 수도 있다&lt;/p&gt;
&lt;pre id=&quot;code_1632229227127&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.springframework.core.convert.converter;

public interface Converter&amp;lt;S, T&amp;gt; {
    T convert(S source);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 컨버터 인터페이스는 스프링에서 제공하는 확장 가능한 컨버터 인터페이스다, 즉 이 컨버터 인터페이스를 구현해서 등록하게 되면 추가적인 타입 변환을 할 수 있는 것이다 (과거에는 PropertyEditor를 이용해서 타입을 변환하였으나, 동시성 문제 때문에 현재는 Converter를 대부분 사용하고 있다고 한다)&lt;/p&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;&amp;bull; Integer -&amp;gt; String&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨버터 인터페이스를 구현하기 위한 방법은 굉장히 단순하다 S -&amp;gt; T 로 변환되는 개념이기 때문에 아래와 같이 작성하게 되면 숫자를 문자로 변환하는 타입 컨버터가 된다(단순히 동작 원리의 이해를 위한 것이지, 아래와 같이 단순하게 숫자 -&amp;gt; 문자의 경우 기본 컨버터가 지원한다는 점 참고)&lt;/p&gt;
&lt;pre id=&quot;code_1632229471326&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class IntegerToStringConverter implements Converter&amp;lt;Integer, String&amp;gt; {
    @Override
    public String convert(Integer source) {
        log.info(&quot;convert source={}&quot;, source);
        return String.valueOf(source);
    }
}&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;size18&quot;&gt;&lt;b&gt;&amp;bull; String -&amp;gt; Object&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 숫자를 문자로 변환하는 것을 넘어서 문자를 내가 원하는 객체의 형식으로 컨버팅 할 수도 있다&lt;/p&gt;
&lt;pre id=&quot;code_1632229834294&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@EqualsAndHashCode
public class IpPort {

    private String ip;
    private int port;
    
    public IpPort(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1632229895748&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
public class StringToIpPortConverter implements Converter&amp;lt;String, IpPort&amp;gt; {

    @Override
    public IpPort convert(String source) {
        log.info(&quot;convert source={}&quot;, source);
        String[] split = source.split(&quot;:&quot;);
        String ip = split[0];
        int port = Integer.parseInt(split[1]);
        
        return new IpPort(ip, port);
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 작성할 경우 &quot;127.0.0.1:8080&quot;이라는 문자가 들어왔을 때 IpPort객체의 ip에는 : 이전의 ip가, port에는 : 이후의 포트번호가 들어가게 된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;ConversionService&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허나 이렇게 일일이 타입 컨버터를 불러들여 적용하기에는 매우 불편하다, 그래서 스프링은 개별 컨버터를 모아 두고 그것들을 묶어서 편리하게 사용할 수 있는 기능을 제공하는데 바로 ConversionService이다&lt;/p&gt;
&lt;pre id=&quot;code_1632230128762&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//실제 ConversionService 코드
public interface ConversionService {
    boolean canConvert(@Nullable Class&amp;lt;?&amp;gt; sourceType, Class&amp;lt;?&amp;gt; targetType);
    boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
    &amp;lt;T&amp;gt; T convert(@Nullable Object source, Class&amp;lt;T&amp;gt; targetType);
    Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType,TypeDescriptor targetType);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ConversionService 인터페이스의 경우 단순히 컨버팅의 가능여부와 컨버팅 기능을 제공한다&lt;/p&gt;
&lt;pre id=&quot;code_1632230347715&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class WebConfig implements WebMvcConfigurer {
  
    @Override
    public void addFormatters(FormatterRegistry registry) {
         registry.addConverter(new IntegerToStringConverter());
         registry.addConverter(new StringToIpPortConverter());
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하기 위해서는 Configuration 클래스를 하나 만들어 위와 같이 만든 컨버터를 직접 등록하면 스프링이 기본적으로 제공하는 컨버터보다 높은 우선순위를 가지고 있기 때문에 해당 컨버터들이 작동하게 된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;View Template&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰 템플릿에 렌더링 하는 과정에 컨버터를 사용할 수도 있다, 타임리프의 경우 ${...}의 변수 표현식을 사용하여 내용을 렌더링하는데 컨버팅 서비스를 적용하기 위해서는&amp;nbsp; ${{...}}로 괄호를 하나 더 붙여 사용하게 된다&lt;/p&gt;
&lt;pre id=&quot;code_1632230957007&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt; 

     &amp;lt;span th:text=&quot;${ipPort}&quot;&amp;gt;&amp;lt;/span&amp;gt;
     &amp;lt;span th:text=&quot;${{ipPort}}&quot;&amp;gt;&amp;lt;/span&amp;gt;
     
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&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;컨버전 서비스 적용 : ${{ ... }}&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;th:field를 사용한다면 괄호의 개수와 상관없이 컨버전 서비스가 적용된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Formatter&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨버터의 경우 입력과 출력 타입에 제한이 없는 범용적인 타입 변환 기능을 제공한다, 하지만 대부분의 경우 문자를 다른 타입으로 변환하거나 다른 타입을 문자로 변환하는 상황이 대부분이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex. 1000(숫자) -&amp;gt; &quot;1,000&quot;(문자)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex. &quot;2021-01-01 01:01:11&quot;(문자) -&amp;gt; 2021-01-01 01:01:11(날짜)&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;bull; Converter : 범용적인 사용 (객체 -&amp;gt; 객체)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; Fomatter : 문자에 특화 (객체 -&amp;gt; 문자, 문자 -&amp;gt; 객체) + 현지화(Locale)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1632231702718&quot; class=&quot;java&quot; style=&quot;display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Fomatter 인터페이스 실제 코드
public interface Printer&amp;lt;T&amp;gt; {
    String print(T object, Locale locale);
}

public interface Parser&amp;lt;T&amp;gt; {
    T parse(String text, Locale locale) throws ParseException;
}

public interface Formatter&amp;lt;T&amp;gt; extends Printer&amp;lt;T&amp;gt;, Parser&amp;lt;T&amp;gt; {
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주목해야 할 점이 현지화라는 정보를 같이 넣을 수 있다는 것인데, 예를 들어 숫자를 표기할 때 1,000와 같은 표기법 혹은 날짜의 표기법이 지역마다 다를 수 있다 이를 Locale의 정보를 입력하여 매우 간편하게 특정 포맷으로 변환하는 것은 물론 1000(숫자) -&amp;gt; &quot;1,000&quot;(문자)와 같은 변환은 사실 생각보다 구현하는 것이 복잡하고 또 번거로운 작업이 수반되기 때문에 이러한 작업을 최소화할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1632231761424&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
public class MyNumberFormatter implements Formatter&amp;lt;Number&amp;gt; {

     //&quot;1,000&quot; -&amp;gt; 1000
     @Override
     public Number parse(String text, Locale locale) throws ParseException {
          log.info(&quot;text={}, locale={}&quot;, text, locale);
          NumberFormat format = NumberFormat.getInstance(locale);
          return format.parse(text);
     }
     
     //1000 -&amp;gt; &quot;1,000&quot;     
     @Override
     public String print(Number object, Locale locale) {
          log.info(&quot;object={}, locale={}&quot;, object, locale);
          return NumberFormat.getInstance(locale).format(object);
     }
     
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포맷터의 parse는 문자를 숫자로 변환하고, print의 경우 객체를 문자로 변환한다&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;포맷터 또한 Configuration 클래스에 등록하여 사용하면 된다&lt;/p&gt;
&lt;pre id=&quot;code_1632232014461&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class WebConfig implements WebMvcConfigurer {

     @Override
     public void addFormatters(FormatterRegistry registry) {
          registry.addFormatter(new MyNumberFormatter());
     }
     
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;@Formatter&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링은 자바에서 기본으로 제공하는 타입들에 대해 수 많은 포맷터를 기본으로 제공한다, 하지만 포맷터의 경우 형식이 지정되어 있기 때문에 객체의 각 필드마다 다른 형식의 포맷을 지정하기는 어렵다, 이를 해결하기 위해서 스프링은 애노테이션 기반의 포맷터를 제공한다, 본글에서는 기본으로 제공하는 @NumberFormat과 @DateTimeFormat을 살펴보고자 한다&lt;/p&gt;
&lt;pre id=&quot;code_1632232226224&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Form {
     
     @NumberFormat(pattern = &quot;###,###&quot;)
     private Integer number;
     
     @DateTimeFormat(pattern = &quot;yyyy-MM-dd HH:mm:ss&quot;)
     private LocalDateTime localDateTime;
     
}&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;JSON과 컨버팅&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 컨버터(HttpMessageConverter)의 경우 컨버전 서비스가 적용되지 않는다, 특히 객체를 JSON으로 변환할 때 메시지 컨버터를 사용하는데 메시지의 컨버터의 역할을 생각해보자면 HTTP 메시지 바디의 내용을 객체로 변환하거나 그 반대의 경우를 수행하는 게 제 기능이다. 예를 들어 JSON으로 변환하는 경우 메시지 컨버터는 내부에서 Jackson과 같은 라이브러리를 사용한다 즉 이 과정에서 변환하는 과정은 해당 라이브러리가 담당하는 것이지&amp;nbsp; 컨버전 서비스와는 아무런 관계가 없다는 것이다, 그러하여 만약 이와 같은 작업에 포맷터를 적용하고 싶다면 해당 라이브러리가 제공하는 설정을 통해서 포맷을 설정하여야 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;핵심정리&lt;/b&gt;&lt;/blockquote&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;포맷터의 경우 특별한 컨버터라고 이해하면 쉬운데, 예를 들어 1000이라는 숫자를 &quot;1,000&quot;와 같이 특정한 포맷으로 바꾸고 싶을 때 편리하게 사용할 수 있다, 여기에 Locale이라는 데이터를 더하여 해당 지역에서 사용하는 포맷을 적용시킬 수 있다. 이러한 포맷터의 경우 기본적으로 지정한 형식에 따르기 때문에 각각의 필드에 다른 형식을 적용하기 어려운데 이를 해결하기 위하여 스프링은 애노테이션으로 된 포맷터를 제공하여 각각의 필드에 패턴을 지정하여 해당 포맷으로의 변환을 가능케 해준다&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;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;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인 학습을 위해 작성되는 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;제가 잘못 알고 있는 점에 대한 지적 / 더 나은 방향에 대한 댓글을 환영합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참조 링크:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Spring MVC</category>
      <category>ConversionService</category>
      <category>formatter</category>
      <category>Spring TypeConverter</category>
      <category>TypeConverter</category>
      <author>MAENCO</author>
      <guid isPermaLink="true">https://maenco.tistory.com/90</guid>
      <comments>https://maenco.tistory.com/entry/Spring-MVC-TypeConverter-Formatter#entry90comment</comments>
      <pubDate>Tue, 21 Sep 2021 22:59:34 +0900</pubDate>
    </item>
    <item>
      <title>[Spring MVC] API Error (API 오류 처리)</title>
      <link>https://maenco.tistory.com/entry/Spring-MVC-API-Error-API-%EC%98%A4%EB%A5%98-%EC%B2%98%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(참고: Spring Boot를 기준으로 작성된 글입니다)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;HTML 페이지 vs API 오류&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 HTML 웹페이지를 반환하였던 오류의 경우에는 스프링 부트가 제공하는 BasicErrorController로 충분하였다, 404 혹은 4xx의 오류코드만 확인하여 해당 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;하지만 API의 경우 이것보다 훨씬 세밀하고 복잡한 상황을 마주한다, 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;예를 들어 회원관리에서만 발생하는 오류와, 상품관리에서만 발생하는 오류를 각각 묶어서 처리하고 싶다면 기존의 단순히 HTML을 반환하는 오류처리와는 다르게 각각의 컨트롤러 혹은 예외마다 서로 다른 응답 결과를 출력해야 하는 상황이 발생하는 것이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;HandlerExceptionResolver의 기본 흐름과 목적(Exception Resolver)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 다양한 결과물을 출력하기 위하여 스프링은 HandlerExceptionResolver라는 것을 제공한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Exception Resolver가 어떻게 작동하는지 흐름도를 살펴보자 (인터셉터가 적용되어 있는것 주의)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;313&quot; data-filename=&quot;Screen Shot 2021-09-17 at 3.44.45 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boa4Ry/btrfnWhBWAi/GvB5tH6MLK75uyFMBbkvg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boa4Ry/btrfnWhBWAi/GvB5tH6MLK75uyFMBbkvg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boa4Ry/btrfnWhBWAi/GvB5tH6MLK75uyFMBbkvg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fboa4Ry%2FbtrfnWhBWAi%2FGvB5tH6MLK75uyFMBbkvg1%2Fimg.png&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;313&quot; data-filename=&quot;Screen Shot 2021-09-17 at 3.44.45 PM.png&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;b&gt;1.&lt;/b&gt; preHandle을 호출한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2.&lt;/b&gt; 핸들러(컨트롤러)를 호출한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.&lt;/b&gt; 예외가 발생한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4.&lt;/b&gt; postHandle은 호출되지 않고 afterCompletion이 되어진다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;321&quot; data-filename=&quot;Screen Shot 2021-09-17 at 3.48.02 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eArwnw/btrfm2wpH7d/Dk3gw0S1h8KNLgKyF6frVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eArwnw/btrfm2wpH7d/Dk3gw0S1h8KNLgKyF6frVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eArwnw/btrfm2wpH7d/Dk3gw0S1h8KNLgKyF6frVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeArwnw%2Fbtrfm2wpH7d%2FDk3gw0S1h8KNLgKyF6frVk%2Fimg.png&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;321&quot; data-filename=&quot;Screen Shot 2021-09-17 at 3.48.02 PM.png&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;b&gt;1.&lt;/b&gt; preHandle을 호출한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2.&lt;/b&gt; 핸들러(컨트롤러)를 호출한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.&lt;/b&gt; 예외가 발생한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4.&lt;/b&gt; postHandle을 여전히 호출되지 않고 ExceptionResolver에 예외를 처리할 수 있는지 확인한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5.&lt;/b&gt; afterCompletion을 호출한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;6.&lt;/b&gt; 만약 ExceptionResolver에서 처리할 수 있다면 ModelAndView를 반환한다&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼핏 보면 큰 차이가 없을 것 같지만, 막상 활용을 하게 된다면 큰 차이가 있다. 특히 API의 오류인 경우 굉장히 다양한 형식으로 오류 메시지를 반환하거나 코드를 반환하여야 하는데, 기존의(ExceptionResolver 적용 전) 경우 결국 예외가 터지게 되면 WAS로 흘러들어 가 에러 페이지를 호출하는 과정을 겪는 반면에, ExceptionResolver를 적용하게 되면 이 예외를 잡아 해결하고 정상적인 흐름처럼 클라이언트에게 반환할 수 있다&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;즉 ExceptionResolver를 적용하는 핵심은 try ~ catch를 하듯 Exception(예외)이 터지게 되면 이를 해결하여(Resolver) 정상적인 흐름처럼 변경하는 것이 주된 목적이다&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;ExceptionResolver가 반환하는 값에 따른 동작은 아래와 같다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 빈(Empty) ModelAndView&lt;/b&gt; : 빈 모델 뷰를 반환하면 뷰를 렌더링 하지 않고 정상 흐름처럼 서블릿에게 리턴된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. ModelAndView 지정&lt;/b&gt; : 모델 뷰에 model, view 등의 정보를 지정해서 반환하게 되면 뷰를 렌더링 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. null&lt;/b&gt; : null을 반환하게 되면, 다음 ExceptionResolver를 찾아서 실행한다, 만약 없다면 예외 처리가 안되고 기존처럼 예외를 서블릿 밖으로 즉 WAS로 던지게 된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;ExceptionResolver의 종류와 우선순위&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ExceptionResolver를 찾지 못하면 다음 Resolver를 검색하는데 이 과정에서 우선순위가 존재하며 아래와 같다&lt;/p&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;1. ExceptionHandlerExceptionResolver (중요)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@ExceptionResolver를 처리한다 API 예외 처리의 경우 &lt;u&gt;대부분 이 기능으로 해결한다&lt;/u&gt;, 위에서 설명한 ExceptionResolver의 경우 모델 뷰를 반환하는데 이는 API 응답에 필요가 없을뿐더러, API의 JSON형식 응답을 위해서 매우 번거로운 작업이 필요하다. 이런 모든 문제를 해결하는 것이 바로 @ExceptionResolver이다&lt;/p&gt;
&lt;pre id=&quot;code_1631863728841&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//예외 발생 시 API응답에 사용할 객체
@Data
@AllArgsConstructor
public class ErrorResult {
    private String code;
    private String message;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1631863792417&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
    return new ErrorResult(&quot;BAD&quot;, e.getMessage());
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시처럼 아주 간단하게 애노테이션만으로 예외가 발생하였을 때 해당 예외를 캐치하여 API 응답에 사용한 객체를 반환한다&lt;/p&gt;
&lt;pre id=&quot;code_1631863915053&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;code&quot;: &quot;BAD&quot;,
    &quot;message&quot;: (생략)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 @ExceptionHandler의 예외를 지정하지 않으면 해당 메서드 파라미터 예외를 사용하게 되는데 이럴 경우 ResponseEntity를 사용하여 응답 코드를 동적으로 변경할 수 있다 (아래에서 살펴볼 ResponseStatus는 동적 변경 불가능)&lt;/p&gt;
&lt;pre id=&quot;code_1631864333992&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//사용자가 직접 만든 익셉션
@Slf4j
public class ExampleExeption extends RuntimeException{

    public ExampleExeption() {
        super();
    }

    public ExampleExeption(String message) {
        super(message);
    }

    public ExampleExeption(String message, Throwable cause) {
        super(message, cause);
    }

    public ExampleExeption(Throwable cause) {
        super(cause);
    }

    protected ExampleExeption(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1631864282293&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ExceptionHandler
public ResponseEntity&amp;lt;ErrorResult&amp;gt; exampleExHandle(ExampleException e) {}&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;bull; @ReponseBody&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; HttpEntity&amp;lt;B&amp;gt;, ResponseEntity&amp;lt;B&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; String&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; View&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; Map, Model&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; @ModelAttribute&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;u&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler-args&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann- exceptionhandler-args&lt;/a&gt;&lt;/u&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;bull; @ExceptionHnalder의 우선순위&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@ExceptionHandler 또한 예외처리에서 우선순위가 존재한다&lt;/p&gt;
&lt;pre id=&quot;code_1631864447521&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ExceptionHandler(부모예외.class) 
public String 부모예외처리()(부모예외 e) {}

@ExceptionHandler(자식예외.class) 
public String 자식예외처리()(자식예외 e) {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 클래스와 자식 클래스가 같이 있다고 한다면 부모 클래스의 예외 처리 시 자식 클래스 또한 같이 예외 처리가 된다, 이때 스프링에서 항상 디테일한 것이 우선순위를 가지듯이 자식 클래스와 같은 더 디테일한 예외조건이 있다면 해당 조건이 적용된다, 이를 활용한다면 범용적인 예외에서 디테일한 예외로 범위를 지정할 수 있는 즉 다양한 예외를 한 번에 처리할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1631864808449&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ExceptionHandler({AException.class, BException.class})
public String ex(Exception e) {
    log.info(&quot;exception e&quot;, e);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. ReponseStatusExceptionResolver&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 상태 코드를 지정할 수 있다 (ex. @ResponseStatus(value = HttpStatus.NOT_FOUND)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 사용하는 이유는 라이브러리를 활용하는 경우 개발자가 직접 예외 코드를 변경할 수 없기 때문에 이를 적용할 때 사용하게 된다, 예시와 함께 살펴보자&lt;/p&gt;
&lt;pre id=&quot;code_1631862320394&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = &quot;잘못된 요청 오류&quot;) 
public class BadRequestException extends RuntimeException {
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1631862522532&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//실제 ResponseStatusExceptionResolver 코드
protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response)
    throws IOException {

    if (!StringUtils.hasLength(reason)) {
        response.sendError(statusCode);
    } else {
        String resolvedReason = (this.messageSource != null ?
        this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :reason);
        response.sendError(statusCode, resolvedReason);
    }
    return new ModelAndView();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ResponseStatusExceptionResolver가 작동하는 코드를 확인해보면 결국 서블릿의 response.sendError(statusCode, resolvedReson)을 사용하기 때문에 sendError(400)을 호출하게 되면 WAS에서 오류 페이지를 요청하게 된다&lt;/p&gt;
&lt;pre id=&quot;code_1631862655688&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/api/responsereoslver&quot;)
public String responseStatus() {
    throw new BadRequestException();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위와 같이 API 오류에 반환하게 된다면 아래와 같이 출력된다&lt;/p&gt;
&lt;pre id=&quot;code_1631862722668&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;status&quot;: 400,     
    &quot;error&quot;: &quot;Bad Request&quot;,
    &quot;exception&quot;: &quot;hello.exception.exception.BadRequestException&quot;, 
    &quot;message&quot;: &quot;잘못된 요청 오류&quot;,\
    &quot;path&quot;: &quot;/api/responsereoslver&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 반환되는 reason의 경우 굉장히 재밌는 기능을 제공하는데, 바로 스프링 메시지를 활용할 수 있다는 것이다, 예를 들어 반환되는 reason에 error.badRequest와 같이 작성한 후 스프링 메시지 소스(messages.properties)에 등록하면 해당 내용이 출력된다&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_1631865277733&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
    return new ErrorResult(&quot;BAD&quot;, e.getMessage());
}&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;size18&quot;&gt;&lt;b&gt;3. DefaultHandlerExceptionResolver&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 우선순위로 호출되는 DefaultHandlerExceptionResolver는 스프링 내부의 기본 예외 처리를 한다, 대표적으로 파라미터 바인딩 시점에 타입이 맞지 않은 경우 TypeMismatchException이 발생하는데, 이런 경우 예외가 발생하였기 때문에 그냥 두게 되면 서블릿 컨테이너까지 올라가게 되고 결과적으로는 서버 내부에서 발생한 오류 즉 500 에러코드가 발생한다, 하지만 타입 미스매치 같은 오류는 클라이언트가 잘못 보낸 것이지 서버상의 오류는 아니다&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;이때 DefaultHandlerExceptionResolver가 각 상황에 맞는 응답 코드를 리턴해준다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청에 맞는 컨트롤러(핸들러)를 못 찾는 경우 -&amp;gt; 404 Not Found&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러 실행 중 예외가 발생하는 경우 -&amp;gt; 500 Internal Server Error&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러의 파라미터 타입 미스매치 경우 -&amp;gt; 400 BadRequest&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1631863048367&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/api/defaulthandler&quot;)
public String defaultException(@RequestParam Integer data) {
    return &quot;ok&quot;; (정상적인 경우 ok가 반환)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위와 같은 컨트롤러에 문자열인 데이터를 보냈다고 한다면 기존처럼 500 에러코드를 반환하는 것이 아닌 아래와 같이 400 에러코드를 반환하게 된다&lt;/p&gt;
&lt;pre id=&quot;code_1631863554658&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;status&quot;: 400,
    &quot;error&quot;: &quot;Bad Request&quot;,
    &quot;exception&quot;: &quot;org.springframework.web.method.annotation.MethodArgumentTypeMismatchException&quot;,
    &quot;message&quot;: (생략)
    &quot;path&quot;: &quot;/api/defaulthandler&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;ExceptionResolver 활용 후의 흐름 정리&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 살펴본 다양한 ExceptionResolver가 적용되고 나서의 흐름을 한번 더 정리해보고자 한다&lt;/p&gt;
&lt;pre id=&quot;code_1631865354715&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//예외 예시
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
    return new ErrorResult(&quot;BAD&quot;, e.getMessage());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;376&quot; data-filename=&quot;Screen Shot 2021-09-17 at 4.58.42 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KiGoq/btrfjXJClgo/DfFtvZOEsfnDlKyOb9e8kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KiGoq/btrfjXJClgo/DfFtvZOEsfnDlKyOb9e8kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KiGoq/btrfjXJClgo/DfFtvZOEsfnDlKyOb9e8kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKiGoq%2FbtrfjXJClgo%2FDfFtvZOEsfnDlKyOb9e8kK%2Fimg.png&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;376&quot; data-filename=&quot;Screen Shot 2021-09-17 at 4.58.42 PM.png&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;span style=&quot;color: #9d9d9d;&quot;&gt;3번까지는 기존과 동일한 흐름 (예외 발생은 IllegalArgumentException)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4.1.&lt;/b&gt; 예외가 발생하였기 때문에 ExceptionResolver가 작동한다, 이때 가장 우선순위가 높은 @ExceptionHandler에서 가 작동하고 해당 컨트롤러에 발생한 예외를 처리할 수 있는 @ExceptionHanlder가 존재하는지 확인한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4.2.&lt;/b&gt; ResponseStatus에 지정된 응답 코드를 반환한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5.&lt;/b&gt; afterCompletion 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6.&lt;/b&gt; 예외처리가 되었으므로 API 응답&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;@ControllerAdvice&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API의 경우 각 컨트롤러마다 다른 예외를 응답해야 하는 상황이 필요하다 이때 @ControllerAdvice를 활용하여 각 컨트롤러에 적용시킬 예외처리를 나누어 관리할 수 있다 (만약 @ControllerAdvice를 지정하지 않으면 모든 컨트롤러에 적용된다 (글로벌 적용))&lt;/p&gt;
&lt;pre id=&quot;code_1631865846269&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}

// Target all Controllers within specific packages
@ControllerAdvice(&quot;org.example.controllers&quot;)
public class ExampleAdvice2 {}

// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class,AbstractController.class})
public class ExampleAdvice3 {}&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;핵심정리&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 오류 처리 같은 경우, 단순한 웹페이지를 반환하는 것과는 달리 다양한 형식을 사용하거나 각각의 컨트롤러에 각기 다른 예외를 적용시켜야 하는 상황이 빈번하다, 또한 단순히 데이터를 반환하는데 WAS에서 다시 요청을 할 필요도 없다&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;이러한 문제점을 해결하기 위해 ExceptionResolver(HandlerExceptionResolver)를 사용하여, 예외가 발생하였을 때 Resolver에서 예외를 처리할 수 있는지 확인하고 예외 처리가 된다면 정상적인 흐름처럼 클라이언트에게 응답하게 된다&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;ExceptionResolver는 총 3가지가 있지만 대부분 API 응답에 사용하는 Resolver는 ExceptionHandlerExceptionResolver로 애노테이션화 되어 있어 매우 간편하게 사용할 수 있다, @ExceptionHandler를 사용하여 적용시킬 예외를 지정하고, 지정하지 않는다면 파라미터와 반환 값을 동적으로 만들어 반환할 수도 있다&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;마지막으로 @ControllerAdvice를 사용하게 되면 예외를 적용시키고자 하는 애노테이션, 패키지, 클래스까지 지정하여 각각 다른 예외 조건들을 설정할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인 학습을 위해 작성되는 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;제가 잘못 알고 있는 점에 대한 지적 / 더 나은 방향에 대한 댓글을 환영합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참조 링크:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://jaehun2841.github.io/2018/08/30/2018-08-25-spring-mvc-handle-exception/#responsestatusexceptionresolver&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jaehun2841.github.io/2018/08/30/2018-08-25-spring-mvc-handle-exception/#responsestatusexceptionresolver&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Spring MVC</category>
      <category>@ControllerAdvice</category>
      <category>@ExceptionHandlere</category>
      <category>@ResponseStatus</category>
      <category>DefaultHandlerExceptionResolver</category>
      <category>ExceptionHandlerExceptionResolver</category>
      <category>ExceptionResolver</category>
      <category>ResponseStatusExeceptionResolver</category>
      <category>Spring API Error</category>
      <author>MAENCO</author>
      <guid isPermaLink="true">https://maenco.tistory.com/89</guid>
      <comments>https://maenco.tistory.com/entry/Spring-MVC-API-Error-API-%EC%98%A4%EB%A5%98-%EC%B2%98%EB%A6%AC#entry89comment</comments>
      <pubDate>Fri, 17 Sep 2021 17:21:03 +0900</pubDate>
    </item>
  </channel>
</rss>