<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>IT Village</title>
    <link>https://itvillage.tistory.com/</link>
    <description>IT Village는 IT와 관련된 다양한 정보를 공유하고자 개설된 블로그입니다. 개발에 입문하시는 많은 초보자 분들에게 조금이나마 도움이 될 수 있었으면 좋겠습니다.^^</description>
    <language>ko</language>
    <pubDate>Tue, 14 Apr 2026 19:38:44 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Kevin's IT Village</managingEditor>
    <image>
      <title>IT Village</title>
      <url>https://tistory1.daumcdn.net/tistory/1647673/attach/98f4e12df8f34c2eac09c4c7c0b75ac8</url>
      <link>https://itvillage.tistory.com</link>
    </image>
    <item>
      <title>AI 시대에 Java 개발자로 입문하기 위한 경쟁력에 대한 이야기</title>
      <link>https://itvillage.tistory.com/entry/AI-%EC%8B%9C%EB%8C%80%EC%97%90-Java-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A1%9C-%EC%9E%85%EB%AC%B8%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EA%B2%BD%EC%9F%81%EB%A0%A5%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%EC%95%BC%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. Kevin입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 오랜만에 포스팅을 하는 것 같은데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 프로그래밍이나 어떤 기술에 대한 구체적인 이야기를 하려고 하는 건 아니에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쩌면 그것보다 더 중요한 이야기일 수 있는데 저의 생각이 담긴 이 글이 Java 개발자로 입문하고자 하는 분들에게 작은 동기부여 그리고 포기하지 마시라는 작은 응원이 될 수 있으면 하는 바램입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 지금부터 글을 적어볼게요. 처음에는 `부정적인` 분위기에서 곧 `긍정적인` 분위기로 바뀝니다. ^^;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Java 개발자로 입문하는 길은 더 좁아졌다?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현실적으로 보면 신입 개발자 채용이 줄어드는 건 사실이라고 생각해요. AI가 코드 레벨에서 많은 일을 대신하고 있다는 사실을 부정할 수는 없으니까요. 이건 숨기기 어려운 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;`팩트`&lt;/span&gt;&lt;/b&gt;라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2. 개발자는 더 적게 필요해진다?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 개발자의 숫자로만 따진다면 과거보다 더 적게 필요해진다고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술이 발달할수록 인간의 손이 직접적으로 닿는 곳 중에 기술로 대체하기 쉬운 부분부터 순차적으로 대체하는 사례는 흔히들 찾아볼 수 있는 일이니까요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공장 자동화로 인해서 제품을 생산하는 생산 인력이 로봇과 기계로 대체되는게 좋은 사례 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 최근에 많이 볼 수 있는 사례는 패스트푸드 점에서 주문할 때 사용할 수 있는 키오스크를 들 수 있구요. 키오스크가 설치되는 만큼 매장에서 일하는 직원 수는 줄어들 테니까요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 개발자도 이런 기술 발달의 영향을 피해 갈 수는 없다고 생각합니다. 더군다나 AI가 인간에게 미치는 영향은 이때까지 나왔던 그 어떤 기술보다 영향력이 더 크다는 생각이 들기 때문이에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 표면적으로 &lt;span style=&quot;color: #ee2323;&quot;&gt;`AI로 인해 개발자의 숫자는 줄어들 것이다`&lt;/span&gt;라는 의견에 저도 대표적으로 공감하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만..(지금까지는 좀 부정적인 이야기이고 이제부터 긍정적인 이야기를 시작해 볼게요. ^^)&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3. 어떻게 보면 좋은 개발자가 되기 더 쉬워졌을 수 있다.&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI를 본격적으로 실무에서 사용하기 이전에 과연 개발자가 되기 쉬워졌을까를 생각해 보면 사실 그렇지도 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자로 입문하고 취업하는 게 지금보다는 쉬웠겠지만 제대로 된 개발자로 성장해서 좋은 기업에 취업하기 어려운 건 그때나 지금이나 마찬가지라는 생각이 들거든요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 왜 그때보다 지금이 더 좋은 개발자가 되기 쉬워졌을까를 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;`문제 해결&lt;/b&gt;`&lt;/span&gt;이라는 관점에서 생각해 보면 이해하기가 좀 쉬워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI를 자유롭게 활용할 수 있는 지금은 소프트웨어적으로 어떤 문제가 발생했을 때, 나를 도와줄 수 있는 강력한 서포터가 항상 내 옆에 있기 때문에 오히려 문제 해결하기가 훨씬 더 쉬워졌다는 생각이 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 포함해서 과거의 개발자들은 애플리케이션에 어떤 문제가 발생했을 때, 가장 의지할 수 있는 최고의 서포터가 바로 `구글링`이었는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체 디버깅으로 해결할 수 없는 문제들을 구글링으로 문제를 해결하기까지의 과정이 지금과는 말도 안 되게 험난했던 기억이 아직도 뚜렷하니까요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 AI라는 강력한 도구가 항상 곁에 있을 수 있기 때문에 AI와 호흡만 잘 맞출 수 있다면 좋은 개발자가 될 가능성은 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;예전보다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 더 높아졌다고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4.&amp;nbsp; AI 시대에 맞는 경쟁력 있는 좋은 개발자란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;`다른 사람보다 경쟁력이 있는 개발자는 예전이나 지금이나 반드시 개발자의 길을 가게 된다`&lt;/span&gt;&lt;/b&gt;라고 저는 그렇게 생각하고 있습니다. ^^&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에도 현장에서 늘 있던 웃픈 말 중에 하나가 &lt;span style=&quot;color: #ee2323;&quot;&gt;`개발자는 많은데 뽑을 개발자가 없다`&lt;/span&gt;였습니다. 이 말의 의미가 이해되시죠?&lt;br /&gt;경쟁력 있는 개발자는 그때도 많지 않았고, 지금도 많지 않다고 생각해요. &lt;br /&gt;&lt;br /&gt;그런데 그때와 다르게 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;지금은 경쟁력 있는 개발자가 될 수 있는 가능성이 더 높아졌다&lt;/span&gt;&lt;/b&gt;고 생각합니다. &lt;br /&gt;바로 AI가 항상 여러분들 옆에서 도와줄 수 있기 때문이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI로부터 최적의 도움을 받으려면 발생한 문제에 대해서 논리적이면서 구체적인 상황을 AI에게 잘 설명해야 되겠죠.&lt;br /&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;자 그러면 AI에게 설명을 잘할 수 있는 가장 확실한 방법을 생각해 봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI를 잘 활용할 수 있는 것이 앞으로 개발자의 가장 훌륭한 경쟁력이 될 거라고 생각하지만 AI를 활용하든 그렇지 않든 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;어떤 문제를 체계적으로 해결하려는 연습이 되어 있지 않으면 AI를 활용한 경쟁력 있는 개발자가 되는 건 불가능&lt;/span&gt;&lt;/b&gt;하지 않을까 조심스럽게 생각해 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5. 아는만큼 활용할 수 있는 AI&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어적인 문제를 잘 해결하기 위해 AI의 도움을 정확하게 잘 받으려면 AI에게 문제에 걸맞은 정확한 질문을 해야 하고 AI가 답변을 제시하면 그 답변이 적절한 답이 맞는지를 검증할 수 있어야 합니다. 왜냐하면 AI는 거짓말도 밥 먹듯이 잘 하니까요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;`아는 만큼 보인다`&lt;/span&gt;&lt;/b&gt;라는 명언 들어보셨죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 소프트웨어적인 지식을 정확하게 알고 있다면 AI에게 질문도 더 정확하게 할 수 있고, AI의 답변도 더 정확하게 검증할 수 있다고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;`아는 만큼 보인다면 5년 10년 동안 &lt;span style=&quot;text-align: start;&quot;&gt;개발 지식을&amp;nbsp;&lt;/span&gt; 많이 쌓아야 하는 건가?`&lt;/span&gt; 이렇게 생각하실 수도 있지만 앞에서 얘기드렸다시피 저희에게는 AI라는 최고의 서포터가 있기 때문에 아는 만큼 보이기 위한 시간은 획기적으로 단축할 수 있을 거라고 생각해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;6. 프로그래밍 기본기를 습득하는 방식의 변화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무리 AI 시대라고 해도 아는 만큼 AI를 잘 활용하기 위한 시작은 프로그래밍의 기본기를 잘 습득하는 것이라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 프로그래밍의 기본기는 예나 지금이나 변함없이 중요하다는거죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 과거와는 다른 방식이어야 한다고 생각해요.&lt;/p&gt;
&lt;blockquote data-end=&quot;694&quot; data-start=&quot;689&quot; data-ke-style=&quot;style2&quot;&gt;과거에는..&lt;br /&gt;`프로그래밍 문법 &amp;rarr; 자료구조/알고리즘 &amp;rarr; 많은 연습 &amp;rarr; 몇 달 후 &amp;nbsp;작동하는 애플리케이션 겨우 하나 만들기` 였다면,&lt;br /&gt;&lt;br /&gt;현재는..&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;`프로그래밍 문법 &amp;rarr; 바로 미니 프로젝트 &amp;rarr; AI 도움으로 즉시 결과 확인 &lt;span style=&quot;color: #ee2323; text-align: left;&quot;&gt;&amp;rarr;&lt;/span&gt; 더 나은 코드로의 진화`&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;694&quot; data-start=&quot;689&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;807&quot; data-start=&quot;805&quot; data-ke-size=&quot;size16&quot;&gt;즉, 기본기는 여전히 필요하지만, 기본기를 배우는 방식이 아래와 같이 바뀌어야 된다고 생각합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;807&quot; data-start=&quot;805&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;내가 먼저 작은 프로젝트라도 직접 수행하고 AI에게 피드백을 받은 후에 내 것으로 만든다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;807&quot; data-start=&quot;805&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;그리고 더 나은 방식을 AI와 고민하면서 거기에서 더 나은 경험을 배우는 과정을 반복한다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;807&quot; data-start=&quot;805&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 해야 나 스스로가 해박해지면서 AI도 잘 활용할 수 있는 경쟁력 있는 개발자가 될 수 있을 것 같습니다.&lt;/p&gt;
&lt;p data-end=&quot;694&quot; data-start=&quot;689&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;7. 미니 프로젝트를 스스로 해보는 것의 중요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로우 코드, 노코드, 바이브 코딩 등등&amp;nbsp; 코드 하나 몰라도 AI한테 다 맡기면 모든 걸 다 제대로 만들 수 있을 것 같지만 결국 AI한테 제대로 일을 맡기려면 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;인간이 제대로 사고할 수 있어야&lt;/span&gt;&lt;/b&gt; 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자로써 제대로 사고하기 위한 가장 확실한 방법은 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;프로젝트를 스스로 많이 수행해 보는 것&lt;/span&gt;&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 가지고 있는 능력만큼의 작은 프로젝트를 스스로 수행해 보세요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 나서 1차적으로 다 만든 프로젝트를 개선하기 위해 AI의 도움을 받아서 더 나은 방식의 애플리케이션을 만들어 보세요.&lt;br /&gt;이렇게 하면 여러분들의 개발 스킬과 AI를 잘 활용할 수 있는 힘을 모두 축적할 수 있을거라는 생각이 드네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;`일거 양득, 일타 쌍피`&lt;/span&gt;라는 말은 이럴 때 가장 어울리는 것 같습니다. ^^&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 지켜야 할 규칙은 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;`내가 먼저 만들어본다. 그다음에 AI의 도움을 받는다`&lt;/span&gt;&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자로 입문을 하게 되면 만들면서 AI의 도움을 그때그때 받겠지만 입문 전까지는 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;`선 사고, 후 도움`&lt;/span&gt;&lt;/b&gt; 이 규칙을 지켜야 되지 않을까 하고 생각해 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;7. 결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부트 캠프에서 학생들을 가르칠 때도 직접 생각하고 코드를 타이핑하는 연습을 하지 않는 학생들이 굉장히 많았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;눈으로만 보고, 귀로만 듣는 방법으로 좋은 개발자가 될 수 없듯이 내가 지식이 없이 AI에게 무조건 적으로 요청만 하면서 경쟁력 있는 개발자가 될 수는 없다고 생각해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신입으로 취업하는게 과거보다 더 어려워졌다는 뉴스 기사를 자주 접하는 요즘,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;각자의 의견이 다르겠지만&lt;span&gt;&amp;nbsp;저는&lt;/span&gt;&lt;/span&gt; &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;`사고하는 개발자 + AI의 활용`&lt;/span&gt;&lt;/b&gt;이라면 충분히 승산이 있다고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 한줄 결론은,&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;AI 시대에도 프로그래밍 기본기가 필요하다. 아니, AI 시대라서 더 필요하다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;`나는 할 수 있다!!`&lt;/span&gt;&lt;/b&gt;고 여러분 스스로를 믿으세요. 그리고 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;포기하지 마세요.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저도 처음 개발자로 입문하기 전에는 고졸에 비전공에 프로그래밍 지식도 전무했었지만 결국 여기까지 왔으니 여러분도 분명 개발자가 될 수 있을 거에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자라는 직업은 꽤나 매력이 있는 직종입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막까지 포기하지 마시고 여러분 앞을 가로막고 있는 벽을 반드시 넘을 수 있길 바래 볼게요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝까지 읽어주셨다면 감사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://inf.run/rbWKg&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Java 실무 프로젝트 입문편] 객체지향 사고력 훈련 (미니 프로젝트 3종 실습)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1762527001055&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;[Java 실무 프로젝트 입문편] 객체지향 사고력 훈련 - 미니 프로젝트 3종 실습| Kevin - 인프런 강의&quot; data-og-description=&quot;현재 평점 5.0점인 강의를 만나보세요. 작은 프로젝트부터 구조 잡는 연습까지! Java 객체지향 설계와 실습 경험이 부족한 분들을 위한 직접 만들어보며 성장하는 미니 프로젝트 실전 입문 강의. &quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://inf.run/rbWKg&quot; data-og-url=&quot;https://www.inflearn.com/course/java-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AF%B8%EB%8B%88%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%8A%B5&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BtDqq/hyZMtg64qg/kcfRUDlBxtKhDiZeJ2N9S1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/bjgsZL/hyZMyCJI3p/0kXQKeU6ryeTa1TXFHL6Xk/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/jS3pD/hyZMECW8Ej/IV0OJplkeWjKVqkkyK5zW1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781&quot;&gt;&lt;a href=&quot;https://inf.run/rbWKg&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inf.run/rbWKg&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BtDqq/hyZMtg64qg/kcfRUDlBxtKhDiZeJ2N9S1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/bjgsZL/hyZMyCJI3p/0kXQKeU6ryeTa1TXFHL6Xk/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/jS3pD/hyZMECW8Ej/IV0OJplkeWjKVqkkyK5zW1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Java 실무 프로젝트 입문편] 객체지향 사고력 훈련 - 미니 프로젝트 3종 실습| Kevin - 인프런 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;현재 평점 5.0점인 강의를 만나보세요. 작은 프로젝트부터 구조 잡는 연습까지! Java 객체지향 설계와 실습 경험이 부족한 분들을 위한 직접 만들어보며 성장하는 미니 프로젝트 실전 입문 강의.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://inf.run/Dt3vb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Java&amp;nbsp;실무&amp;nbsp;프로젝트&amp;nbsp;심화편]&amp;nbsp;Spring&amp;nbsp;Core&amp;nbsp;+&amp;nbsp;H2&amp;nbsp;기반&amp;nbsp;푸드&amp;nbsp;트럭&amp;nbsp;키오스크&amp;nbsp;시스템&amp;nbsp;제작&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1762526993213&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;[Java 실무 프로젝트 심화편] Spring Core + H2 기반 푸드 트럭 키오스크 시스템 제작| Kevin - 인프런 강&quot; data-og-description=&quot;실무 감각을 키우고 싶은 입문 개발자를 위한 Java 심화 프로젝트 강의! Spring Core, JdbcClient, H2 DB로 구성된 콘솔 기반 키오스크 주문 시스템을 함께 만들어갑니다. 요구사항을 분석하고 기능 단위&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://inf.run/Dt3vb&quot; data-og-url=&quot;https://www.inflearn.com/course/java-%EC%8B%A4%EB%AC%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%AC%ED%99%94%ED%8E%B8-spr&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eM4vd/hyZMZ1HV3m/2JB3nFUKXyQFd0zOajh3b0/img.png?width=403&amp;amp;height=262&amp;amp;face=0_0_403_262,https://scrap.kakaocdn.net/dn/wJUCc/hyZNqDleO5/1qXXHsRg2MAsJ6jLVUKzaK/img.png?width=403&amp;amp;height=262&amp;amp;face=0_0_403_262,https://scrap.kakaocdn.net/dn/mw6NL/hyZNgOgAJp/KlKybSKtV7QUGCowIFi2NK/img.png?width=517&amp;amp;height=517&amp;amp;face=0_0_517_517&quot;&gt;&lt;a href=&quot;https://inf.run/Dt3vb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inf.run/Dt3vb&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eM4vd/hyZMZ1HV3m/2JB3nFUKXyQFd0zOajh3b0/img.png?width=403&amp;amp;height=262&amp;amp;face=0_0_403_262,https://scrap.kakaocdn.net/dn/wJUCc/hyZNqDleO5/1qXXHsRg2MAsJ6jLVUKzaK/img.png?width=403&amp;amp;height=262&amp;amp;face=0_0_403_262,https://scrap.kakaocdn.net/dn/mw6NL/hyZNgOgAJp/KlKybSKtV7QUGCowIFi2NK/img.png?width=517&amp;amp;height=517&amp;amp;face=0_0_517_517');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Java 실무 프로젝트 심화편] Spring Core + H2 기반 푸드 트럭 키오스크 시스템 제작| Kevin - 인프런 강&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실무 감각을 키우고 싶은 입문 개발자를 위한 Java 심화 프로젝트 강의! Spring Core, JdbcClient, H2 DB로 구성된 콘솔 기반 키오스크 주문 시스템을 함께 만들어갑니다. 요구사항을 분석하고 기능 단위&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT 트렌드/트렌드 공통</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/83</guid>
      <comments>https://itvillage.tistory.com/entry/AI-%EC%8B%9C%EB%8C%80%EC%97%90-Java-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A1%9C-%EC%9E%85%EB%AC%B8%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EA%B2%BD%EC%9F%81%EB%A0%A5%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%EC%95%BC%EA%B8%B0#entry83comment</comments>
      <pubDate>Fri, 7 Nov 2025 14:32:29 +0900</pubDate>
    </item>
    <item>
      <title>Spring Boot 콘솔 애플리케이션에서 H2 인메모리 데이터베이스를 TCP 서버 모드로 설정하는 방법</title>
      <link>https://itvillage.tistory.com/entry/Spring-Boot-%EC%BD%98%EC%86%94-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-H2-%EC%9D%B8%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-TCP-%EC%84%9C%EB%B2%84-%EB%AA%A8%EB%93%9C%EB%A1%9C-%EC%84%A4%EC%A0%95%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;!-- 제목은 블로그 시스템에서 자동 처리됨 (예: &lt;h1&gt;) --&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ Spring Boot 콘솔 프로젝트에서 H2 DB를 TCP 서버 모드로 설정하는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring 웹 애플리케이션에서 H2 인메모리 DB를 사용하는것과 마찬가지로 Spring Boot로 콘솔 기반 프로젝트에서도 H2 DB를 이용할 수 있는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 Spring 웹 애플리케이션에서 H2를 사용할 때처럼 콘솔 프로젝트에도 동일한 방식으로 사용할 수 있다라고 생각하기 쉬운데 &lt;b&gt;웹 애플리케이션과 콘솔 애플리케이션은 구조적으로 동작 방식이 다릅니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  웹 애플리케이션 vs 콘솔 애플리케이션의 차이점&lt;/h3&gt;
&lt;table border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;5&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;Spring 웹 애플리케이션&lt;/th&gt;
&lt;th&gt;Spring 콘솔 애플리케이션&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;실행 방식&lt;/td&gt;
&lt;td&gt;서버로 실행됨 (8080 등에서 계속 대기)&lt;/td&gt;
&lt;td&gt;메인 메서드 실행 후 바로 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H2 DB 유지&lt;/td&gt;
&lt;td&gt;실행 중엔 메모리 DB 유지 가능&lt;/td&gt;
&lt;td&gt;애플리케이션 종료 시 H2 DB도 즉시 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;외부 도구 접근&lt;/td&gt;
&lt;td&gt;결과 확인을 위해 브라우저나 툴에서 접근하기 쉬움&lt;/td&gt;
&lt;td&gt;브라우저나 툴에서 접근하기 어려움&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 저는 콘솔 애플리케이션에서 H2 DB를 &lt;b&gt;TCP 서버 모드로 띄워서 외부 툴(IntelliJ 등)에서도 접근 가능하도록 구성&lt;/b&gt;하는 방식을 적용했고, 잘 동작하는걸 확인을 했는데 지금부터 그 구성 방식을 설명 드리도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  H2 TCP 서버 모드 설정 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Spring Boot 기본 H2 설정&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password:
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정은 Spring Boot의 프로퍼티 설정 파일인 application.yml의 설정 내용인데 H2를 메모리 DB로 사용하기 위한 설정이기 때문에 애플리케이션이 종료되면 DB도 함께 종료되어서 메모리에 저장된 데이터가 사라집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⚠️주의:&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Spring Boot는 Embedded 방식으로 DB를 연결해야 하며, TCP URL 사용 시&lt;span&gt; Spring Boot 애플리케이션에서 Embedded 방식의 DB에 연결하는 것이 아니라 Spring Boot 애플리케이션 외부의 H2 DB에 연결하려하기 때문에 &lt;span style=&quot;color: #ef5369;&quot;&gt;tcp://&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;형식으로 설정하면 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Database not found&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;오류가 발생할 수 있으므로 주의해야합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. H2 TCP 서버를 코드로 실행하기&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Slf4j
@Configuration
public class H2ServerConfig {

    @Bean
    public CommandLineRunner startH2Server() {
        log.info(&quot;# h2 server started&quot;);
        return args -&amp;gt; {
            Server.createTcpServer(&quot;-tcp&quot;, &quot;-tcpAllowOthers&quot;, &quot;-tcpPort&quot;, &quot;9092&quot;).start();
        };
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔 애플리케이션 종료 시, H2 DB가 함께 종료되지 않도록 Spring Boot에서는 &lt;span style=&quot;color: #ef5369;&quot;&gt;@Bean&lt;/span&gt; 애너테이션으로 H2를 TCP 서버 모드로 띄울 수 있도록합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  IntelliJ에서 연결하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 IntelliJ에서 H2에 접속할 수 있도록 아래와 같은 순서로 설정하도록 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Database 탭 &amp;rarr; Data Source &amp;rarr; H2 선택&lt;/li&gt;
&lt;li&gt;JDBC URL: &lt;code&gt;jdbc:h2:tcp://localhost:9092/mem:testdb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;User: &lt;code&gt;sa&lt;/code&gt;, Password: 비워도 OK&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이렇게 설정하면 콘솔 애플리케이션이 즉시 종료되지 않고 애플리케이션이 실행 중일 때 H2에 접속할 수 있게 됩니다.&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  흔히 발생하는 오류와 해결 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;잘못된 설정 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서 application.yml에서의 H2 설정을 클래스 레벨에서도 구성할 수 있는데 아래와 같이 &lt;span style=&quot;color: #ef5369;&quot;&gt;tcp://&lt;/span&gt;로 시작하는 URL과 같이 설정하면 어떻게 될까요?&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;System.setProperty(&quot;spring.datasource.url&quot;, &quot;jdbc:h2:tcp://localhost:9092/mem:testdb&quot;); // ❌ 오류 발생
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명했던 것 처럼 마찬가지로 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Spring 애플리케&lt;/span&gt;이션이 외부 TCP 서버로 접속을 시도하게 되어, &lt;code&gt;Database not found&lt;/code&gt; 또는 &lt;code&gt;Connection refused&lt;/code&gt;와 같은 오류가 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정상 설정 예시&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;System.setProperty(&quot;spring.datasource.url&quot;, &quot;jdbc:h2:mem:testdb&quot;); // ✅ 올바른 방식
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml이 아닌 클래스 레벨에서 H2를 설정할 경우에는 꼭 &lt;span style=&quot;color: #ef5369;&quot;&gt;tcp://localhost:9092&lt;/span&gt;를 제외하고 지정해야 한다는 사실을 기억하세요.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  H2 관련 포트 정리&lt;/h2&gt;
&lt;table border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;5&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;모드&lt;/th&gt;
&lt;th&gt;기본 포트&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;H2 TCP 서버&lt;/td&gt;
&lt;td&gt;9092&lt;/td&gt;
&lt;td&gt;외부 툴(JDBC 클라이언트) 접속용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H2 Web Console&lt;/td&gt;
&lt;td&gt;8082&lt;/td&gt;
&lt;td&gt;브라우저에서 사용하는 Web UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spring Boot&lt;/td&gt;
&lt;td&gt;8080&lt;/td&gt;
&lt;td&gt;Spring 애플리케이션 기본 포트&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  마무리 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;콘솔 기반 Spring Boot 앱에서는 H2를 &lt;b&gt;TCP 서버 모드&lt;/b&gt;로 띄워야 실시간 DB 확인이 가능하다는 사실을 기억해 주세요.&lt;/li&gt;
&lt;li&gt;Spring 내부는 &lt;code&gt;mem:testdb&lt;/code&gt;로 접근, 외부 툴은 &lt;code&gt;tcp://localhost:9092&lt;/code&gt;로 연결해야 한다는 사실을 잊지 마세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스트가 Spring Boot 기반의 콘솔 애플리케이션에서 H2를 효율적으로 사용하는 데 도움이 되었길 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다!&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring Boot</category>
      <category>h2 database</category>
      <category>h2 tcp server mode</category>
      <category>In-memory Database</category>
      <category>intellij h2 연결</category>
      <category>java spring 설정</category>
      <category>spring boot</category>
      <category>spring boot 디버깅</category>
      <category>spring console application</category>
      <category>개발환경 세팅</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/79</guid>
      <comments>https://itvillage.tistory.com/entry/Spring-Boot-%EC%BD%98%EC%86%94-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-H2-%EC%9D%B8%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-TCP-%EC%84%9C%EB%B2%84-%EB%AA%A8%EB%93%9C%EB%A1%9C-%EC%84%A4%EC%A0%95%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry79comment</comments>
      <pubDate>Fri, 4 Apr 2025 15:49:35 +0900</pubDate>
    </item>
    <item>
      <title>[4편] 1부 이후, 2부에선 어떤 걸 배우게 될까?</title>
      <link>https://itvillage.tistory.com/entry/4%ED%8E%B8-1%EB%B6%80-%EC%9D%B4%ED%9B%84-2%EB%B6%80%EC%97%90%EC%84%A0-%EC%96%B4%EB%96%A4-%EA%B1%B8-%EB%B0%B0%EC%9A%B0%EA%B2%8C-%EB%90%A0%EA%B9%8C</link>
      <description>&lt;p data-end=&quot;332&quot; data-start=&quot;269&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;작은 프로젝트 세 개를 해보니, &lt;br /&gt;이제 조금은 애플리케이션 구현에 자신감이 생겼습니다. &lt;br /&gt;그런데 그다음엔 뭘 해야 하죠?&amp;rdquo;&lt;/p&gt;
&lt;p data-end=&quot;428&quot; data-start=&quot;334&quot; data-ke-size=&quot;size16&quot;&gt;1부 강의를 끝낸 분들이 종종 묻는 질문입니다.&lt;br /&gt;그리고 이 질문이 나오는 순간, &lt;b&gt;&amp;ldquo;이제부터가 진짜구나&amp;rdquo;&lt;/b&gt;라는 답을 드리고 싶어요.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;459&quot; data-start=&quot;435&quot; data-ke-size=&quot;size23&quot;&gt;  1부는 입문, 2부는 확장입니다&lt;/h3&gt;
&lt;p data-end=&quot;471&quot; data-start=&quot;461&quot; data-ke-size=&quot;size16&quot;&gt;1부에서 우리는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;538&quot; data-start=&quot;472&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;494&quot; data-start=&quot;472&quot;&gt;콘솔 기반의 작은 프로젝트를 통해&lt;/li&gt;
&lt;li data-end=&quot;519&quot; data-start=&quot;495&quot;&gt;설계 &amp;rarr; 구현 &amp;rarr; 검증이라는 사이클을&lt;/li&gt;
&lt;li data-end=&quot;538&quot; data-start=&quot;520&quot;&gt;직접 여러 번 반복해 봤습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;594&quot; data-start=&quot;540&quot; data-ke-size=&quot;size16&quot;&gt;그 과정에서 &amp;ldquo;어떻게 나눌까?&amp;rdquo;, &amp;ldquo;이건 어떤 책임이지?&amp;rdquo;&lt;br /&gt;이런 객체지향적 질문들을 훈련했죠.&lt;/p&gt;
&lt;p data-end=&quot;676&quot; data-start=&quot;596&quot; data-ke-size=&quot;size16&quot;&gt;하지만 현실에서의 프로젝트는&lt;br /&gt;&lt;b&gt;이보다 조금 더 복잡하고, 더 유연한 구조&lt;/b&gt;를 요구합니다.&lt;br /&gt;그걸 경험해 보는 게 바로 2부의 목표입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;701&quot; data-start=&quot;683&quot; data-ke-size=&quot;size23&quot;&gt;  2부에서 바뀌는 것들&lt;/h3&gt;
&lt;h4 data-end=&quot;727&quot; data-start=&quot;703&quot; data-ke-size=&quot;size20&quot;&gt;✅ 프로젝트의 성격이 달라집니다&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;783&quot; data-start=&quot;728&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;750&quot; data-start=&quot;728&quot;&gt;1부: 기능 중심 / 단순한 구조&lt;/li&gt;
&lt;li data-end=&quot;783&quot; data-start=&quot;751&quot;&gt;2부: &lt;b&gt;실제 실무에서처럼 역할이 명확한 시스템 구성&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;872&quot; data-start=&quot;785&quot; data-ke-size=&quot;size16&quot;&gt;예: 고객과 직원이 각기 다른 화면(콘솔 인터페이스)을 사용하고,&lt;br /&gt;내부에서는 주문, 메뉴, 영수증, 매출 등 다양한 역할이 분리되어 존재합니다.&lt;/p&gt;
&lt;h4 data-end=&quot;895&quot; data-start=&quot;874&quot; data-ke-size=&quot;size20&quot;&gt;✅ 설계가 더 중요해집니다&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;953&quot; data-start=&quot;896&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;919&quot; data-start=&quot;896&quot;&gt;1부에선 &amp;ldquo;일단 만들어보자&amp;rdquo;였다면,&lt;/li&gt;
&lt;li data-end=&quot;953&quot; data-start=&quot;920&quot;&gt;2부에선 &lt;b&gt;만들기 전에 먼저 설계를 고민하게 됩니다&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1005&quot; data-start=&quot;955&quot; data-ke-size=&quot;size16&quot;&gt;클래스 다이어그램을 그려보거나,&lt;br /&gt;요구사항을 기능 단위로 분리해 보는 훈련을 하게 됩니다.&lt;/p&gt;
&lt;h4 data-end=&quot;1035&quot; data-start=&quot;1007&quot; data-ke-size=&quot;size20&quot;&gt;✅ 테스트와 리팩터링의 감각을 배웁니다&lt;/h4&gt;
&lt;p data-end=&quot;1121&quot; data-start=&quot;1036&quot; data-ke-size=&quot;size16&quot;&gt;코드를 먼저 만들고 나서&lt;br /&gt;&amp;ldquo;이걸 테스트하려면 구조가 이래야겠네&amp;rdquo;&lt;br /&gt;&amp;ldquo;이건 너무 많은 역할을 하고 있구나&amp;rdquo;&lt;br /&gt;같은 생각이 자연스럽게 생기게 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;1181&quot; data-start=&quot;1123&quot; data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;구현 &amp;rarr; 회고 &amp;rarr; 개선&lt;/b&gt;의 흐름을&lt;br /&gt;작은 규모에서부터 확장된 구조로 연결시켜보게 되는 거죠.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;1212&quot; data-start=&quot;1188&quot; data-ke-size=&quot;size23&quot;&gt;  조금 더 실무에 가까워지는 여정&lt;/h3&gt;
&lt;p data-end=&quot;1291&quot; data-start=&quot;1214&quot; data-ke-size=&quot;size16&quot;&gt;2부 강의는&lt;br /&gt;이제 막 &lt;b&gt;객체지향을 &amp;lsquo;감&amp;rsquo;으로 이해한 입문자&lt;/b&gt;가&lt;br /&gt;조금 더 &lt;b&gt;실무스러운 사고방식&lt;/b&gt;을 익히는 데에 초점을 둡니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1378&quot; data-start=&quot;1293&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1306&quot; data-start=&quot;1293&quot;&gt;레이어드 아키텍처&lt;/li&gt;
&lt;li data-end=&quot;1325&quot; data-start=&quot;1307&quot;&gt;도메인 중심의 클래스 분리&lt;/li&gt;
&lt;li data-end=&quot;1340&quot; data-start=&quot;1326&quot;&gt;간단한 저장소 구현&lt;/li&gt;
&lt;li data-end=&quot;1359&quot; data-start=&quot;1341&quot;&gt;H2 인메모리 데이터베이스&lt;/li&gt;
&lt;li data-end=&quot;1378&quot; data-start=&quot;1360&quot;&gt;JUnit을 통한 단위 테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1430&quot; data-start=&quot;1380&quot; data-ke-size=&quot;size16&quot;&gt;이런 기술들도 간단히 등장하지만,&lt;br /&gt;기술 자체보다 &lt;b&gt;구조와 사고 흐름에 집중&lt;/b&gt;합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;1459&quot; data-start=&quot;1437&quot; data-ke-size=&quot;size23&quot;&gt;  이런 분께 2부를 추천합니다&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1607&quot; data-start=&quot;1461&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1493&quot; data-start=&quot;1461&quot;&gt;1부를 들으며 &quot;이제 구현이 좀 재밌다&quot; 느끼신 분&lt;/li&gt;
&lt;li data-end=&quot;1535&quot; data-start=&quot;1494&quot;&gt;뭔가 만들 수는 있는데 &amp;ldquo;내가 맞게 하고 있나?&amp;rdquo; 의문이 생기는 분&lt;/li&gt;
&lt;li data-end=&quot;1577&quot; data-start=&quot;1536&quot;&gt;코드가 길어지면 &lt;b&gt;점점 흐름을 통제하지 못하는 느낌&lt;/b&gt;을 겪는 분&lt;/li&gt;
&lt;li data-end=&quot;1607&quot; data-start=&quot;1578&quot;&gt;&amp;ldquo;실무에서 이런 건 어떻게 하지?&amp;rdquo;라는 궁금증이 드 분&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;110&quot; data-start=&quot;86&quot; data-ke-size=&quot;size23&quot;&gt;  강의 둘러보기&lt;/h3&gt;
&lt;p data-end=&quot;215&quot; data-start=&quot;112&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://www.inflearn.com/course/java-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AF%B8%EB%8B%88%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%8A%B5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-end=&quot;215&quot; data-start=&quot;115&quot;&gt;Java 실무 프로젝트 입문편 - 객체지향 사고력 훈련 (미니 프로젝트 3종 실습)&lt;/a&gt;&lt;/p&gt;
&lt;p data-end=&quot;215&quot; data-start=&quot;112&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; data-start=&quot;115&quot; data-end=&quot;215&quot;&gt;Java 실무 프로젝트 심화편 - 오픈 예정&lt;/a&gt;&lt;/p&gt;
&lt;p data-end=&quot;304&quot; data-start=&quot;217&quot; data-ke-size=&quot;size16&quot;&gt;2부에서는&lt;br /&gt;&lt;b&gt;Spring 프레임워크를 이용해 콘솔 기반 키오스크 주문 시스템&lt;/b&gt;을 함께 만들어봅니다.&lt;/p&gt;
&lt;p data-end=&quot;304&quot; data-start=&quot;217&quot; data-ke-size=&quot;size16&quot;&gt;1부보다 조금 더 복잡한 구조로,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;374&quot; data-start=&quot;305&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;315&quot; data-start=&quot;305&quot;&gt;손님용 콘솔&lt;/li&gt;
&lt;li data-end=&quot;326&quot; data-start=&quot;316&quot;&gt;직원용 콘솔&lt;/li&gt;
&lt;li data-end=&quot;374&quot; data-start=&quot;327&quot;&gt;주문, 메뉴, 매출 관리 등&lt;br /&gt;여러 역할이 명확하게 나뉘는 시스템을 설계합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;461&quot; data-start=&quot;376&quot; data-ke-size=&quot;size16&quot;&gt;또한 &lt;b&gt;Spring의 JdbcClient를 활용해 H2 DB와 연동&lt;/b&gt;하는 실습도 포함되어 있어 간단한 영속성 처리까지 경험할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;535&quot; data-start=&quot;463&quot; data-ke-size=&quot;size16&quot;&gt;Java 개발 입문자들을 위해 1부보다 한 단계 더 깊이 있는 객체지향 설계와 구조적 사고를 경험할 수 있다는 사실을 기억해 주세요!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-end=&quot;1929&quot; data-start=&quot;1917&quot; data-ke-size=&quot;size26&quot;&gt;  시리즈 목차&lt;/h2&gt;
&lt;blockquote data-end=&quot;2016&quot; data-start=&quot;1931&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;2016&quot; data-start=&quot;1933&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 `Java 구현이 막막한 당신에게` 시리즈의 마지막 편입니다.&lt;br /&gt;그래도 끝이 아니라, 이제 다음 단계가 시작되는 순간일지도 모르죠  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;2033&quot; data-start=&quot;2018&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2033&quot; data-start=&quot;2018&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;시리즈 전체 글&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;1710&quot; data-end=&quot;1899&quot;&gt;
&lt;li data-start=&quot;1710&quot; data-end=&quot;1755&quot;&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/%E2%80%9CJava-%EB%AC%B8%EB%B2%95%EC%9D%80-%EC%95%84%EB%8A%94%EB%8D%B0-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%B4%EC%9A%94%E2%80%9D-%E2%80%93-%EA%B7%B8%EB%9F%B4-%EB%95%90-%EC%9D%B4%EB%A0%87%EA%B2%8C-%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot;&gt;[0편] Java 문법은 아는데 구현이 막막해요 &amp;ndash; 그럴 땐 이렇게 해봤습니다&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1756&quot; data-end=&quot;1789&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/1%ED%8E%B8-%EC%99%9C-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%A0%EA%B9%8C-%E2%80%93-Java-%EC%9E%85%EB%AC%B8%EC%9E%90%EC%9D%98-%EA%B3%A0%EB%AF%BC&quot;&gt;[1편] 왜 구현이 막막한가? &amp;ndash; Java 초심자의 고민&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1790&quot; data-end=&quot;1815&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/2%ED%8E%B8-%EC%9E%91%EC%9D%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5&quot;&gt;[2편] 작은 프로젝트에서 배우는 객체지향&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1816&quot; data-end=&quot;1857&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/3%ED%8E%B8-%EA%B0%95%EC%9D%98-%EB%B0%A9%EC%8B%9D-%EC%8A%A4%EC%8A%A4%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EB%B9%84%EA%B5%90%ED%95%98%EB%A9%B0-%EC%84%B1%EC%9E%A5%ED%95%98%EA%B8%B0&quot;&gt;[3편] 강의 방식: 스스로 만들고, 비교하며 성장하기&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1858&quot; data-end=&quot;1899&quot;&gt;[4편] 1부 이후, 2부에선 어떤 걸 배우게 될까?(현재 글)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java Backend 개발자 되기/Java 구현이 막막한 당신에게</category>
      <category>java</category>
      <category>junit</category>
      <category>객체지향 설계</category>
      <category>도메인 설계</category>
      <category>설계 확장</category>
      <category>실무 아키텍처</category>
      <category>자바 강의</category>
      <category>자바 실무</category>
      <category>자바 입문자</category>
      <category>자바 프로젝트</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/78</guid>
      <comments>https://itvillage.tistory.com/entry/4%ED%8E%B8-1%EB%B6%80-%EC%9D%B4%ED%9B%84-2%EB%B6%80%EC%97%90%EC%84%A0-%EC%96%B4%EB%96%A4-%EA%B1%B8-%EB%B0%B0%EC%9A%B0%EA%B2%8C-%EB%90%A0%EA%B9%8C#entry78comment</comments>
      <pubDate>Wed, 26 Mar 2025 11:43:38 +0900</pubDate>
    </item>
    <item>
      <title>[3편] 강의 방식: 스스로 만들고, 비교하며 성장하기</title>
      <link>https://itvillage.tistory.com/entry/3%ED%8E%B8-%EA%B0%95%EC%9D%98-%EB%B0%A9%EC%8B%9D-%EC%8A%A4%EC%8A%A4%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EB%B9%84%EA%B5%90%ED%95%98%EB%A9%B0-%EC%84%B1%EC%9E%A5%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-end=&quot;262&quot; data-start=&quot;243&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;이거 정답(레퍼런스) 코드 주시는 건가요?&amp;rdquo;&lt;/p&gt;
&lt;p data-end=&quot;367&quot; data-start=&quot;264&quot; data-ke-size=&quot;size16&quot;&gt;학생들을 가르치다 보면 강의 초반에 많이 듣는 질문 중 하나입니다.&lt;br /&gt;당연히 정답(레퍼런스) 코드가 있겠지만 이 강의의 목표는 &amp;lsquo;정답 코드&amp;rsquo;를 주는 것이 아니라,&lt;br /&gt;&lt;b&gt;자신이 작성한 코드와 강사가 작성한 코드 중에서 더 나은 코드를 도출해 &amp;lsquo;자신만의 코드&amp;rsquo;를 만들 수 있는 사고력을 기르는 것&lt;/b&gt;에 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;402&quot; data-start=&quot;374&quot; data-ke-size=&quot;size23&quot;&gt;  직접 해보는 것만큼 강력한 학습은 없다&lt;/h3&gt;
&lt;p data-end=&quot;494&quot; data-start=&quot;404&quot; data-ke-size=&quot;size16&quot;&gt;Java에 대한 문법은 책이나 온라인 강의로도 배울 수 있고,&lt;br /&gt;좋은 코드는 인터넷에도 많습니다.&lt;br /&gt;하지만 그걸 &lt;b&gt;스스로 처음부터 구현해 보는 경험&lt;/b&gt;은&lt;br /&gt;완전히 다른 차원의 학습입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;542&quot; data-start=&quot;496&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;511&quot; data-start=&quot;496&quot;&gt;어떤 클래스를 만들지&lt;/li&gt;
&lt;li data-end=&quot;526&quot; data-start=&quot;512&quot;&gt;어떤 책임을 나눌지&lt;/li&gt;
&lt;li data-end=&quot;542&quot; data-start=&quot;527&quot;&gt;어떤 흐름으로 동작시킬지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;587&quot; data-start=&quot;544&quot; data-ke-size=&quot;size16&quot;&gt;이걸 직접 고민하면서 만들어보는 경험은&lt;br /&gt;그 사람만의 사고 구조를 만듭니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;615&quot; data-start=&quot;594&quot; data-ke-size=&quot;size23&quot;&gt;  그다음, 비교가 시작됩니다&lt;/h3&gt;
&lt;p data-end=&quot;638&quot; data-start=&quot;617&quot; data-ke-size=&quot;size16&quot;&gt;강의는 이런 구조로 구성되어 있습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;813&quot; data-start=&quot;640&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;693&quot; data-start=&quot;640&quot;&gt;&lt;b&gt;먼저 혼자서 만들어본다&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;693&quot; data-start=&quot;665&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;693&quot; data-start=&quot;665&quot;&gt;요구사항 분석 &amp;rarr; 직접 구현 (실패해도 괜찮음)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;753&quot; data-start=&quot;695&quot;&gt;&lt;b&gt;강사(Kevin)의 구현과 비교해 본다&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;753&quot; data-start=&quot;724&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;753&quot; data-start=&quot;724&quot;&gt;클래스 분리, 책임 분산, 테스트 방식 등을 비교&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;813&quot; data-start=&quot;755&quot;&gt;&lt;b&gt;왜 그렇게 만들었는지 설명을 듣는다&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;813&quot; data-start=&quot;787&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;813&quot; data-start=&quot;787&quot;&gt;좋은 코드의 작성 패턴뿐만이 아니라 &amp;lsquo;생각의 흐름&amp;rsquo;을 공유&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;863&quot; data-start=&quot;815&quot; data-ke-size=&quot;size16&quot;&gt;이 과정은 단순한 코드 암기가 아니라&lt;br /&gt;&lt;b&gt;설계적 사고의 틀을 만드는 훈련&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;890&quot; data-start=&quot;870&quot; data-ke-size=&quot;size23&quot;&gt;  혼자 구현할 때 얻는 것&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;975&quot; data-start=&quot;892&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;912&quot; data-start=&quot;892&quot;&gt;내가 뭘 모르는지 알게 됩니다&lt;/li&gt;
&lt;li data-end=&quot;938&quot; data-start=&quot;913&quot;&gt;내가 자주 실수하는 패턴을 알게 됩니다&lt;/li&gt;
&lt;li data-end=&quot;975&quot; data-start=&quot;939&quot;&gt;내가 코드를 짤 때 어떤 순서로 생각하는지 점검할 수 있습니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1010&quot; data-start=&quot;977&quot; data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;나를 객관화할 수 있는 절호의 기회&lt;/b&gt;가 생깁니다.&lt;/p&gt;
&lt;p data-end=&quot;1118&quot; data-start=&quot;1012&quot; data-ke-size=&quot;size16&quot;&gt;그리고 강의의 비교 파트를 통해&lt;br /&gt;&amp;ldquo;아, 이렇게 구조를 바꿨더니 테스트가 쉬워졌네&amp;rdquo;&lt;br /&gt;&amp;ldquo;이건 책임을 나눈 게 훨씬 유연하구나&amp;rdquo;&lt;br /&gt;이런 인사이트를 얻는 순간,&lt;br /&gt;진짜 성장이 시작됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;1155&quot; data-start=&quot;1125&quot; data-ke-size=&quot;size23&quot;&gt;  코드가 아니라, '사고방식'을 배우는 강의&lt;/h3&gt;
&lt;p data-end=&quot;1191&quot; data-start=&quot;1157&quot; data-ke-size=&quot;size16&quot;&gt;이 강의는&lt;br /&gt;단순히 코드 예제를 따라 치는 강의가 아닙니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;1229&quot; data-start=&quot;1193&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1229&quot; data-start=&quot;1195&quot; data-ke-size=&quot;size16&quot;&gt;내가 구현 &amp;rarr; 비교 &amp;rarr; 리팩터링 &amp;rarr; 이해 &amp;rarr; 내 것으로 만들기&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;1309&quot; data-start=&quot;1231&quot; data-ke-size=&quot;size16&quot;&gt;이런 &lt;b&gt;자기 주도형 구조&lt;/b&gt;로 되어 있기 때문에,&lt;br /&gt;코드를 &amp;lsquo;이해&amp;rsquo;하는 것이 아니라&lt;br /&gt;&lt;b&gt;&amp;lsquo;생각하는 법&amp;rsquo;을 배우는 과정&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;1328&quot; data-start=&quot;1316&quot; data-ke-size=&quot;size23&quot;&gt;  참고 강의&lt;/h3&gt;
&lt;p data-end=&quot;1433&quot; data-start=&quot;1330&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://www.inflearn.com/course/java-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AF%B8%EB%8B%88%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%8A%B5&quot; data-start=&quot;1428&quot; data-end=&quot;1528&quot;&gt;Java 실무 프로젝트 입문편 - 객체지향 사고력 훈련 (미니 프로젝트 3종 실습)&lt;/a&gt;&lt;/p&gt;
&lt;p data-end=&quot;1568&quot; data-start=&quot;1435&quot; data-ke-size=&quot;size16&quot;&gt;정답 코드를 외우는 것이 아니라,&lt;br /&gt;&lt;b&gt;&amp;lsquo;왜 이렇게 만들었는가&amp;rsquo;를 함께 고민하고 성장하는 강의&lt;/b&gt;입니다.&lt;br /&gt;직접 구현해보고 비교해보는 방식은&lt;br /&gt;입문자에게도, 다시 기본을 다지고 싶은 분에게도&lt;br /&gt;가장 강력한 학습법이 될 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-end=&quot;1608&quot; data-start=&quot;1586&quot; data-ke-size=&quot;size26&quot;&gt;  시리즈 목차 &amp;amp; 다음 글 안내&lt;/h2&gt;
&lt;blockquote data-end=&quot;1694&quot; data-start=&quot;1610&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1694&quot; data-start=&quot;1612&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 Java 구현이 막막한 당신에게 시리즈의 일부입니다.&lt;br /&gt;스스로 구현하고 비교하는 과정을 통해, 사고력과 설계력을 함께 키워보세요!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;1709&quot; data-start=&quot;1696&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;시리즈 목차&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;1710&quot; data-end=&quot;1899&quot;&gt;
&lt;li data-start=&quot;1710&quot; data-end=&quot;1755&quot;&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/%E2%80%9CJava-%EB%AC%B8%EB%B2%95%EC%9D%80-%EC%95%84%EB%8A%94%EB%8D%B0-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%B4%EC%9A%94%E2%80%9D-%E2%80%93-%EA%B7%B8%EB%9F%B4-%EB%95%90-%EC%9D%B4%EB%A0%87%EA%B2%8C-%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot;&gt;[0편] Java 문법은 아는데 구현이 막막해요 &amp;ndash; 그럴 땐 이렇게 해봤습니다&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1756&quot; data-end=&quot;1789&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/1%ED%8E%B8-%EC%99%9C-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%A0%EA%B9%8C-%E2%80%93-Java-%EC%9E%85%EB%AC%B8%EC%9E%90%EC%9D%98-%EA%B3%A0%EB%AF%BC&quot;&gt;[1편] 왜 구현이 막막한가? &amp;ndash; Java 초심자의 고민&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1790&quot; data-end=&quot;1815&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/2%ED%8E%B8-%EC%9E%91%EC%9D%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5&quot;&gt;[2편] 작은 프로젝트에서 배우는 객체지향&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1816&quot; data-end=&quot;1857&quot;&gt;[3편] 강의 방식: 스스로 만들고, 비교하며 성장하기(현재 글)&lt;/li&gt;
&lt;li data-start=&quot;1858&quot; data-end=&quot;1899&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/4%ED%8E%B8-1%EB%B6%80-%EC%9D%B4%ED%9B%84-2%EB%B6%80%EC%97%90%EC%84%A0-%EC%96%B4%EB%96%A4-%EA%B1%B8-%EB%B0%B0%EC%9A%B0%EA%B2%8C-%EB%90%A0%EA%B9%8C&quot;&gt;[4편] 1부 이후, 2부에선 어떤 걸 배우게 될까?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1952&quot; data-start=&quot;1901&quot; data-ke-size=&quot;size16&quot;&gt;  다음 글: &lt;a href=&quot;https://itvillage.tistory.com/entry/4%ED%8E%B8-1%EB%B6%80-%EC%9D%B4%ED%9B%84-2%EB%B6%80%EC%97%90%EC%84%A0-%EC%96%B4%EB%96%A4-%EA%B1%B8-%EB%B0%B0%EC%9A%B0%EA%B2%8C-%EB%90%A0%EA%B9%8C&quot;&gt;&lt;b&gt;[4편] 1부 이후, 2부에선 어떤 걸 배우게 될까?&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Java 구현이 막막한 당신에게</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/77</guid>
      <comments>https://itvillage.tistory.com/entry/3%ED%8E%B8-%EA%B0%95%EC%9D%98-%EB%B0%A9%EC%8B%9D-%EC%8A%A4%EC%8A%A4%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EB%B9%84%EA%B5%90%ED%95%98%EB%A9%B0-%EC%84%B1%EC%9E%A5%ED%95%98%EA%B8%B0#entry77comment</comments>
      <pubDate>Wed, 26 Mar 2025 11:43:15 +0900</pubDate>
    </item>
    <item>
      <title>[2편] 작은 프로젝트에서 배우는 객체지향</title>
      <link>https://itvillage.tistory.com/entry/2%ED%8E%B8-%EC%9E%91%EC%9D%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5</link>
      <description>&lt;p data-end=&quot;328&quot; data-start=&quot;278&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;객체지향이 중요하대서 이론은 공부했는데...&lt;br /&gt;막상 어떻게 적용해야 할지 모르겠어요.&amp;rdquo;&lt;/p&gt;
&lt;p data-end=&quot;424&quot; data-start=&quot;330&quot; data-ke-size=&quot;size16&quot;&gt;Java를 공부한 입문자들에게 가장 많이 듣는 이야기입니다.&lt;br /&gt;책을 보고 개념은 어느 정도 이해했는데,&lt;br /&gt;&lt;b&gt;직접 객체지향으로 무언가를 만들어본 경험은 거의 없다&lt;/b&gt;는 거죠.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;460&quot; data-start=&quot;431&quot; data-ke-size=&quot;size23&quot;&gt;  객체지향은 '이해'보다 '적용'이 어렵다&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;626&quot; data-start=&quot;462&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;540&quot; data-start=&quot;462&quot;&gt;추상화, 캡슐화, 상속, 다형성&amp;hellip;&lt;br /&gt;책으로 보면 뭔가 멋져 보이는데&lt;br /&gt;막상 프로젝트에 쓰려고 하면 어디에 써야 할지 막막하죠.&lt;/li&gt;
&lt;li data-end=&quot;626&quot; data-start=&quot;542&quot;&gt;특히 작은 프로젝트를 만들다 보면&lt;br /&gt;&amp;ldquo;굳이 클래스를 나눠야 해?&amp;rdquo;, &amp;ldquo;이걸 인터페이스로 만드는 게 맞아?&amp;rdquo;&lt;br /&gt;같은 고민에 빠지게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;687&quot; data-start=&quot;628&quot; data-ke-size=&quot;size16&quot;&gt;그런데 바로 이런 &lt;b&gt;혼란과 시행착오 과정&lt;/b&gt;이&lt;br /&gt;진짜 객체지향을 습득하게 해주는 가장 좋은 기회입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;719&quot; data-start=&quot;694&quot; data-ke-size=&quot;size23&quot;&gt;  작은 프로젝트 = 객체지향 훈련장&lt;/h3&gt;
&lt;p data-end=&quot;800&quot; data-start=&quot;721&quot; data-ke-size=&quot;size16&quot;&gt;작은 콘솔 프로젝트라도&lt;br /&gt;역할 분리 &amp;rarr; 책임 분리 &amp;rarr; 메시지 전달 흐름을 따라가다 보면&lt;br /&gt;&lt;b&gt;자연스럽게 객체지향적인 사고가 시작됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;823&quot; data-start=&quot;802&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 흐름을 겪어보게 되죠:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;951&quot; data-start=&quot;825&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;854&quot; data-start=&quot;825&quot;&gt;기능 목록을 쪼개다가, 반복되는 책임을 발견하고&lt;/li&gt;
&lt;li data-end=&quot;880&quot; data-start=&quot;855&quot;&gt;해당 역할을 담당할 클래스를 하나 만들고&lt;/li&gt;
&lt;li data-end=&quot;921&quot; data-start=&quot;881&quot;&gt;그 클래스가 너무 커지면 다시 분리하거나 상속이나 인터페이스 구조를 고민하게 되고&lt;/li&gt;
&lt;li data-end=&quot;951&quot; data-start=&quot;922&quot;&gt;테스트를 해보면서 유연한 구조를 고민하게 되는&amp;hellip;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;1024&quot; data-start=&quot;953&quot; data-ke-size=&quot;size16&quot;&gt;이런 반복이 쌓이면서&lt;br /&gt;&lt;b&gt;자연스럽게 설계 감각이 생기고&lt;/b&gt;,&lt;br /&gt;&amp;ldquo;객체지향이 뭔지 이제 조금은 알겠어&amp;rdquo;라는 순간이 찾아옵니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;1058&quot; data-start=&quot;1031&quot; data-ke-size=&quot;size23&quot;&gt;  중요한 건 '정답'이 아니라 '시도'&lt;/h3&gt;
&lt;p data-end=&quot;1130&quot; data-start=&quot;1060&quot; data-ke-size=&quot;size16&quot;&gt;입문자분들이 흔히 하는 실수 중 하나는&lt;br /&gt;&amp;ldquo;객체지향 설계의 정답이 있겠지&amp;rdquo; 하고&lt;br /&gt;인터넷에서 구조를 그대로 베끼는 겁니다.&lt;/p&gt;
&lt;p data-end=&quot;1144&quot; data-start=&quot;1132&quot; data-ke-size=&quot;size16&quot;&gt;하지만 진짜 실력은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1224&quot; data-start=&quot;1145&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1160&quot; data-start=&quot;1145&quot;&gt;내가 먼저 만들어보고&lt;/li&gt;
&lt;li data-end=&quot;1173&quot; data-start=&quot;1161&quot;&gt;불편함을 느끼고&lt;/li&gt;
&lt;li data-end=&quot;1189&quot; data-start=&quot;1174&quot;&gt;더 좋은 구조를 찾고&lt;/li&gt;
&lt;li data-end=&quot;1224&quot; data-start=&quot;1190&quot;&gt;&lt;b&gt;다른 사람이 만든 코드의 구조와 비교하면서 배우는 과정&lt;/b&gt;에서 나옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1283&quot; data-start=&quot;1226&quot; data-ke-size=&quot;size16&quot;&gt;그렇기 때문에,&lt;br /&gt;처음부터 정답이라 생각되는 코드를 보지 않고&lt;br /&gt;&lt;b&gt;내가 먼저 직접 만들어보는 경험&lt;/b&gt;이 정말 중요합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;1315&quot; data-start=&quot;1290&quot; data-ke-size=&quot;size23&quot;&gt;  이 강의에서는 이런 훈련을 합니다&lt;/h3&gt;
&lt;p data-end=&quot;1353&quot; data-start=&quot;1317&quot; data-ke-size=&quot;size16&quot;&gt;강의에서는&lt;br /&gt;총 3가지 미니 프로젝트를 직접 만들어보게 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1427&quot; data-start=&quot;1355&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1373&quot; data-start=&quot;1355&quot;&gt;요구사항을 먼저 분석해 보고&lt;/li&gt;
&lt;li data-end=&quot;1390&quot; data-start=&quot;1374&quot;&gt;스스로 설계/구현한 뒤&lt;/li&gt;
&lt;li data-end=&quot;1427&quot; data-start=&quot;1391&quot;&gt;&lt;b&gt;강사(Kevin)가 구현한 버전과 비교하며&lt;/b&gt; 객체지향적 사고를 훈련합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1484&quot; data-start=&quot;1429&quot; data-ke-size=&quot;size16&quot;&gt;강의의 목적은&lt;br /&gt;&lt;b&gt;&amp;ldquo;정답 코드&amp;rdquo;를 주는 것이 아니라,&lt;br /&gt;&amp;ldquo;사고 방식&amp;rdquo;을 훈련하는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;1503&quot; data-start=&quot;1491&quot; data-ke-size=&quot;size23&quot;&gt;  참고 강의&lt;/h3&gt;
&lt;p data-end=&quot;1608&quot; data-start=&quot;1505&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://www.inflearn.com/course/java-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AF%B8%EB%8B%88%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%8A%B5&quot; data-start=&quot;1428&quot; data-end=&quot;1528&quot;&gt;Java 실무 프로젝트 입문편 - 객체지향 사고력 훈련 (미니 프로젝트 3종 실습)&lt;/a&gt;&lt;/p&gt;
&lt;p data-end=&quot;1664&quot; data-start=&quot;1610&quot; data-ke-size=&quot;size16&quot;&gt;3가지 미니 프로젝트를 통해&lt;br /&gt;입문자도 부담 없이 객체지향 감각을 키울 수 있도록 구성했습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-end=&quot;1704&quot; data-start=&quot;1682&quot; data-ke-size=&quot;size26&quot;&gt;  시리즈 목차 &amp;amp; 다음 글 안내&lt;/h2&gt;
&lt;blockquote data-end=&quot;1787&quot; data-start=&quot;1706&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1787&quot; data-start=&quot;1708&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 Java 구현이 막막한 당신에게 시리즈의 일부입니다.&lt;br /&gt;작고 반복 가능한 훈련으로, 객체지향에 한 걸음 더 다가가보세요  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;1802&quot; data-start=&quot;1789&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1802&quot; data-start=&quot;1789&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;시리즈 목차&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;1710&quot; data-end=&quot;1899&quot;&gt;
&lt;li data-start=&quot;1710&quot; data-end=&quot;1755&quot;&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/%E2%80%9CJava-%EB%AC%B8%EB%B2%95%EC%9D%80-%EC%95%84%EB%8A%94%EB%8D%B0-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%B4%EC%9A%94%E2%80%9D-%E2%80%93-%EA%B7%B8%EB%9F%B4-%EB%95%90-%EC%9D%B4%EB%A0%87%EA%B2%8C-%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot;&gt;[0편] Java 문법은 아는데 구현이 막막해요 &amp;ndash; 그럴 땐 이렇게 해봤습니다&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1756&quot; data-end=&quot;1789&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/1%ED%8E%B8-%EC%99%9C-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%A0%EA%B9%8C-%E2%80%93-Java-%EC%9E%85%EB%AC%B8%EC%9E%90%EC%9D%98-%EA%B3%A0%EB%AF%BC&quot;&gt;[1편] 왜 구현이 막막한가? &amp;ndash; Java 초심자의 고민&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1790&quot; data-end=&quot;1815&quot;&gt;[2편] 작은 프로젝트에서 배우는 객체지향(현재 글)&lt;/li&gt;
&lt;li data-start=&quot;1816&quot; data-end=&quot;1857&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/3%ED%8E%B8-%EA%B0%95%EC%9D%98-%EB%B0%A9%EC%8B%9D-%EC%8A%A4%EC%8A%A4%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EB%B9%84%EA%B5%90%ED%95%98%EB%A9%B0-%EC%84%B1%EC%9E%A5%ED%95%98%EA%B8%B0&quot;&gt;[3편] 강의 방식: 스스로 만들고, 비교하며 성장하기&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1858&quot; data-end=&quot;1899&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/4%ED%8E%B8-1%EB%B6%80-%EC%9D%B4%ED%9B%84-2%EB%B6%80%EC%97%90%EC%84%A0-%EC%96%B4%EB%96%A4-%EA%B1%B8-%EB%B0%B0%EC%9A%B0%EA%B2%8C-%EB%90%A0%EA%B9%8C&quot;&gt;[4편] 1부 이후, 2부에선 어떤 걸 배우게 될까?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2056&quot; data-start=&quot;2004&quot; data-ke-size=&quot;size16&quot;&gt;  다음 글: &lt;a href=&quot;https://itvillage.tistory.com/entry/3%ED%8E%B8-%EA%B0%95%EC%9D%98-%EB%B0%A9%EC%8B%9D-%EC%8A%A4%EC%8A%A4%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EB%B9%84%EA%B5%90%ED%95%98%EB%A9%B0-%EC%84%B1%EC%9E%A5%ED%95%98%EA%B8%B0&quot;&gt;&lt;b&gt;[3편] 강의 방식: 스스로 만들고, 비교하며 성장하기&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Java 구현이 막막한 당신에게</category>
      <category>java</category>
      <category>객체지향</category>
      <category>객체지향 설계</category>
      <category>설계 감각</category>
      <category>입문자 개발자</category>
      <category>자바</category>
      <category>자바 공부법</category>
      <category>자바 구현</category>
      <category>자바 미니 프로젝트</category>
      <category>자바 입문자</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/76</guid>
      <comments>https://itvillage.tistory.com/entry/2%ED%8E%B8-%EC%9E%91%EC%9D%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5#entry76comment</comments>
      <pubDate>Wed, 26 Mar 2025 11:42:27 +0900</pubDate>
    </item>
    <item>
      <title>[1편] 왜 구현이 막막할까? &amp;ndash; Java 입문자의 고민</title>
      <link>https://itvillage.tistory.com/entry/1%ED%8E%B8-%EC%99%9C-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%A0%EA%B9%8C-%E2%80%93-Java-%EC%9E%85%EB%AC%B8%EC%9E%90%EC%9D%98-%EA%B3%A0%EB%AF%BC</link>
      <description>&lt;p data-end=&quot;275&quot; data-start=&quot;251&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;Java 문법은 이제 대충 알겠는데 구현이 너무 어렵습니다.&amp;rdquo;&lt;/p&gt;
&lt;p data-end=&quot;397&quot; data-start=&quot;277&quot; data-ke-size=&quot;size16&quot;&gt;Java 입문자를 대상으로 강의를 하다 보면, 정말 자주 듣는 말입니다.&lt;br /&gt;심지어 객체지향이나 상속, 캡슐화 같은 개념도 알고 있어요.&lt;br /&gt;그런데 막상 &amp;ldquo;작은 애플리케이션 하나 구현해 보세요&amp;rdquo; 하면, 손이 멈춰버립니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;424&quot; data-start=&quot;404&quot; data-ke-size=&quot;size23&quot;&gt;  익숙하지만 낯선 그 순간&lt;/h3&gt;
&lt;p data-end=&quot;501&quot; data-start=&quot;426&quot; data-ke-size=&quot;size16&quot;&gt;Java는 분명히 익숙해졌는데,&lt;br /&gt;&amp;ldquo;뭐부터 해야하지?&amp;rdquo; , &amp;ldquo;어떤 클래스부터 만들지?&amp;rdquo;, &amp;ldquo;이걸 어떻게 나눠야 하지?&amp;rdquo;&lt;br /&gt;이런 질문 앞에서 멈칫하게 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;569&quot; data-start=&quot;503&quot; data-ke-size=&quot;size16&quot;&gt;이런 상황이 오는 이유는 단순히 &amp;lsquo;공부가 부족해서&amp;rsquo;가 아닙니다.&lt;br /&gt;오히려 &lt;b&gt;문법 위주로만 학습해 왔기 때문&lt;/b&gt;이죠.&lt;/p&gt;
&lt;p data-end=&quot;651&quot; data-start=&quot;571&quot; data-ke-size=&quot;size16&quot;&gt;클래스는 만들 수 있지만,&lt;br /&gt;&lt;b&gt;&amp;ldquo;왜&amp;rdquo; 만드는지&lt;/b&gt;, &lt;b&gt;&amp;ldquo;어떻게 역할을 나누는지&amp;rdquo;&lt;/b&gt;,&lt;br /&gt;이런 설계적 사고가 없으면 구현은 늘 막막합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;672&quot; data-start=&quot;658&quot; data-ke-size=&quot;size23&quot;&gt;  제자들의 사례&lt;/h3&gt;
&lt;p data-end=&quot;708&quot; data-start=&quot;674&quot; data-ke-size=&quot;size16&quot;&gt;제가 부트캠프나 인프런 강의에서 만난 많은 학생들이 그랬어요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;848&quot; data-start=&quot;710&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;754&quot; data-start=&quot;710&quot;&gt;CRUD는 할 줄 아는데, &lt;b&gt;어떤 기능부터 만들어야 할지 모르겠어요&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;804&quot; data-start=&quot;755&quot;&gt;Spring MVC 구조를 배우긴 했는데, &lt;b&gt;Controller가 모든 역할을 다 해버려요&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;848&quot; data-start=&quot;805&quot;&gt;객체지향이 중요하다는 건 알지만, &lt;b&gt;어떻게 적용해야 할지 감이 오질 않아요&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;899&quot; data-start=&quot;850&quot; data-ke-size=&quot;size16&quot;&gt;이런 분들이 공통적으로 말하던 건 하나입니다.&lt;br /&gt;&amp;ldquo;애플리케이션을 구현 하라고 하면, 막막해져요.&amp;rdquo;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;922&quot; data-start=&quot;906&quot; data-ke-size=&quot;size23&quot;&gt;  이유는 명확합니다&lt;/h3&gt;
&lt;p data-end=&quot;993&quot; data-start=&quot;924&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;문제 해결을 위한 사고력&amp;rdquo;을 훈련할 기회가 없었기 때문&lt;/b&gt;입니다.&lt;br /&gt;우리 사회는 &amp;lsquo;정답&amp;rsquo;을 배우는 데 익숙해져 있죠.&lt;/p&gt;
&lt;p data-end=&quot;1029&quot; data-start=&quot;995&quot; data-ke-size=&quot;size16&quot;&gt;하지만 구현은 &lt;b&gt;정답이 아니라 선택과 책임&lt;/b&gt;의 연속입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1112&quot; data-start=&quot;1031&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1056&quot; data-start=&quot;1031&quot;&gt;이 클래스는 어떤 역할을 맡아야 할까?&lt;/li&gt;
&lt;li data-end=&quot;1086&quot; data-start=&quot;1057&quot;&gt;어떤 책임을 다른 객체에게 위임할 수 있을까?&lt;/li&gt;
&lt;li data-end=&quot;1112&quot; data-start=&quot;1087&quot;&gt;변경이 생기면 어느 부분이 영향을 받을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1175&quot; data-start=&quot;1114&quot; data-ke-size=&quot;size16&quot;&gt;이런 고민은 코드를 많이 외운다고 생기는 게 아니라,&lt;br /&gt;&lt;b&gt;직접 부딪히고, 설계하고, 고치면서 생깁니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;1205&quot; data-start=&quot;1182&quot; data-ke-size=&quot;size23&quot;&gt;  그래서 이 시리즈를 시작합니다&lt;/h3&gt;
&lt;p data-end=&quot;1305&quot; data-start=&quot;1207&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 `Java 구현이 막막한 당신에게`라는 시리즈의 첫 번째 이야기입니다.&lt;br /&gt;저 역시 개발자로 입문했을 때 학생들과 같은 고민을 수없이 했기 때문에&lt;br /&gt;이제는 그 과정을 정리하고 함께 나누고 싶었습니다.&lt;/p&gt;
&lt;p data-end=&quot;1371&quot; data-start=&quot;1307&quot; data-ke-size=&quot;size16&quot;&gt;다음 편에서는,&lt;br /&gt;&lt;b&gt;작은 프로젝트 하나가 어떻게 객체지향 사고력을 키워주는지&lt;/b&gt;에 대해&lt;br /&gt;이야기해 보려 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-end=&quot;1390&quot; data-start=&quot;1378&quot; data-ke-size=&quot;size23&quot;&gt;  참고 강의&lt;/h3&gt;
&lt;p data-end=&quot;1528&quot; data-start=&quot;1392&quot; data-ke-size=&quot;size16&quot;&gt;이 시리즈는 아래 강의 내용을 기반으로 하고 있습니다:&lt;br /&gt;  &lt;a href=&quot;https://www.inflearn.com/course/java-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AF%B8%EB%8B%88%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%8A%B5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-end=&quot;1528&quot; data-start=&quot;1428&quot;&gt;Java 실무 프로젝트 입문편 - 객체지향 사고력 훈련 (미니 프로젝트 3종 실습)&lt;/a&gt;&lt;/p&gt;
&lt;p data-end=&quot;1642&quot; data-start=&quot;1530&quot; data-ke-size=&quot;size16&quot;&gt;강의에서는 먼저 스스로 만들어보고,&lt;br /&gt;이후 저의 설계와 구현을 비교해 보는 방식으로 학습이 진행됩니다.&lt;br /&gt;&lt;b&gt;정답이 아니라 사고력을 함께 키워가는 과정&lt;/b&gt;,&lt;br /&gt;그게 제가 이 강의를 만든 이유입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-end=&quot;1682&quot; data-start=&quot;1660&quot; data-ke-size=&quot;size26&quot;&gt;  시리즈 목차 &amp;amp; 다음 글 안내&lt;/h2&gt;
&lt;blockquote data-end=&quot;1750&quot; data-start=&quot;1684&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1750&quot; data-start=&quot;1686&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 `Java 구현이 막막한 당신에게` 시리즈의 일부입니다.&lt;br /&gt;차근차근 읽어나가며, 함께 성장해봐요  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;1765&quot; data-start=&quot;1752&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;시리즈 목차&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;1710&quot; data-end=&quot;1899&quot;&gt;
&lt;li data-start=&quot;1710&quot; data-end=&quot;1755&quot;&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/%E2%80%9CJava-%EB%AC%B8%EB%B2%95%EC%9D%80-%EC%95%84%EB%8A%94%EB%8D%B0-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%B4%EC%9A%94%E2%80%9D-%E2%80%93-%EA%B7%B8%EB%9F%B4-%EB%95%90-%EC%9D%B4%EB%A0%87%EA%B2%8C-%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot;&gt;[0편] Java 문법은 아는데 구현이 막막해요 &amp;ndash; 그럴 땐 이렇게 해봤습니다&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1756&quot; data-end=&quot;1789&quot;&gt;[1편] 왜 구현이 막막한가? &amp;ndash; Java 초심자의 고민(현재 글)&lt;/li&gt;
&lt;li data-start=&quot;1790&quot; data-end=&quot;1815&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/2%ED%8E%B8-%EC%9E%91%EC%9D%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5&quot;&gt;[2편] 작은 프로젝트에서 배우는 객체지향&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1816&quot; data-end=&quot;1857&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/3%ED%8E%B8-%EA%B0%95%EC%9D%98-%EB%B0%A9%EC%8B%9D-%EC%8A%A4%EC%8A%A4%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EB%B9%84%EA%B5%90%ED%95%98%EB%A9%B0-%EC%84%B1%EC%9E%A5%ED%95%98%EA%B8%B0&quot;&gt;[3편] 강의 방식: 스스로 만들고, 비교하며 성장하기&lt;/a&gt;&lt;/li&gt;
&lt;li data-start=&quot;1858&quot; data-end=&quot;1899&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itvillage.tistory.com/entry/4%ED%8E%B8-1%EB%B6%80-%EC%9D%B4%ED%9B%84-2%EB%B6%80%EC%97%90%EC%84%A0-%EC%96%B4%EB%96%A4-%EA%B1%B8-%EB%B0%B0%EC%9A%B0%EA%B2%8C-%EB%90%A0%EA%B9%8C&quot;&gt;[4편] 1부 이후, 2부에선 어떤 걸 배우게 될까?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2047&quot; data-start=&quot;1976&quot; data-ke-size=&quot;size16&quot;&gt;  다음 글: &lt;a href=&quot;https://itvillage.tistory.com/entry/2%ED%8E%B8-%EC%9E%91%EC%9D%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5&quot;&gt;&lt;b&gt;[2편] 작은 프로젝트에서 배우는 객체지향&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Java 구현이 막막한 당신에게</category>
      <category>java</category>
      <category>java 입문자</category>
      <category>객체지향</category>
      <category>객체지향 설계</category>
      <category>구현이 막막해요</category>
      <category>부트캠프</category>
      <category>자바</category>
      <category>자바 구현</category>
      <category>자바 미니 프로젝트</category>
      <category>자바 입문</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/75</guid>
      <comments>https://itvillage.tistory.com/entry/1%ED%8E%B8-%EC%99%9C-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%A0%EA%B9%8C-%E2%80%93-Java-%EC%9E%85%EB%AC%B8%EC%9E%90%EC%9D%98-%EA%B3%A0%EB%AF%BC#entry75comment</comments>
      <pubDate>Wed, 26 Mar 2025 11:27:15 +0900</pubDate>
    </item>
    <item>
      <title>[0편] &amp;ldquo;Java 문법은 아는데 구현이 막막해요&amp;rdquo; &amp;ndash; 그럴 땐 이렇게 했습니다</title>
      <link>https://itvillage.tistory.com/entry/%E2%80%9CJava-%EB%AC%B8%EB%B2%95%EC%9D%80-%EC%95%84%EB%8A%94%EB%8D%B0-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%B4%EC%9A%94%E2%80%9D-%E2%80%93-%EA%B7%B8%EB%9F%B4-%EB%95%90-%EC%9D%B4%EB%A0%87%EA%B2%8C-%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;781&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o8yyK/btsMTHsm2dg/sQszl21768v8bLE5SNSEHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o8yyK/btsMTHsm2dg/sQszl21768v8bLE5SNSEHk/img.png&quot; data-alt=&quot;[Java 실무 프로젝트 입문편] 객체지향 사고력 훈련 - 미니 프로젝트 3종 실습 강의 썸네일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o8yyK/btsMTHsm2dg/sQszl21768v8bLE5SNSEHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo8yyK%2FbtsMTHsm2dg%2FsQszl21768v8bLE5SNSEHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;781&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;781&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[Java 실무 프로젝트 입문편] 객체지향 사고력 훈련 - 미니 프로젝트 3종 실습 강의 썸네일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Java 프로젝트가 막막할 때, 작게 시작해 보세요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;Java 문법은 배웠는데, 직접 무언가를 구현하려니까 너무 막막했어요.&amp;rdquo;&lt;/b&gt; 이런 고민, 한 번쯤 해보셨나요? Java는 분명 문법도 많고, 클래스나 인터페이스 같은 개념도 어렵죠. 그래서 학원이나 부트캠프를 수료하고도 &lt;b&gt;&amp;ldquo;&lt;/b&gt;이제 뭘 만들어야 하지?&lt;b&gt;&amp;rdquo;&lt;/b&gt; 라며 길을 잃기 쉬운데요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Spring 전에, 작고 단단한 구현 경험이 필요합니다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 입문자들이 Java를 배운 뒤 곧바로 Spring으로 넘어가지만, &lt;b&gt;막상 프로젝트 구현을 시작하려면 손이 안 움직이는 경우&lt;/b&gt;가 많습니다. 그 이유는 간단합니다. &lt;b&gt;&amp;ldquo;코드를 직접 구조화해서 만들면서 익힌 적이 없기 때문&amp;rdquo;&lt;/b&gt;입니다.&lt;br /&gt;&lt;br /&gt;이 강의는 그런 분들을 위해 만들어졌습니다.&lt;br /&gt;&lt;br /&gt;✔️ 설계 ✔️ 구현 ✔️ 테스트 ✔️ 리팩토링 이 모든 단계를 직접 해보는 미니 프로젝트 실습 과정입니다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이 강의는 어떤 강의인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;작지만 핵심이 있는 3가지 Java 미니 프로젝트&lt;/b&gt;를 구현합니다. 좋은 코드 구조, 객체지향 설계, 테스트, 책임 분리 등 &lt;b&gt;실무에 필요한 감각을 처음부터 끝까지&lt;/b&gt; 경험할 수 있어요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;  &lt;b&gt;구구단 앱 (Stream API로 구현)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;  &lt;b&gt;할 일 관리 앱 - 설계와 테스트 중심 구현&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;  &lt;b&gt;할 일 관리 앱 - Google Tasks API 연동&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 만들어보며, &lt;b&gt;&amp;ldquo;내가 생각한 설계를 코드로 옮기는 훈련&amp;rdquo;&lt;/b&gt;을 할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이런 분들에게 꼭 추천합니다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Java 문법은 아는데, &lt;b&gt;뭘 만들어야 할지 모르는 분&lt;/b&gt; &lt;br /&gt;- &amp;ldquo;구현해 보라&amp;rdquo;는 말에 &lt;b&gt;두려움을 느끼는 분&lt;/b&gt; &lt;br /&gt;- Spring 전에 Java의 &lt;b&gt;기초를 실전처럼 다지고 싶은 분&lt;/b&gt; &lt;br /&gt;- 코드 품질, 구조, 설계에 대한 &lt;b&gt;감각을 키우고 싶은 분&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  수강 전 vs 수강 후, 이렇게 달라집니다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &amp;ldquo;요구사항을 코드로 옮기기 막막했어요&amp;rdquo; &amp;rarr; 클래스와 기능 단위로 구조화하는 능력이 생깁니다 &lt;br /&gt;- &amp;ldquo;객체지향 설계를 해본 적이 없어요&amp;rdquo; &amp;rarr; OOP 사고방식으로 실제 설계를 해봅니다 &lt;br /&gt;- &amp;ldquo;그냥 코드만 외우고 있었어요&amp;rdquo; &amp;rarr; 구조를 이해하고 직접 만들어보는 습관이 생깁니다 &lt;br /&gt;- &amp;ldquo;강의는 보는데, 코드가 머리에 안 남아요&amp;rdquo; &amp;rarr; 먼저 구현하고, 강사 코드와 비교하는 방식으로 배웁니다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;수강생의 실전 감각을 키우는 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 강의는 단순히 강사의 코드를 따라치는 방식이 아닙니다. &lt;b&gt;&amp;ldquo;요구사항 문서&amp;rdquo;를 바탕으로 먼저 스스로 구현한 후&lt;/b&gt;, 강사 코드와 비교해보며 더 나은 구조를 고민하게 됩니다.&lt;br /&gt;✅ 내가 먼저 만들고 ✅ 내가 비교하고 ✅ 내가 개선해보는 흐름 이게 바로 이 강의의 핵심 방식입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  지금 수강하면 할인된 가격에 수강할 수 있어요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2025년 4월 4일까지 30% 할인&lt;/b&gt;   &lt;a href=&quot;https://inf.run/ioMQi&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;강의 바로 가기 (인프런)&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  이 강의로 기대할 수 있는 것&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Java 객체지향 설계 감각 &lt;br /&gt;✔ 프로젝트 구조 설계 경험 &lt;br /&gt;✔ 코드 테스트 및 리팩토링 역량 &lt;br /&gt;✔ 강의 따라하기가 아닌 &lt;b&gt;진짜 실전 감각&lt;/b&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  관련 시리즈 안내&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 강의는 시리즈 1부입니다. &lt;br /&gt;2부에서는 Spring Core + H2 기반의 구조적 키오스크 시스템을 제작하게 됩니다. &lt;br /&gt;➡️ 시리즈 전체 구성을 통해 &lt;b&gt;Java &amp;rarr; Spring&lt;/b&gt;으로의 전환을 자연스럽게 경험하세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;구현은 어렵지만, 시작하면 생각보다 멀지 않습니다.&amp;rdquo;&lt;/b&gt; 작게, 단단하게, 확실하게 시작해보세요. 이 강의가 여러분의 첫 구조화된 프로젝트가 되길 바랍니다. &lt;br /&gt;&lt;br /&gt; &amp;zwj;♂️ 글쓴이: Kevin (Java 백엔드 강사 / 미니 프로젝트 실전 시리즈 제작자)&lt;/p&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;이&amp;nbsp;글은&amp;nbsp;[Java&amp;nbsp;구현이&amp;nbsp;막막한&amp;nbsp;당신에게]&amp;nbsp;시리즈의&amp;nbsp;시작입니다.&amp;nbsp;&amp;nbsp; &lt;br /&gt;아래 시리즈 글들을 통해, 작은 프로젝트로 객체지향 설계와 Java 애플리케이션 구현을 연습하는 여정을 함께 할 수 있었으면 좋겠네요.&lt;br /&gt;&lt;br /&gt; &amp;nbsp;관련&amp;nbsp;강의:&amp;nbsp;&amp;nbsp; &lt;br /&gt;[&lt;a href=&quot;https://www.inflearn.com/course/java-백엔드-미니프로젝트-실습&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Java&amp;nbsp;실무&amp;nbsp;프로젝트&amp;nbsp;입문편&amp;nbsp;-&amp;nbsp;객체지향&amp;nbsp;사고력&amp;nbsp;훈련&amp;nbsp;(미니&amp;nbsp;프로젝트&amp;nbsp;3종&amp;nbsp;실습)&lt;/a&gt;]&lt;/p&gt;
&lt;figure id=&quot;og_1743126285479&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;[Java 실무 프로젝트 입문편] 객체지향 사고력 훈련 - 미니 프로젝트 3종 실습 강의 | Kevin - 인프런&quot; data-og-description=&quot;Kevin | , [임베딩 영상]이런 분들께 추천해요Java 문법은 배웠지만, 실제로 어디서부터 어떻게 구현해야 할지 모르는 분Java 프로젝트 실습 경험이 부족한 분Spring을 배우긴 했지만, Java 기본기가 약&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/java-백엔드-미니프로젝트-실습&quot; data-og-url=&quot;https://www.inflearn.com/course/java-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AF%B8%EB%8B%88%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%8A%B5&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bG1cLW/hyYxEXJizQ/iwitNDSZAt8Gkcq0ppkPMk/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/jqyjd/hyYyVrbogR/53jj4KuRHi3saGPY0cLIk1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/bhmgC6/hyYub3UKdo/kmdc2pC4fyb37yPo5bopy0/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/java-백엔드-미니프로젝트-실습&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/java-백엔드-미니프로젝트-실습&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bG1cLW/hyYxEXJizQ/iwitNDSZAt8Gkcq0ppkPMk/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/jqyjd/hyYyVrbogR/53jj4KuRHi3saGPY0cLIk1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/bhmgC6/hyYub3UKdo/kmdc2pC4fyb37yPo5bopy0/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Java 실무 프로젝트 입문편] 객체지향 사고력 훈련 - 미니 프로젝트 3종 실습 강의 | Kevin - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Kevin | , [임베딩 영상]이런 분들께 추천해요Java 문법은 배웠지만, 실제로 어디서부터 어떻게 구현해야 할지 모르는 분Java 프로젝트 실습 경험이 부족한 분Spring을 배우긴 했지만, Java 기본기가 약&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1608&quot; data-start=&quot;1586&quot; data-ke-size=&quot;size26&quot;&gt;  시리즈 목차 &amp;amp; 다음 글 안내&lt;/h2&gt;
&lt;blockquote data-end=&quot;1694&quot; data-start=&quot;1610&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1694&quot; data-start=&quot;1612&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 Java 구현이 막막한 당신에게 시리즈의 일부입니다.&lt;br /&gt;스스로 구현하고 비교하는 과정을 통해, 사고력과 설계력을 함께 키워보세요!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;1709&quot; data-start=&quot;1696&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;시리즈 목차&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1899&quot; data-start=&quot;1710&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1755&quot; data-start=&quot;1710&quot;&gt;[0편] Java 문법은 아는데 구현이 막막해요 &amp;ndash; 그럴 땐 이렇게 해봤습니다(현재 글)&lt;/li&gt;
&lt;li data-end=&quot;1789&quot; data-start=&quot;1756&quot;&gt;&lt;a href=&quot;https://itvillage.tistory.com/entry/1%ED%8E%B8-%EC%99%9C-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%A0%EA%B9%8C-%E2%80%93-Java-%EC%9E%85%EB%AC%B8%EC%9E%90%EC%9D%98-%EA%B3%A0%EB%AF%BC&quot;&gt;[1편] 왜 구현이 막막한가? &amp;ndash; Java 초심자의 고민&lt;/a&gt;&lt;/li&gt;
&lt;li data-end=&quot;1815&quot; data-start=&quot;1790&quot;&gt;&lt;a href=&quot;https://itvillage.tistory.com/entry/2%ED%8E%B8-%EC%9E%91%EC%9D%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5&quot;&gt;[2편] 작은 프로젝트에서 배우는 객체지향&lt;/a&gt;&lt;/li&gt;
&lt;li data-end=&quot;1857&quot; data-start=&quot;1816&quot;&gt;&lt;a href=&quot;https://itvillage.tistory.com/entry/3%ED%8E%B8-%EA%B0%95%EC%9D%98-%EB%B0%A9%EC%8B%9D-%EC%8A%A4%EC%8A%A4%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EB%B9%84%EA%B5%90%ED%95%98%EB%A9%B0-%EC%84%B1%EC%9E%A5%ED%95%98%EA%B8%B0&quot;&gt;[3편] 강의 방식: 스스로 만들고, 비교하며 성장하기&lt;/a&gt;&lt;/li&gt;
&lt;li data-end=&quot;1899&quot; data-start=&quot;1858&quot;&gt;&lt;a href=&quot;https://itvillage.tistory.com/entry/4%ED%8E%B8-1%EB%B6%80-%EC%9D%B4%ED%9B%84-2%EB%B6%80%EC%97%90%EC%84%A0-%EC%96%B4%EB%96%A4-%EA%B1%B8-%EB%B0%B0%EC%9A%B0%EA%B2%8C-%EB%90%A0%EA%B9%8C&quot;&gt;[4편] 1부 이후, 2부에선 어떤 걸 배우게 될까?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1952&quot; data-start=&quot;1901&quot; data-ke-size=&quot;size16&quot;&gt;  다음 글: &lt;a href=&quot;https://itvillage.tistory.com/entry/1%ED%8E%B8-%EC%99%9C-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%A0%EA%B9%8C-%E2%80%93-Java-%EC%9E%85%EB%AC%B8%EC%9E%90%EC%9D%98-%EA%B3%A0%EB%AF%BC&quot;&gt;&lt;b&gt;[1편] 1부 이후, 2부에선 어떤 걸 배우게 될까?&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Java 구현이 막막한 당신에게</category>
      <category>java 구현 훈련</category>
      <category>java 미니 프로젝트</category>
      <category>java 실습 강의</category>
      <category>객체지향 설계 실습</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/74</guid>
      <comments>https://itvillage.tistory.com/entry/%E2%80%9CJava-%EB%AC%B8%EB%B2%95%EC%9D%80-%EC%95%84%EB%8A%94%EB%8D%B0-%EA%B5%AC%ED%98%84%EC%9D%B4-%EB%A7%89%EB%A7%89%ED%95%B4%EC%9A%94%E2%80%9D-%E2%80%93-%EA%B7%B8%EB%9F%B4-%EB%95%90-%EC%9D%B4%EB%A0%87%EA%B2%8C-%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4#entry74comment</comments>
      <pubDate>Sun, 23 Mar 2025 07:46:58 +0900</pubDate>
    </item>
    <item>
      <title>Java Stream API 한방에 이해하기</title>
      <link>https://itvillage.tistory.com/entry/Java-Stream-API-%ED%95%9C%EB%B0%A9%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
      <description>&lt;h1&gt;Java의 Stream은 위에서 아래로 흐르는 시냇물이다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래밍에서 데이터를 처리할 때, 우리는 보통 &lt;code&gt;for&lt;/code&gt; 문을 이용한 반복문을 먼저 떠올립니다.&lt;br /&gt;하지만 Java 8부터 등장한 Stream API는 완전히 다른 방식으로 데이터를 다루게 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream은 마치 위에서 아래로 흐르는 시냇물처럼&lt;br /&gt;컬렉션(List, Set 등)의 데이터를 흘려보내며&lt;br /&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;  코드 스니핏 (Java):&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; names = List.of(&quot;kevin&quot;, &quot;tom&quot;, &quot;emma&quot;);

names.stream()
    .filter(name -&amp;gt; name.length() &amp;gt;= 5)
    .map(String::toUpperCase)
    .forEach(System.out::println);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 &lt;code&gt;names&lt;/code&gt; 리스트에 담긴 데이터를&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;길이가 5 이상인지 판단해서 필터링해주고 (&lt;code&gt;filter&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;대문자로 변환한 후에 (&lt;code&gt;map&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;한 줄씩 출력 (&lt;code&gt;forEach&lt;/code&gt;) 하는 구조입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Stream이 제공하는 선언형 스타일을 통해&lt;br /&gt;하나의 흐름 속에서 데이터를 가공하고 처리하는 전체 과정을 명확하게 표현할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;Stream은 각자 자기 할 일만 한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream의 구성은 체이닝 구조로 이루어져 있으며, 각 연산은 자신이 맡은 역할만 수행합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;filter()&lt;/code&gt;: 조건을 걸어 원하는 요소만 통과시킵니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;map()&lt;/code&gt;: 데이터를 다른 형식으로 변환합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sorted()&lt;/code&gt;: 데이터를 정렬합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;collect()&lt;/code&gt;: 최종 결과를 컬렉션 형태로 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;forEach()&lt;/code&gt;: 하나씩 꺼내서 소비(처리)합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 코드처럼 각 연산은 파이프라인처럼 연결되어 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  코드 스니핏 (Java):&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; cities = List.of(&quot;Seoul&quot;, &quot;Busan&quot;, &quot;Incheon&quot;);

cities.stream()
    .filter(city -&amp;gt; city.length() &amp;gt; 5)
    .map(String::toLowerCase)
    .forEach(System.out::println);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;filter()&lt;/code&gt;는 길이가 5보다 큰 도시만 남기고,&lt;br /&gt;&lt;code&gt;map()&lt;/code&gt;은 해당 도시명을 소문자로 바꾸며,&lt;br /&gt;&lt;code&gt;forEach()&lt;/code&gt;는 최종 출력 처리를 담당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단계는 &lt;b&gt;자기 할 일만 수행&lt;/b&gt;하고,&lt;br /&gt;다른 연산과는 &lt;b&gt;독립적으로 설계&lt;/b&gt;되어 있어 유지보수가 쉬워집니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;Stream은 &amp;lsquo;대용량 데이터&amp;rsquo;를 흘려 보내기 위해 태어났다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 사람들이 처음엔 &amp;ldquo;for문 대신 쓰면 깔끔하네?&amp;rdquo;라고 Stream을 이해하지만,&lt;br /&gt;사실 Java가 Stream을 만든 진짜 이유는 따로 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 바로 &lt;b&gt;대용량 데이터를 효율적으로 다루기 위해서&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 방식은 데이터를 전부 메모리에 올려놓고 처리했지만 Stream은 데이터를 &lt;b&gt;하나씩 읽는 순간 바로 처리&lt;/b&gt;할 수 있습니다.&lt;br /&gt;즉, 메모리를 덜 사용하고, 처리 효율도 훨씬 높다는 말이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말이 이해되지 않는 다면 다음 예를 통해 Stream의 중요한 특징을 이해해 보세요.&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border: 1px dashed #c1c1c1;&quot;&gt;&lt;button style=&quot;background-color: orange; color: white; border: none; padding: 5px;&quot;&gt;Note&lt;/button&gt;
&lt;p data-end=&quot;150&quot; data-start=&quot;117&quot; data-ke-size=&quot;size16&quot;&gt;공장에서 하루에 라면을 1만 개 생산한다고 가정해봅시다.&lt;/p&gt;
&lt;p data-end=&quot;234&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;만약 면 1만 개를 &lt;b&gt;모두 만든 다음&lt;/b&gt;에 포장 작업을 시작하려면,&lt;br /&gt;완성된 면을 담아둘 &lt;b&gt;엄청나게 큰 그릇&lt;/b&gt;이 필요하겠죠.&lt;/p&gt;
&lt;p data-end=&quot;311&quot; data-start=&quot;241&quot; data-ke-size=&quot;size16&quot;&gt;반면에 면이 &lt;b&gt;하나 만들어질 때마다 바로 포장 단계로 넘어간다면&lt;/b&gt;,&lt;br /&gt;그릇 같은 중간 저장 공간은 필요 없습니다.&lt;/p&gt;
&lt;p data-end=&quot;370&quot; data-start=&quot;318&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 각 공정이 바로바로 이어지는 방식이 바로 &lt;b&gt;Stream의 처리 방식&lt;/b&gt;과 유사합니다.&lt;/p&gt;
&lt;p data-end=&quot;370&quot; data-start=&quot;318&quot; data-ke-size=&quot;size16&quot;&gt;공정이 바로바로 이어진다는 의미는 1만 개의 면이 모두 만들어질 때까지 기다릴 필요가 없다는 뜻이기 때문에 &lt;b&gt;공정 시간 또한 단축될 수 있다는 의미&lt;/b&gt;입니다.&lt;/p&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;  아래 그림은 방금 설명한 Stream의 처리 흐름을 시각적으로 표현한 것입니다.&lt;br /&gt;Java Stream이 데이터를 처리하는 과정을 &lt;b&gt;공장에서 제품을 생산할 때 사용하는 컨베이어 벨트에서의 흐름&lt;/b&gt;으로 그려봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  그림: 데이터소스에서 데이터가 하나씩 흐르며 filter &amp;rarr; map &amp;rarr; collect 단계를 거치는 구조&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 3월 27일 오후 11_37_26.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byY93U/btsMZrbL19s/3zho4O8tfar6dPkB29MWk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byY93U/btsMZrbL19s/3zho4O8tfar6dPkB29MWk0/img.png&quot; data-alt=&quot;데이터 소스에서 데이터가 하나씩 흐르며 filter &amp;amp;rarr; map &amp;amp;rarr; collect 단계를 거치는 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byY93U/btsMZrbL19s/3zho4O8tfar6dPkB29MWk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyY93U%2FbtsMZrbL19s%2F3zho4O8tfar6dPkB29MWk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2025년 3월 27일 오후 11_37_26.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데이터 소스에서 데이터가 하나씩 흐르며 filter &amp;rarr; map &amp;rarr; collect 단계를 거치는 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 그림을 보면, Stream이 어떤 구조로 데이터를 처리하는지 한눈에 이해할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 여러분의 이해를 돕기 위해 코드 예시로 설명하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 파일에서 한 줄씩 데이터를 읽고 싶다고 해봅시다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기존 방식:&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  코드 스니핏 (Java):&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; lines = Files.readAllLines(Path.of(&quot;data.txt&quot;));
for (String line : lines) {
    if (line.contains(&quot;ERROR&quot;)) {
        System.out.println(line.toUpperCase());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 &lt;code&gt;data.txt&lt;/code&gt;의 모든 라인을 &lt;code&gt;List&amp;lt;String&amp;gt;&lt;/code&gt;에 한꺼번에 읽어들인 뒤,&lt;br /&gt;해당 리스트를 반복문으로 순회하며 원하는 라인을 찾아 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 직관적이고 익숙하게 느껴질 수 있지만,&lt;br /&gt;&lt;b&gt;문제가 되는 건 파일 크기입니다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일에 수천만 줄이 있다면, 그 모든 데이터를 한꺼번에 메모리에 올리게 되며&lt;br /&gt;&amp;rarr; &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;메모리 사용량이 급증하고 OutOfMemoryError가 발생할 위험&lt;/b&gt;&lt;/span&gt;도 있습니다.&lt;/li&gt;
&lt;li&gt;모든 데이터를 메모리에 읽어놓고 그 후에야 처리하므로&lt;br /&gt;&amp;rarr; &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;처리 지연&lt;/b&gt;&lt;/span&gt;이 발생하며, 반응성도 낮습니다.&lt;/li&gt;
&lt;li&gt;실시간으로 라인이 계속 추가되는 로그 파일이라면&lt;br /&gt;&amp;rarr; 매번 파일을 다시 읽어야 하므로 비효율적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stream 방식:&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  코드 스니핏 (Java):&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;try (Stream&amp;lt;String&amp;gt; lines = Files.lines(Path.of(&quot;data.txt&quot;))) {
    lines.filter(line -&amp;gt; line.contains(&quot;ERROR&quot;))
         .map(String::toUpperCase)
         .forEach(System.out::println);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 모든 데이터를 한꺼번에 메모리에 올릴때 까지 기다리는게 아니라 파일에서 한 줄씩 읽어오면서&lt;br /&gt;조건에 맞는 줄만 필터링하고, 대문자로 변환 후 출력까지 이어집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 줄씩 읽고 처리하므로 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;메모리 사용량이 매우 적습니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Stream의 lazy evaluation(지연 처리) 덕분에 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;필요한 작업만 최소로 수행합니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;처리 속도도 빨라지고, 대용량 로그 처리나 실시간 분석에도 적합합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;Java의 Stream과 Lambda는 베스트 프렌드이다. 그래서 게으르다&lt;/h1&gt;
&lt;p data-end=&quot;293&quot; data-start=&quot;183&quot; data-ke-size=&quot;size16&quot;&gt;Java의 Stream은 선언형 API입니다.&lt;br /&gt;즉, &amp;ldquo;어떻게 처리할지&amp;rdquo;를 구체적으로 지시하기보다는&lt;br /&gt;&amp;ldquo;이렇게 처리하겠다&amp;rdquo;는 &lt;b&gt;의도를 선언&lt;/b&gt;하는 방식으로 코드를 작성하게 해줍니다.&lt;/p&gt;
&lt;p data-end=&quot;397&quot; data-start=&quot;300&quot; data-ke-size=&quot;size16&quot;&gt;그런데 재미있는 점은, 이렇게 처리 흐름을 선언해도&lt;br /&gt;&lt;b&gt;최종 연산(forEach, collect 등)이 실행되기 전까지는 아무런 동작도 하지 않는다는 점&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;453&quot; data-start=&quot;404&quot; data-ke-size=&quot;size16&quot;&gt;이런 동작 방식을 우리는 lazy evaluation(지연 처리)라고 부르죠.&lt;/p&gt;
&lt;p data-end=&quot;557&quot; data-start=&quot;460&quot; data-ke-size=&quot;size16&quot;&gt;그리고 이 Stream은 Lambda(람다)와 함께 사용할 때 그 진가가 드러납니다.&lt;br /&gt;바로 반복적인 처리를 훨씬 더 간결하고 직관적으로 표현할 수 있기 때문이에요.&lt;/p&gt;
&lt;p data-end=&quot;557&quot; data-start=&quot;460&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  코드 스니핏 (Java):&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; list = List.of(&quot;apple&quot;, &quot;banana&quot;, &quot;grape&quot;);

list.stream()
    .filter(s -&amp;gt; s.startsWith(&quot;b&quot;))
    .map(String::toUpperCase)
    .forEach(System.out::println);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 코드는 먼저 &quot;b&quot;로 시작하는 문자열만 골라낸 다음,&lt;br /&gt;그 문자열을 대문자로 변환해서 출력합니다.여기서 중요한 건, 리스트에 있는 모든 데이터를 한꺼번에 처리하는 것이 아니라&lt;br /&gt;&lt;b&gt;필요한 데이터만 골라서&lt;/b&gt;, 그때그때 &lt;b&gt;필요한 작업만 수행&lt;/b&gt;한다는 점이에요.&lt;br /&gt;이런 방식이 바로 &lt;b&gt;Stream의 지연 처리(lazy evaluation)&lt;/b&gt; 특성을 잘 보여줍니다.&lt;/li&gt;
&lt;li data-end=&quot;420&quot; data-start=&quot;389&quot;&gt;따라서 실행 결과는 &quot;BANANA&quot; 하나만 출력되죠.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;Stream을 어떨 때 사용하면 좋을까?&lt;/h1&gt;
&lt;p data-end=&quot;266&quot; data-start=&quot;164&quot; data-ke-size=&quot;size16&quot;&gt;Java Stream은 단순히 코드를 깔끔하게 만드는 것뿐 아니라, &lt;b&gt;데이터를 다루는 다양한 상황에서 효율적인 해결책&lt;/b&gt;이 되어줍니다.&lt;br /&gt;다음과 같은 경우에 특히 효과적이에요:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;726&quot; data-start=&quot;268&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;352&quot; data-start=&quot;268&quot;&gt;반복 작업이 많고, 데이터를 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;필터링하거나 가공하는 로직이 자주 등장할 때&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;(예: 조건에 맞는 값만 골라내고, 형식을 바꾸는 작업 등)&lt;/li&gt;
&lt;li data-end=&quot;448&quot; data-start=&quot;354&quot;&gt;기존 코드가 너무 복잡해서 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;가독성이나 유지보수에 부담을 느낄 때&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;rarr; Stream은 처리 흐름을 명확하게 보여주기 때문에, 읽기 쉽고 수정도 편해집니다.&lt;/li&gt;
&lt;li data-end=&quot;551&quot; data-start=&quot;450&quot;&gt;파일, DB, API 등에서 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;연속적으로 들어오는 데이터를 실시간으로 처리해야 할 때&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&amp;rarr; 한꺼번에 데이터를 모두 모으지 않고, 한 줄씩 흘려보내며 처리할 수 있어요.&lt;/li&gt;
&lt;li data-end=&quot;644&quot; data-start=&quot;553&quot;&gt;다루는 데이터의 양이 크고, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;메모리 사용량을 최소화하면서 처리하고 싶을 때&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;rarr; 필요한 데이터만 읽고, 처리하고, 버리기 때문에 훨씬 효율적입니다.&lt;/li&gt;
&lt;li data-end=&quot;726&quot; data-start=&quot;646&quot;&gt;성능이 중요한 상황에서 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;병렬 처리(parallel stream)를 통해&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;데이터를 동시에 나눠서 처리하고 싶을 때도 유용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;실제로 어떻게 써보면 좋을까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream의 개념을 이해했더라도, 실제로 써보지 않으면 감이 잘 안 올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 저는 Stream의 개념만 이해하는 예제가 아니라 Stream을 애플리케이션 구현에 실제로 적용해 볼 수 있는 실사용 예를 강의에 담았습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구구단 애플리케이션 실습: Stream 기본 처리 흐름을 익히기 좋은 입문 실습&lt;/li&gt;
&lt;li&gt;사전지식 실습 문제: 조금 더 복잡한 데이터 가공 흐름을 직접 경험&lt;/li&gt;
&lt;li&gt;할일 관리 애플리케이션 프로젝트: 리스트 필터링, 상태 변경, 정렬 등&lt;br /&gt;실제 서비스에서 발생할 수 있는 상황을 Stream으로 구현해볼 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 문법만 배우는 게 아니라, Stream을 &lt;b&gt;&amp;ldquo;실제로 사용할 수 있는 수준까지&amp;rdquo;&lt;/b&gt; 도달할 수 있게 도와주는 강의입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  Java 백엔드 미니 프로젝트 실습 강의 보기&lt;br /&gt;&lt;a href=&quot;https://www.inflearn.com/course/java-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AF%B8%EB%8B%88%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%8A%B5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.inflearn.com/course/java-백엔드-미니프로젝트-실습&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743124386694&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;[Java 실무 프로젝트 입문편] 객체지향 사고력 훈련 - 미니 프로젝트 3종 실습 강의 | Kevin - 인프런&quot; data-og-description=&quot;Kevin | , [임베딩 영상]이런 분들께 추천해요Java 문법은 배웠지만, 실제로 어디서부터 어떻게 구현해야 할지 모르는 분Java 프로젝트 실습 경험이 부족한 분Spring을 배우긴 했지만, Java 기본기가 약&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/java-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AF%B8%EB%8B%88%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%8A%B5&quot; data-og-url=&quot;https://www.inflearn.com/course/java-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AF%B8%EB%8B%88%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%8A%B5&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lGrql/hyYyPYNvIT/ydkV22AaHnseENAaGxxZK0/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/Ehs83/hyYvnbPrFq/571AaqgD3WpWPMCiimmSc1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/VjA4A/hyYvq0FUe8/Klo5z15LxiPtQP4UsFBXNK/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/java-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AF%B8%EB%8B%88%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%8A%B5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/java-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AF%B8%EB%8B%88%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%8A%B5&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lGrql/hyYyPYNvIT/ydkV22AaHnseENAaGxxZK0/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/Ehs83/hyYvnbPrFq/571AaqgD3WpWPMCiimmSc1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/VjA4A/hyYvq0FUe8/Klo5z15LxiPtQP4UsFBXNK/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Java 실무 프로젝트 입문편] 객체지향 사고력 훈련 - 미니 프로젝트 3종 실습 강의 | Kevin - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Kevin | , [임베딩 영상]이런 분들께 추천해요Java 문법은 배웠지만, 실제로 어디서부터 어떻게 구현해야 할지 모르는 분Java 프로젝트 실습 경험이 부족한 분Spring을 배우긴 했지만, Java 기본기가 약&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Java</category>
      <category>java</category>
      <category>java 8</category>
      <category>Java Stream</category>
      <category>java 미니 프로젝트</category>
      <category>Lambda</category>
      <category>Stream API</category>
      <category>대용량 처리</category>
      <category>람다식</category>
      <category>자바</category>
      <category>자바 기초</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/72</guid>
      <comments>https://itvillage.tistory.com/entry/Java-Stream-API-%ED%95%9C%EB%B0%A9%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0#entry72comment</comments>
      <pubDate>Thu, 13 Mar 2025 22:23:03 +0900</pubDate>
    </item>
    <item>
      <title>JwtAuthorizationFilter에 try ~ catch 문이 있고, 없고의 차이</title>
      <link>https://itvillage.tistory.com/entry/JwtAuthorizationFilter%EC%97%90-try-catch-%EB%AC%B8%EC%9D%B4-%EC%9E%88%EA%B3%A0-%EC%97%86%EA%B3%A0%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security 기반의 애플리케이션에 아이디/패스워드 로그인 인증 후, 전달 받은 JWT를 이용해 서버 측 리소스에 접근한다고 가정해 봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, 매 요청마다 JWT를 request header에 포함해서 요청 전송을 할텐데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 JWT를 JwtAuthorizationFilter가 검증할 경우, 두 가지 케이스를 테스트 해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JwtAuthorizationFilter에서 인증이나 접근 권한 등의 예외를 &lt;span style=&quot;color: #ef5369;&quot;&gt;try ~ catch로 잡는 경우&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1702895470833&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class JwtVerificationFilter extends OncePerRequestFilter {
    private final JwtTokenizer jwtTokenizer;
    private final CustomAuthorityUtils authorityUtils;

    public JwtVerificationFilter(JwtTokenizer jwtTokenizer,
                                 CustomAuthorityUtils authorityUtils) {
        this.jwtTokenizer = jwtTokenizer;
        this.authorityUtils = authorityUtils;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
       // JWT 검증 시, 발생하는 예외를 try ~ catch 문으로 catch 하는 경우
       try {
            Map&amp;lt;String, Object&amp;gt; claims = verifyJws(request);
            setAuthenticationToContext(claims);
        } catch (Exception e) {
            request.setAttribute(&quot;exception&quot;, e);
        }

        filterChain.doFilter(request, response);
    }
    
    ...
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SecurityFilterChain 동작이 ExceptionTranslationFilter 까지 넘어 간다.&lt;/li&gt;
&lt;li&gt;ExceptionTranslationFilter에서 다시 AuthorizationFilter로 동작이 넘어간다.&lt;/li&gt;
&lt;li&gt;AuthorizationDecision.isGranted()이기 때문에 AccessDeniedException이 throw된다.&lt;/li&gt;
&lt;li&gt;ExceptionTranslationFilter에서 예외를 catch 한다.&lt;/li&gt;
&lt;li&gt;ExceptionTranslationFilter 내부에서 AccessDeniedException을 처리하는 로직을 수행한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;handleAccessDeniedException()&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증 여부를 체크해서 인증이 되지 않았으면 &lt;span style=&quot;color: #ef5369;&quot;&gt;AuthenticationEntryPoint&lt;/span&gt;를 이용해 예외를 처리한다.&lt;/li&gt;
&lt;li&gt;인증은 된 상태이고 권한이 적절하지 않다면&amp;nbsp; &lt;span style=&quot;color: #ef5369;&quot;&gt;AccessDeniedHandler&lt;/span&gt;를 이용해 예외를 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JwtAuthorizationFilter에서 인증이나 접근 권한 등의 예외를 &lt;span style=&quot;color: #ef5369;&quot;&gt;try ~ catch로 잡지 않는 경우&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1702895407285&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class JwtVerificationFilter extends OncePerRequestFilter {
    private final JwtTokenizer jwtTokenizer;
    private final CustomAuthorityUtils authorityUtils;

    public JwtVerificationFilter(JwtTokenizer jwtTokenizer,
                                 CustomAuthorityUtils authorityUtils) {
        this.jwtTokenizer = jwtTokenizer;
        this.authorityUtils = authorityUtils;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // JWT 검증 시, try ~ catch 문으로 catch 하지 않는 경우
        Map&amp;lt;String, Object&amp;gt; claims = verifyJws(request);

        filterChain.doFilter(request, response);
    }

    ...
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예외가 throw 되어서 톰캣 같은 서블릿 컨테이너 레벨까지 전파된다.&lt;/li&gt;
&lt;li&gt;`/error` URL로 리다이렉트 된다.&lt;/li&gt;
&lt;li&gt;Spring Framework의 BasicErrorController의 error() 핸들러 메서드가 `/error` 요청을 수신한다.&lt;/li&gt;
&lt;li&gt;디폴트 에러 메시지를 JSON 형태로 리턴한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java Backend 개발자 되기/Spring Security</category>
      <category>authorization</category>
      <category>jwt</category>
      <category>security filter</category>
      <category>Spring Security</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/71</guid>
      <comments>https://itvillage.tistory.com/entry/JwtAuthorizationFilter%EC%97%90-try-catch-%EB%AC%B8%EC%9D%B4-%EC%9E%88%EA%B3%A0-%EC%97%86%EA%B3%A0%EC%9D%98-%EC%B0%A8%EC%9D%B4#entry71comment</comments>
      <pubDate>Mon, 18 Dec 2023 18:45:43 +0900</pubDate>
    </item>
    <item>
      <title>ObjectMapper를 이용한 JSON 역직렬화(Deserialization) 이야기</title>
      <link>https://itvillage.tistory.com/entry/ObjectMapper%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-JSON-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94Deserialization-%EC%9D%B4%EC%95%BC%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring MVC에서 ObjectMapper로 json 포맷의 request body를 DTO 객체로 역직렬화 하는 방법은 크게 두 가지이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디폴트 생성자가 있을 경우,&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우는 다음과 같은 순서를 따라 역직렬화를 수행한다. 역직렬화의 시작점은 BeanDeserializer이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;BeanDeserializer.deserializeFromObject()에서 디폴트 생성자를 이용한 리플렉션 기법(Constructor.newInstance(Object[] args)으로 DTO 객체를 생성한다.&lt;/li&gt;
&lt;li&gt;FieldProperty.deserializeAndSet(...)에서 리플렉션으로 얻어온 Field에 JSON 포맷에서 얻은 값을 set한다. 여기서의 Field는 DTO 객체의 멤버 필드의 메타 정보를 포함한 객체이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디폴트 생성자가 있을 경우의 결론은 &lt;span style=&quot;color: #ef5369;&quot;&gt;디폴트 생성자를 이용해서 Java의 리플렉션을 이용해 Field에 값을 세팅한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디폴트 생성자가 없을 경우,&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 디폴트 생성자가 없을 경우이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우는 IntelliJ에서 Gradle 빌드 옵션(IntelliJ &amp;gt; Settings &amp;gt; Build, Execution, Deployment &amp;gt; Build Tools &amp;gt; Gradle)을 Gradle(default)로 설정하느냐 IntelliJ로 설정하느냐에 따라 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;여기서의 핵심은 PropertyBasedCreator 객체가 null이냐 그렇지 않느냐이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Gradle 빌드 옵션을 Gradle(default)로 설정할 경우,&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;디폴트 생성자가 없어도 argument가 있는 Constructor 정보를 이용한 리플렉션 기법을 통해 JSON에서 DTO 객체로 역직렬화가 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;PropertyBasedCreator 객체가 null이 아니다.&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;PropertyBasedCreator가 null이 아니므로, &lt;u&gt;BeanDeserializer.deserializeFromObjectUsingNonDefault()&lt;/u&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에서 시작해 내부적으로 더 들어 가면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PropertyBasedCreator가&lt;span&gt;&amp;nbsp;&lt;/span&gt;StdValueInstantiator를 이용해 역시 리플렉션 기법으로 argument가 있는 Constructor로 객체를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;argument가 있다라는 것은 DTO 클래스의 필드에 생성자 주입을 통해 바로 값이 세팅된다라는 의미와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;Gradle 빌드 옵션을&lt;span&gt; IntelliJ로 &lt;/span&gt;&lt;/span&gt;설정할 경우,&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디폴트 생성자가 없으면 JSON에서 DTO 객체로 역직렬화가 되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;PropertyBasedCreator 객체가 null이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PropertyBasedCreator가 null 이므로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;디폴트 생성자나 argument가 있는 생성자를 이용해 리플렉션으로 DTO 클래스의 객체를 생성할 수 없다.&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;PropertyBasedCreator에 대해서는 아래와 같이 설명하고 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Object&amp;nbsp;that&amp;nbsp;is&amp;nbsp;used&amp;nbsp;to&amp;nbsp;collect&amp;nbsp;arguments&amp;nbsp;for&amp;nbsp;non-default&amp;nbsp;creator&amp;nbsp;(non-default-constructor,&amp;nbsp;or&amp;nbsp;argument-taking&amp;nbsp;factory&amp;nbsp;method)&amp;nbsp;before&amp;nbsp;creator&amp;nbsp;can&amp;nbsp;be&amp;nbsp;called.&amp;nbsp;Since&amp;nbsp;ordering&amp;nbsp;of&amp;nbsp;JSON&amp;nbsp;properties&amp;nbsp;is&amp;nbsp;not&amp;nbsp;guaranteed,&amp;nbsp;this&amp;nbsp;may&amp;nbsp;require&amp;nbsp;buffering&amp;nbsp;of&amp;nbsp;values&amp;nbsp;other&amp;nbsp;than&amp;nbsp;ones&amp;nbsp;being&amp;nbsp;passed&amp;nbsp;to&amp;nbsp;creator.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 디폴트 생성자가 아닌 생성자의 argument를 수집하는데 사용한다고 설명이 나와 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Gradle 빌드 옵션이 Gradle(default)일 경우&lt;/span&gt;&lt;/b&gt;, 디폴트 생성자가 없어도 리플렉션을 통해 argument가 있는 Constructor 정보로 DTO 객체를 생성할 수 있다. 즉, 어떤 경우도 역직렬화가 가능하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Gradle 빌드 옵션을, IntelliJ로 설정하면&lt;/span&gt;&lt;/b&gt; PropertyBasedCreator 객체가 생성되지 않으므로, 디폴트 생성자가 반드시 있어야지만 역직렬화가 가능하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Gradle 빌드 옵션을 바꾸지 말자.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java Backend 개발자 되기/Spring MVC</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/66</guid>
      <comments>https://itvillage.tistory.com/entry/ObjectMapper%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-JSON-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94Deserialization-%EC%9D%B4%EC%95%BC%EA%B8%B0#entry66comment</comments>
      <pubDate>Thu, 9 Feb 2023 17:23:40 +0900</pubDate>
    </item>
    <item>
      <title>Reactor Sequence에서 발생하는 signal의 전파 흐름</title>
      <link>https://itvillage.tistory.com/entry/Reactor-Sequence%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-signal%EC%9D%98-%EC%A0%84%ED%8C%8C-%ED%9D%90%EB%A6%84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 시간에는 Reactor Sequence 상에서 발생하는 signal의 전파 흐름을 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일반적인 Signal의 전파 흐름&lt;/h3&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0px; padding: 0px; border: none; background-color: #fafafa; border-radius: 4px; height: 305px;&quot; width=&quot;827&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5; width: 24.7188px;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;13&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0px; text-align: left; width: 769.219px;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;import&lt;/span&gt;&amp;nbsp;lombok.extern.slf4j.Slf4j;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;import&lt;/span&gt;&amp;nbsp;reactor.core.publisher.Flux;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@Slf4j&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt; ReactorSignalEventPropagationExample01 {&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;static&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;void&lt;/span&gt;&amp;nbsp;main(&lt;span style=&quot;color: #066de2;&quot;&gt;String&lt;/span&gt;[] args) {&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Flux&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.range(&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;,&amp;nbsp;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.filter(n&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;&amp;nbsp;n&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0099cc;&quot;&gt;0&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.map(n&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;&amp;nbsp;n&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;*&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0099cc;&quot;&gt;2&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.subscribe(data&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;&amp;nbsp;log.info(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;#&amp;nbsp;onNext:&amp;nbsp;{}&quot;&lt;/span&gt;,&amp;nbsp;data));&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;코드 1-1 일반적인 signal 이벤트 흐름&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0px 2px 4px 0px; width: 17.0625px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 1-1은 Reactor Sequence 상에서 발생하는 signal의 전파 흐름을 확인하기 위한 예제 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 코드 상에는 &lt;code&gt;log()&lt;/code&gt; Operator가 빠져있지만 &lt;code&gt;range()&lt;/code&gt;, &lt;code&gt;filter()&lt;/code&gt;, &lt;code&gt;map()&lt;/code&gt; Operator 하단에 각각 &lt;code&gt;log()&lt;/code&gt; Operator를 추가해서 실행하면 아래와 같은 로그를 출력합니다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0px; padding: 0px; border: none; background-color: #fafafa; border-radius: 4px; height: 316px;&quot; width=&quot;1133&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5; width: 22.75px;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;13&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;14&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0px; text-align: left; width: 1078.16px;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;355&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;DEBUG&amp;nbsp;reactor.util.Loggers&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;Using&amp;nbsp;Slf4j&amp;nbsp;logging&amp;nbsp;framework&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;369&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.range&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onSubscribe([Synchronous&amp;nbsp;Fuseable]&amp;nbsp;Operators.ScalarSubscription)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;371&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.filter&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onSubscribe([Fuseable]&amp;nbsp;FluxFilterFuseable.FilterFuseableSubscriber)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;371&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.map&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onSubscribe([Fuseable]&amp;nbsp;FluxMapFuseable.MapFuseableSubscriber)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;372&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.map&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;request(unbounded)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;372&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.filter&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;request(unbounded)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;372&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.range&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;request(unbounded)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;372&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.range&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onNext(&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;372&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.filter&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onNext(&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;372&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.map&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onNext(&lt;span style=&quot;color: #0099cc;&quot;&gt;2&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;372&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;com.codestates.example.schedulers.SchedulersExample04&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;#&amp;nbsp;onNext:&amp;nbsp;&lt;span style=&quot;color: #0099cc;&quot;&gt;2&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;373&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.range&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onComplete()&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;373&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.filter&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onComplete()&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;18&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;51&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;02.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;373&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.map&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onComplete()&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0px 2px 4px 0px; width: 16.0938px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그를 보면, 코드 1-1에서 발생하는 signal은 아래와 같은 순서로 전파됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;range | onSubscribe() --&amp;gt; filter | onSubscribe() --&amp;gt; map | onSubscribe() &lt;br /&gt;--&amp;gt; map | request() --&amp;gt; filter | request() --&amp;gt; range | request() &lt;br /&gt;--&amp;gt; range | onNext() --&amp;gt; filter | onNext() --&amp;gt; map | onNext() &lt;br /&gt;--&amp;gt; range | onComplete() --&amp;gt; filter | onComplete() --&amp;gt; map | onComplete()&lt;/blockquote&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이를 통해 알 수 있는 사실 두 가지를 살펴보면,&lt;/span&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 대부분의 Operator들은 각각 자신만의 &lt;code&gt;Subscriber&lt;/code&gt;를 포함하고 있고, 이 &lt;code&gt;Subscriber&lt;/code&gt;와 서로 signal을 주고 받으면서 데이터를 전송하고 데이터를 전달 받는다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다름으로 onSubscribe, onNext, onComplete signal은 Downstream 쪽으로 전파되고, request signal은 Upstream 쪽으로 전파되는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;subscribeOn()이 추가되었을 때 Signal의 전파 흐름&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 subscribeOn() Operator가 Operator 체인에 추가되면 signal의 전파 흐름은 어떻게 변할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0px; padding: 0px; border: none; background-color: #fafafa; border-radius: 4px; height: 348px;&quot; width=&quot;834&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5; width: 25.0469px;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;13&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;14&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;15&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;16&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;17&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0px; text-align: left; width: 775.734px;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;import&lt;/span&gt;&amp;nbsp;lombok.extern.slf4j.Slf4j;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;import&lt;/span&gt;&amp;nbsp;reactor.core.publisher.Flux;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;import&lt;/span&gt;&amp;nbsp;reactor.core.scheduler.Schedulers;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@Slf4j&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;ReactorSignalEventPropagationExample02&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;static&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;void&lt;/span&gt;&amp;nbsp;main(&lt;span style=&quot;color: #066de2;&quot;&gt;String&lt;/span&gt;[]&amp;nbsp;args)&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;throws&lt;/span&gt;&amp;nbsp;InterruptedException&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Flux&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.range(&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;,&amp;nbsp;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.doOnSubscribe(subscription&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;&amp;nbsp;log.info(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;#&amp;nbsp;doOnSubscribe&quot;&lt;/span&gt;))&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .subscribeOn(Schedulers.boundedElastic()) // (1)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.map(n&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;&amp;nbsp;n&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;*&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0099cc;&quot;&gt;2&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.subscribe(data&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;&amp;nbsp;log.info(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;#&amp;nbsp;onNext:&amp;nbsp;{}&quot;&lt;/span&gt;,&amp;nbsp;data));&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Thread.sleep(100L);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;&lt;a style=&quot;color: #e5e5e5text-decoration:none;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #010101;&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #010101;&quot;&gt;코드 1-2 subscribeOn()이 추가되었을 때 signal 이벤트 흐름&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0px 2px 4px 0px; width: 17.2188px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 1-2는 subscribeOne() Operator가 Operator체인에 추가되었을 때 signal의 전파 흐름을 살펴보기 위한 예제 코드입니다. ( 로그 출력 결과가 복잡해 보일 것 같아 filter() Operator는 제외했습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 1-1과 마찬가지로 log() Operator를 각각의 Operator 다음에 추가해서 코드를 실행하면 로그 출력 결과는 아래와 같습니다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;13&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;14&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;15&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;16&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;17&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;18&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;27.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;992&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.subscribeOn&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;onSubscribe(FluxSubscribeOn.SubscribeOnSubscriber)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;27.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;993&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.map&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;onSubscribe(FluxMap.MapSubscriber)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;27.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;994&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.map&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;request(unbounded)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;27.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;994&lt;/span&gt;&amp;nbsp;[main]&amp;nbsp;INFO&amp;nbsp;log.subscribeOn&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;request(unbounded)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;003&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.range&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onSubscribe([Synchronous&amp;nbsp;Fuseable]&amp;nbsp;Operators.ScalarSubscription)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;004&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;com.codestates.example.schedulers.SchedulersExample05&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;#&amp;nbsp;doOnSubscribe&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;005&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.doOnSubscribe&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onSubscribe([Fuseable]&amp;nbsp;FluxPeekFuseable.PeekFuseableSubscriber)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;006&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.doOnSubscribe&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;request(unbounded)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;006&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.range&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;request(unbounded)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;006&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.range&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onNext(&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;006&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.doOnSubscribe&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onNext(&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;006&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.subscribeOn&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;onNext(&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;006&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.map&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;onNext(&lt;span style=&quot;color: #0099cc;&quot;&gt;2&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;006&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;com.codestates.example.schedulers.SchedulersExample05&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;#&amp;nbsp;onNext:&amp;nbsp;&lt;span style=&quot;color: #0099cc;&quot;&gt;2&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;006&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.range&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onComplete()&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;006&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.doOnSubscribe&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;|&lt;/span&gt;&amp;nbsp;onComplete()&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;006&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.subscribeOn&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;onComplete()&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;21&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;48&lt;/span&gt;:&lt;span style=&quot;color: #0099cc;&quot;&gt;28.&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;006&lt;/span&gt;&amp;nbsp;[boundedElastic&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #0099cc;&quot;&gt;1&lt;/span&gt;]&amp;nbsp;INFO&amp;nbsp;log.map&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;-&lt;/span&gt;&amp;nbsp;onComplete()&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0 2px 4px 0;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그가 좀 복잡해보이는데 어쨌든 코드 1-2의 signal 전파 흐름을 보면 코드 1-1과 조금 다르다른 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;subscribeOn()&lt;/code&gt;이 추가되지 않았던 코드 1-1에서는 &lt;code&gt;range()&lt;/code&gt;부터 차례대로 &lt;code&gt;onSubscribe signal&lt;/code&gt;이 전파되었는데 코드 1-2의 경우에는 &lt;code&gt;subscribeOn()&lt;/code&gt;부터 &lt;code&gt;onSubscribe signal&lt;/code&gt;이 Downstream쪽으로 전파되고, &lt;code&gt;request signal&lt;/code&gt;이 다시 Upstream 쪽으로 전파되지만 &lt;code&gt;range()&lt;/code&gt;까지 전파되는 것이 아니라 &lt;code&gt;subscribeOn()&lt;/code&gt;까지만 전파되고 이 후 부터는 &lt;code&gt;range()&lt;/code&gt;부터 &lt;code&gt;onSubscribe signal&lt;/code&gt;이 &lt;code&gt;subscribeOn()&lt;/code&gt;까지만 전파된 후에 &lt;code&gt;request signal&lt;/code&gt;이 다시 range()까지 전파되고 이 후부터는 &lt;code&gt;onNext signal&lt;/code&gt;이 Downstream 끝까지 전파되고, 다음에 onComplete signal 역시 Downstream 끝까지 전파되는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;signal의 전파 흐름이 굉장히 복잡한데 이 복잡함을 덜 복잡하게 이해하기 위해서는 signal의 전파 흐름을 subscribeOn() Operator를 기준으로 생각하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;subscribeOn()&lt;/code&gt; Operator를 기준으로 signal 흐름을 요약하면,&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;먼저 &lt;code&gt;onSubscribe signal&lt;/code&gt;이 Downstream 쪽으로 전파 &lt;br /&gt;-&amp;gt; &lt;code&gt;request signal&lt;/code&gt;이 Upstream 쪽으로 전파되지만 &lt;code&gt;subscribeOn()&lt;/code&gt; Operator까지만 전파&lt;br /&gt;-&amp;gt; &lt;code&gt;range()&lt;/code&gt; 부터 &lt;code&gt;onSubscribe signal&lt;/code&gt;이 전파되지만 &lt;code&gt;subscribeOn()&lt;/code&gt; 까지만 전파&lt;br /&gt;-&amp;gt; &lt;code&gt;request signal&lt;/code&gt;이 Upstream 쪽으로 전파&lt;br /&gt;-&amp;gt; &lt;code&gt;onNext signal&lt;/code&gt;이 Downstream 끝까지 전파&lt;br /&gt;-&amp;gt; &lt;code&gt;onComplete signal&lt;/code&gt;이 Downstream 끝까지 전파&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명한 signal 전파 흐름을 코드 상의 흐름으로 간략하게 표현하면 아래의 [그림 1-1]과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-015.png&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbrqeF/btrXo5DdXfp/fTHG0yvsIcn9xeyBerklg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbrqeF/btrXo5DdXfp/fTHG0yvsIcn9xeyBerklg1/img.png&quot; data-alt=&quot;[그림 1-1] subscribeOn()이 추가될 경우의 signal 전파 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbrqeF/btrXo5DdXfp/fTHG0yvsIcn9xeyBerklg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbrqeF%2FbtrXo5DdXfp%2FfTHG0yvsIcn9xeyBerklg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;804&quot; height=&quot;469&quot; data-filename=&quot;K-015.png&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;469&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-1] subscribeOn()이 추가될 경우의 signal 전파 흐름&lt;/figcaption&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;code&gt;subscribeOn()&lt;/code&gt; Operator가 추가 되었을 때 signal 전파 흐름이 어떻게 되는지 Reactor 코드 내부에서 사용되는 코드를 통해 조금 더 구체적으로 표현하면 아래의 [그림 1-2]와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-016.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBazVK/btrXnvXepy2/jnvq5LeYA4cE6erdVCB4Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBazVK/btrXnvXepy2/jnvq5LeYA4cE6erdVCB4Rk/img.png&quot; data-alt=&quot;[그림 1-2] subscribeOn()이 추가될 경우의 signal 전파 흐름(detail description)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBazVK/btrXnvXepy2/jnvq5LeYA4cE6erdVCB4Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBazVK%2FbtrXnvXepy2%2Fjnvq5LeYA4cE6erdVCB4Rk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1262&quot; height=&quot;774&quot; data-filename=&quot;K-016.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;774&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-2] subscribeOn()이 추가될 경우의 signal 전파 흐름(detail description)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[그림 1-2]의 signal 전파 흐름이 꽤 복잡해 보이지만 Reactor에서 실제 사용되는 컴포넌트들을 이용해 [그림 1-1]을 조금 더 구체적으로 표현한 것 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 [그림 1-2]를 이해하는것이 어렵게 느껴진다면 [그림 1-1]만 이해해도 충분하다고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Operator 체인상에서 doOnSubscribe()의 위치에 따른 실행 쓰레드의 변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[그림 1-2]를 보면 &lt;code&gt;doOnSubscribe()&lt;/code&gt; Operator는 &lt;code&gt;subscribeOn()&lt;/code&gt; 위 쪽에 추가되어있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, &lt;code&gt;doOnSubscribe()&lt;/code&gt;의 실행 쓰레드는 코드 1-2의 로그 출력 결과에서도 확인할 수 있다시피 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;boundedElastic-1&lt;/code&gt;&lt;/span&gt; 쓰레드에서 실행되는 이유가 뭘까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;doOnSubscribe()&lt;/code&gt; Operator는&lt;/span&gt; onSubscribe()가 호출되었을 때 이어서 호출되는 일종의 콜백입니다.&lt;br /&gt;따라서 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;onSubscribe()&lt;/code&gt;가 호출될 때의 실행 쓰레드와 동일한 쓰레드에서 실행&lt;/span&gt;이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;doOnSubscribe()&lt;/code&gt;를 호출되게 하는 &lt;code&gt;onSubscribe()&lt;/code&gt;는 어디서 호출될까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[그림 1-2]의 signal 전파 흐름에서도 알 수 있듯이 &lt;code&gt;range()&lt;/code&gt;에서 &lt;code&gt;onSubscribe()&lt;/code&gt;가 호출되면 뒤이어 &lt;code&gt;doOnSubscribe()&lt;/code&gt;가 호출됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;range()의 실행 쓰레드는?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;boundedElastic-1&lt;/span&gt;&lt;/code&gt; 쓰레드입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 range()에서 호출하는 onSubscribe()도 &lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;boundedElastic-1&lt;/span&gt;&lt;/code&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;쓰레드이고, &lt;/span&gt;doOnSubscribe()의 실행 쓰레드 역시 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;boundedElastic-1&lt;/code&gt;&lt;/span&gt; 쓰레드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;code&gt;doOnSubscribe()&lt;/code&gt;를 &lt;code&gt;subscribeOn()&lt;/code&gt; 바로 아래에 추가한다면 doOnSubscribe()의 실행 쓰레드는 무엇일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, &lt;code&gt;doOnSubscribe()&lt;/code&gt;의 실행 쓰레드는 &lt;code&gt;subscribeOn()&lt;/code&gt;에서 호출되는 &lt;code&gt;onSubscribe()&lt;/code&gt;의 영향을 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[그림 1-2]에서 subscribeOn()의 실행 쓰레드는 무엇인가요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;main&lt;/code&gt; 쓰레드&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;따라서 &lt;code&gt;subscribeOn()&lt;/code&gt;에서 호출하는 &lt;code&gt;onSubscribe()&lt;/code&gt;도&lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt; main&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&amp;nbsp;쓰레드&lt;/span&gt;이고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;doOnSubscribe()의 실행 쓰레드 역시&lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt; main&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&amp;nbsp;쓰레드&lt;/span&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헷갈리겠지만 이것 하나만 기억하면 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;doOnSubscribe()의 실행 쓰레드는 doOnSubscribe() 바로 위 쪽에 있는 Operator가 호출하는 onSubscribe()의 실행 쓰레드와 같다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 글이 Reactor Sequence 상에서 발생하는 signal의 전파 흐름과 subscribeOn()과 doOnSubscribe()의 실행 쓰레드의 관계를 이해하는데 조금이라도 도움이 될 수 있길 바래봅니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>리액티브 프로그래밍/Reactor</category>
      <category>doOnSubscribe</category>
      <category>reactor</category>
      <category>Reactor signal</category>
      <category>scheduler</category>
      <category>subscribeOn</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/65</guid>
      <comments>https://itvillage.tistory.com/entry/Reactor-Sequence%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-signal%EC%9D%98-%EC%A0%84%ED%8C%8C-%ED%9D%90%EB%A6%84#entry65comment</comments>
      <pubDate>Sat, 28 Jan 2023 23:10:44 +0900</pubDate>
    </item>
    <item>
      <title>Spring Security OAuth 2 컴포넌트 요약</title>
      <link>https://itvillage.tistory.com/entry/Spring-Security-OAuth-2-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%9A%94%EC%95%BD</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ OAuth2LoginAuthenticationFilter&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OAuth 2 인증을 처리하기 위한 Filter&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ CommonOAuth2Provider&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신뢰할 만한 OAuth 2 Provider 목록 제공&lt;/li&gt;
&lt;li&gt;실질적으로 각 Provider에게 넘겨줘야 되는 Client 정보를 포함한 ClientRegistration 객체를 생성한다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ ClientRegistration&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 OAuth 2 Provider 사용을 위해 Client에게 필요한 등록 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public enum CommonOAuth2Provider {

	GOOGLE {

		@Override
		public Builder getBuilder(String registrationId) {
			ClientRegistration.Builder builder = getBuilder(registrationId,
					ClientAuthenticationMethod.CLIENT_SECRET_BASIC, DEFAULT_REDIRECT_URL);
      // Provider가 제공할 Resource 범위
			builder.scope(&quot;openid&quot;, &quot;profile&quot;, &quot;email&quot;);

      // Client가 OAuth 2 인증을 위해 Redirect하는 URI(구글의 로그인 인증 화면)
			builder.authorizationUri(&quot;&amp;lt;https://accounts.google.com/o/oauth2/v2/auth&amp;gt;&quot;);

      // Client가 Access Token과 Refresh Token을 얻기 위해 호출하는 URI
			builder.tokenUri(&quot;&amp;lt;https://www.googleapis.com/oauth2/v4/token&amp;gt;&quot;);

      // JWT 검증용 Key Set을 조회할 수 있는 URI
			builder.jwkSetUri(&quot;&amp;lt;https://www.googleapis.com/oauth2/v3/certs&amp;gt;&quot;);

      // Access Token 발행자 정보 조회 URI
			builder.issuerUri(&quot;&amp;lt;https://accounts.google.com&amp;gt;&quot;);

      // Resource Owner의 User Info를 조회하기 위한 URI
			builder.userInfoUri(&quot;&amp;lt;https://www.googleapis.com/oauth2/v3/userinfo&amp;gt;&quot;);

      // Resource Owner의 이름에 접근하기 위한 Attribute Name
			builder.userNameAttributeName(IdTokenClaimNames.SUB);

      // Client Name. Provider를 구분하는 용도로 사용
			builder.clientName(&quot;Google&quot;);
			return builder;
		}

	},
  
  ...
  ...

}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4️⃣ OAuth2AuthorizedClientService&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OAuth 2로 인증된 Client(OAuth2AuthorizedClient)를 관리하는 서비스 클래스&lt;/li&gt;
&lt;li&gt;OAuth2AuthorizedClientService 인터페이스의 구현 클래스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;InMemoryOAuth2AuthorizedClientService&lt;/li&gt;
&lt;li&gt;JdbcOAuth2AuthorizedClientService&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;5️⃣ ClientRegistrationRepository&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OAuth 2로 인증된 Client(OAuth2AuthorizedClient)를 저장하는 역할을 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ClientRegistrationRepository 인터페이스의 구현 클래스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;InMemoryClientRegistrationRepository&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;OAuth2AuthorizedClientService가 ClientRegistrationRepository를 사용해 OAuth2AuthorizedClient 를 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;br /&gt;6️⃣ OAuth2AuthorizedClientManager&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OAuth2AuthorizedClient 를 관리하는 관리자 역할을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;br /&gt;7️⃣ OAuth2AuthorizedClientProvider&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Authorization Grant 유형을 설정할 수 있는 인터페이스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Authorization Code&lt;/li&gt;
&lt;li&gt;Implicit Grant Type&lt;/li&gt;
&lt;li&gt;Client Credentials&lt;/li&gt;
&lt;li&gt;Resource Owner Password Credentials&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java Backend 개발자 되기/Spring Security</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/64</guid>
      <comments>https://itvillage.tistory.com/entry/Spring-Security-OAuth-2-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%9A%94%EC%95%BD#entry64comment</comments>
      <pubDate>Thu, 26 Jan 2023 18:57:35 +0900</pubDate>
    </item>
    <item>
      <title>ContentCachingRequestWrapper 사용 예</title>
      <link>https://itvillage.tistory.com/entry/ContentCachingRequestWrapper-%EC%82%AC%EC%9A%A9-%EC%98%88</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring MVC 기반의 웹 애플리케이션을 개발하다보면 Interceptor나 AOP Advice 등에서 request body를 사용해야하는 경우가 종종 있을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reqeust 전송 시, header에 추가되 전송되는 JWT의 claims에 포함된 memberId와 request body에 포함된 memberId가 동일한지를 검증하는 예를 들어보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1674570621167&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Component
@Aspect
public class MemberVerifyAdvice extends VerifyAdvice {
    private final ObjectMapper objectMapper;
    public MemberVerifyAdvice(JwtTokenizer jwtTokenizer, ObjectMapper objectMapper) {
        super(jwtTokenizer);
        this.objectMapper = objectMapper;
    }

    @Pointcut(
            &quot;execution(* com.itvillage.member.controller.MemberController.postQnaQuestionOfMember(..)) || &quot; +
                    &quot;execution(* com.itvillage.qna.controller.QnaQuestionController.postAnswerOfQuestion(..))&quot;
    )
    public void verifyMySelfWithPointcut(){}

    @Before(value = &quot;verifyMySelfWithPointcut()&quot;)
    public void verifyMySelf(JoinPoint joinPoint) throws IOException {
        String jws = getHeader(&quot;Authorization&quot;).substring(7);
        long memberIdFromJws = getMemberIdFromJws(jws);

        long memberIdFromRequest = extractMemberId();

        checkSameMember(memberIdFromJws, memberIdFromRequest);

    }

    private long extractMemberId() throws IOException {
        // (1)
        HttpServletRequest request = 
                  ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
        
        
        String body = IOUtils.toString(request.getReader());  // (2-1)
        or
        String body = 
        	StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8); // (2-2)

        ...
        ...
        
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;코드 1-1 MemberVerifyAdvice&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 코드 1-1에서 처럼 request body를 파싱하기 위해 AOP Advice에서 (1)과 같이 RequestContextHolder를 이용해 HttpServletRequest를 얻습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 (2-1)과 같이 apache commons io 라이브러리를 이용하거나 (2-2)와 같이 Spring에서 제공하는 StreamUtils를 이용해 request body를 얻기 위한 시도를 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 (2-1) 또는 (2-2)와 같은 코드가 실행되면 request body가 비어있거나 &lt;u&gt;getInputStream() has already been called for this request&lt;/u&gt; 같은 에러가 발생해 정상적으로 request body를 얻을 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⭐ 이유는 Spring Boot에 내장된 Tomcat의 경우 &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpServletRequest의 InputStream을 한번 read 하게되면 다시 읽을 수 없도록 구현되어 있기 때문&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이 말은 AOP Advice에서 얻은 HttpServletRequest는 Advice에 도달하기 전에 이미 어딘가에서 InputStream을 read 했다는 의미와도 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Advice에서 request body를 정상적으로 사용하려면 어떻게 해야할까요?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우에는 InputStream을 애플리케이션 어딘가에서 read 하기 전에 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;u&gt;ContentCachingRequestWrapper&lt;/u&gt;를 이용해 원본 HttpServletRequest를 캐싱해 두면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;즉, 캐싱해서 저장해 두었다가 나중에 필요할 때 꺼내서 사용할 수 있도록 하는 것입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;u&gt;ContentCachingRequestWrapper&lt;/u&gt;로 원본 HttpServletRequest를 캐싱하기 좋은 지점은 바로 필터입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;필터를 하나 추가해서 해당 필터에서 &lt;u&gt;ContentCachingRequestWrapper&lt;/u&gt;로 원본 HttpServletRequest를 캐싱하면 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;&lt;span&gt;ContentCachingRequestWrapper로&amp;nbsp;원본&amp;nbsp;HttpServletRequest를&amp;nbsp;캐싱하기&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;span&gt;Filter 생성&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1674605125494&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class HttpServletWrappingFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        // ContentCachingRequestWrapper로 HttpServletRequest 캐싱
        ContentCachingRequestWrapper wrappingRequest = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper wrappingResponse = new ContentCachingResponseWrapper(response);
        filterChain.doFilter(wrappingRequest, wrappingResponse);
        wrappingResponse.copyBodyToResponse();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;코드 1-2 HttpServletWrappingFilter&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;코드 1-2와 같이 ContentCachingRequestWrapper를 이용해 HttpServletRequest와 HttpServletResponse를 캐싱하는 필터를 생성합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;이렇게하면 나중에 원본 HttpServletRequest를 사용하고자 하는 곳에서 ContentCachingResponseWrapper를 이용해 request body를 사용하면 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;span&gt;Filter 추가&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1674606042154&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class WebConfiguration {
    @Bean
    public FilterRegistrationBean&amp;lt;HttpServletWrappingFilter&amp;gt; firstFilterRegister()  {
    	// HttpServletWrappingFilter 추가
        FilterRegistrationBean&amp;lt;HttpServletWrappingFilter&amp;gt; registrationBean =
                new FilterRegistrationBean&amp;lt;&amp;gt;(new HttpServletWrappingFilter());
        registrationBean.setOrder(Integer.MIN_VALUE);

        return registrationBean;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;코드 1-3 FilterRegistrationBean&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;코드 1-3과 같이 &lt;u&gt;FilterRegistrationBean&lt;/u&gt;을 이용해 앞에서 생성한 &lt;u&gt;HttpServletWrappingFilter&lt;/u&gt;를 &lt;u&gt;FilterChain&lt;/u&gt;에 추가합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;span&gt;ContentCachingRequestWrapper로 원본 HttpServletRequest 사용&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1674606371300&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Component
@Aspect
public class MemberVerifyAdvice extends VerifyAdvice {
    private final ObjectMapper objectMapper;
    public MemberVerifyAdvice(JwtTokenizer jwtTokenizer, ObjectMapper objectMapper) {
        super(jwtTokenizer);
        this.objectMapper = objectMapper;
    }

    @Pointcut(
            &quot;execution(* com.itvillage.member.controller.MemberController.postQnaQuestionOfMember(..)) || &quot; +
                    &quot;execution(* com.itvillage.qna.controller.QnaQuestionController.postAnswerOfQuestion(..))&quot;
    )
    public void verifyMySelfWithPointcut(){}

    @Before(value = &quot;verifyMySelfWithPointcut()&quot;)
    public void verifyMySelf(JoinPoint joinPoint) throws IOException {
        String jws = getHeader(&quot;Authorization&quot;).substring(7);
        long memberIdFromJws = getMemberIdFromJws(jws);

        long memberIdFromRequest = extractMemberId();

        checkSameMember(memberIdFromJws, memberIdFromRequest);

    }

    private long extractMemberId() throws IOException {
        HttpServletRequest request = 
                  ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
        
		// (1)
		ContentCachingRequestWrapper requestWrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        byte[] body = requestWrapper.getContentAsByteArray();  // (2)
        
        ...
        ...
        
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;코드&amp;nbsp; 1-3 개선된 MemberVerifyAdvice&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;코드 1-3과 같이 (1)에서 WebUtils를 이용해 ContentCachingRequestWrapper 얻은 후에&amp;nbsp; (2)에서 ContentCachingRequestWrapper의 getContentAsByteArray()로 request body를 얻고 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이 글에서 처럼 request body를 read하려는데 정상적으로 read하지 못하는 경우가 있다면 이 글이 도움이 되길 바래봅니다.&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring MVC</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/63</guid>
      <comments>https://itvillage.tistory.com/entry/ContentCachingRequestWrapper-%EC%82%AC%EC%9A%A9-%EC%98%88#entry63comment</comments>
      <pubDate>Tue, 24 Jan 2023 23:57:18 +0900</pubDate>
    </item>
    <item>
      <title>JWT를 이용한 인증(Authentication) 및 자격 검증(Authorization) 프로세스</title>
      <link>https://itvillage.tistory.com/entry/JWT%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9D%B8%EC%A6%9DAuthentication-%EB%B0%8F-%EA%B6%8C%ED%95%9C-%EB%B6%80%EC%97%ACAuthorization-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security에서 JWT를 발급 받는 방법은 크게 두 가지로 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째는 구글 검색을 통해서도 흔히 볼 수 있는 Controller를 이용해서 아이디/패스워드 로그인 후, JWT를 발급 받는 방식이고, 두 번째는 Spring Security에서 지원하는 Security Filter를 확장하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller를 이용하는 것이 안좋은 방법이라고 보기는 힘들지만 그래도 Spring Security라는 보안 프레임워크를 사용하는 개발자 입장에서는 Controller까지 request가 전달되기 전에 Security Filter 레벨에서 인증을 처리하는 것이 조금 더 자연스럽지 않을까하는 생각을 해보게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에는 구체적인 코드는 없지만 JWT를 이용해 어떤식으로 인증이 처리 되는지 그리고 JWT를 이용해서 클라이언트의 자격을 검증하는 프로세스가 어떻게 진행 되는지를 그림으로만 표현하도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에 있는 그림을 보고 CSR(Client Side Rendering) 방식의 애플리케이션에서 JWT를 이용해 인증 및 클라이언트의 자격 검증 로세스를 어떻게 코드로 구현할 수 있는지에 대한 인사이트를 얻어갈 수 있기를 바래봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JWT 인증 프로세스&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1253&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/euDRGq/btrWLVAEMaS/5Jw39EiZy1tHXJSCi1W7zK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/euDRGq/btrWLVAEMaS/5Jw39EiZy1tHXJSCi1W7zK/img.png&quot; data-alt=&quot;[그림 1-1] JWT 인증 프로세스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/euDRGq/btrWLVAEMaS/5Jw39EiZy1tHXJSCi1W7zK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeuDRGq%2FbtrWLVAEMaS%2F5Jw39EiZy1tHXJSCi1W7zK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1253&quot; height=&quot;738&quot; data-origin-width=&quot;1253&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-1] JWT 인증 프로세스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[그림 1-1]의 JWT 인증 프로세스는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트가 최초 로그인 인증을 위해 username(이메일 등의 로그인 아이디)과 password를 request body에 실어서 전송합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백엔드 애플리케이션 쪽으로 전송된 username과 password는 &lt;code&gt;JwtAuthenticationFilter&lt;/code&gt;에 전달되어 &lt;code&gt;JwtAuthenticationFilter&lt;/code&gt;에서부터 클라이언트에 대한 인증 프로세스가 시작됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JwtAuthenticationFilter&lt;/code&gt;는 &lt;code&gt;UsernamePasswordAuthenticationFilter&lt;/code&gt;를 확장해서 구현하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JwtAuthenticationFilter&lt;/code&gt;에서 &lt;code&gt;AuthenticationManager&lt;/code&gt;의 구현 클래스인 &lt;code&gt;ProdiverManager&lt;/code&gt;를 거쳐 &lt;span style=&quot;color: #ef5369;&quot;&gt;인증되지 않은(unauthenticated) Authentication&lt;/span&gt; 객체가 &lt;code&gt;AuthenticationProvider&lt;/code&gt;에게 전달됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuthenticationProvider&lt;/code&gt;는 인증되지 않은(unauthenticated) Authentication 객체에 포함된 username을 &lt;code&gt;UserDetailsService&lt;/code&gt;의 구현 클래스인 &lt;code&gt;MemberDetailsService&lt;/code&gt;에게 전달합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;loadUserByUsername(String username)&lt;/code&gt;을 이용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserDetailsService&lt;/code&gt;&lt;span&gt;의 구현 클래스인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;code&gt;MemberDetailsService&lt;/code&gt;는 데이터베이스에서 사용자의 Credential(암호화 된 패스워드)을 조회한 뒤 조회한 Credential을 포함한 &lt;code&gt;UserDetails&lt;/code&gt;를 &lt;code&gt;AuthenticationProvider&lt;/code&gt;에게 리턴합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuthenticationProvider&lt;/code&gt;는 전달 받은 &lt;code&gt;UserDetails&lt;/code&gt;에 포함된 Credential(암호화 된 패스워드)과 &lt;span style=&quot;color: #ef5369;&quot;&gt;인증되지 않은(unauthenticated) Authentication 객체&lt;/span&gt;에 포함된 password를 암호화 해서 둘을 비교합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 패스워드를 비교해서 검증에 성공하면 &lt;span style=&quot;color: #ef5369;&quot;&gt;인증된(authenticated) Authentication&lt;/span&gt; 객체를 &lt;code&gt;JwtAuthenticationFilter&lt;/code&gt;에게 전달합니다.&lt;/li&gt;
&lt;li&gt;두 패스워드를 비교해서 검증에 실패하면 AuthenticationException을 throw합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;JwtAuthenticationFilter&lt;/code&gt;는 전달 받은 인증된(authenticated) Authentication을 이용해 Access Token과 Refresh Token을 생성해 response header에 추가합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JWT를 이용한 클라이언트 자격 검증 프로세스&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1253&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/USyRA/btrWI8oaJrw/RswUaVfB91qZH38eSpRDr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/USyRA/btrWI8oaJrw/RswUaVfB91qZH38eSpRDr1/img.png&quot; data-alt=&quot;[그림 1-2] JWT를 이용한 클라이언트 자격 검증 프로세스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/USyRA/btrWI8oaJrw/RswUaVfB91qZH38eSpRDr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUSyRA%2FbtrWI8oaJrw%2FRswUaVfB91qZH38eSpRDr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1253&quot; height=&quot;738&quot; data-origin-width=&quot;1253&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-2] JWT를 이용한 클라이언트 자격 검증 프로세스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 측에서 JWT를 성공적으로 전달 받았다면 이제 전달 받은 Access Token을 이용해서 백엔드 애플리케이션의 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;[그림 1-2]의 JWT를 이용한 클라이언트 자격 검증 프로세스는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트는 백엔드 애플리케이션의 API 엔드포인트를 이용하기 위해 request를 전송할 때 마다 request header에 Access Token을 실어보냅니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JwtVerificationFilter&lt;/code&gt;는 전달 받은 Access Token이 유효한 JWT인지를 검증하기 위해 &lt;code&gt;JwtTokenizer&lt;/code&gt;에게 JWT 검증을 요청합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JwtTokenizer&lt;/code&gt;는 Access Token의 Signature를 검증합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Access Token이 유효한 JWT이면 &lt;span style=&quot;color: #ef5369;&quot;&gt;Claims&lt;/span&gt;를 리턴합니다.&lt;/li&gt;
&lt;li&gt;Access Token이 유효하지 않으면 Exception을 throw합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JwtVerificationFilter&lt;/code&gt;는 전달 받은 &lt;span style=&quot;color: #ef5369;&quot;&gt;Claims&lt;/span&gt;를 이용해 username과 role 정보를 포함한 Authentication 객체를 &lt;span&gt;&lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;SecurityContext&lt;/span&gt;&lt;/code&gt;에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;저장합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이제 &lt;span style=&quot;color: #ef5369;&quot;&gt;인증된(authenticated) Authentication&lt;/span&gt; 객체가 &lt;code&gt;SecurityContext&lt;/code&gt;에 저장되어 있으므로 &lt;code&gt;DispatcherServlet&lt;/code&gt;에 도달하기 전의 마지막 Security Filter까지 문제 없이 통과할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DispatcherServlet&lt;/code&gt;부터는 Spring MVC의 기본적인 요청 처리 흐름에서 알 수 있듯이 Controller까지 요청이 전달됩니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java Backend 개발자 되기/Spring Security</category>
      <category>jwt</category>
      <category>Spring Security</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/61</guid>
      <comments>https://itvillage.tistory.com/entry/JWT%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9D%B8%EC%A6%9DAuthentication-%EB%B0%8F-%EA%B6%8C%ED%95%9C-%EB%B6%80%EC%97%ACAuthorization-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4#entry61comment</comments>
      <pubDate>Thu, 19 Jan 2023 21:51:11 +0900</pubDate>
    </item>
    <item>
      <title>JWT 자격 검증 시, SecurityContext는 언제 비워(clear)질까?</title>
      <link>https://itvillage.tistory.com/entry/SecurityContext%EB%8A%94-%EC%96%B8%EC%A0%9C-%EC%84%B8%EC%85%98%EA%B3%BC-%EC%97%B0%EA%B2%B0%EB%90%98%EA%B3%A0-%EC%96%B8%EC%A0%9C-%EB%B9%84%EC%9B%8Cclear%EC%A7%88%EA%B9%8C-1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 쪽에서 전송한 username과 password에 대한 인증을 처리하는 &lt;code&gt;JwtAuthenticationFilter&lt;/code&gt;(UsernamePassworAuthenticationFilter를 확장)에서 로그인 인증에 성공한 뒤, JWT를 클라이언트 쪽에 응답으로 전달했다고 가정해 봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 클라이언트는 백엔드 애플리케이션의 API 엔드포인트를 이용할 때 마다 백엔드 애플리케이션으로부터 전달 받은 JWT를 header에 포함시켜 request를 전송합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;만약&amp;nbsp;&lt;/span&gt;JWT를 검증하는 필터인 &lt;code&gt;JwtVerificationFilter&lt;/code&gt;&lt;span&gt;가&lt;/span&gt;&amp;nbsp;구현되어 있는 상태라면&lt;/span&gt; 백엔드 애플리케이션에서는 이 &lt;code&gt;JwtVerificationFilter&lt;/code&gt;를 이용해 클라이언트의 request header 등에 포함된 JWT를 검증하는 처리를 할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;13&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;14&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;15&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;16&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;17&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;18&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;19&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;20&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;21&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;22&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;23&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;24&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;25&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;26&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;JwtVerificationFilter&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;extends&lt;/span&gt;&amp;nbsp;OncePerRequestFilter&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Override&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;protected&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;void&lt;/span&gt;&amp;nbsp;doFilterInternal(HttpServletRequest&amp;nbsp;request,&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HttpServletResponse&amp;nbsp;response,&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FilterChain&amp;nbsp;filterChain)&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;throws&lt;/span&gt;&amp;nbsp;ServletException,&amp;nbsp;IOException&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;System.out.println(&quot;#&amp;nbsp;JwtVerificationFilter&quot;);&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;try&lt;/span&gt;&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Map&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #066de2;&quot;&gt;String&lt;/span&gt;,&amp;nbsp;Object&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;&amp;nbsp;claims&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;verifyJws(request);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;(1)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setAuthenticationToContext(claims);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;(2)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;catch&lt;/span&gt;&amp;nbsp;(SignatureException&amp;nbsp;se)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;request.setAttribute(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;exception&quot;&lt;/span&gt;,&amp;nbsp;se);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;catch&lt;/span&gt;&amp;nbsp;(ExpiredJwtException&amp;nbsp;ee)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;request.setAttribute(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;exception&quot;&lt;/span&gt;,&amp;nbsp;ee);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;catch&lt;/span&gt;&amp;nbsp;(Exception&amp;nbsp;e)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;request.setAttribute(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;exception&quot;&lt;/span&gt;,&amp;nbsp;e);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;filterChain.doFilter(request,&amp;nbsp;response);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;코드 1-1 JwtVerificationFilter 코드 일부&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0 2px 4px 0;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #e5e5e5; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 1-1은 &lt;code&gt;JwtVerificationFilter&lt;/code&gt;의 코드 일부입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 (1)에서 클라언트로부터 전달 받은 JWT를 검증합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증에 성공하면 (2)에서 처럼 &lt;code&gt;인증된(authenticated) Authentication&lt;/code&gt; 객체를 SecurityContext에 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;span style=&quot;background-color: #9feec3;&quot;&gt;그런데 만약에 (2)와 같이 Authentication 객체를 SecurityContext에 저장하지 않으면 어떻게 될까요?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;SecurityFilterChain&lt;/code&gt;에서 아직 처리가 진행되지 않은 즉, 뒤에 남아있는 Filter 어딘가에서 SecurityContext에 저장된 Authentication 객체가 있는지 확인했는데 Authentication 객체가 존재하지 않아서 결국엔 정상적인 요청 프로세스가 진행되지 못하고 클라이언트 쪽에 에러 메시지를 전송하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⭐ &lt;span style=&quot;color: #ef5369;&quot;&gt;따라서 JWT 검증 이후에 SecurityContext에 Authentication 객체를 반드시 저장해 주어야 한다는 사실을 기억해야 합니다.&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;style3&quot;&gt;  &lt;br /&gt;코드 1-1과 같은 로직에서는 클라이언트 쪽에서 request를 전송할 때 마다 JWT를 검증하고, SecurityContext에 Authentication 객체를 매 번 저장할 텐데 SecurityContext에 Authentication 객체가 매번 저장되어서 쌓여있거나(물론 그런일은 없겠지만), Authentication 객체가 SecurityContext에 남아 있는건 아닐까?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 &lt;span style=&quot;color: #ef5369;&quot;&gt;Spring Security에서는 적절한 시점에&amp;nbsp;SecurityContext를 사용할 수 없도록 Authentication 객체가 저장되어 있는 SecurityContext 자체를 삭제&lt;/span&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 적절한 시점은 언제일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트의 요청을 모두 처리하고 응답을 리턴하는 어느 시점에 더이상 Authentication 객체가 필요 없어질텐데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더이상 필요없어진 SecurityContext를 삭제하는 역할을 하는 것이 바로 SecurityContextPersistenceFilter입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;SecurityContextPersistenceFilter는 SecurityContext를 저장, 로드, 삭제하는 등의 역할을 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;13&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;14&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;15&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;16&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;17&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;18&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;19&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;20&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;21&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;22&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;23&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;24&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;25&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;26&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;27&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;28&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;29&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;30&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;31&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;32&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;33&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;34&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;35&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;36&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;37&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;38&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;39&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;40&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;41&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;42&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;43&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;44&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;45&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;46&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;47&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;48&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;49&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;50&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;51&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;52&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;53&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;54&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;55&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;56&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;57&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;SecurityContextPersistenceFilter&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;extends&lt;/span&gt;&amp;nbsp;GenericFilterBean&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;SecurityContextPersistenceFilter()&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;this&lt;/span&gt;(&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;HttpSessionSecurityContextRepository());&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;SecurityContextPersistenceFilter(SecurityContextRepository&amp;nbsp;repo)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;this&lt;/span&gt;.repo&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;repo;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;private&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;void&lt;/span&gt;&amp;nbsp;doFilter(HttpServletRequest&amp;nbsp;request,&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HttpServletResponse&amp;nbsp;response,&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FilterChain&amp;nbsp;chain)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;throws&lt;/span&gt;&amp;nbsp;IOException,&amp;nbsp;ServletException&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HttpRequestResponseHolder&amp;nbsp;holder&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;HttpRequestResponseHolder(request,&amp;nbsp;response);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;(1)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SecurityContext&amp;nbsp;contextBeforeChainExecution&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;this&lt;/span&gt;.repo.loadContext(holder);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;try&lt;/span&gt;&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SecurityContextHolder.setContext(contextBeforeChainExecution);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;if&lt;/span&gt;&amp;nbsp;(contextBeforeChainExecution.getAuthentication()&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;null&lt;/span&gt;)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.debug(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;Set&amp;nbsp;SecurityContextHolder&amp;nbsp;to&amp;nbsp;empty&amp;nbsp;SecurityContext&quot;&lt;/span&gt;);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;else&lt;/span&gt;&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;if&lt;/span&gt;&amp;nbsp;(&lt;span style=&quot;color: #a71d5d;&quot;&gt;this&lt;/span&gt;.logger.isDebugEnabled())&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;this&lt;/span&gt;.logger&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.debug(LogMessage.&lt;span style=&quot;color: #066de2;&quot;&gt;format&lt;/span&gt;(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;Set&amp;nbsp;SecurityContextHolder&amp;nbsp;to&amp;nbsp;%s&quot;&lt;/span&gt;,&amp;nbsp;contextBeforeChainExecution));&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chain.doFilter(holder.getRequest(),&amp;nbsp;holder.getResponse());&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;finally&lt;/span&gt;&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SecurityContext&amp;nbsp;contextAfterChainExecution&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;SecurityContextHolder.getContext();&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;(2)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SecurityContextHolder.clearContext();&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;(3)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;this&lt;/span&gt;.repo.saveContext(contextAfterChainExecution,&amp;nbsp;holder.getRequest(),&amp;nbsp;holder.getResponse());&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;request.removeAttribute(FILTER_APPLIED);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;this&lt;/span&gt;.logger.debug(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;Cleared&amp;nbsp;SecurityContextHolder&amp;nbsp;to&amp;nbsp;complete&amp;nbsp;request&quot;&lt;/span&gt;);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;void&lt;/span&gt;&amp;nbsp;setForceEagerSessionCreation(&lt;span style=&quot;color: #066de2;&quot;&gt;boolean&lt;/span&gt;&amp;nbsp;forceEagerSessionCreation)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;this&lt;/span&gt;.forceEagerSessionCreation&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;forceEagerSessionCreation;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: 9px; font-style: italic; text-align: right; letter-spacing: 0px;&quot;&gt;코드 1-2 &lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #010101;&quot;&gt;SecurityContextPersistenceFilter 코드 일부&lt;/span&gt;&lt;span style=&quot;font-size: 9px; font-style: italic; text-align: right; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0 2px 4px 0;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #e5e5e5; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 1-2는 &lt;code&gt;SecurityContextPersistenceFilter&lt;/code&gt;의 코드 일부입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 JWT가 request header 등에 포함되어 전송되었다고 가정하면 SecurityFilterChain의 Filter 중 하나인 &lt;code&gt;SecurityContextPersistenceFilter&lt;/code&gt;에 request가 전달 되었을 때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1)과 같이 &lt;code&gt;SecurityContextRepository&lt;/code&gt;에서 기존에 저장되어 있는 SecurityContext를 로드하는데 &lt;span style=&quot;color: #ef5369;&quot;&gt;JWT를 이용하는 방식에서는 이 SecurityContext 객체 안에 Authenticaion 객체가 저장되어 있지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 JWT 인증 방식은 Stateless 즉, &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpSession에서 인증된 Authentication을 포함한 SecurityContext를 관리하지 않기 때문&lt;/span&gt;인데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HttpSession을 사용하지 않는 이유는 아래에서 다시 설명하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 SecurityFiterChain의 실행이 끝난 시점 즉, 클라이언트의 요청이 Controller를 거쳐서 처리를 다 끝낸 후에 메서드 실행 흐름이 &lt;code&gt;SecurityContextPersistenceFilter&lt;/code&gt;로 넘어오게 되면 (2)와 같이 SecurityContext를 비우게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 (3)에서 SecurityFilterChain 실행 후에 생성된 &lt;code&gt;contextAfterChainExecution&lt;/code&gt;(&lt;b&gt;clear하기 직 전에 인증된 Authentication 객체가 저장되어 있는 SecurityContext&lt;/b&gt;)이 SecurityContextRepository에 저장되긴 하는데, 여기서 저장되는 &lt;code&gt;contextAfterChainExecution&lt;/code&gt;는 Spring Security가 Session 인증 방식으로 동작할 경우에는 HttpSession에 &lt;code&gt;contextAfterChainExecution&lt;/code&gt;을 저장하고, &lt;span style=&quot;color: #ef5369;&quot;&gt;JWT 인증 방식으로 동작할 경우에는 HttpSession에 저장하는 것이 아니라 단순히 this.repo.saveContext(...)와 같이 메서드 호출만 할 뿐이지 내부적으로는 아무 동작도 하지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;code&gt;SecurityContextRepository&lt;/code&gt;가 &lt;code&gt;saveContext(...)&lt;/code&gt;를 호출하지만 아무런 동작도 하지 않는 이유는&amp;nbsp; JWT 인증 방식을 사용하기 위해 SecurityConfiguration에서 아래와 같은 설정을 추가하기 때문입니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SessionCreationPolicy.STATELESS는 HttpSession을 사용하지 않겠다는 의미인데, 이 경우 코드 1-2의 (3)에서 사용하는 SecurityContextRepository의 구현 클래스는 HttpSessionSecurityContextRepository가 아니라 &lt;code&gt;NullSecurityContextRepository&lt;/code&gt;가 됩니다. &lt;code&gt;NullSecurityContextRepository&lt;/code&gt;의 &lt;code&gt;saveContext(...)&lt;/code&gt;는 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public final class NullSecurityContextRepository implements SecurityContextRepository {
	...
    ...
    
	@Override
	public void saveContext(SecurityContext context, 
    						HttpServletRequest request, 
                            HttpServletResponse response) {
	}

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 보면, NullSecurityContextRepository의 saveContext(...)에는 &lt;span style=&quot;color: #ef5369;&quot;&gt;아무런 로직이 없이 그냥 비어있습니다.&lt;/span&gt;  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SecurityContext를 clear 한다는 의미는 무엇일까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 한 가지만 더 이야기 하고 이번 글을 마치도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 코드 1-2의 (2)에서 &lt;code&gt;SecurityContextHolder.clearContext();&lt;/code&gt;를 통해 SecurityContext를 비운다(clear)라고 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 clear의 의미는 정확하게 무엇일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SecurityContext를 관리하는 관리 주체는 &lt;code&gt;SecurityContextHolder&lt;/code&gt;인데, SecurityContextHolder는 SecurityContext를 어떻게 관리할지에 대한 몇 가지 전략(&lt;code&gt;SecurityContextHolderStrategy&lt;/code&gt;)을 취할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 프로퍼티에 별도의 전략을 설정하지 않을 경우, 디폴트 전략은 &lt;code&gt;ThreadLocalSecurityContextHolderStrategy&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, SecurityContext를 ThreadLocal 변수에 저장해서 관리하겠다라는 전략입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말은 &lt;code&gt;clearContext()&lt;/code&gt;를 호출하면 &lt;span style=&quot;color: #ef5369;&quot;&gt;현재 실행 중인 Thread에서 사용하는 ThreadLocal 변수에 저장된 데이터 즉, SecurityContext 객체를 지우겠다라는 의미&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 &lt;code&gt;ThreadLocalSecurityContextHolderStrategy&lt;/code&gt;의 코드를 보면 아래와 같이 구성되어 있습니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {

   // (1) SecurityContext를 ThreadLocal 변수에서 관리한다.
   private static final ThreadLocal&amp;lt;SecurityContext&amp;gt; contextHolder = new ThreadLocal&amp;lt;&amp;gt;();

   @Override
   public void clearContext() {
      contextHolder.remove();
   }

   @Override
   public SecurityContext getContext() {
      SecurityContext ctx = contextHolder.get();
      if (ctx == null) {
         ctx = createEmptyContext();
         contextHolder.set(ctx);
      }
      return ctx;
   }

   @Override
   public void setContext(SecurityContext context) {
      Assert.notNull(context, &quot;Only non-null SecurityContext instances are permitted&quot;);
      contextHolder.set(context);
   }

   @Override
   public SecurityContext createEmptyContext() {
      return new SecurityContextImpl();
   }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 보면 (1)과 같이 ThreadLocal&amp;lt;SecurityContext&amp;gt; 필드 변수를 포함하고 있으며, &lt;code&gt;setContext(SecurityContext context)&lt;/code&gt;로 SecurityContext를 ThreadLocal 변수에 저장하고, &lt;code&gt;getContext()&lt;/code&gt;로 ThreadLocal 변수에 저장된 SecurityContext를 가지고 옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;code&gt;clearContext()&lt;/code&gt;로 &lt;span style=&quot;color: #ef5369;&quot;&gt;ThreadLocal 변수에 저장되어 있는 SecurityContext를 제거&lt;/span&gt;하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SecurityContextPersistenceFilter의&amp;nbsp;역할&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 SecurityContextPersistenceFilter의 역할에 대해서 아래와 같이 간단하게 정리하고 글을 마무리 하도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;✔️ SecurityFilterChain이 시작하는 시점에.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  세션 기반 자격 증명일 경우,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP Session에서 SecurityContext를 로드(load)한다.&lt;/li&gt;
&lt;li&gt;로드한 SecurityContext를 SecurityContextHolder에 저장한다(hold)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  &lt;/b&gt;토큰 기반(JWT 등) 자격 증명일 경우,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비어있는 SecurityContext를 로드(load)한다. 즉, 아무일도 하지 않음.&lt;/li&gt;
&lt;li&gt;로드한 비어 있는 SecurityContext를 SecurityContextHolder에 저장한다(hold)&lt;/li&gt;
&lt;li&gt;⭐⭐ &lt;b&gt;비어 있으므로 누군가가 채워줘야 한다.&lt;/b&gt; ⭐⭐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;✔️ SecurityFilterChain이 끝나는 시점에.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  세션 기반 자격 증명일 경우&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SecurityContextHolder에 저장된 SecurityContext를 제거(clear)한다.&lt;/li&gt;
&lt;li&gt;SecurityContext를 HTTP Session에 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  &lt;/b&gt;토큰 기반(JWT 등) 자격 증명일 경우,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SecurityContextHolder에 저장된 SecurityContext를 제거(clear)한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  참고로 &lt;span style=&quot;color: #ef5369;&quot;&gt;SecurityContextPersistenceFilter&lt;/span&gt;는 Spring Security 5.7 이후로 Deprecated 되었으며, 5.7 버전 이후로는 &lt;span style=&quot;color: #ef5369;&quot;&gt;SecurityContextPersistenceFilter&lt;/span&gt; 대신에 &lt;span style=&quot;color: #ef5369;&quot;&gt;SecurityContextHolderFilter&lt;/span&gt;를 사용하기를 권장하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글이 SecurityContext를 이해하는데 도움이 되셨길 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring Security</category>
      <category>securityContext</category>
      <category>SecurityContextHolder</category>
      <category>SecurityContextPersistenceFilter</category>
      <category>SecurityContextRepository</category>
      <category>ThreadLocalSecurityContextHolderStrategy</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/60</guid>
      <comments>https://itvillage.tistory.com/entry/SecurityContext%EB%8A%94-%EC%96%B8%EC%A0%9C-%EC%84%B8%EC%85%98%EA%B3%BC-%EC%97%B0%EA%B2%B0%EB%90%98%EA%B3%A0-%EC%96%B8%EC%A0%9C-%EB%B9%84%EC%9B%8Cclear%EC%A7%88%EA%B9%8C-1#entry60comment</comments>
      <pubDate>Thu, 19 Jan 2023 21:19:07 +0900</pubDate>
    </item>
    <item>
      <title>Spring Security에서의 권한 부여 (Authorization) 처리 흐름</title>
      <link>https://itvillage.tistory.com/entry/Spring-Security%EC%97%90%EC%84%9C%EC%9D%98-%EA%B6%8C%ED%95%9C-%EB%B6%80%EC%97%AC-Authorization-%EC%B2%98%EB%A6%AC-%ED%9D%90%EB%A6%84</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;769&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LoTcd/btrWwsmB9eb/LzydB7M0hwq9vWRjjMrMx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LoTcd/btrWwsmB9eb/LzydB7M0hwq9vWRjjMrMx0/img.png&quot; data-alt=&quot;Spring Security의 권한 부여 처리 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LoTcd/btrWwsmB9eb/LzydB7M0hwq9vWRjjMrMx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLoTcd%2FbtrWwsmB9eb%2FLzydB7M0hwq9vWRjjMrMx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1132&quot; height=&quot;769&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;769&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Security의 권한 부여 처리 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Security Filter Chain에서 URL을 통해 사용자의 액세스를 제한하는 &lt;b&gt;권한 부여 Filter&lt;/b&gt;는 바로 &lt;code&gt;AuthorizationFilter&lt;/code&gt; 이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SecurityContextHolder로 부터 Authentication을 획득&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SecurityContextHolder로 부터 획득한Authentication과 HttpServletRequest를 &lt;code&gt;AuthorizationManager&lt;/code&gt;에게 전달&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuthorizationManager&lt;/code&gt;는 권한 부여 처리를 총괄하는 매니저 역할을 한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RequestMatcherDelegatingAuthorizationManager&lt;/code&gt;는 &lt;code&gt;AuthorizationManager&lt;/code&gt;를 구현하는 구현체 중 하나이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;RequestMatcherDelegatingAuthorizationManager&lt;/code&gt;는 &lt;code&gt;RequestMatcher&lt;/code&gt; 평가식을 기반으로 해당 평가식에 매치되는 &lt;code&gt;AuthorizationManager&lt;/code&gt;에게 권한 부여 처리를 위임하는 역할&lt;/li&gt;
&lt;li&gt;⭐ &lt;code&gt;RequestMatcherDelegatingAuthorizationManager&lt;/code&gt;가 직접 권한 부여 처리를 하는 것이 아니라 &lt;code&gt;RequestMatcher&lt;/code&gt;를 통해 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;매치되는 AuthorizationManager 구현 클래스에게 위임&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RequestMatcherDelegatingAuthorizationManager&lt;/code&gt; 내부에서 매치되는 &lt;code&gt;AuthorizationManager&lt;/code&gt; 구현 클래스가 있다면 해당 &lt;code&gt;AuthorizationManager&lt;/code&gt; 구현 클래스가 사용자의 권한을 체크&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;적절한 권한이 아니라면 (5)와 같이 AccessDeniedException이 throw되고 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;ExceptionTranslationFilter&lt;/b&gt;&lt;/span&gt;가 AccessDeniedException을 처리&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java Backend 개발자 되기/Spring Security</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/57</guid>
      <comments>https://itvillage.tistory.com/entry/Spring-Security%EC%97%90%EC%84%9C%EC%9D%98-%EA%B6%8C%ED%95%9C-%EB%B6%80%EC%97%AC-Authorization-%EC%B2%98%EB%A6%AC-%ED%9D%90%EB%A6%84#entry57comment</comments>
      <pubDate>Tue, 17 Jan 2023 22:59:26 +0900</pubDate>
    </item>
    <item>
      <title>Spring Security에서의 인증(Authentication) 처리 흐름</title>
      <link>https://itvillage.tistory.com/entry/Spring-Security%EC%97%90%EC%84%9C%EC%9D%98-%EC%9D%B8%EC%A6%9DAuthentication-%EC%B2%98%EB%A6%AC-%ED%9D%90%EB%A6%84</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf7BmN/btrWwf8QCCJ/EeY3vtYc6GpKcuZdaYetI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf7BmN/btrWwf8QCCJ/EeY3vtYc6GpKcuZdaYetI0/img.png&quot; data-alt=&quot;Spring Security 인증 컴포넌트의 인터랙션 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf7BmN/btrWwf8QCCJ/EeY3vtYc6GpKcuZdaYetI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf7BmN%2FbtrWwf8QCCJ%2FEeY3vtYc6GpKcuZdaYetI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;742&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Security 인증 컴포넌트의 인터랙션 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;UsernamePasswordAuthenticationFilter&lt;/code&gt; 는 Username/Password 기반의 인증 요청을 처리한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;UsernamePasswordAuthenticationToken&lt;/code&gt; 생성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;아직 인증되지 않은 Authentication 객체&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuthenticationManager&lt;/code&gt;는 인증 처리를 총괄하는 매니저 역할을 하는 인터페이스.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ProviderManager&lt;/code&gt;는 AuthenticationManager를 구현한 구현 클래스&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserDetails&lt;/code&gt;는 사용자의 자격을 증명해주는 크리덴셜(Credentials) 과 권한 정보를 가지고 있는 객체.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserDetailsService&lt;/code&gt;가 사용자의 Credential과 권한 정보를 조회해서 UserDetails 를 생성.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserDetailsService&lt;/code&gt;가 AuthenticationProvider에게 UserDetails 를 전달.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuthenticationProvider&lt;/code&gt;는 전달 받은 UserDetails에서 패스워드가 일치하는지 검증.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuthenticationProvider&lt;/code&gt;가 패스워드 검증에 성공하면 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;인증에 성공한 사용자의 정보(Principal, Credential, GrantedAuthorities)를 포함한 Authentication을 생성.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인증된 Authentication&lt;/b&gt;을 전달 받은 &lt;b&gt;UsernamePasswordAuthenticationFilter&lt;/b&gt;는 &lt;code&gt;SecurityContextHolder&lt;/code&gt;를 이용해 &lt;code&gt;SecurityContext&lt;/code&gt;에 &lt;b&gt;인증된 Authentication&lt;/b&gt;을 저장.&lt;br /&gt;&lt;code&gt;SecurityContext&lt;/code&gt;는 다시 &lt;b&gt;HttpSession&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;에 저장되어 &lt;/span&gt;&lt;b&gt;사용자의 인증 상태를 유지.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Spring Security의 인증 컴포넌트&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Username/Password 방식의 인증 처리의 시작점&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ UsernamePasswordAuthenticationToken&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자의 인증 처리가 되기 전에는 인증되지 않은 Authentication.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Username과 Password 정보를 포함하고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용자의 인증 처리가 된 이후에는 인증된 Authentication
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Principal, Authorities를 포함하고 있다.&lt;/li&gt;
&lt;li&gt;Credentials 정보는 민감한 정보이므로 인증 이 후에는 제거된다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3️⃣ AuthenticationManager&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증 처리를 지시하는 매니저 역할을 한다.&lt;/li&gt;
&lt;li&gt;인증 매니저 계의 총 지배인&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4️⃣ ProviderManager&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AuthenticationManager의 구현 클래스&lt;/li&gt;
&lt;li&gt;인증 매니저 계의 지배인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인증이 성공적으로 이루어진 후, Crendentials를 제거한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증 끝났으니까 민감한 정보는 가지고 있을 필요가 없음.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5️⃣ AuthenticationProvider&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증된 사용자인지를 판단하는 역할&lt;/li&gt;
&lt;li&gt;인증 매니저의 지시를 받는 현장 담당자 역할&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6️⃣ UserDetails&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;크리덴셜 저장소에 저장된 크리덴셜 정보를 가지고 있는 객체&lt;/li&gt;
&lt;li&gt;AuthenticationProvider는 UserDetailsService로부터 UserDetails를 전달 받는다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;7️⃣ UserDetailsService&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UserDetails 객체를 생성해서 AuthenticationProvider에게 제공하는 역할을 한다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;8️⃣ SecurityContext와 SecurityContextHolder&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증된 Authentication 정보를 저장하는 저장 객체&lt;/li&gt;
&lt;li&gt;SecurityContext는 인증 성공 이 후, HTTP Session에 저장된다.&lt;/li&gt;
&lt;li&gt;HTTP Session은 최종적으로 &lt;span style=&quot;color: #ef5369;&quot;&gt;StandardManager&lt;/span&gt;의 상위 클래스인 &lt;span style=&quot;color: #ef5369;&quot;&gt;ManagerBase&lt;/span&gt;에서 관리된다.&lt;/li&gt;
&lt;li&gt;⭐ &lt;span style=&quot;color: #ef5369;&quot;&gt;HTTP Session에 누가 저장 할까?&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최초 인증에 성공할 경우에는 UsernamePasswordAuthenticationFilter에서&amp;nbsp; &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpSessionSecurityContextRepository&lt;/span&gt;를 이용해 HTTP Session에 저장한다.&lt;/li&gt;
&lt;li&gt;인증 이 후에 재요청 시에는 &lt;span style=&quot;color: #ef5369;&quot;&gt;SecurityContextPersistenceFilter&lt;/span&gt;가 저장한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증 이 후 재요청 시의 프로세스는 대략 아래와 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP 요청&lt;/li&gt;
&lt;li&gt;--&amp;gt; &lt;span style=&quot;color: #ef5369;&quot;&gt;SecurityContextPersistenceFilter&lt;/span&gt;가 HTTP Session에서 SecurityContext를 꺼낸 후, SecurityContextHolder에 저장&lt;/li&gt;
&lt;li&gt;-&amp;gt; 이 후, 요청이 끝나는 시점에 &lt;span style=&quot;color: #ef5369;&quot;&gt;SecurityContextPersistenceFilter&lt;/span&gt;의 finally 블럭에서
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SecurityContextHolder에 저장된 SecurityContext를 지운 뒤,&lt;/li&gt;
&lt;li&gt;--&amp;gt; &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpSessionSecurityContextRepository&lt;/span&gt;를 이용해 HTTP Session에 SecurityContext를 다시 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;555&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y6dyi/btrWv7CWuGC/2rq52NFnCaTH26lmLk8cB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y6dyi/btrWv7CWuGC/2rq52NFnCaTH26lmLk8cB1/img.png&quot; data-alt=&quot;SecurityContext의 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y6dyi/btrWv7CWuGC/2rq52NFnCaTH26lmLk8cB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY6dyi%2FbtrWv7CWuGC%2F2rq52NFnCaTH26lmLk8cB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;555&quot; height=&quot;263&quot; data-origin-width=&quot;555&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SecurityContext의 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring Security</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/56</guid>
      <comments>https://itvillage.tistory.com/entry/Spring-Security%EC%97%90%EC%84%9C%EC%9D%98-%EC%9D%B8%EC%A6%9DAuthentication-%EC%B2%98%EB%A6%AC-%ED%9D%90%EB%A6%84#entry56comment</comments>
      <pubDate>Tue, 17 Jan 2023 22:54:15 +0900</pubDate>
    </item>
    <item>
      <title>그림과 코드로 알아보는 Spring MVC 요청 처리 흐름</title>
      <link>https://itvillage.tistory.com/entry/%EA%B7%B8%EB%A6%BC%EA%B3%BC-%EC%BD%94%EB%93%9C%EB%A1%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EB%8A%94-Spring-MVC-%EC%9A%94%EC%B2%AD-%EC%B2%98%EB%A6%AC-%ED%9D%90%EB%A6%84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 흔히 알고 있는 Spring MVC의 요청 처리 흐름 한 가운데에는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;DispatcherServlet&lt;/code&gt;&lt;/span&gt;이 자리잡고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트로부터 들어오는 HTTP request는 Servlet Filter를 거쳐 DispatcherServlet에 전달되고, DispatcherServlet은 해당 request를 처리할 Controller를 검색하고 대상 Controller에게 request를 전달하기 위해 HandlerMapping과 HandlerAdapter 등의 컴포넌트를 이용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분 다 알고 있는 내용이기 때문에 자세한 설명은 필요없을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 구글링을 통해 그림으로 표현한 Spring MVC의 요청 처리 흐름에 대한 자료는 많이 볼 수 있지만 코드 베이스의 자료는 생각보다 찾기 힘든 것 같아서 이 참에 코드 베이스의 흐름을 정리해 볼까 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSR(Server Side Rendering) 방식의 코드 흐름&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR(Server Side Rendering) 방식은 CSR(Client Side Rendering) 방식의 애플리케이션이 본격적으로 사용되기 이 전부터 사용되던 전통적인 방식으로 아파치 톰캣(Apache Tomcat) 같은 서블릿 컨테이너가 Thymeleaf 등의 템플릿 기술을 이용해 HTML 템플릿을 렌더링 한 후에 클라이언트 쪽으로 내려주는 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DTRq6/btrWq3Nen7E/cOmjvl0TZbc0dOwKVzLXzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DTRq6/btrWq3Nen7E/cOmjvl0TZbc0dOwKVzLXzK/img.png&quot; data-alt=&quot;[그림 1-1] SSR 방식의 Spring MVC 요청 처리 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DTRq6/btrWq3Nen7E/cOmjvl0TZbc0dOwKVzLXzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDTRq6%2FbtrWq3Nen7E%2FcOmjvl0TZbc0dOwKVzLXzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1041&quot; height=&quot;600&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-1] SSR 방식의 Spring MVC 요청 처리 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[그림 1-1]은 우리가 흔히들 알고 있는 Spring MVC의 요청 처리 흐름 중에서 SSR(Server Side Rendering) 방식의 요청 처리 흐름입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR 방식의 요청 처리 흐름의 특징은 ViewResolver가 적절한 뷰 정보를 해석한 후에 대상 View 객체에게 응답 데이터를 전달하고 View를 통해 이 응답 데이터를 HTML 같은 템플릿에 채워 넣어서 최종적으로 클라이언트에게 전송한다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 [그림 1-1]에 있는 Spring MVC 컴포넌트들을 모두 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR 방식의 요청 처리 흐름을 코드로 표현하면 다음과 같은 순서로 호출이 됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;DispatcherServlet&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;doDispatch(HttpServletRequest request, HttpServletResponse response)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;getHandler&lt;/span&gt;(HttpServletRequest request)&amp;nbsp; &lt;span style=&quot;color: #1b711d; background-color: #9feec3;&quot;&gt;// HandlerMapping을 통해 HandlerChain을 얻는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;HandlerAdapter ha = &lt;span style=&quot;color: #ef5369;&quot;&gt;getHandlerAdapter&lt;/span&gt;(mappedHandler.getHandler());&lt;/li&gt;
&lt;li&gt;mv&amp;nbsp;=&amp;nbsp;ha.handle(processedRequest,&amp;nbsp;response,&amp;nbsp;mappedHandler.getHandler());&lt;/li&gt;
&lt;li&gt;processDispatchResult(processedRequest,&amp;nbsp;response,&amp;nbsp;mappedHandler,&amp;nbsp;mv,&amp;nbsp;dispatchException);&lt;/li&gt;
&lt;li&gt;render(mv,&amp;nbsp;request,&amp;nbsp;response);&lt;/li&gt;
&lt;li&gt;View view = &lt;span style=&quot;color: #ef5369;&quot;&gt;resolveViewName&lt;/span&gt;(viewName, mv.getModelInternal(), locale, request);&amp;nbsp; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;// ViewResolver가 View를 해석해 적절한 View 객체를 얻는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;view.&lt;span style=&quot;color: #ef5369;&quot;&gt;render&lt;/span&gt;(mv.getModelInternal(), request, response);&amp;nbsp; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;// View를 렌더링한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;ThymeleafView&lt;/span&gt; (Thymeleaf 템플릿 기술을 사용할 경우)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;render(final&amp;nbsp;Map&amp;lt;String,&amp;nbsp;?&amp;gt;&amp;nbsp;model,&amp;nbsp;final&amp;nbsp;HttpServletRequest&amp;nbsp;request,&amp;nbsp;final&amp;nbsp;HttpServletResponse&amp;nbsp;response)&lt;/li&gt;
&lt;li&gt;renderFragment(this.markupSelectors,&amp;nbsp;model,&amp;nbsp;request,&amp;nbsp;response);&lt;/li&gt;
&lt;li&gt;String&amp;nbsp;viewTemplateName&amp;nbsp;=&amp;nbsp;getTemplateName();&lt;/li&gt;
&lt;li&gt;ISpringTemplateEngine&amp;nbsp;viewTemplateEngine&amp;nbsp;=&amp;nbsp;getTemplateEngine();&lt;/li&gt;
&lt;li&gt;viewTemplateEngine.process(templateName,&amp;nbsp;processMarkupSelectors,&amp;nbsp;context,&amp;nbsp;templateWriter);&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;TemplateEngine&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;process(new&amp;nbsp;TemplateSpec(template,&amp;nbsp;templateSelectors,&amp;nbsp;null,&amp;nbsp;&amp;nbsp;null,null),&amp;nbsp;context,&amp;nbsp;writer);&lt;/li&gt;
&lt;li&gt;TemplateManager&amp;nbsp;templateManager&amp;nbsp;=&amp;nbsp;this.configuration.getTemplateManager();&lt;/li&gt;
&lt;li&gt;templateManager.parseAndProcess(templateSpec,&amp;nbsp;context,&amp;nbsp;writer);&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;TemplateManager&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TemplateModel&amp;nbsp;cached&amp;nbsp;=&amp;nbsp;&amp;nbsp;this.templateCache.get(cacheKey);&lt;/li&gt;
&lt;li&gt;cached.process(processingHandlerChain);&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;TemplateEngine&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;writer.flush();&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 흐름을 보면 꽤 복잡한 흐름을 거치고 실제 내부 코드들은 더 복잡하지만 어쨌든 각각의 컴포넌트들이 각자 맡은바 역할에만 집중하고 있는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View를 렌더링하는 과정은 일반적으로 Thymeleaf 기술을 사용하고 있다고 가정했을 때, &lt;code&gt;View&lt;/code&gt; 인터페이스의 구현 클래스인 &lt;code&gt;ThymeleafView&lt;/code&gt;가 &lt;code&gt;TemplateEngine&lt;/code&gt;과 &lt;code&gt;TemplateManager&lt;/code&gt;를 이용해 클라이언트에게 전송할 HTML에 응답 데이터를 채워 넣은 후, 최종 템플릿을 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 마지막은 &lt;code&gt;Writer&lt;/code&gt;가 write한 템플릿 데이터를 flush 해서 클라이언트에게 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿 데이터는 &lt;code&gt;TemplateEngine&lt;/code&gt;과 &lt;code&gt;TemplateManager&lt;/code&gt; 어딘가에서 write 된다라고 생각하면 될 것 같습니다. ^^;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CSR(Client Side Rendering) 방식의 코드 흐름&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSR(Client Side Rendering) 방식은 SSR 방식처럼 HTML 템플릿을 렌더링 해서 클라이언트 쪽으로 내려주는것이 아니라 Controller가 리턴한 데이터를 주로 JSON 형태로 HTTP response body에 실어서 응답 데이터만 클라이언트 쪽에 전달해주는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 말하는 클라이언트는 Apache나 Nginx 같은 웹서버에서 실행되는 Frontend 측 웹 앱이 될 수도 있고, 스마트 폰에서 실행되는 네이티브 앱이 될 수도 있으며, 데스크탑에서 실행되는 데스크탑 애플리케이션이 될 수도 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-006.png&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crPxb8/btrWpZdHGmj/OSeVTkkF2tXDDKce5Ah9F0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crPxb8/btrWpZdHGmj/OSeVTkkF2tXDDKce5Ah9F0/img.png&quot; data-alt=&quot;[그림 1-2] CSR 방식의 Spring MVC 요청 처리 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crPxb8/btrWpZdHGmj/OSeVTkkF2tXDDKce5Ah9F0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrPxb8%2FbtrWpZdHGmj%2FOSeVTkkF2tXDDKce5Ah9F0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1041&quot; height=&quot;600&quot; data-filename=&quot;K-006.png&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-2] CSR 방식의 Spring MVC 요청 처리 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[그림 1-2]은 Spring MVC의 요청 처리 흐름 중에서 CSR(Client Side Rendering) 방식의 요청 처리 흐름입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림에서 보다시피 View 자체가 필요 없기 때문에 &lt;span style=&quot;color: #ef5369;&quot;&gt;ViewResolver와 View의 로직을 타지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1673877465927&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		...
		...

		// HandlerAdapter가 리턴한 ModelAndView가 null이면 뷰를 렌더링하지 않는다.
		if (mv != null &amp;amp;&amp;amp; !mv.wasCleared()) {
			render(mv, request, response); 
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace(&quot;No view rendering, null ModelAndView returned.&quot;);
			}
		}
		...
		...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 DispatcherServlet의 processDispatchResult() 코드의 일부입니다.&lt;br /&gt;코드를 보면 HandlerAdapter가 리턴 받은 ModelAndView 객체가 null이면 뷰를 렌더링하지 않는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이 말은 Controller에서 리턴한 객체가 ResponseEntity 같은 타입의 객체인 경우 내부적으로 ModelAndView 객체를 생성하지 않는다는 의미와도 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSR 방식의 요청 처리 흐름의 코드는 앞에서 살펴보았던 SSR 방식의 코드 흐름 중, &lt;span style=&quot;color: #ef5369;&quot;&gt;DispatcherServlet의 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException) 내부에서 render(mv, request, response)의 호출부터 이 후 나머지 과정의 처리를 생략하면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 글을 통해 Spring MVC의 요청 처리 흐름을 조금 더 깊게 이해하는데 조금이라도 도움이 될 수 있길 바랍니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring MVC</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/55</guid>
      <comments>https://itvillage.tistory.com/entry/%EA%B7%B8%EB%A6%BC%EA%B3%BC-%EC%BD%94%EB%93%9C%EB%A1%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EB%8A%94-Spring-MVC-%EC%9A%94%EC%B2%AD-%EC%B2%98%EB%A6%AC-%ED%9D%90%EB%A6%84#entry55comment</comments>
      <pubDate>Mon, 16 Jan 2023 23:07:04 +0900</pubDate>
    </item>
    <item>
      <title>메시지 암호화에 대한 짧은 이야기</title>
      <link>https://itvillage.tistory.com/entry/%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%95%94%ED%98%B8%ED%99%94%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A7%A7%EC%9D%80-%EC%9D%B4%EC%95%BC%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 일반적으로 생각하는 대표적인 암호화의 대상은 크리덴셜(Credential)의 한 종류인 패스워드입니다. 패스워드는 암호화의 대상인 메시지를 조금 더 구체적으로 표현한 것이라고 볼 수 있을테구요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 시간에는 암호화에 대한 아주 쉬운 이야기를 짧게 해보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화란 무엇인지 암호화 유형에는 기본적으로 어떤것이 있는지 등을 아주 쉽게 이해할 수 있도록 짧게 정리해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;암호화란?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자의 민감한 정보를 보호하기 위한 기술&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Crypto 또는 Cipher라는 비슷한 용어가 있음&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Ciphertext: 암호화 된 텍스트&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Cryptography
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암호학적 관점에서의 암호화 전체를 아우르는 말&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Crypto Currency
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암호 화폐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encryption / Decryption
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암호학 관점에서의 데이터 변환 과정&lt;/li&gt;
&lt;li&gt;Encryption
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암호화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Decryption
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복호화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encoding / Decoding
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암호학 관점은 아님&lt;/li&gt;
&lt;li&gt;데이터의 형식이나 패턴을 목적에 맞게 변환하는 과정&lt;/li&gt;
&lt;li&gt;예)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Encoding
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;encode(원본 데이터) &amp;rarr; Base64 형식의 문자열로 인코딩&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Decoding
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;decode(Base64 형식으로 인코딩된 문자열) &amp;rarr; 원본 데이터&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;암호화 대상의 기준&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 애플리케이션의 아이디/패스워드 로그인을 생각해보면 암호화의 대상 기준은 무엇이 되어야하는가를 잘 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패스워드는 민감한(Sensitive) 정보이므로 타인에게 평문(Plain Text) 형태로 유출이 되면 안될테니까요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 애플리케이션 구현시 외부에 유출되면 문제가 발생할 수 있는 정보들은 암호화의 대상이 될 수 있으며, 아래에서 나열한 항목들은 암호화를 진지하게 고려해 보아야 하는 대상이라고 볼 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그인 인증을 위한 패스워드&lt;/li&gt;
&lt;li&gt;구글, AWS 등의 외부 서비스를 이용하기 위한 계정 정보 또는 Secret 정보&lt;/li&gt;
&lt;li&gt;JWT를 생성하고 검증하는데 필요한 Secret 정보&lt;/li&gt;
&lt;li&gt;데이터베이스 접속 정보
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스 접속 정보에서 root 계정을 사용하는 분들이 많은데 root 계정이 아닌 별도의 사용자 계정을 만들어서 사용하길 권장합니다.&lt;/li&gt;
&lt;li&gt;root는 말 그대로 최고 권한을 가진 계정이기 때문에 직접적으로 사용하지 않는 것이 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;암/복호화 가능 여부에 따른 암호화 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;양방향 암호화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암호화도 되고 복호화도 되는 암호화 방식&lt;/li&gt;
&lt;li&gt;암호화 된 메시지를 상대방이 볼 수 있어야 하는 암호화 방식이며, 복호화가 되어야 정상적인 메시지를 확인할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단방향 암호화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암호화는 되지만 복호화는 되지 않는 암호화 방식&lt;/li&gt;
&lt;li&gt;암호화된 메시지를 굳이 복호화 해서 확인할 필요가 없을 때 사용할 수 있는 암호화 방식입니다.&lt;/li&gt;
&lt;li&gt;단방향 암호화를 적용하는 대표적인 항목이 로그인 패스워드입니다. 로그인 패스워드의 경우 사용자가 입력한 패스워드를 암호화 한 데이터와 이미 암호화 되어 저장되어 있는 데이터가 일치한지 여부만 판단하면 되기 때문에 복호화가 필요없습니다.&lt;/li&gt;
&lt;li&gt;단방향 암호화는 일반적으로 해시 알고리즘을 사용하는데 엄밀하게 말하자면 해시 알고리즘 자체가 암호화라고 보기는 어렵지만 어쨌든 원본 메시지를 보호할 수 있다는 관점에서의 암호화라고 생각하면 좋을 것 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;키 유형에 따른 암호화 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대칭키 암호화 방식
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 키를 서로 공유해서 해당 키로 암호화/복호화를 수행&lt;/li&gt;
&lt;li&gt;대칭키 암호화 방식은 하나의 키를 서로 공유해야 하기 때문에 공유된 키가 유출되면 심각한 문제가 발생할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;비대칭키 암호화 방식
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암호화를 수행하는 키와 복호화를 수행하는 키가 다릅니다.&lt;/li&gt;
&lt;li&gt;Public Key와 Private Key&lt;/li&gt;
&lt;li&gt;⭐ &lt;b&gt;Public Key로 암호화 하는 것은 민감한 데이터 또는 메시지 보호를 위해서.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;⭐ &lt;b&gt;Private Key&lt;/b&gt;로 암호화 하는 것은 디지털 서명(Digital Signature)을 위해서.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;블록체인에서 이 방식을 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring Security</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/54</guid>
      <comments>https://itvillage.tistory.com/entry/%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%95%94%ED%98%B8%ED%99%94%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A7%A7%EC%9D%80-%EC%9D%B4%EC%95%BC%EA%B8%B0#entry54comment</comments>
      <pubDate>Mon, 16 Jan 2023 14:30:24 +0900</pubDate>
    </item>
    <item>
      <title>Spring WebFlux + R2DBC 환경에서 H2 web console 사용하기</title>
      <link>https://itvillage.tistory.com/entry/Spring-WebFlux-R2DBC-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-H2-Console-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;※ 이 글은 아끼는 저의 학생이 공유한 글을 기반으로 재편집한 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 Spring MVC 환경에서는 인메모리 DB인 h2의 web console을 이용하기 수월했었는데, Spring WebFlux + R2DBC 환경에서는 Spring MVC 환경에서 사용하던 방법 그대로 h2의 web console을 이용할 수 없기 때문에 별도의 설정을 해주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring WebFlux + R2DBC 환경에서 h2 web console을 사용하기 위한 설정 방법은 다음과 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Gradle 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;== build.gradle ==&lt;/p&gt;
&lt;pre id=&quot;code_1673828305666&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
	...
	implementation 'io.r2dbc:r2dbc-h2'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle의 dependencies에 &lt;code&gt;implementation 'io.r2dbc:r2dbc-h2'&lt;/code&gt;를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;application.yml 설정&lt;/h3&gt;
&lt;pre id=&quot;code_1673828488292&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  sql:
    init:
      schema-locations: classpath*:db/h2/schema.sql
      data-locations: classpath*:db/h2/data.sql
  r2dbc:         # (1)                                   
    url: r2dbc:h2:mem:///test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password:
h2:       # (2)
  console:
    port: 8090
logging:
  level:
    org:
      springframework:&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(1)과 같이 R2DBC용 H2 Connection 정보를 설정합니다.&lt;/li&gt;
&lt;li&gt;(2)와 같이 h2 콘솔 포트를 8090으로 설정합니다. 포트 번호는 편한 번호로 설정하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;h2 console 서버 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;==== H2ServerConfig.java ====&lt;/p&gt;
&lt;pre id=&quot;code_1673828741924&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Component
public class H2ServerConfig {
    @Value(&quot;${h2.console.port}&quot;)
    private Integer port;
    private Server webServer;

    @EventListener(ContextRefreshedEvent.class)
    public void start() throws java.sql.SQLException {
        log.info(&quot;started h2 console at port {}.&quot;, port);
        this.webServer = Server.createWebServer(&quot;-webPort&quot;, port.toString()).start();
    }

    @EventListener(ContextClosedEvent.class)
    public void stop() {
        log.info(&quot;stopped h2 console at port {}.&quot;, port); this.webServer.stop();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml에서 추가한 포트 번호를 H2 Server를 start 하기 전에 지정해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;애플리케이션 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션을 실행한 후, &lt;code&gt;http://localhost:8090&lt;/code&gt;으로 접속해서 h2 web console로 접속이 잘 되는지 확인합니다.&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring WebFlux</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/53</guid>
      <comments>https://itvillage.tistory.com/entry/Spring-WebFlux-R2DBC-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-H2-Console-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0#entry53comment</comments>
      <pubDate>Mon, 16 Jan 2023 09:34:25 +0900</pubDate>
    </item>
    <item>
      <title>HTTPS의 동작 원리와 CA의 역할을 그림으로..</title>
      <link>https://itvillage.tistory.com/entry/HTTPS%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC%EC%99%80-CA%EC%9D%98-%EC%97%AD%ED%95%A0%EC%9D%84-%EA%B7%B8%EB%A6%BC%EC%9C%BC%EB%A1%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;HTTPS의 동작 원리와 CA를 그림으로 표현한다면 한마디로 아래와 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTTPS의 동작 원리&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;https.png&quot; data-origin-width=&quot;1153&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yp46Z/btrWcJVY2hd/GIy6zFfATkKNXakMYnRuv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yp46Z/btrWcJVY2hd/GIy6zFfATkKNXakMYnRuv1/img.png&quot; data-alt=&quot;HTTPS의 동작 원리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yp46Z/btrWcJVY2hd/GIy6zFfATkKNXakMYnRuv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyp46Z%2FbtrWcJVY2hd%2FGIy6zFfATkKNXakMYnRuv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1153&quot; height=&quot;742&quot; data-filename=&quot;https.png&quot; data-origin-width=&quot;1153&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HTTPS의 동작 원리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CA의 역할&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ca.png&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pSTCb/btrWdo4YxP7/cgj9kioFILqbZsSeZi9B20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pSTCb/btrWdo4YxP7/cgj9kioFILqbZsSeZi9B20/img.png&quot; data-alt=&quot;CA의 역할&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pSTCb/btrWdo4YxP7/cgj9kioFILqbZsSeZi9B20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpSTCb%2FbtrWdo4YxP7%2Fcgj9kioFILqbZsSeZi9B20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1016&quot; height=&quot;678&quot; data-filename=&quot;ca.png&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CA의 역할&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring Security</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/52</guid>
      <comments>https://itvillage.tistory.com/entry/HTTPS%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC%EC%99%80-CA%EC%9D%98-%EC%97%AD%ED%95%A0%EC%9D%84-%EA%B7%B8%EB%A6%BC%EC%9C%BC%EB%A1%9C#entry52comment</comments>
      <pubDate>Fri, 13 Jan 2023 22:34:33 +0900</pubDate>
    </item>
    <item>
      <title>No default constructor for entity 에러</title>
      <link>https://itvillage.tistory.com/entry/No-default-constructor-for-entity</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Data JPA에서 &lt;code&gt;findById()&lt;/code&gt; 같은 API를 사용해 데이터를 조회할 때, &lt;code&gt;No default constructor for entity&lt;/code&gt; 라는 에러가 발생하는 이유가 무엇인지 조금 더 디테일하게 분석해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Data JPA에서는 개발자가 JpaRepository 등의 인터페이스를 상속하는 Repository 인터페이스를 구현하는 구현 클래스의 인스턴스를 생성하기 위해 내부적으로 Dynamic Proxy라는 기술과 Reflection을 이용하는데 &lt;code&gt;RepositoryFactorySupport&lt;/code&gt;에서 해당 인스턴스(Repository의 Proxy)를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, 여기까지는 Spring에서 지원하는 Repository 인터페이스(개발자가 JpaRepository 같은 인터페이스를 상속한 Custom Repository)의 Proxy를 생성하는 역할을 할 뿐이고, 이 Proxy에서 타겟 클래스인 SimpleJpaRepository의 쿼리 메서드를 이용한 과정까지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 원한건 &lt;code&gt;No default constructor for entity&lt;/code&gt;라는 에러가 발생하는 이유를 조금 더 디테일하게 알고 싶은것이었기 때문에 조금 더 분석을 해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분석 결과 Spring Data에서 제공하는 RepositoryComposition이 RepositoryMethodInvoker를 이용해 앞에서 얻은 Proxy의 타겟 클래스인 SimpleJpaRepository에 구현된 쿼리 메서드(&lt;code&gt;findById()&lt;/code&gt; 같은)를 호출합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음부터는 Hibernate ORM의 영역입니다. Hibernate ORM에서 EntityManager의 역할을 하는 Session 인터페이스의 구현 클래스인 &lt;code&gt;SessionImpl&lt;/code&gt;에서 데이터베이스에 접속해 데이터를 조회합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;SessionImpl&lt;/code&gt;로부터 데이터베이스에 데이터를 조회하는 복잡한 로직이 시작되고 결국 조회한 데이터를 개발자가 정의 Entity 클래스의 object로 변환하기 위해 아래와 같이 &lt;code&gt;PojoInstantiator&lt;/code&gt;의 &lt;code&gt;instantiate()&lt;/code&gt;에서 Reflection을 통해 얻은 &lt;code&gt;Constructor&lt;/code&gt; 인스턴스로 &lt;code&gt;newInstance()&lt;/code&gt;를 호출해 &lt;span style=&quot;color: #ef5369;&quot;&gt;Entity 클래스의 object를 생성&lt;/span&gt;합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1672671973292&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or &amp;lt;http://www.gnu.org/licenses/lgpl-2.1.html&amp;gt;.
 */
package org.hibernate.tuple;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Constructor;

import org.hibernate.InstantiationException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.mapping.Component;

/**
 * Defines a POJO-based instantiator for use from the tuplizers.
 */
public class PojoInstantiator implements Instantiator, Serializable {
	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( PojoInstantiator.class.getName() );

	private transient Constructor constructor;

	private final Class mappedClass;
	private final transient ReflectionOptimizer.InstantiationOptimizer optimizer;
	private final boolean embeddedIdentifier;
	private final boolean isAbstract;
    ...
    ...
    
	public Object instantiate() {
		if ( isAbstract ) {
			throw new InstantiationException( &quot;Cannot instantiate abstract class or interface: &quot;, mappedClass );
		}
		else if ( optimizer != null ) {
			return optimizer.newInstance();
		}
		else if ( constructor == null ) {
			throw new InstantiationException( &quot;No default constructor for entity: &quot;, mappedClass );
		}
		else {
			try {
				return applyInterception( constructor.newInstance( (Object[]) null ) );
			}
			catch ( Exception e ) {
				throw new InstantiationException( &quot;Could not instantiate entity: &quot;, mappedClass, e );
			}
		}
	}
    
    ...
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그런데, 코드를 보면 constructor가 null이라면 &lt;code&gt;InstantiationException&lt;/code&gt;을 throw하도록 구현된 것을 볼 수 있습니다.&lt;br /&gt;이유는 &lt;span style=&quot;color: #ef5369;&quot;&gt;Java의 Reflection에서 object를 생성하기 위해서는 파라미터가 없는 디폴트 생성자(default constructor)가 필요하기 때문&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유때문에 &lt;span style=&quot;color: #ef5369;&quot;&gt;Spring Data JPA에서 사용되는 Entity 클래스에는 디폴트 생성자가 반드시 있어야한다&lt;/span&gt;는 사실을 기억하면 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 클래스에 파라미터를 가지는 별도의 생성자를 추가하지 않으면 내부적으로 디폴트 생성자가 있다고 가정하지만 오버로딩된 생성자가 추가되는 순간 디폴트 생성자는 개발자가 직접 지정을 해야한다는 사실은 잘 알고 있을거라 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 lombok에서 지원하는 &lt;span style=&quot;color: #ef5369;&quot;&gt;@Builder 패턴 등을 사용할 경우에는 항상 디폴트 생성자를 기본으로 가질 수 있도록 해야한다&lt;/span&gt;는 사실을 기억하면 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring Data JPA</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/51</guid>
      <comments>https://itvillage.tistory.com/entry/No-default-constructor-for-entity#entry51comment</comments>
      <pubDate>Tue, 3 Jan 2023 00:14:47 +0900</pubDate>
    </item>
    <item>
      <title>NIO의 구성요소와 동작 방식</title>
      <link>https://itvillage.tistory.com/entry/NIO%EC%9D%98-%EA%B5%AC%EC%84%B1%EC%9A%94%EC%86%8C%EC%99%80-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;NIO의 구성 요소&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Channel&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 소켓에 대한 채널을 의미하며 &lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;NetworkChannel&lt;/span&gt;&lt;/code&gt;&amp;nbsp;인터페이스로 정의된다. &lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;NetworkChannel&lt;/span&gt;&lt;/code&gt; 인터페이스의 구현 클래스로는 &lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;ServerSocketChannel&lt;/span&gt;&lt;/code&gt;, &lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;SocketChannel&lt;/span&gt;&lt;/code&gt;, &lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;DatagramChannel&lt;/span&gt;&lt;/code&gt; 등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;※ 소켓이란?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 프로그램과 클라이언트 프로그램의 양방향 통신을 위한 양방향 소프트웨어 엔드포인트이다. 엔드포인트는 IP 주소와 포트 번호로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 스트림은 read/write 둘 중 하나만 수행할 수 있는 단방향이지만 &lt;b&gt;Channel은 read/write 모두 수행할 수 있는 양방향 입출력 클래스&lt;/b&gt;이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ServerSocketChannel&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;ServerSocketChannel&lt;/span&gt;&lt;/code&gt;은 클라이언트의 연결 요청이 들어오면 [open] - [bind] - [accept] 단계를 거친 후, &lt;code&gt;SocketChannel&lt;/code&gt;을 생성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SocketChannel&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트가 전달한 데이터를 read하고, 클라이언트에게 데이터를 write하기 위한 채널이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ServerSocketChannel&lt;/code&gt;이 클라이언트의 연결을 accept하면 &lt;code&gt;SocketChannel&lt;/code&gt;이 생성된다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Selector&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Selector&lt;/code&gt;는 채널에 I/O 이벤트가 있는지 없는지를 알려주는 일종의 이벤트 리스너(Event Listener)의 역할을 한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ServerSocketChannel&lt;/code&gt;과 &lt;code&gt;SocketChannel&lt;/code&gt;은 &lt;code&gt;register()&lt;/code&gt; 메서드를 통해 &lt;code&gt;Selector&lt;/code&gt;로 등록된다.&lt;/li&gt;
&lt;li&gt;Selector에 등록된 채널은 I/O 이벤트 발생 시, 이벤트 리스닝의 대상이 된다.&lt;/li&gt;
&lt;li&gt;즉, &lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Selector.select()&lt;/span&gt;&lt;/code&gt;를 호출하면 I/O 이벤트가 있는 채널 set을 리턴한다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;SelectionKey&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SelectionKey&lt;/code&gt;는 Selector에 등록된 채널이 어떤 I/O 이벤트를 처리할지를 지정하는 객체이다.&lt;/li&gt;
&lt;li&gt;SelectionKey 유형
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SelectionKey.OP_ACCEPT&lt;/li&gt;
&lt;li&gt;SelectionKey.OP_CONNECT&lt;/li&gt;
&lt;li&gt;SelectionKey.OP_READ&lt;/li&gt;
&lt;li&gt;SelectionKey.OP_WRITE&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SelectionKey&lt;/code&gt;는 채널의 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;register()&lt;/code&gt;&lt;/span&gt;를 호출하면 리턴된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;SelectionKey.attach()&lt;/span&gt;&lt;/code&gt;를 통해 &lt;code&gt;SelectionKey&lt;/code&gt; 유형의 이벤트를 처리할 이벤트 핸들러를 등록할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SelectionKey&lt;/code&gt;의 유형은 &lt;code&gt;SelectionKey.interestOps()&lt;/code&gt;로 지정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;ByteBuffer&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 read/write 할 수 있는 버퍼로 Java의 NIO에서 사용된다.&lt;/li&gt;
&lt;li&gt;byte buffer가 사용하는 메모리의 위치에 따라서 non-direct buffer와 &lt;b&gt;direct buffer&lt;/b&gt;로 나눌 수 있다.&lt;/li&gt;
&lt;li&gt;non-direct buffer는 우리가 흔히 알고 있는 JVM의 heap memory를 사용하지만 &lt;b&gt;direct buffer&lt;/b&gt;는 OS(Operatoion System)의 memory를 사용한다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Charset&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;character(문자)를 byte로 인코딩하거나 byte를 charter(문자)로 디코딩할 때 사용하는 클래스이다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;NIO의 동작 방식&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ServerSocketChannel의 바인딩 흐름&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-003.png&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;703&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1Lm8s/btrThxLiJbm/47XKsSZ9tHZEkJ9oqnYafK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1Lm8s/btrThxLiJbm/47XKsSZ9tHZEkJ9oqnYafK/img.png&quot; data-alt=&quot;ServerSocketChannel의 바인딩 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1Lm8s/btrThxLiJbm/47XKsSZ9tHZEkJ9oqnYafK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1Lm8s%2FbtrThxLiJbm%2F47XKsSZ9tHZEkJ9oqnYafK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;911&quot; height=&quot;703&quot; data-filename=&quot;K-003.png&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;703&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ServerSocketChannel의 바인딩 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ServerSocketChannel과 Selector의 생성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션이 실행되면 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;ServerSocketChannel.open()&lt;/code&gt;&lt;/span&gt;을 통해 &lt;code&gt;ServerSocketChannel&lt;/code&gt;을 생성한다.&lt;/li&gt;
&lt;li&gt;애플리케이션이 실행되면 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;Selector.open()&lt;/code&gt;&lt;/span&gt;을 통해 &lt;code&gt;Selector&lt;/code&gt;를 생성한다. 생성된 Selector는 ServerSocketChannel 또는 SocketChannel 등을 Selector에 등록하는데 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;ServerSocketChannel.bind()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;를 통해 ServerSocketChannel을 바인딩한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서의 바인딩은&amp;nbsp; IP 주소와 로컬 포트 번호를 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;서버 소켓과 연결함&lt;/span&gt;&lt;/b&gt;을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;ServerSocketChannel.register()&lt;/span&gt;&lt;/code&gt;를 통해서 Selector에 ServerSocketChannel을 등록한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SelectionKey.OP_ACCEPT를 지정함으로써 클라이언트의 connect 요청 이벤트가 발생하면 클라이언트의 connect 요청을 수락하기 위한 로직을 처리할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ServerSocketChannel과 Selector를 통한 SocketChannel 연결 흐름&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-004.png&quot; data-origin-width=&quot;1401&quot; data-origin-height=&quot;740&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh6uLV/btrTjGAzVPb/QsLNb32iuzbZaTzIvg8euK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh6uLV/btrTjGAzVPb/QsLNb32iuzbZaTzIvg8euK/img.png&quot; data-alt=&quot;Client와 SocketChannel 연결 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh6uLV/btrTjGAzVPb/QsLNb32iuzbZaTzIvg8euK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh6uLV%2FbtrTjGAzVPb%2FQsLNb32iuzbZaTzIvg8euK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1401&quot; height=&quot;740&quot; data-filename=&quot;K-004.png&quot; data-origin-width=&quot;1401&quot; data-origin-height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Client와 SocketChannel 연결 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션이 실행되고, &lt;code&gt;ServerSocketChannel&lt;/code&gt;이 바인딩 된 이후에 &lt;code&gt;Selector&lt;/code&gt;는 무한 루프 상태에서 &lt;code&gt;SelectionKey&lt;/code&gt; 유형에 해당하는 &lt;span&gt;I/O 이벤트를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 처리하기 위해 대기 모드를 유지한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Selector.select()&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;를 통해 발생하는 이벤트를 기다린다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이벤트가 발생하는지에 대한 일종의 이벤트 리스닝이라고 볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Selector.selectedKeys()&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;를 통해 이벤트가 발생된 key를 의미하는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;SelectionKey&lt;/code&gt;&lt;/span&gt; set을 얻는다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서는 클라이언트의 connect 요청 흐름에 대해서만 설명하므로 SelectionKey.OP_ACCEPT에 해당하는 이벤트만 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;SelectionKey.channel()&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;을 통해 &lt;code&gt;ServerSocketChannel&lt;/code&gt;을 얻는다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;ServerSocketChannel.accept()&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;을 통해 클라이언트의 connect 요청을 기다리고, 클라이언트의 connect 요청이 들어오면 SocketChannel을 생성한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;ServerSocketChannel&lt;/code&gt;에서 사용하는 포트 번호는 클라이언트의 연결 요청을 수락하기 위해서 계속해서 사용되어야 하므로 &lt;span style=&quot;color: #ef5369;&quot;&gt;SocketChannel은 별도의 새 로컬 포트 번호를 할당 받아서 바인딩&lt;/span&gt;된다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Client와 SocketChannel의 데이터 read/write 흐름&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-006.png&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6tkID/btrTtgWQBfN/mkC4eTALeou7aKqrElqCKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6tkID/btrTtgWQBfN/mkC4eTALeou7aKqrElqCKK/img.png&quot; data-alt=&quot;Client와 SocketChannel의 데이터 read/write 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6tkID/btrTtgWQBfN/mkC4eTALeou7aKqrElqCKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6tkID%2FbtrTtgWQBfN%2FmkC4eTALeou7aKqrElqCKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1391&quot; height=&quot;730&quot; data-filename=&quot;K-006.png&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Client와 SocketChannel의 데이터 read/write 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Client와 SocketChannel이 바인딩 되면 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;code&gt;Selector.select()&lt;/code&gt;&lt;/span&gt;를 통해 I/O 이벤트가 발생할 때까지 대기한다.&lt;/li&gt;
&lt;li&gt;I/O 이벤트가 발생하면 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;code&gt;Selector.selectedKeys()&lt;/code&gt;&lt;/span&gt;를 이벤트가 발생한 SelectioinKey set을 얻는다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SelectionKey.channel()&lt;/code&gt;을 통해 이벤트가 발생한 SocketChannel을 얻을 수 있으며, 이 SocketChannel은 해당 이벤트를 처리할 이벤트 핸들러에게 전달된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SelectionKey.attachment()&lt;/code&gt;를 통해 이벤트를 처리할 이벤트 핸들러를 얻을 수 있으며, 이벤트 핸들러가 SocketChannel을 이용해 이벤트를 처리한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java/NIO</category>
      <category>nio</category>
      <category>SelectioinKey</category>
      <category>selector</category>
      <category>ServerSocketChannel</category>
      <category>SocketChannel</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/49</guid>
      <comments>https://itvillage.tistory.com/entry/NIO%EC%9D%98-%EA%B5%AC%EC%84%B1%EC%9A%94%EC%86%8C%EC%99%80-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D#entry49comment</comments>
      <pubDate>Tue, 6 Dec 2022 23:11:21 +0900</pubDate>
    </item>
    <item>
      <title>Mac OS에서 시스템 환경 변수 등록하는 방법</title>
      <link>https://itvillage.tistory.com/entry/Mac-OS%EC%97%90%EC%84%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EB%93%B1%EB%A1%9D%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mac OS의 기본 셸 확인하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mac OS &lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;Catalina 이 전 버전까지는 기본 셸이 Bash shell(bash)이었으나, Catalina부터 Z shell(zsh)이 기본 셸로 변경되었습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;&amp;nbsp;&lt;br /&gt;따라서 Mac OS에서 시스템 환경 변수를 등록하기 위해서는 현재 사용하고 있는 Mac OS의 기본 셸이 무엇인지부터 확인을 해야 합니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;Mac OS에서 기본 셸은 아래의 터미널 명령으로 손쉽게 확인할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669278953058&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kevin@hwang ~ % echo $SHELL
/bin/zsh
or
/bin/bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Z shell에서 시스템 환경 변수 등록 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Z shell의 경우 다음과 같은 절차를 따릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래와 같이 vi 편집기로 .zshrc 파일을 생성 또는 오픈합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1669279168284&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kevin@hwang ~ % vi ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.zshrc 파일에 아래와 같이 등록하고자 하는 환경 변수명과 값을 입력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1669279300075&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export JWT_SECRET_KEY=kevin111111111111111111111111111111111111111111&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래와 같이 source 명령어를 이용해 환경 변수를 등록합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1669279389368&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kevin@hwang ~ % source ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Bash shell에서 시스템 환경 변수 등록 방법&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bash shell의 경우 다음과 같은 절차를 따릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래와 같이 vi 편집기로 .bashrc 파일을 생성 또는 오픈합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1669279423418&quot; class=&quot;angelscript&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kevin@hwang ~ % vi ~/.bashrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.bashrc 파일에 아래와 같이 등록하고자 하는 환경 변수명과 값을 입력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1669279423418&quot; class=&quot;routeros&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export JWT_SECRET_KEY=kevin111111111111111111111111111111111111111111&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래와 같이 source 명령어를 이용해 환경 변수를 등록합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1669279423419&quot; class=&quot;mel&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kevin@hwang ~ % source ~/.bashrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;터미널 사용이 익숙하지 않을 경우 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 터미널을 사용해서 Shell을 편집하는 것이 익숙하지 않다면 아래와 같은 방법으로 시스템 환경 변수를 등록할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;⌘⇧. (Command + Shfit + . )을 눌러 숨김 파일을 표시합니다.&lt;/li&gt;
&lt;li&gt;아래와 같이 사용자 계정의 디렉토리에서 .zprofile을 찾아 더블 클릭한 후 파일을 엽니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2022-11-25_2.22.36.png&quot; data-origin-width=&quot;2538&quot; data-origin-height=&quot;1300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sK7J4/btrR7I1enIe/J9Hwo4nsolsgxNHRrMkLW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sK7J4/btrR7I1enIe/J9Hwo4nsolsgxNHRrMkLW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sK7J4/btrR7I1enIe/J9Hwo4nsolsgxNHRrMkLW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsK7J4%2FbtrR7I1enIe%2FJ9Hwo4nsolsgxNHRrMkLW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2538&quot; height=&quot;1300&quot; data-filename=&quot;2022-11-25_2.22.36.png&quot; data-origin-width=&quot;2538&quot; data-origin-height=&quot;1300&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래와 같이 등록하고자 하는 환경 변수명과 값을 입력한 후, 창을 닫습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2022-11-25_2.22.53.png&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BOadU/btrR5xMFTCE/Ks2vyf6mL3bGiIsM1zwuhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BOadU/btrR5xMFTCE/Ks2vyf6mL3bGiIsM1zwuhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BOadU/btrR5xMFTCE/Ks2vyf6mL3bGiIsM1zwuhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBOadU%2FbtrR5xMFTCE%2FKs2vyf6mL3bGiIsM1zwuhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1376&quot; height=&quot;846&quot; data-filename=&quot;2022-11-25_2.22.53.png&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;846&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;열려진 터미널과 IntelliJ IDE가 있다면 창을 닫고 다시 시작합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래와 같이 터미널과 IntelliJ IDE를 다시 오픈해서 환경 변수가 잘 등록 되었는지 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2022-11-25_2.24.59.png&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JNYy3/btrR6esGxhb/2yDghQ2nQM1k2onZUdGr11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JNYy3/btrR6esGxhb/2yDghQ2nQM1k2onZUdGr11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JNYy3/btrR6esGxhb/2yDghQ2nQM1k2onZUdGr11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJNYy3%2FbtrR6esGxhb%2F2yDghQ2nQM1k2onZUdGr11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1148&quot; height=&quot;774&quot; data-filename=&quot;2022-11-25_2.24.59.png&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;774&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2022-11-25_2.30.18.png&quot; data-origin-width=&quot;2524&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3Ffdl/btrR7ihkzeD/jeit5ZLkzjyiH1HOEX6Xj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3Ffdl/btrR7ihkzeD/jeit5ZLkzjyiH1HOEX6Xj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3Ffdl/btrR7ihkzeD/jeit5ZLkzjyiH1HOEX6Xj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3Ffdl%2FbtrR7ihkzeD%2Fjeit5ZLkzjyiH1HOEX6Xj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2524&quot; height=&quot;1490&quot; data-filename=&quot;2022-11-25_2.30.18.png&quot; data-origin-width=&quot;2524&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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;span style=&quot;color: #ee2323;&quot;&gt;★ 시스템 환경 변수를 Spring Boot 애플리케이션에서 정상적으로 로드하기 위해서는 IntelliJ 같은 IDE를 반드시 Restart 하도록합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring MVC</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/47</guid>
      <comments>https://itvillage.tistory.com/entry/Mac-OS%EC%97%90%EC%84%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EB%93%B1%EB%A1%9D%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry47comment</comments>
      <pubDate>Thu, 24 Nov 2022 17:47:05 +0900</pubDate>
    </item>
    <item>
      <title>HttpMessageConverter에 대한 이야기</title>
      <link>https://itvillage.tistory.com/entry/HttpMessageConverter%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%EC%95%BC%EA%B8%B0</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;HttpMessageConverter란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 브라우저 같은 클라이언트에서 보여지는 HTML 컨텐츠가 렌더링(Rendering)되는 방식은 크게 두 가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나는 웹 애플리케이션 서버에서 동적으로 변하는 데이터를 포함하는 HTML을 만들어서 HTML 자체를 한번에 클라이언트 쪽으로 내려주는 방식입니다. &lt;br /&gt;이 방식은 JSP나 타임리프(Thymeleaf) 같은 기술을 사용해서 HTML을 템플릿화 한 다음에 Controller의 핸들러 메서드에서 리턴하는 모델 데이터를 템플릿에 동적으로 채워 넣은 후, 최종적으로 완성된 HTML을 클라이언트 쪽으로 내려주는 방식입니다. 이 방식을 바로 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;서버 사이드 렌더링(Server Side Rendering)&lt;/b&gt;&lt;/span&gt;이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 하나는 클라이언트 쪽을 담당하는 Apache나 NginX 같은 웹 서버에 HTML을 올려 놓은 후, 자바스크립트의 Ajax 기술을 이용해서 웹 애플리케이션 서버에 데이터를 요청 합니다.&lt;br /&gt;그리고 응답으로 전달받은 데이터를 웹 서버에 올려둔 HTML에 동적으로 채워 넣은 후, 최종적으로 완성된 HTML을 클라이언트 쪽으로 내려주는 방식입니다. 이 방식을 바로 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;클라이언트 사이드 렌더링(Client Side Rendering)&lt;/span&gt;&lt;/b&gt;이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 두가지 방식 중에서 HttpMessageConverter는 두 번째 방식인 클라이언트 사이드 렌더링과 관련이 있습니다.&lt;br /&gt;즉, 클라이언트 사이드 렌더링을 위해 필요한 데이터를 특정 형식으로 변환해 주는 것이 바로 &lt;code&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;HttpMessageConverter&lt;/span&gt;&lt;/b&gt;&lt;/code&gt;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HttpMessageConverter의 사용 목적&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;HttpMessageConverter&lt;/code&gt;는 크게 두 가지 목적으로 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나는 웹 서버에서 전송된 Reqeust Body를 DTO 같은 클래스의 객체로 변환해서 웹 애플리케이션 서버 쪽에서 사용할 수 있도록 해주는 것이고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 하나는 웹 서버 쪽으로 전달할 응답 데이터를 Response Body로 변환해주는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HttpMessageConverter의 동작 과정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-026.png&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvNOOb/btrBq791uRn/kUOdghxvhThP4kYQYnaPz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvNOOb/btrBq791uRn/kUOdghxvhThP4kYQYnaPz1/img.png&quot; data-alt=&quot;[그림 1-1] Spring MVC 요청/응답 동작 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvNOOb/btrBq791uRn/kUOdghxvhThP4kYQYnaPz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvNOOb%2FbtrBq791uRn%2FkUOdghxvhThP4kYQYnaPz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;962&quot; height=&quot;542&quot; data-filename=&quot;K-026.png&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-1] Spring MVC 요청/응답 동작 과정&lt;/figcaption&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;[그림 1-1]은 클라이언트의 요청을 처리하는 Spring MVC의 기본 동작 과정입니다.&lt;br /&gt;이 Spring MVC의 동작 과정에서 HandlerAdapter의 동작 과정을 조금 더 구체적으로 설명하겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;요청 처리 시 HandlerAdapter의 동작 과정&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerMapping&lt;/span&gt;을 통해 적절한 &lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerAdapter&lt;/span&gt;를 찾으면 &lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerAdapter&lt;/span&gt;는 &lt;span style=&quot;color: #ef5369;&quot;&gt;Controller&lt;/span&gt;로 넘겨줄 파라미터를 결정하기 위해 이 작업을 &lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerMethodArgumentResolver&lt;/span&gt;에게 위임합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt; HandlerMethodArgumentResolver&lt;/span&gt;는 &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpMessageConverter&lt;/span&gt;에게 HTTP Request Body를 특정 타입의 객체로 변환해주기를 요청합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;HttpMessageConverter&lt;/span&gt;는 HTTP Request Body를 특정 타입의 객체로 변환합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerMethodArgumentResolver&lt;/span&gt;는 변환된 데이터를 전달 받아서 이 데이터를 다시 &lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerAdapter&lt;/span&gt;에게 전달합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerAdapter&lt;/span&gt;&lt;/span&gt;는 &lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerMethodArgumentResolver&lt;/span&gt;로부터 전달 받은 데이터를 핸들러 메서드의 파라미터로 포함 시킨 후, 핸들러 메서드를 호출합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;응답 처리 시 HandlerAdapter의 동작 과정&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;핸들러 메서드가 응답으로 전달할 데이터를 리턴합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerMethodReturnValueHandler&lt;/span&gt;는 핸들러 메서드로부터 전달 받은 응답 데이터를 &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpMessageConverter&lt;/span&gt;에게 전달합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;HttpMessageConverter&lt;/span&gt;는 &lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerMethodReturnValueHandler&lt;/span&gt;로부터 전달 받은 데이터를 HTTP Response Body에 포함되는 형식의 데이터로 변환합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerMethodReturnValueHandler&lt;/span&gt;는 &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpMessageConverter&lt;/span&gt;로부터 전달 받은 데이터를 &lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerAdapter&lt;/span&gt;에게 전달합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;REST API에서의 HttpMessageConverter&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring MVC에서는 기본적으로 아래의 HttpMessageConverter 구현체가 활성화 되어 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ByteArrayHttpMessageConverter&lt;/li&gt;
&lt;li&gt;StringHttpMessageConverter&lt;/li&gt;
&lt;li&gt;ResourceHttpMessageConverter&lt;/li&gt;
&lt;li&gt;SourceHttpMessageConverter&lt;/li&gt;
&lt;li&gt;FormHttpMessageConverter&lt;/li&gt;
&lt;li&gt;Jaxb2RootElementHttpMessageConverter&lt;/li&gt;
&lt;li&gt;MappingJackson2HttpMessageConverter&lt;/li&gt;
&lt;li&gt;MappingJacksonHttpMessageConverter&lt;/li&gt;
&lt;li&gt;AtomFeedHttpMessageConverter&lt;/li&gt;
&lt;li&gt;RssChannelHttpMessageConverter&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span&gt;만약 우리가 HTTP Request 또는 Response를 위한 통신 프로토콜로 JSON을 사용하는 REST API Controller를 구현했다고 가정해 봅시다.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;특정 Controller의 핸들러 메서드 파라미터에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestBody&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;애너테이션이 추가되거나 파라미터의 타입이 &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpEntity&lt;/span&gt;라면 위에서 나열한 HttpMessageConverter가 순서대로 HTTP &lt;/span&gt;Request Body를 체크해서 핸들러 파라미터에 지정한 클래스로 변환이 되는지, HTTP Message의 Content Type을 지원하는지 여부를 확인하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두가지 조건을 모두 만족한다면 아마도 &lt;span style=&quot;color: #ef5369;&quot;&gt;MappingJackson2HttpMessageConverter&lt;/span&gt;가 Request Body를 해당 클래스의 객체로 변환할 것이고, 조건에 부합하는 &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpMessageConverter&lt;/span&gt;가 존재하지 않는다면 예외가 발생할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 응답 데이터를 JSON 형식으로 전달하는 상황을 생각해 봅시다.&lt;br /&gt;핸들러 메서드에 &lt;span style=&quot;color: #ef5369;&quot;&gt;@ResponseBody&lt;/span&gt; 애너테이션을 추가하거나 핸들러 메서드의 리턴 타입이 &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpEntity&lt;/span&gt;&lt;span&gt;라면 역시 &lt;span&gt;위에서 나열한 &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpMessageConverter&lt;/span&gt;가 순서대로 핸들러 메서드의 리턴 값을 체크해서 해당 클래스 타입을 지원하는지, Accept Header에 명시된 MediaType으로 변환이 되는지 &lt;/span&gt;여부를 확인하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HttpMessageConverter가 동작하는 시점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HttpMessageConverter가 동작하는 시점은 JSON을 DTO로 deserialization 할 때와 DTO를 JSON으로 serialization 할 때가 다릅니다. 아래는 &lt;code&gt;HttpMessageConverter&lt;/code&gt;의 대략적인 동작 흐름입니다. 참고하세요.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;JSON -&amp;gt; DTO로 deserialization&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;HandlerMethodArgumentResolverComposite.resolveArgument()&lt;/code&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;code&gt;RequestResponseBodyMethodProcessor.resolveArgument() &lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;code&gt;RequestResponseBodyMethodProcessor.readWithMessageConverters()&lt;/code&gt; &amp;gt; &lt;code&gt;converter.read()&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;code&gt;objectReader.readValue()&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DTO -&amp;gt; JSON serialization&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;HandlerMethodReturnValueHandlerComposite.handleReturnValue()&lt;/code&gt;&amp;nbsp; &lt;br /&gt;&amp;rarr;&amp;nbsp;&lt;code&gt;HttpEntityMethodProcessor.handleReturnValue()&lt;/code&gt;&amp;nbsp; &lt;br /&gt;&amp;rarr;&amp;nbsp;&lt;code&gt;AbstractMessageConverterMethodProcessor.writeWithMessageConverters()&lt;/code&gt;&amp;nbsp;&amp;gt;&amp;nbsp;&lt;code&gt;genericConverter.write()&lt;/code&gt;&amp;nbsp; &lt;br /&gt;&amp;rarr;&amp;nbsp;&lt;code&gt;AbstractGenericHttpMessageConverter.write()&lt;/code&gt;&amp;nbsp; &lt;br /&gt;&amp;rarr;&amp;nbsp;&lt;code&gt;AbstractJackson2HttpMessageConverter.writeInternal()&lt;/code&gt;&amp;nbsp;&amp;gt;&amp;nbsp;&lt;code&gt;objectWriter.writeValue(generator,&amp;nbsp;value)&lt;br /&gt;&lt;/code&gt;&amp;rarr; write() and flush() 과정을 거친다.&lt;br /&gt;&amp;rarr; ...&lt;br /&gt;&amp;rarr; ...&lt;br /&gt;&amp;rarr; &lt;code&gt;CoyoteAdapter.service()&lt;/code&gt; &amp;gt; &lt;code&gt;response.finishResponse()&lt;/code&gt; // response가 close되면서 클라이언트 쪽에 출력&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HttpMessageConverter 참고 자료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-framework/docs/current/javadoc-api/&lt;/a&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1651831335637&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Spring Framework 5.3.19 API&quot; data-og-description=&quot;&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/&quot; data-og-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring Framework 5.3.19 API&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.spring.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring MVC</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/46</guid>
      <comments>https://itvillage.tistory.com/entry/HttpMessageConverter%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%EC%95%BC%EA%B8%B0#entry46comment</comments>
      <pubDate>Fri, 6 May 2022 19:02:29 +0900</pubDate>
    </item>
    <item>
      <title>ResponseEntity 알아보기</title>
      <link>https://itvillage.tistory.com/entry/ResponseEntity-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;ResponseEntity란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt; ResponseEntity&lt;/span&gt;는 &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpEntity.html&quot;&gt;HttpEntity&lt;/a&gt;의 확장 클래스로써&amp;nbsp;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpStatus.html&quot;&gt;HttpStatus&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt; 상태 코드를 추가한 &lt;/span&gt;&lt;/span&gt;전체 HTTP 응답(상태 코드, 헤더 및 본문)을 표현하는 클래스입니다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ResponseEntity를 어디에 사용할 수 있나요?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ResponseEntity 클래스는 주로 @Controller 또는 @RestController 애너테이션이 붙은 Controller 클래스의 핸들러 메서드(Handler Method)에서 요청 처리에 대한 응답을 구성하는데 사용됩니다. Used in RestTemplate as well as in @Controller methods.&lt;/li&gt;
&lt;li&gt;그리고 RestTemplate으로 외부의 API 통신에 대한 응답을 전달 받아서 처리할 경우, 역시 ResponseEntity를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ResponseEntity를 어떻게 사용해야 할까요?&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;가장 일반적인 방식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 일반적인 방식은 [코드 1-1]과 같이 new로 ResponseEntity 객체를 생성하는 방식입니다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RestController&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RequestMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/v1/coffees&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;ResponseEntityExample01&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@PostMapping&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;ResponseEntity&amp;nbsp;postCoffee(Coffee&amp;nbsp;coffee)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;coffee&amp;nbsp;정보&amp;nbsp;저장&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;ResponseEntity&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;(coffee,&amp;nbsp;HttpStatus.CREATED);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;[코드 1-1]&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[코드 1-1]은 ResponseEntity 객체를 생성하면서 응답의 body 데이터와 HttpStatus의 상태를 전달하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;13&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;14&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;15&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RestController&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RequestMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/v1/coffees&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;ResponseEntityExample01&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@GetMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/{coffee-id}&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;ResponseEntity&amp;nbsp;getCoffee(@PathVariable(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;coffee-id&quot;&lt;/span&gt;)&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;long&lt;/span&gt;&amp;nbsp;coffeeId)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;if&lt;/span&gt; (coffeeId &amp;lt;&amp;nbsp;&lt;span style=&quot;color: #0099cc;&quot;&gt;0&lt;/span&gt;)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;ResponseEntity&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;(&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;coffeeId&amp;nbsp;should&amp;nbsp;be&amp;nbsp;greater&amp;nbsp;than&amp;nbsp;0&quot;&lt;/span&gt;,&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HttpStatus.BAD_REQUEST);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;ResponseEntity&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;(&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;Coffee(),&amp;nbsp;HttpStatus.OK);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;[코드 1-2]&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 1-2는 coffeeId에 따라서 HttpStatus 상태를 동적으로 지정하는 예입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border: 1px dashed #c1c1c1;&quot;&gt;&lt;button style=&quot;background-color: orange; color: white; border: none; padding: 5px;&quot;&gt;Note&lt;/button&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 1-2는 요청 데이터에 대한 유효성 검사가 API 계층에 포함에 직접 포함이 되어 있는데, 이는 좋은 개발 방식은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Handler Method는 단순히 클라이언트의 요청을 전달 받고, 처리된 응답을 다시 클라이언트에게 전달하는 역할만 하도록 로직을 단순화 하는 것이 좋으며, 유효성 검사나 비즈니스 처리 등의 로직은 별도의 메서드 또는 클래스로 분리를 하는 것이 좋습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;13&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;14&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;15&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;16&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;17&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RestController&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RequestMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/v1/coffees&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;ResponseEntityExample01&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@GetMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/{coffee-id}&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;ResponseEntity&amp;nbsp;getCoffee(@PathVariable(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;coffee-id&quot;&lt;/span&gt;)&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;long&lt;/span&gt;&amp;nbsp;coffeeId)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;if&lt;/span&gt; (coffeeId&amp;nbsp;&amp;lt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0099cc;&quot;&gt;0&lt;/span&gt;)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;ResponseEntity&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;(&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;coffeeId&amp;nbsp;should&amp;nbsp;be&amp;nbsp;greater&amp;nbsp;than&amp;nbsp;0&quot;&lt;/span&gt;,&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HttpStatus.BAD_REQUEST);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HttpHeaders&amp;nbsp;headers&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;HttpHeaders();&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;headers.&lt;span style=&quot;color: #066de2;&quot;&gt;add&lt;/span&gt;(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;Custom-Header&quot;&lt;/span&gt;,&amp;nbsp;&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;bar&quot;&lt;/span&gt;);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;ResponseEntity&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;(&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;Coffee(),&amp;nbsp;headers,&amp;nbsp;HttpStatus.OK);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;[코드 1-3]&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 &lt;span style=&quot;color: #ef5369;&quot;&gt;ResponseEntity&lt;/span&gt;에 Custom header를 포함하고 싶다면 코드 1-3과 같이 HttpHeaders에 원하는 header를 추가하고 &lt;span style=&quot;color: #ef5369;&quot;&gt;ResponseEntity&lt;/span&gt;의 생성자로 headers 객체를 전달하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;BodyBuilder를 이용한 메서드 체인 방식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;ResponseEntity&lt;/span&gt; 클래스의 생성자로 body, header, HttpStatus 등을 추가하는 방식과 달리 BodyBuilder 클래스를 이용하면 각각의 항목들을&amp;nbsp; 메서드 체인 방식으로 전달할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RestController&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RequestMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/v1/coffees&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;ResponseEntityExample02&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@PostMapping&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;ResponseEntity&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;lt;&lt;/span&gt;Coffee&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;&amp;nbsp;postCoffee(Coffee&amp;nbsp;coffee)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;coffee&amp;nbsp;정보&amp;nbsp;저장&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;long&lt;/span&gt;&amp;nbsp;savedCoffeeId&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;1L;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt;&amp;nbsp;ResponseEntity.created(URI.create(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/members/&quot;&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;+&lt;/span&gt;&amp;nbsp;savedCoffeeId)).body(coffee);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;[코드 1-4]&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 1-4는 코드 1-1을 ResponseEntity.BodyBuilder를 이용해서 메서드 체인 방식으로 ResponseEntity 객체를 리턴해주고 있습니다.&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border: 1px dashed #c1c1c1;&quot;&gt;&lt;button style=&quot;background-color: mediumseagreen; color: white; border: none; padding: 5px;&quot;&gt;Info&lt;/button&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;created() 메서드의 경우 URI를 지정할 수 있는데, 이는 새롭게 생성된 리소스에 대한 접근 URI를 Location 헤더 값으로 포함시킴으로써 클라이언트 쪽에서 이 정보를 이용해 해당 리소스에 접근할 수 있도록 해줍니다.&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;13&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;14&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;15&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RestController&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RequestMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/v1/coffees&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;ResponseEntityExample02&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@GetMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/{coffee-id}&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;ResponseEntity&amp;nbsp;getCoffee(@PathVariable(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;coffee-id&quot;&lt;/span&gt;)&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;long&lt;/span&gt;&amp;nbsp;coffeeId)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;if&lt;/span&gt; (coffeeId&amp;nbsp;&amp;lt;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #0099cc;&quot;&gt;0&lt;/span&gt;)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt;&amp;nbsp;ResponseEntity.badRequest().body(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;coffeeId&amp;nbsp;should&amp;nbsp;be&amp;nbsp;greater&amp;nbsp;than&amp;nbsp;0&quot;&lt;/span&gt;);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HttpHeaders&amp;nbsp;headers&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;HttpHeaders();&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;headers.&lt;span style=&quot;color: #066de2;&quot;&gt;add&lt;/span&gt;(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;Custom-Header&quot;&lt;/span&gt;,&amp;nbsp;&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;bar&quot;&lt;/span&gt;);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt;&amp;nbsp;ResponseEntity.ok().headers(headers).body(&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;Coffee());&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;[코드 1-5]&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 1-5는 코드 1-3을 BodyBuilder를 이용해서 메서드 체인 방식으로 변경한 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ResponseEntity 관련 자료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Framework API docs: &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java Backend 개발자 되기/Spring MVC</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/44</guid>
      <comments>https://itvillage.tistory.com/entry/ResponseEntity-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0#entry44comment</comments>
      <pubDate>Thu, 21 Apr 2022 14:01:43 +0900</pubDate>
    </item>
    <item>
      <title>Controller 핸들러 메서드의 Argument 알아보기</title>
      <link>https://itvillage.tistory.com/entry/Controller-%ED%95%B8%EB%93%A4%EB%9F%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EC%9D%98-Argument-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Controller의 핸들러 메서드는 다양한 유형의 Argument(인수)를 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중에서 REST API 애플리케이션에서 자주 사용되는 유형의 Argument를 간단히 살펴보도록 하겠습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4653%; text-align: center;&quot;&gt;Method Argument&lt;/td&gt;
&lt;td style=&quot;width: 74.5347%; text-align: center;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4653%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestParam&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5347%;&quot;&gt;쿼리 파라미터, form-data 등의 Servlet request Parameter를 바인딩 해야 할 경우 사용합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4653%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestHeader&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5347%;&quot;&gt;request header를 바인딩해서 header의 key/value에 접근할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4653%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestBody&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5347%;&quot;&gt;request body를 읽어서 지정한 Java 객체로 deserialization 해줍니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4653%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestPart&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5347%;&quot;&gt;'multipart/form-data' 형식의 request 데이터를 part 별로 바인딩할 수 있도록 해줍니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4653%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@PathVariable&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5347%;&quot;&gt;@RequestMapping 에 패턴 형식으로 정의된 URL의 변수에 바인딩할 수 있도록 해줍니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4653%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@MatrixVariable&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5347%;&quot;&gt;URL 경로 세그먼트 부분에 key/value 쌍으로 된 데이터에 바인딩할 수 있도록 해줍니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4653%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;HttpEntity&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5347%;&quot;&gt;request header와 body에 접근할 수 있는 컨테이너 객체를 사용할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4653%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;javax.servlet.ServletRequest,&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;javax.servlet.ServletResponse&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5347%;&quot;&gt;로우 레벨의 ServeletRequest와 ServletResponse의 정보가 필요할 때 사용할 수 있습니다.&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;size18&quot;&gt;&lt;b&gt;@RequestParam&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;@RequestParam은 클라이언트 쪽에서 쿼리 파라미터,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;form data,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;x-www-form-urlencoded 등의 형식으로 전달되는 요청 데이터를 바인딩해서 사용할 수 있도록 해줍니다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;13&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;14&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;15&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;16&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@Controller&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RequestMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/coffees&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt; CoffeeController {&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;...&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@GetMapping&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt; Coffee getCoffee(@RequestParam(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;coffeeId&quot;&lt;/span&gt;)&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;int&lt;/span&gt; coffeeId) {&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Coffee coffee = coffeeService.getCoffee(coffeeId);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt; coffee;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;...&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;[코드 1-1]&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0 2px 4px 0;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #e5e5e5; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&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;@RequestHeader&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestHeader&lt;/span&gt;는 HTTP request header의 key/value 쌍의 데이터에 접근할 수 있도록 해줍니다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5; width: 22px;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0px; text-align: left; width: 532px;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@GetMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/coffee&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;void&lt;/span&gt;&amp;nbsp;getCoffee(&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@RequestHeader(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;)&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;String&lt;/span&gt;&amp;nbsp;contentType,&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@RequestHeader(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;Content-Length&quot;&lt;/span&gt;)&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;long&lt;/span&gt;&amp;nbsp;contentLength)&amp;nbsp;{&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//...&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #010101;&quot;&gt;[코드 1-2]&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0px 2px 4px 0px; width: 15px;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #e5e5e5; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&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;@RequestBody&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestBody&lt;/span&gt;는 HTTP request body를 읽어서 지정한 Java 객체로 변환(deserialization) 해줍니다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@PostMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/coffees&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;void&lt;/span&gt;&amp;nbsp;handle(@RequestBody&amp;nbsp;Coffee&amp;nbsp;coffee)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;...&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;coffeeService.save(coffee)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #010101;&quot;&gt;[코드 1-3]&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0 2px 4px 0;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #e5e5e5; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border: 1px dashed #c1c1c1;&quot;&gt;&lt;button style=&quot;background-color: mediumseagreen; color: white; border: none; padding: 5px;&quot;&gt;Info&lt;/button&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestBody&lt;/span&gt;는 특히 리소스를 등록하는 &lt;span style=&quot;color: #ef5369;&quot;&gt;@PostMapping&lt;/span&gt;에서 주로 사용됩니다.&lt;/p&gt;
&lt;/div&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;@RequestPart&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;'multipart/form-data' 형식의 request 데이터를 part 별로 바인딩할 수 있도록 해줍니다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5; width: 31px;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;13&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;14&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0px; text-align: left; width: 556.359px;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@Controller&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;CoffeeController&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@PostMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/coffees&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;String&lt;/span&gt; postCoffee(@RequestPart(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;coffee&quot;&lt;/span&gt;)&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;Coffee coffee&lt;/span&gt;,&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@RequestPart(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;file&quot;&lt;/span&gt;)&amp;nbsp;MultipartFile&amp;nbsp;photoFile)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;if&lt;/span&gt;&amp;nbsp;(&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;!&lt;/span&gt;photoFile.isEmpty())&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;byte&lt;/span&gt;[]&amp;nbsp;bytes&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;file.getBytes();&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;TODO&amp;nbsp;파일&amp;nbsp;저장&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;success&quot;&lt;/span&gt;;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;failed&quot;&lt;/span&gt;;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #010101;&quot;&gt;[코드 1-4]&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0px 2px 4px 0px; width: 15px;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #e5e5e5; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;예를 들어, 클라이언트 쪽에서 커피 사진이 포함된 multpart/form-data 타입의 커피 정보를 전송한다면,&amp;nbsp; [코드 1-4]와 같이&amp;nbsp; form data를 part별로 나누어서 전달 받을 수 있습니다.&lt;/p&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;@PathVariable&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@PathVariable&lt;/span&gt;은 &lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestMapping&lt;/span&gt; 에 패턴 형식으로 정의된 URL의 변수에 바인딩할 수 있도록 해줍니다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@GetMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/members/{member-id}/coffees/{coffee-id}&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt; Pet getCoffee(@PathVariable(&quot;member-id&quot;) Long memberId, &lt;br /&gt;@PathVariable(&quot;coffee-id&quot;) Long coffeeId) {&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;...&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;[코드 1-5]&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0 2px 4px 0;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #e5e5e5; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[코드 1-5]와 같이 '{variable name}'과 같은 형태의 URL 변수가 여러개 있을 경우, @PathVariable을 핸들러 메서드에 순차적으로 추가해서 URL 변수의 값을 받을 수 있습니다.&lt;/p&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;@MatrixVariable&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@MatrixVariable&lt;/span&gt;은 URL 경로 세그먼트 부분에 key/value 쌍으로 된 데이터에 바인딩할 수 있도록 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@MatrixVariable&lt;/span&gt;의 사용은 예제 코드를 보는게 가장 이해가 빠릅니다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;클라이언트&amp;nbsp;요청&amp;nbsp;URL&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #999999;&quot;&gt;// GET /coffees/42;q=11;r=22&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@GetMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/coffees/{coffeeId}&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;void&lt;/span&gt;&amp;nbsp;getCoffee(@PathVariable(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;coffee-id&quot;&lt;/span&gt;)&amp;nbsp;coffeeId,&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@MatrixVariable&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;int&lt;/span&gt;&amp;nbsp;q,&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@MatrixVariable&amp;nbsp;&lt;span style=&quot;color: #066de2;&quot;&gt;int&lt;/span&gt;&amp;nbsp;r)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;coffeeId&amp;nbsp;==&amp;nbsp;42&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;q&amp;nbsp;==&amp;nbsp;11&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;r&amp;nbsp;==&amp;nbsp;22&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;[코드 1-6]&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0 2px 4px 0;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #e5e5e5; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;만약에 클라이언트 쪽 요청 URL이 '/coffees/42;q=11;r=22'와 같다면 세미콜론(;) 뒤에 오는 q, r을 변수로 보고 각 변수의 값을 [코드 1-6]과 같이 핸들러 메서드에서 전달 받을 수 있습니다.&lt;/p&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;HttpEntity&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;클라이언트 요청 body와 header 정보를 &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpEntity&lt;/span&gt; 객체를 통해 한번에 전달 받을 수 있습니다. 물론 &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpServletRequest&lt;/span&gt; 객체를 통해서도 요청 body와 header에 접근할 수 있지만 &lt;span style=&quot;color: #ef5369;&quot;&gt;HttpEntity&lt;/span&gt; 객체를 통해 조금 더 간단하게 두 정보에 접근할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;10&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;11&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;12&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RestController&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@RequestMapping(&lt;span style=&quot;color: #63a35c;&quot;&gt;&quot;/v1/coffees&quot;&lt;/span&gt;)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;HttpEntityExample&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@PostMapping&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;Coffee&amp;nbsp;postCoffee(HttpEntity&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;lt;&lt;/span&gt;Coffee&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;&amp;gt;&lt;/span&gt;&amp;nbsp;entity)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Coffee&amp;nbsp;coffee&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;entity.getBody();&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HttpHeaders&amp;nbsp;headers&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;entity.getHeaders();&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;coffee&amp;nbsp;정보&amp;nbsp;저장&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;return&lt;/span&gt;&amp;nbsp;coffee;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;[코드 1-7]&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0 2px 4px 0;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #e5e5e5; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>Java Backend 개발자 되기/Spring MVC</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/41</guid>
      <comments>https://itvillage.tistory.com/entry/Controller-%ED%95%B8%EB%93%A4%EB%9F%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EC%9D%98-Argument-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0#entry41comment</comments>
      <pubDate>Tue, 19 Apr 2022 14:36:42 +0900</pubDate>
    </item>
    <item>
      <title>@RequestMapping 애너테이션</title>
      <link>https://itvillage.tistory.com/entry/RequestMapping-%EC%95%A0%EB%84%88%ED%85%8C%EC%9D%B4%EC%85%98</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;​@RequestMapping 애너테이션은 어디에 사용할 수 있나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestMapping&lt;/span&gt; 애너테이션은 클라이언트의 요청과 Controller의 핸들러 메서드를 매핑하기 위해서 사용하는 애너테이션입니다. 즉, @RequestMapping 애너테이션에 정의된 URI에 매치되는 요청을 처리할 수 있도록 해줍니다.&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 5px; border: 1px dashed #c1c1c1;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestMapping&lt;/span&gt; 애너테이션은 클래스 레벨에 사용할 수 있고, 메서드 레벨에 사용할 수 있습니다.&lt;br /&gt;일반적으로 공통 URI는 클래스 레벨에 정의하고, 핸들러 메서드별로 달라지는 URI는 각각의 핸들러 메서드에 정의합니다.&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@RequestMapping 애너테이션 단축 표현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@RequestMapping&lt;/span&gt; 애너테이션은 HTTP Method 에 해당하는 단축 표현들을 주로 사용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@GetMapping&lt;span style=&quot;color: #000000;&quot;&gt;: HTTP Get Method에 해당하는 단축 표현으로 서버의 리소스를 조회할 때 사용&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@PostMapping&lt;span style=&quot;color: #000000;&quot;&gt;: HTTP Post Method에 해당하는 단축 표현으로 서버에 리소스를 등록(저장)할 때 사용&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@PutMapping&lt;span style=&quot;color: #000000;&quot;&gt;: HTTP Put Method에 해당하는 단축 표현으로 서버의 리소스를 수정할 때 사용. 리소스의 모든 정보를 수정할 때 사용한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@PatchMapping&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;span style=&quot;color: #000000;&quot;&gt;HTTP Put Method에 해당하는 단축 표현으로 서버의 리소스를 수정할 때 사용. 리소스의 일부 정보만 수정할 때 사용한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@DeleteMapping&lt;span style=&quot;color: #000000;&quot;&gt;: HTTP Delete Method에 해당하는 단축 표현으로 서버의 리소스를 삭제할 때 사용.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 5px; border: 1px dashed #c1c1c1;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;일반적으로 클래스 레벨에는 @RequestMapping 애너테이션을 사용하고, 메서드 레벨에서는 단축 표현을 사용하는 것을 권장하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@RequestMapping 애너테이션에 사용되는 Attribute&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@RequestMapping 애너테이션에 사용되는 Attribute는 생각보다 많이 사용되지 않습니다. 하지만 필요할 경우 그때 그때 설정해서 사용할 수 있으므로 아래 링크를 확인하시고 어떤 Attribute가 있는 살펴보기 바랍니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dzone.com/articles/using-the-spring-requestmapping-annotation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dzone.com/articles/using-the-spring-requestmapping-annotation&lt;/a&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring MVC</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/40</guid>
      <comments>https://itvillage.tistory.com/entry/RequestMapping-%EC%95%A0%EB%84%88%ED%85%8C%EC%9D%B4%EC%85%98#entry40comment</comments>
      <pubDate>Tue, 19 Apr 2022 09:34:32 +0900</pubDate>
    </item>
    <item>
      <title>Postman 기본 사용법</title>
      <link>https://itvillage.tistory.com/entry/Postman-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 시간에는 REST API 요청을 테스트하기 위한 API 테스트 툴인 Postman의 기본 사용법을 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 요청을 보내는 최소한의 사용법만 살펴볼 예정이므로 Postman의 세부 기능이 궁금하신 분들은 &lt;a href=&quot;https://learning.postman.com/docs/getting-started/introduction/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 사이트의 매뉴얼&lt;/a&gt;을 참고해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Postman은 웹에서도 사용 가능하지만 여기서는 편의를 위해 실행 파일을 다운로드한 후, 설치하는 것을 기준으로 설명합니다.&lt;br /&gt;이 글의 Postman 실행 파일은 Windows 기준이지만 다른 OS의 경우도 사용법은 크게 다르지 않을 것이라 생각합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;실행 파일 다운로드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.postman.com/downloads/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Postman 사이트&lt;/a&gt;에서 Postman 실행 파일을 다운로드 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;실행 파일 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드 받은 실행 파일을 실행한 후, 안내에 따라서 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;설치된 파일 실행&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-017.png&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pl4IE/btrzKQvadlY/xEAQtKaP7KaDnhjkq0Qab1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pl4IE/btrzKQvadlY/xEAQtKaP7KaDnhjkq0Qab1/img.png&quot; data-alt=&quot;[그림 1-1] Postman 파일 실행 시 첫 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pl4IE/btrzKQvadlY/xEAQtKaP7KaDnhjkq0Qab1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpl4IE%2FbtrzKQvadlY%2FxEAQtKaP7KaDnhjkq0Qab1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;937&quot; height=&quot;612&quot; data-filename=&quot;K-017.png&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;612&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-1] Postman 파일 실행 시 첫 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치된 Postman 파일을 실행하면 [그림 1-1]과 같은 화면이 오픈됩니다. 여기서 처음 사용을 하는 분은 [Create Account] 버튼을 눌러서 계정을 생성한 후, [Sign In] 버튼을 눌러 로그인을 진행하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ Postman은 특이하게 컴퓨터에 설치된 파일을 실행해도 계정 생성이나 로그인은 웹 브라우저로 자동 이동해서 진행이 되니 이점 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-018.png&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3THbg/btrzEBtg8PQ/QHb4FvRW7uSjQ3pIGc5Fr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3THbg/btrzEBtg8PQ/QHb4FvRW7uSjQ3pIGc5Fr0/img.png&quot; data-alt=&quot;[그림 1-2] 로그인 후, 애플리케이션 리다이렉션 메시지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3THbg/btrzEBtg8PQ/QHb4FvRW7uSjQ3pIGc5Fr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3THbg%2FbtrzEBtg8PQ%2FQHb4FvRW7uSjQ3pIGc5Fr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;626&quot; height=&quot;273&quot; data-filename=&quot;K-018.png&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;273&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-2] 로그인 후, 애플리케이션 리다이렉션 메시지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 후, [그림 1-2]와 같이 데스크탑 애플리케이션으로 리다이섹션 할지 묻는 창이 열립니다. [Postman 열기] 버튼을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;워크스페이스(Workspace) 생성&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-020.png&quot; data-origin-width=&quot;963&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1x2ek/btrzIprySQ4/hZbNilDCMNLvT0jqsnTA6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1x2ek/btrzIprySQ4/hZbNilDCMNLvT0jqsnTA6K/img.png&quot; data-alt=&quot;[그림 1-3] 워크 스페이스 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1x2ek/btrzIprySQ4/hZbNilDCMNLvT0jqsnTA6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1x2ek%2FbtrzIprySQ4%2FhZbNilDCMNLvT0jqsnTA6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;963&quot; height=&quot;636&quot; data-filename=&quot;K-020.png&quot; data-origin-width=&quot;963&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-3] 워크 스페이스 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[그림 1-3]과 같이 [Workspaces] 탭을 선택한 후, [Create Workspace] 버튼을 클릭한 후, 워크스페이스 이름을 입력해서 워크스페이스를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;컬렉션(Collections) 생성&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-023.png&quot; data-origin-width=&quot;955&quot; data-origin-height=&quot;627&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P5pOZ/btrzIsoxcyr/EhvTr2QNKfF4OknRYpmMS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P5pOZ/btrzIsoxcyr/EhvTr2QNKfF4OknRYpmMS1/img.png&quot; data-alt=&quot;[그림 1-4] 컬렉션 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P5pOZ/btrzIsoxcyr/EhvTr2QNKfF4OknRYpmMS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP5pOZ%2FbtrzIsoxcyr%2FEhvTr2QNKfF4OknRYpmMS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;955&quot; height=&quot;627&quot; data-filename=&quot;K-023.png&quot; data-origin-width=&quot;955&quot; data-origin-height=&quot;627&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-4] 컬렉션 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[그림 1-4]와 같이 [+](Create new Collection) 버튼을 눌러서 컬렉션을 생성합니다.&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;h4 data-ke-size=&quot;size20&quot;&gt;API 요청 정보 추가&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-025.png&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qMXko/btrzKR8KwIa/ADQkAN0wGQkXyOymR4GQk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qMXko/btrzKR8KwIa/ADQkAN0wGQkXyOymR4GQk1/img.png&quot; data-alt=&quot;[그림 1-5] API 요청 정보 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qMXko/btrzKR8KwIa/ADQkAN0wGQkXyOymR4GQk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqMXko%2FbtrzKR8KwIa%2FADQkAN0wGQkXyOymR4GQk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;989&quot; height=&quot;640&quot; data-filename=&quot;K-025.png&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-5] API 요청 정보 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 컬렉션의 [...] 메뉴를 클릭한 후, [Add request] 메뉴를 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-028.png&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ce4bT9/btrzJ2QvUkg/k2JtjnKZRflWx5X2XQM4wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ce4bT9/btrzJ2QvUkg/k2JtjnKZRflWx5X2XQM4wK/img.png&quot; data-alt=&quot;[그림 1-6] 요청 정보 입력 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ce4bT9/btrzJ2QvUkg/k2JtjnKZRflWx5X2XQM4wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fce4bT9%2FbtrzJ2QvUkg%2Fk2JtjnKZRflWx5X2XQM4wK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1039&quot; height=&quot;678&quot; data-filename=&quot;K-028.png&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림 1-6] 요청 정보 입력 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[그림 1-6]과 같이 요청에 필요한 HTTP Method를 선택한 후, 요청에 필요한 URI를 입력합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[그림 1-6]에서는 HTTP Method로 POST를 선택했기 때문에 [Body] 탭에서 데이터 타입을 선택할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청에 필요한 정보를 모두 입력했다면 [Send] 버튼을 눌러서 서버로부터 응답 메시지를 전달 받을 수 있습니다.&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/RESTful API</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/38</guid>
      <comments>https://itvillage.tistory.com/entry/Postman-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95#entry38comment</comments>
      <pubDate>Mon, 18 Apr 2022 17:48:27 +0900</pubDate>
    </item>
    <item>
      <title>Spring Boot 애플리케이션의 부트스트랩(Bootstrap) 과정 알아보기</title>
      <link>https://itvillage.tistory.com/entry/Spring-Boot-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%EB%B6%80%ED%8A%B8%EC%8A%A4%ED%8A%B8%EB%9E%A9Bootstrap-%EA%B3%BC%EC%A0%95-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spring Boot에서의 부트스트랩이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 부트스트랩(Bootstrap)이란 어떠한 과정이 시작되어 알아서 진행되는 일련의 과정을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터의 부팅 과정을 생각해보면 이해가 쉬울것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 컴퓨터 전원을 켜게 되면 일반적으로 다음과 같은 작업이 내부적으로 진행이 됩니다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전원을 켜면 컴퓨터 전원이 잘 공급되는지를 확인한다.&lt;/li&gt;
&lt;li&gt;중앙 처리 장치의 상태를 점검한다.&lt;/li&gt;
&lt;li&gt;ROM에서 컴퓨터의 입출력 장치 및 램과 같은 주요 기본 장치들에 대한 정보를 읽어들여서 정상 동작 유무를 확인한다.&lt;/li&gt;
&lt;li&gt;운영체제를 램으로 읽어 들여서 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot에서의 부트스트랩도 컴퓨터 부팅 과정과 유사합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 애플리케이션이 실행이 되면, 클라이언트가 사용 가능한 애플리케이션으로 동작하기 위한 사전 작업을 거치게 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spring Boot 애플리케이션을 어떻게 부트스트랩 시킬 수 있을까요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[코드 1-1]&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@Configuration&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@EnableAutoConfiguration&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;CoffeeApplication&amp;nbsp;&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;static&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;void&lt;/span&gt;&amp;nbsp;main(&lt;span style=&quot;color: #066de2;&quot;&gt;String&lt;/span&gt;[]&amp;nbsp;args)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SpringApplication.run(CoffeeApplication.&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;,&amp;nbsp;args);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0 2px 4px 0;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #e5e5e5; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 Spring Boot 애플리케이션은 [코드 1-1]과 같이 run() 메서드를 호출해서 부트스트랩을 진행할 수 있습니다.&lt;/p&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-2]&lt;/p&gt;
&lt;div class=&quot;colorscripter-code&quot; style=&quot;color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position: relative !important; overflow: auto;&quot;&gt;
&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 6px; border-right: 2px solid #e5e5e5;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;1&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;2&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;3&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;4&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;5&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;6&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;7&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;8&lt;/div&gt;
&lt;div style=&quot;line-height: 130%;&quot;&gt;9&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 6px 0; text-align: left;&quot;&gt;
&lt;div style=&quot;margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;&quot;&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@Configuration&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;@EnableAutoConfiguration&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;&amp;nbsp;CoffeeApplication&amp;nbsp;&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;static&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;void&lt;/span&gt;&amp;nbsp;main(&lt;span style=&quot;color: #066de2;&quot;&gt;String&lt;/span&gt;[]&amp;nbsp;args)&amp;nbsp;{&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SpringApplication&amp;nbsp;application&amp;nbsp;&lt;span style=&quot;color: #0086b3;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #a71d5d;&quot;&gt;=&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #a71d5d;&quot;&gt;new&lt;/span&gt;&amp;nbsp;SpringApplication(CoffeeApplication.&lt;span style=&quot;color: #a71d5d;&quot;&gt;class&lt;/span&gt;);&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #999999;&quot;&gt;//&amp;nbsp;애플리케이션&amp;nbsp;설정&amp;nbsp;커스터마이징..&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;application.run(args)&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;
&lt;div style=&quot;padding: 0 6px; white-space: pre; line-height: 130%;&quot;&gt;}&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;vertical-align: bottom; padding: 0 2px 4px 0;&quot;&gt;&lt;a style=&quot;text-decoration: none; color: white;&quot; href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-size: 9px; word-break: normal; background-color: #e5e5e5; color: white; border-radius: 10px; padding: 1px;&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[코드 1-2]와 같이 애플리케이션 부트스트랩 전에 애플리케이션 설정을 커스터마이징 할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spring Boot에서는 부트스트랩 과정에서 어떤 일이 일어날까요?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 패스 내에서 &lt;b&gt;ApplicationContext 인스턴스를 생성&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;커맨드라인 매개 변수들을 Spring의 프로퍼티로 구성하기 위해 &lt;b&gt;CommandLinePropertySource를 등록&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모든 싱글톤 bean들을 로드한 후, 애플리케이션 컨텍스트를 갱신한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CommandLineRunner bean들이 존재한다면 트리거(Trigger)&lt;/b&gt; 시킨다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java Backend 개발자 되기/Spring Boot</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/37</guid>
      <comments>https://itvillage.tistory.com/entry/Spring-Boot-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%EB%B6%80%ED%8A%B8%EC%8A%A4%ED%8A%B8%EB%9E%A9Bootstrap-%EA%B3%BC%EC%A0%95-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0#entry37comment</comments>
      <pubDate>Tue, 12 Apr 2022 18:35:36 +0900</pubDate>
    </item>
    <item>
      <title>@SpringBootApplication 의 역할</title>
      <link>https://itvillage.tistory.com/entry/SpringBootApplication-%EC%9D%98-%EC%97%AD%ED%95%A0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[코드 1-1]&lt;/p&gt;
&lt;pre id=&quot;code_1649752361074&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
public class CoffeeApplication {

    public static void main(String[] args) {
        SpringApplication.run(CoffeeApplication.class, args);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[코드 1-1]과 같이 Spring Boot Application 클래스에 &lt;span style=&quot;color: #ef5369;&quot;&gt;@SpringBootApplication&lt;/span&gt; 애너테이션을 추가하면 기본적으로 해당 애플리케이션이 Spring Boot 기반의 애플리케이션으로 동작하도록 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 &lt;span style=&quot;color: #ef5369;&quot;&gt;@SpringBootApplication &lt;span style=&quot;color: #000000;&quot;&gt;은 내부적으로 어떤 기능을 하는 걸까요?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@SpringBootApplication&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;은 다음과 같은 세 가지 기능을 활성화 하는데 사용됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@EnableAutoConfiguration&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Boot의 자동 구성 메카니즘을 활성화합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@ComponentScan&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션 내의 패키지에서 &lt;span style=&quot;color: #ef5369;&quot;&gt;@Component&lt;/span&gt; 애너테이션이 붙은 클래스들에 대한 스캐닝을 활성화합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@SpringBootConfiguration&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Context에 Bean을 추가적으로 등록하거나 Configuration 클래스를 추가적으로 임포트 하는 기능을 활성화합니다.&lt;/li&gt;
&lt;li&gt;Spring Boot의 @*Test 애너테이션을 사용해서 테스트를 진행할 경우,&amp;nbsp; 자동으로 &lt;span style=&quot;color: #ef5369;&quot;&gt;@SpringBootConfiguration&lt;/span&gt;을 검색합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;@SpringBootApplication&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;을 무조건 붙여야 하는것은 아니며, [코드 1-2]와 &lt;span style=&quot;color: #ef5369;&quot;&gt;@SpringBootApplication&lt;span style=&quot;color: #000000;&quot;&gt; 이 지원하는 세 가지 기능을 개별적으로 적용할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[코드 1-2]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649753930866&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootConfiguration(proxyBeanMethods = false)
@EnableAutoConfiguration
@Import({ CoffeeConfiguration.class, MemberConfiguration.class })
public class CoffeeApplication {

    public static void main(String[] args) {
        SpringApplication.run(CoffeeApplication.class, args);
    }

}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Java Backend 개발자 되기/Spring Boot</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/36</guid>
      <comments>https://itvillage.tistory.com/entry/SpringBootApplication-%EC%9D%98-%EC%97%AD%ED%95%A0#entry36comment</comments>
      <pubDate>Tue, 12 Apr 2022 17:59:24 +0900</pubDate>
    </item>
    <item>
      <title>RESTful API의 URL 작성 규칙</title>
      <link>https://itvillage.tistory.com/entry/RESTful-API%EC%9D%98-URL-%EC%9E%91%EC%84%B1-%EA%B7%9C%EC%B9%99</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;REST API란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST(Representational State Transfer)는 HTTP 네트워크 상의 리소스(Resource, 자원)를 정의하고 해당 리소스를 URI라는 고유한 주소로 접근하는 접근 방식을 의미하며, REST API란 REST 방식을 통해서 리소스에 접근하기 위한 서비스 API를 지칭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;REST에서 의미하는 리소스란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST에서 의미하는 자원은 데이터베이스에 저장된 데이터, 문서, 이미지, 동영상 등 HTTP 통신을 통해 주고 받을 수 있는 모든 것을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;URI(Uniform Resource Identifier)와 URL(Uniform Resource Locator)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URI는 네트워크 상에 있는 특정 리소스를 식별하는 통합 자원 식별자(Uniform Resource Identifier)를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL은 인터넷에 있는 리소스를 나타내는 통합 리소스 식별자를 의미하며, 우리가 흔히들 이야기하는 웹 상의 주소를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URI는 URL의 상위 개념으로 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URI는 리소스를 식별하는 식별자 역할을 하고, URL은 리소스의 위치를 가리킵니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예) http://www.itivllage.tistory.com/manage? id = 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예에서 '&lt;span style=&quot;color: #ef5369;&quot;&gt;http://www.itivllage.tistory.com/manage&lt;/span&gt;'까지는 리소스의 위치를 가리키는 URL이라고 할 수 있고, '&lt;span style=&quot;color: #ef5369;&quot;&gt;http://www.itivllage.tistory.com/manage? id = 1&lt;/span&gt;'는 리소스를 식별하기 위한 'id = 1'이라는 고유 식별자 붙었으므로 URI라고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;REST API URI 작성 규칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이번에는 HTTP 상에서 REST API 서비스를 만드는 입장에서 REST API URI를 작성하는 규칙들을 살펴보도록 하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;URI 작성 기본 규칙&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URI의 마지막이 '/'로 끝나지 않아야 합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;better
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://www.itivllage.tistory.com/coffees (ㅇ)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;worse
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://www.itivllage.tistory.com/coffees/ (x)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;동사 보다는 명사를 사용합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;better
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://www.itivllage.tistory.com/coffees (ㅇ)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;worse
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;http://www.itivllage.tistory.com/coffees/update (x)&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;단수형 보다는 복수형 명사를 사용합니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;better&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;http://www.itivllage.tistory.com/coffees (ㅇ)&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;worse
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://www.itivllage.tistory.com/coffee (x)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;URI는 기본 소문자로 사용합니다.&lt;/li&gt;
&lt;li&gt;언더스코어( _ ) 대신에 하이픈(-)을 사용합니다.&lt;/li&gt;
&lt;li&gt;파일 확장자는 URI에 포함하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;URI에서 리소스 간의 관계를 표현하는 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API를 작성하다보면 특정 리소스 간의 관계를 URI로 표현해야 할 경우가 굉장히 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이해를 위해 예시를 살펴보도록 하겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'http://www.itivllage.tistory.com/members'는 전체 회원에 대한 리소스를 의미합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;'http://www.itivllage.tistory.com/members/1'는 1이라는 ID를 가지는 회원에 대한 리소스를 의미합니다..&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;'&lt;/span&gt;http://www.itivllage.tistory.com/members/1/orders'는&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 1이라는 ID를 가지는 회원의 주문에 대한 리소스를 의미합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;더 읽을 거리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/azure/architecture/best-practices/api-design&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.microsoft.com/ko-kr/azure/architecture/best-practices/api-design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dzone.com/articles/7-rules-for-rest-api-uri-design-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dzone.com/articles/7-rules-for-rest-api-uri-design-1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/REST&quot;&gt;https://ko.wikipedia.org/wiki/REST&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1649750185857&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;REST - 위키백과, 우리 모두의 백과사전&quot; data-og-description=&quot;대한민국의 힙합 음악가에 대해서는 R-EST 문서를 참고하십시오. REST(Representational State Transfer)는 월드 와이드 웹과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍처의 한 형식이다. 이&quot; data-og-host=&quot;ko.wikipedia.org&quot; data-og-source-url=&quot;https://ko.wikipedia.org/wiki/REST&quot; data-og-url=&quot;https://ko.wikipedia.org/wiki/REST&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/z9ccz/hyN1ARZxR4/O90d8duOtWwYKGvKk0JTz0/img.jpg?width=220&amp;amp;height=290&amp;amp;face=0_0_220_290&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/REST&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.wikipedia.org/wiki/REST&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/z9ccz/hyN1ARZxR4/O90d8duOtWwYKGvKk0JTz0/img.jpg?width=220&amp;amp;height=290&amp;amp;face=0_0_220_290');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;REST - 위키백과, 우리 모두의 백과사전&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;대한민국의 힙합 음악가에 대해서는 R-EST 문서를 참고하십시오. REST(Representational State Transfer)는 월드 와이드 웹과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍처의 한 형식이다. 이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.wikipedia.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1649750183932&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;7 Rules for REST API URI Design - DZone Integration&quot; data-og-description=&quot;URIs, or Uniform Resource Identifiers, should be designed to be readable and clearly communicate the API resource model. These rules will help you succeed.&quot; data-og-host=&quot;dzone.com&quot; data-og-source-url=&quot;https://dzone.com/articles/7-rules-for-rest-api-uri-design-1&quot; data-og-url=&quot;https://dzone.com/articles/7-rules-for-rest-api-uri-design-1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c8VJpT/hyN0nmqsS1/tMRCrmHzY2kM4B7ELmfOIK/img.jpg?width=648&amp;amp;height=405&amp;amp;face=0_0_648_405,https://scrap.kakaocdn.net/dn/c9WznN/hyN1IWMBNZ/JsvLsFc0fAPuZXWATbwkdk/img.jpg?width=648&amp;amp;height=405&amp;amp;face=0_0_648_405&quot;&gt;&lt;a href=&quot;https://dzone.com/articles/7-rules-for-rest-api-uri-design-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dzone.com/articles/7-rules-for-rest-api-uri-design-1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c8VJpT/hyN0nmqsS1/tMRCrmHzY2kM4B7ELmfOIK/img.jpg?width=648&amp;amp;height=405&amp;amp;face=0_0_648_405,https://scrap.kakaocdn.net/dn/c9WznN/hyN1IWMBNZ/JsvLsFc0fAPuZXWATbwkdk/img.jpg?width=648&amp;amp;height=405&amp;amp;face=0_0_648_405');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;7 Rules for REST API URI Design - DZone Integration&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;URIs, or Uniform Resource Identifiers, should be designed to be readable and clearly communicate the API resource model. These rules will help you succeed.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dzone.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1649750179618&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;웹 API 디자인 모범 사례 - Azure Architecture Center&quot; data-og-description=&quot;플랫폼 독립성과 서비스 진화를 지원하는 웹 API 설계를 위한 모범 사례를 알아봅니다.&quot; data-og-host=&quot;docs.microsoft.com&quot; data-og-source-url=&quot;https://docs.microsoft.com/ko-kr/azure/architecture/best-practices/api-design&quot; data-og-url=&quot;https://docs.microsoft.com/ko-kr/azure/architecture/best-practices/api-design&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/09LCK/hyN0foqoBk/Mk0KM81CcncqNkdBrUzhdk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/azure/architecture/best-practices/api-design&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.microsoft.com/ko-kr/azure/architecture/best-practices/api-design&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/09LCK/hyN0foqoBk/Mk0KM81CcncqNkdBrUzhdk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;웹 API 디자인 모범 사례 - Azure Architecture Center&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;플랫폼 독립성과 서비스 진화를 지원하는 웹 API 설계를 위한 모범 사례를 알아봅니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/RESTful API</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/35</guid>
      <comments>https://itvillage.tistory.com/entry/RESTful-API%EC%9D%98-URL-%EC%9E%91%EC%84%B1-%EA%B7%9C%EC%B9%99#entry35comment</comments>
      <pubDate>Tue, 12 Apr 2022 17:02:02 +0900</pubDate>
    </item>
    <item>
      <title>Spring MVC의 동작 방식 추가 설명</title>
      <link>https://itvillage.tistory.com/entry/Spring-MVC%EC%9D%98-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D-%EC%B6%94%EA%B0%80-%EC%84%A4%EB%AA%85</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #d44c47;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;Handler&lt;/span&gt;&lt;span data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt; 용어의 의미 &lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핸들(Handle) 이라고 하면 일반적으로 자동차의 핸들을 제일 먼저 떠올릴 수 있는데, 자동차의 핸들은 운전자가 직접 핸들을 움직이면서 직접적으로 &lt;span data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;자동차의 주행을 처리하는 역할&lt;/span&gt;을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring MVC에서는 자동차의 핸들과 마찬가지로 &lt;span data-token-index=&quot;5&quot; data-reactroot=&quot;&quot;&gt;클라이언트의 요청을 처리하는 처리자&lt;/span&gt;를 &lt;span data-token-index=&quot;7&quot; data-reactroot=&quot;&quot;&gt;Handler&lt;/span&gt;라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Spring MVC에서 &lt;span data-token-index=&quot;9&quot; data-reactroot=&quot;&quot;&gt;Handler&lt;/span&gt;는 누구일까요? &lt;br /&gt;Spring MVC에서의 &lt;span data-token-index=&quot;11&quot; data-reactroot=&quot;&quot;&gt;요청 Handler&lt;/span&gt;는 바로 여러분들이 작성하는 Controller 클래스를 의미합니다. 그리고 Controller 클래스에 있는 &lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;lsquo;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;13&quot; data-reactroot=&quot;&quot;&gt;@GetMapping&lt;/span&gt;, &lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;15&quot; data-reactroot=&quot;&quot;&gt;@PostMapping&lt;/span&gt;&amp;rsquo;&lt;/span&gt; 같은 애너테이션이 붙어 있는 메서드들을 핸들러 메서드라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HandlerMapping이란 의미는 결국 &lt;span data-token-index=&quot;19&quot; data-reactroot=&quot;&quot;&gt;사용자의 요청과 이 요청을 처리하는 Handler를 매핑해주는&lt;/span&gt; 역할을 하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 사용자의 요청과 Handler 메서드의 매핑은 어떤 기준으로 이루어질까요? &lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #eb5757;&quot; data-token-index=&quot;21&quot; data-reactroot=&quot;&quot;&gt;@GetMapping(&amp;rdquo;/coffee&amp;rdquo;)&lt;/span&gt; 처럼 &lt;span data-token-index=&quot;23&quot; data-reactroot=&quot;&quot;&gt;HTTP Request Method&lt;/span&gt;(GET, POST 등)와 &lt;span data-token-index=&quot;25&quot; data-reactroot=&quot;&quot;&gt;Mapping URL&lt;/span&gt;을 기준으로 해당 Handler와 매핑이 되는데 Spring 에서는 여러가지 유형의 HandlerMapping 클래스를 제공하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 Spring에서 디폴트로 지정한 &amp;lsquo;&lt;span style=&quot;background-color: #ffffff; color: #eb5757;&quot; data-token-index=&quot;27&quot; data-reactroot=&quot;&quot;&gt;RequestMappingHandlerMapping&lt;/span&gt;&amp;rsquo;을 대부분 사용하는데 원한다면 얼마든지 HandlerMapping 전략을 바꿀 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #d44c47;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;Adapter&lt;/span&gt;의 의미&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서 사용하는 클래스나 인터페이스의 이름을 살펴보다 보면 흥미로운 용어들이 많이 나옵니다. &lt;br /&gt;Adapter라는 용어도 그 중에 하나라고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리말로 아답터, 어댑터(Adapter, Adaptor)하면 220V 전압을 110V 전압으로 또는 그 반대로 바꿔주는 어댑터(일명, 돼지코)나 USB 충전기를 떠올릴 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-007.png&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ncpzp/btryMnu8x1r/aHKJ9ueywwbIG4OwbY9pw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ncpzp/btryMnu8x1r/aHKJ9ueywwbIG4OwbY9pw0/img.png&quot; data-alt=&quot;220V를 5V 전압으로 바꿔주는 USB 충전기 예&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ncpzp/btryMnu8x1r/aHKJ9ueywwbIG4OwbY9pw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fncpzp%2FbtryMnu8x1r%2FaHKJ9ueywwbIG4OwbY9pw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;637&quot; height=&quot;431&quot; data-filename=&quot;K-007.png&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;220V를 5V 전압으로 바꿔주는 USB 충전기 예&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &lt;span data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;Adapter는 무언가를 다른 형식이나 형태로 바꿔주는 역할&lt;/span&gt;을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 &lt;span style=&quot;color: #ef5369;&quot;&gt;HandlerAdater&lt;/span&gt;는 무엇을 바꿔줄까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring은 객체지향의 설계 원칙을 잘 따르는 아주 유연한 구조를 가지는 프레임워크입니다. &lt;br /&gt;따라서 Spring이 제공하는 Spring MVC에서 지원하는 Handler를 사용해도 되지만 다른 프레임워크의 Handler를 Spring MVC에 통합할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &lt;span data-token-index=&quot;4&quot; data-reactroot=&quot;&quot;&gt;다른 프레임워크의 핸들러를 Spring MVC에 통합하기 위해서 HandlerAdapter&lt;/span&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #d44c47;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;ViewResolver&lt;/span&gt;의 의미&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rsquo;Resolve&amp;rsquo;는 무언가를 해석하고, 해결해주다라는 뜻이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;ViewResolver&lt;/span&gt;는 DispatcherServlet에서 &amp;lsquo;이런 이름을 가진 View를 줘&amp;rsquo; 라고 요청하면 DispatcherServlet에서 전달한 View 이름을 해석한 뒤 적절한 View 객체를 리턴해주는 역할을 합니다.&lt;/p&gt;</description>
      <category>Java Backend 개발자 되기/Spring MVC</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/33</guid>
      <comments>https://itvillage.tistory.com/entry/Spring-MVC%EC%9D%98-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D-%EC%B6%94%EA%B0%80-%EC%84%A4%EB%AA%85#entry33comment</comments>
      <pubDate>Fri, 8 Apr 2022 14:35:18 +0900</pubDate>
    </item>
    <item>
      <title>객체지향 설계 원칙</title>
      <link>https://itvillage.tistory.com/entry/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84-%EC%9B%90%EC%B9%99-SOLID-%EC%9B%90%EC%B9%99</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;객체 지향 설계 원칙이란?&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;수많은 사람들이 고민하고 시행착오를 겪으면서 만든 원칙입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;좀 더 유지보수하기 쉽고, 유연하고, 확장이 쉬운 소프트웨어를 만들 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코드 베이스뿐만 아니라 아키텍쳐 설계에 이르기까지 다양하게 적용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;높은 응집력이란?&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비슷한 일을 하는 기능 즉, 하나의 책임에 포함되는 기능들이 잘 뭉쳐있다면 높은 응집력을 가진다고 볼 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하나의 기능을 변경하는데 여기저기 변경해야 될 곳이 많다면 응집력이 낮다고 볼 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;낮은 결합도란?&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스 간에 의존성이 낮다면 낮은 결합도를 가진다고 볼 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하나의 클래스를 수정하는데 의존하는 다른 클래스를 모두 수정해야 한다면 결합도가 높다고 할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;SOLID 원칙 &lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SRP, OCP, LSP, ISP, DIP의 첫 글자를 딴것이 SOLID 입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;SRP(Single Responsibility Principle, 단일 책임의 원칙)&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스가 제공하는 기능들은 하나의 책임을 수행하는데 집중해야 한다는 원칙입니다.&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;SRP를 지키지 않는다면?&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;응집력이 낮아지고, 결합도는 높아집니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;서로 다른 애플리케이션에 배포될 때 사용하지 않는 기능도 포함됩니다. 즉, 왕따가 발생합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;변수 레벨&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하나의 속성이 여러 의미를 가지는 경우&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메서드 레벨&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;if 문이 많을 경우&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;SRP를 잘 지킨다면?&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;응집력이 높아지고, 결합도는 낮아집니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코드 가독성도 좋아집니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 범위도 작아집니다. 즉 유지보수도 편해집니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;AOP는 별도의 부가 기능을 핵심 로직에서 분리했기 때문에 SRP의 원칙을 지킨 방식이라고도 할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;SRP를 지키는 역할 분리의 예)&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Kevin이라는 클래스가 있다면?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;남편의 역할&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자주 전화해주기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기념일 챙기기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아빠의 역할&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;놀아주기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;씻겨주기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;재워주기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;먹여주기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;책 읽어주기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아들의 역할&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;건강 체크하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;용돈 주기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;안부 전화하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자주 방문하기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사위의 역할&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;건강 체크하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;용돈 주기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;안부 전화하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자주 방문하기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;친구의 역할&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;안부 전화하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;술 마시기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;고민 들어주기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;직원의 역할&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;출근하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;열심히 일하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;회의하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;퇴근하기&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;OCP(Open Close Principle, 개방폐쇄의 원칙)&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;확장에는 열려있고(Open), 변경에는 닫혀 있어야 한다(Close)는 원칙입니다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;변하는것과 변하지 않는것을 잘 구분하면 OCP를 지키기 용이&lt;/b&gt;합니다. 변하는것은 Open, 변하지 않는 것은 Close에 해당됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;변하지 않는것은 변하는 것의 구현에 의존하지 않고, &lt;b&gt;인터페이스를 통해 느슨하게 의존&lt;/b&gt;하게 만듭니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;디자인 패턴 중에서 전략패턴의 전략 인터페이스는 OCP의 Open에 해당되고 Context는 OCP의 Close에 해당됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자동차라는 객체에서 변하지 않는 동작&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;움직이는 것&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자동차 객체에서 변하는 동작&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;수동 모드, 오토메이션 모드, 크루즈컨트롤 모드&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LSP(The Liskov Substitution Principle, 리스코브 치환의 원칙)&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;subclass의 객체는 superclass의 참조 변수에 대입해서 superclass의 역할을 수행하는데 문제가 없어야 한다는 원칙입니다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;OCP 원칙의 기반이 된다고 생각됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ISP(Interface&amp;nbsp;Segregation&amp;nbsp;Principle,&amp;nbsp;인터페이스&amp;nbsp;분리의&amp;nbsp;원칙)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인터페이스의 단일 책임을 위한 원칙입니다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, 일반적인 하나의 인터페이스를 조금 더 구체적인 인터페이스로 쪼개는것이 낫다는 것입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;새의 행동(일반적인 하나의 인터페이스)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;날수 있는 행동(구체적인 인터페이스)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;울수 있는 행동(구체적인 인터페이스)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DIP(Dependency&amp;nbsp;Inversion&amp;nbsp;Principle,&amp;nbsp;의존성&amp;nbsp;역전의&amp;nbsp;원칙)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자주 변경되는 구체 클래스에 의존하지 않고, 추상화(일반화) 된 클래스에 의존하는 것을 의미합니다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;구체 클래스가 추상 클래스에 의존하므로 의존 관계가 역전된 형태입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Spring framework의 IOC 컨테이너&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;직접 객체를 생성하는것이 아니라 프레임워크에서 객체를 제공해줍니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;느슨한 연결 측면에서의 DIP&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추상 클래스에서 구체 클래스의 hook 메서드를 호출하는 것&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;hook 메서드 호출 시, 구체 클래스가 무엇인지 알 필요가 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이벤트 드리븐 방식&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이벤트 리스너를 등록하고 hook 메서드를 통해 해당 이벤트를 처리합니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;hook 메서드 호훌 시, 구체적인 이벤트 리스너가 무엇인지 알 필요가 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java Backend 개발자 되기/객체 지향 프로그래밍</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/32</guid>
      <comments>https://itvillage.tistory.com/entry/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84-%EC%9B%90%EC%B9%99-SOLID-%EC%9B%90%EC%B9%99#entry32comment</comments>
      <pubDate>Mon, 28 Mar 2022 11:27:04 +0900</pubDate>
    </item>
    <item>
      <title>OpenID Connect 방식의 로그인 연동</title>
      <link>https://itvillage.tistory.com/entry/OpenID-Connect-%EB%B0%A9%EC%8B%9D%EC%9D%98-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%97%B0%EB%8F%99</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;: NiFi 서버의 로그인 방식은 LDAP, Kerberos, OpenID Connect, Apache Knox에서 하나를 선택하여 구성할 수 있다(다음 링크 참조:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#user_authentication&quot;&gt;https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#user_authentication&lt;/a&gt;). 이 네가지 방식중에서 OpenID Connect(다음 링크 참조:&amp;nbsp;&lt;a href=&quot;https://openid.net/connect/&quot;&gt;https://openid.net/connect/&lt;/a&gt;) 로그인 방식을 택한것은 별도의 추가적인 인증 서버 설치 및 관리가 필요없어서 관리의 부담이 적고, 설정 자체가 네가지 방식 중에 가장 단순하기때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) Google API 프로젝트 생성 : OpenID Connect 방식의 로그인을 사용하기위해서 Google API를 사용할 것이므로 Google API용 프로젝트를 먼저 생성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a. 아래 링크를 통해 Google API로 접속을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://console.developers.google.com/apis&quot;&gt;https://console.developers.google.com/apis&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;b. (그림 1. Google API Dashboard)와 같이&amp;nbsp;새 프로젝트를 생성하기 위해 1)의 빨간색 박스 영역을 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 1. Google API Dashboard)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-024.png&quot; data-origin-width=&quot;1089&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xBtkP/btq9e6Ed7uF/jq3F69WUtUfa4xb3TuNK20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xBtkP/btq9e6Ed7uF/jq3F69WUtUfa4xb3TuNK20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xBtkP/btq9e6Ed7uF/jq3F69WUtUfa4xb3TuNK20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxBtkP%2Fbtq9e6Ed7uF%2Fjq3F69WUtUfa4xb3TuNK20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1089&quot; height=&quot;692&quot; data-filename=&quot;K-024.png&quot; data-origin-width=&quot;1089&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c. (그림 2. 새프로젝트 생성)과 같이 1)의 &quot;새 프로젝트&quot;를 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ NiFi 서버용 프로젝트(nifi-fitogether)가 이미 생성이 되어 있는 상태이지만 새 프로젝트를 생성하는 방법을 보여주기 위함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 2. 새프로젝트 생성)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드 (1).png&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k9aDs/btq9aGAktaC/er6I5Qxuaj8JlejpnVRbL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k9aDs/btq9aGAktaC/er6I5Qxuaj8JlejpnVRbL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k9aDs/btq9aGAktaC/er6I5Qxuaj8JlejpnVRbL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk9aDs%2Fbtq9aGAktaC%2Fer6I5Qxuaj8JlejpnVRbL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;850&quot; height=&quot;756&quot; data-filename=&quot;다운로드 (1).png&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;756&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: #172b4d;&quot;&gt;d. (그림 3. 새 프로젝트 설정)에서 1) 프로젝트명을 입력 2) &quot;만들기&quot; 버튼을 클릭한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;659&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QxUy8/btq9e5yEjEh/jKepM0gHaxnJZmiynBSSZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QxUy8/btq9e5yEjEh/jKepM0gHaxnJZmiynBSSZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QxUy8/btq9e5yEjEh/jKepM0gHaxnJZmiynBSSZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQxUy8%2Fbtq9e5yEjEh%2FjKepM0gHaxnJZmiynBSSZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1109&quot; height=&quot;659&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;659&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) OAuth 동의 화면 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 4.1. OAuth 동의 화면 설정)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-032.png&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;553&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkVO6P/btq9eNZdp5d/Sz0UKvFUGnCqQWLRIs4nYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkVO6P/btq9eNZdp5d/Sz0UKvFUGnCqQWLRIs4nYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkVO6P/btq9eNZdp5d/Sz0UKvFUGnCqQWLRIs4nYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkVO6P%2Fbtq9eNZdp5d%2FSz0UKvFUGnCqQWLRIs4nYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1127&quot; height=&quot;553&quot; data-filename=&quot;K-032.png&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;553&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 좌측 메인 메뉴에서 &quot;OAuth&quot; 동의 화면을 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) &quot;UserType&quot;에서 &quot;내부&quot;를 선택한 후, &quot;만들기&quot; 버튼을 클릭한다. &quot;만들기&quot; 버튼을 클릭하면 OAuth 동의 화면의 상세 항목 페이지로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 4.1. OAuth 동의 화면 상세)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-091.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;740&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbZxjF/btq9aF2saE5/KjTCqvpFuymAkCHCvAxYFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbZxjF/btq9aF2saE5/KjTCqvpFuymAkCHCvAxYFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbZxjF/btq9aF2saE5/KjTCqvpFuymAkCHCvAxYFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbZxjF%2Fbtq9aF2saE5%2FKjTCqvpFuymAkCHCvAxYFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;740&quot; data-filename=&quot;K-091.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 애플리케이션 이름을 적절하게 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) OAuth 인증을 사용하기 위한 승인된 도메인을 입력하고 엔터키를 누른 후, 맨 하단의 저장 버튼을 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(3)&amp;nbsp;클라이언트 ID 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 5.1. 사용자 인증 정보 생성)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-036.png&quot; data-origin-width=&quot;1423&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9afly/btq9evLj7ua/o4pNNXjusSbusmLjXxIMQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9afly/btq9evLj7ua/o4pNNXjusSbusmLjXxIMQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9afly/btq9evLj7ua/o4pNNXjusSbusmLjXxIMQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9afly%2Fbtq9evLj7ua%2Fo4pNNXjusSbusmLjXxIMQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1423&quot; height=&quot;712&quot; data-filename=&quot;K-036.png&quot; data-origin-width=&quot;1423&quot; data-origin-height=&quot;712&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 좌측의 메인 메뉴에서 &quot;사용자 인증 정보&quot;를 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 사용자 인증 정보를 생성하기 위해서 상단의 &quot;사용자 인증 정보 만들기&quot;를 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) &quot;OAuth 클라이언트 ID&quot;를 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 5.2. 애플리케이션 유형 선택)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-039.png&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RuOiF/btq9eNLHs9N/b0HI6fCCtdURjYXHFYfKgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RuOiF/btq9eNLHs9N/b0HI6fCCtdURjYXHFYfKgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RuOiF/btq9eNLHs9N/b0HI6fCCtdURjYXHFYfKgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRuOiF%2Fbtq9eNLHs9N%2Fb0HI6fCCtdURjYXHFYfKgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1244&quot; height=&quot;712&quot; data-filename=&quot;K-039.png&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;712&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 애플리케이션 유형 항목에서 &quot;웹 애플리케이션&quot;을 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 이름 항목에 적절한 이름을 입력한 후, &quot;생성&quot; 버튼을 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 5.3. OAuth 클라이언트 생성 후 팝업)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img (1).png&quot; data-origin-width=&quot;1171&quot; data-origin-height=&quot;693&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9C1Ej/btq9dww0lKI/HfOx577MP9hKqpNRvuGNw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9C1Ej/btq9dww0lKI/HfOx577MP9hKqpNRvuGNw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9C1Ej/btq9dww0lKI/HfOx577MP9hKqpNRvuGNw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9C1Ej%2Fbtq9dww0lKI%2FHfOx577MP9hKqpNRvuGNw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1171&quot; height=&quot;693&quot; data-filename=&quot;img (1).png&quot; data-origin-width=&quot;1171&quot; data-origin-height=&quot;693&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: OAuth 클라이언트 생성 직 후, 위와 같은 팝업이 뜨는데 &quot;클라이언트 ID&quot;와 &quot;클라이언트 보안 비밀번호&quot;를 copy해서 보관해둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 5.4. OAuth 클라이언트 ID 목록)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-044.png&quot; data-origin-width=&quot;1423&quot; data-origin-height=&quot;667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DTso5/btq9aFg3O5q/v5Qp16GThdqR7TCYAkoiD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DTso5/btq9aFg3O5q/v5Qp16GThdqR7TCYAkoiD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DTso5/btq9aFg3O5q/v5Qp16GThdqR7TCYAkoiD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDTso5%2Fbtq9aFg3O5q%2Fv5Qp16GThdqR7TCYAkoiD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1423&quot; height=&quot;667&quot; data-filename=&quot;K-044.png&quot; data-origin-width=&quot;1423&quot; data-origin-height=&quot;667&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 생성된 OAuth 클라이언트 ID를 설정하기 위해 클라이언트 ID 목록에서 이름 항목을 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 5.5. OAuth 클라이언트 ID 설정)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-046.png&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;747&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpyARe/btq9euFC5rO/kNXGxefnvbc4xVI2ggLxL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpyARe/btq9euFC5rO/kNXGxefnvbc4xVI2ggLxL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpyARe/btq9euFC5rO/kNXGxefnvbc4xVI2ggLxL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpyARe%2Fbtq9euFC5rO%2FkNXGxefnvbc4xVI2ggLxL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1201&quot; height=&quot;747&quot; data-filename=&quot;K-046.png&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;747&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 이름 항목에 이름을 적절하게 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) URI 버튼을 클릭해서 아래 URI 주소를 입력한 후, &quot;저장&quot; 버튼을 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https://xxx.xxxx.com/nifi-api/access/oidc/callback&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(4) nifi.properties 설정 : Google 클라이언트 ID를 이용한 OpenID Connect 방식의 로그인을 위해 nifi.properties(파일 위치: /home/ubuntu/app/nifi-1.11.4/conf)에 아래와 같이 설정을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 6. nifi.properties에 OpenID Connect 설정)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-049.png&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0FUVV/btq9eDbk2of/S7xvphuTQwnFpSVL40v1MK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0FUVV/btq9eDbk2of/S7xvphuTQwnFpSVL40v1MK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0FUVV/btq9eDbk2of/S7xvphuTQwnFpSVL40v1MK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0FUVV%2Fbtq9eDbk2of%2FS7xvphuTQwnFpSVL40v1MK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1376&quot; height=&quot;273&quot; data-filename=&quot;K-049.png&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;273&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) OpenID Connect Provider 검색 URL을 &quot;nifi.security.user.oidc.discovery.url&quot; 프로퍼티의 값으로 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2)&amp;nbsp;(그림 5.3. OAuth 클라이언트 생성 후 팝업)에서 copy하여 보관한 클라이언트 ID를 &quot;nifi.security.user.oidc.client.id&quot; 프로퍼티의 값으로 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3)&amp;nbsp;(그림 5.3. OAuth 클라이언트 생성 후 팝업)에서 copy하여 보관한 클라이언트 보안 비밀번호를 &quot;nifi.security.user.oidc.client.secret&quot; 프로퍼티의 값으로 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(5) authorizers.xml 파일 설정 : OpenID Connect 로그인을 위한 접근 권한을 부여하기 위해 authorizers.xml(/home/ubuntu/app/nifi-1.11.4/conf) 파일을 아래와 같이 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 7. authorizers.xml에 접근 권한 설정)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-053.png&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFG9ty/btq9evq3mZo/2RTfzUT29fqwY6Z85pH240/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFG9ty/btq9evq3mZo/2RTfzUT29fqwY6Z85pH240/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFG9ty/btq9evq3mZo/2RTfzUT29fqwY6Z85pH240/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFG9ty%2Fbtq9evq3mZo%2F2RTfzUT29fqwY6Z85pH240%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1150&quot; height=&quot;626&quot; data-filename=&quot;K-053.png&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;626&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 1), 2)에 Google OAuth 클라이언트 ID를 생성할 때 로그인한 Google 이메일 계정을 입력한 후, 파일을 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(6) 로그인 테스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a. NiFi 서버를 stop and start 시킨다.(/home/ubuntu/app/nifi-1.11.4/bin/nifi.sh stop,&amp;nbsp;/home/ubuntu/app/nifi-1.11.4/bin/nifi.sh start)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;b. NiFi 서버(https://xxx.xxxxx.com/nifi)로 다시 접속을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c. 아래와 같이 로그인을 위해 구글 계정을 선택 또는 입력하는 화면에서&amp;nbsp;authorizers.xml에 등록한 구글 계정을 선택 또는 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 8. 구글 계정 선택 또는 입력)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-055.png&quot; data-origin-width=&quot;555&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGSbed/btq9fv4IhCP/6XS7o5mxKkgZmQl0InldX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGSbed/btq9fv4IhCP/6XS7o5mxKkgZmQl0InldX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGSbed/btq9fv4IhCP/6XS7o5mxKkgZmQl0InldX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGSbed%2Fbtq9fv4IhCP%2F6XS7o5mxKkgZmQl0InldX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;555&quot; height=&quot;602&quot; data-filename=&quot;K-055.png&quot; data-origin-width=&quot;555&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;d. 아래와 같이 입력한 구글 계정의 비밀번호를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 9. 구글 계정의 비밀번호)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-057.png&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ootAf/btq9evYPzHh/peN0EXc90ClYcHZYaLMdzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ootAf/btq9evYPzHh/peN0EXc90ClYcHZYaLMdzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ootAf/btq9evYPzHh/peN0EXc90ClYcHZYaLMdzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FootAf%2Fbtq9evYPzHh%2FpeN0EXc90ClYcHZYaLMdzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;682&quot; data-filename=&quot;K-057.png&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e. 아래와 같이 NiFi 캔버스가 오픈되고, 좌측 상단에 구글 이메일 계정이 보이면 정상적으로 로그인이 된것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 10. OpenID Connect 방식으로 로그인 한 화면)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-059.png&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;775&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cs61Ig/btq9flHX0F5/TEfv34EhfCXteWt2yt3qkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cs61Ig/btq9flHX0F5/TEfv34EhfCXteWt2yt3qkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cs61Ig/btq9flHX0F5/TEfv34EhfCXteWt2yt3qkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcs61Ig%2Fbtq9flHX0F5%2FTEfv34EhfCXteWt2yt3qkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;981&quot; height=&quot;775&quot; data-filename=&quot;K-059.png&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;775&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: #172b4d;&quot;&gt;: 로그인에 성공하면 1)과 같이 구글 계정이 표시된다. 로그 아웃을 하기 위해서는 1)에서 &quot;LOG OUT&quot; 링크를 클릭하면 정상적으로 로그 아웃된다. &quot;LOG OUT&quot; 링크를 클릭했을때 자동으로 다시 로그인이 된다면 구글 사이트로 이동해서 구글 계정 자체를 로그아웃한 다음에 다시 한번 시도한다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Open source/Apache NiFi</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/30</guid>
      <comments>https://itvillage.tistory.com/entry/OpenID-Connect-%EB%B0%A9%EC%8B%9D%EC%9D%98-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%97%B0%EB%8F%99#entry30comment</comments>
      <pubDate>Fri, 9 Jul 2021 16:44:46 +0900</pubDate>
    </item>
    <item>
      <title>Apache NiFi에 SSL 적용</title>
      <link>https://itvillage.tistory.com/entry/Apache-NiFi%EC%97%90-SSL-%EC%A0%81%EC%9A%A9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;: NiFi 서버에 로그인 화면을 적용하기 위해서는 SSL 연동이 필수이다. 현재(2020년 5월 12일 기준) EC2에 생성된 NiFi 서버의 SSL 인증서는 Self Signed Certificate을 사용하였으며, OpenSSL 툴을 사용하지 않고 좀 더 빠르게 Self Signed Certificate을 생성하기 위해 TinyCert(&lt;a href=&quot;https://www.tinycert.org/&quot;&gt;https://www.tinycert.org/&lt;/a&gt;)를 이용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) 인증서 생성 : NiFi 서버에 SSL 설정을 하기 위해서는 먼저 TinyCert 에서 CA 인증서와 서버 인증서를 생성 및 다운로드해야하는데 절차는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a. TinyCert에 접속하여 먼저 Sing Up을 한다. Email Address와 Password, Passphrase를 입력 해야 하는데 Passphrase는 서버 인증서를 NiFi 서버에 구성할때 필요하므로 기억을 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;b. TinyCert에 로그인이 된 상태에서 아래 (그림 1. CA 생성)에서와 같이 메인 화면에서 CA 인증서를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 1. CA 생성)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1375&quot; data-origin-height=&quot;641&quot; data-filename=&quot;K-002.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HUuB4/btq9afiqjvq/2zM9zjeKvuWcjkCAloTw61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HUuB4/btq9afiqjvq/2zM9zjeKvuWcjkCAloTw61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HUuB4/btq9afiqjvq/2zM9zjeKvuWcjkCAloTw61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHUuB4%2Fbtq9afiqjvq%2F2zM9zjeKvuWcjkCAloTw61%2Fimg.png&quot; data-origin-width=&quot;1375&quot; data-origin-height=&quot;641&quot; data-filename=&quot;K-002.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;1) &quot;Create&quot; 버튼을 클릭하여 CA 인증서를 생성한다. CA 인증서 항목은 적절하게 입력해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c. 서버 인증서 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림2. 서버 인증서 생성)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1375&quot; data-origin-height=&quot;641&quot; data-filename=&quot;K-003.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Iaw6Y/btq9eNxY8OY/kqF1dQ1KevQeFVjyfAmQAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Iaw6Y/btq9eNxY8OY/kqF1dQ1KevQeFVjyfAmQAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Iaw6Y/btq9eNxY8OY/kqF1dQ1KevQeFVjyfAmQAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIaw6Y%2Fbtq9eNxY8OY%2FkqF1dQ1KevQeFVjyfAmQAK%2Fimg.png&quot; data-origin-width=&quot;1375&quot; data-origin-height=&quot;641&quot; data-filename=&quot;K-003.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;1) &quot;Create&quot; 버튼을 클릭해서 서버 인증서를 생성한다. 서버 인증서 항목은 적절하게 입력해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;d. CA 다운로드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림3. CA 다운로드)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1375&quot; data-origin-height=&quot;641&quot; data-filename=&quot;K-004.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjq1yX/btq9eu6vlMq/ZfxgzO1Uyho0JJdUXUsRLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjq1yX/btq9eu6vlMq/ZfxgzO1Uyho0JJdUXUsRLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjq1yX/btq9eu6vlMq/ZfxgzO1Uyho0JJdUXUsRLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjq1yX%2Fbtq9eu6vlMq%2FZfxgzO1Uyho0JJdUXUsRLk%2Fimg.png&quot; data-origin-width=&quot;1375&quot; data-origin-height=&quot;641&quot; data-filename=&quot;K-004.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;1) &quot;다운로드&quot; 버튼을 클릭해서 CA 인증서를 다운로드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e. 서버 인증서 다운로드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 4.1. 생성된 서버 인증서 목록)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;641&quot; data-filename=&quot;K-005.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YLUd3/btq9evj1TSP/2wnLGDgWeSdYQ7n8tATI5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YLUd3/btq9evj1TSP/2wnLGDgWeSdYQ7n8tATI5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YLUd3/btq9evj1TSP/2wnLGDgWeSdYQ7n8tATI5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYLUd3%2Fbtq9evj1TSP%2F2wnLGDgWeSdYQ7n8tATI5k%2Fimg.png&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;641&quot; data-filename=&quot;K-005.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;1) 생성된 서버 인증서 목록을 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 4.2. 생성된 서버 인증서 상세)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1427&quot; data-origin-height=&quot;693&quot; data-filename=&quot;K-009.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dl06P9/btq9fk28KNl/A6KK8SSC22Jkp3Ni8AOMNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dl06P9/btq9fk28KNl/A6KK8SSC22Jkp3Ni8AOMNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dl06P9/btq9fk28KNl/A6KK8SSC22Jkp3Ni8AOMNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdl06P9%2Fbtq9fk28KNl%2FA6KK8SSC22Jkp3Ni8AOMNk%2Fimg.png&quot; data-origin-width=&quot;1427&quot; data-origin-height=&quot;693&quot; data-filename=&quot;K-009.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;1) &quot;다운로드&quot; 버튼을 클릭해서 서버 인증서를 다운로드 버튼을 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 서버 인증서 타입을 &quot;PKCS#12 Archive&quot; 타입으로 선택하면 서버 인증서가 다운로드 된다.&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;(2) NiFi 서버에 인증서 업로드 : 로컬 PC에 다운로드 받은 CA 인증서와 서버 인증서를 AWS EC2에 설치된 NiFi 서버쪽으로 업로드를 해야하는데 이를 위해서는 EC2 인스턴스에 해당하는 Private Key가 필요하다. Private Key는 개발 담당자(2020년 5월 12일 기준: 황정식)에게 문의한다. 아래 링크의 문서를 참조해서 다운로드 받은 CA 인증서와 서버 인증서를 NiFi 서버쪽으로 업로드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a. SCP 파일 전송 관련 문서 :&amp;nbsp;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html&quot;&gt;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;b. 현재(2020년 5월 12일 기준) 업로드된 CA 인증서와 서버 인증서의 디렉토리 경로는 (그림 5. CA 인증서와 서버 인증서의 디렉토리 경로)와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 5. CA 인증서와 서버 인증서의 디렉토리 경로)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;267&quot; data-filename=&quot;K-011.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIF1Iu/btq9afbHasI/drqLLXpvST9SSREXsy6Am0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIF1Iu/btq9afbHasI/drqLLXpvST9SSREXsy6Am0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIF1Iu/btq9afbHasI/drqLLXpvST9SSREXsy6Am0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIF1Iu%2Fbtq9afbHasI%2FdrqLLXpvST9SSREXsy6Am0%2Fimg.png&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;267&quot; data-filename=&quot;K-011.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;1) CA 인증서와 서버 인증서가 위치한 디렉토리 경로이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 로컬 PC에서 업로드 된 CA 인증서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 로컬 PC에서 업로드 된 서버 인증서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(3) Truststore 생성 :&amp;nbsp; NiFi 서버는 SSL 설정을 위해서 서버 인증서와 추가적으로 Truststore를 생성해야하는데 Truststore는 로컬 PC에서 업로드한 CA 인증서를 이용해서 (그림 6. Truststore 생성)과 같이 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 6. Truststore 생성)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;106&quot; data-filename=&quot;K-013.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xq60E/btq9fl8OY8S/rNg9KEsO06t8xS7C6nhbk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xq60E/btq9fl8OY8S/rNg9KEsO06t8xS7C6nhbk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xq60E/btq9fl8OY8S/rNg9KEsO06t8xS7C6nhbk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxq60E%2Fbtq9fl8OY8S%2FrNg9KEsO06t8xS7C6nhbk0%2Fimg.png&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;106&quot; data-filename=&quot;K-013.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;1) &amp;lt;ca_file&amp;gt; 부분은 로컬 PC에서 업로드한 CA 인증서 파일명이며, &amp;lt;password&amp;gt;에는 패스워드를 입력하면 된다. 설정된 패스워드에 대해서는 개발 담당자(2020년 5월 12일 기준: 황정식)에게 문의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(4) nifi.properties 설정 : nifi.properties 파일을 열어서 SSL 포트, KeyStore, Truststore 설정을 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a. HTTPS 설정 : NiFi 서버는 http와 https를 동시에 운용할 수 없기때문에 (그림7.1. HTTPS 설정)과 같이 프로퍼티 값을 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 7.1. HTTPS 설정)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;569&quot; data-filename=&quot;K-015.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HVTe3/btq9dEVFF2t/V7hhCyFVSoekBq8RXlTO11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HVTe3/btq9dEVFF2t/V7hhCyFVSoekBq8RXlTO11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HVTe3/btq9dEVFF2t/V7hhCyFVSoekBq8RXlTO11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHVTe3%2Fbtq9dEVFF2t%2FV7hhCyFVSoekBq8RXlTO11%2Fimg.png&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;569&quot; data-filename=&quot;K-015.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;1) &quot;nifi.remote.input.secure&quot; 프로퍼티의 값을 &quot;true&quot;로 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) &quot;nifi.remote.input.http.enabled&quot; 프로퍼티의 값을 &quot;false&quot;로 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) &quot;nifi.web.https.port&quot; 프로퍼티의 값을 &quot;9443&quot;으로 수정한다. NiFi의 https 디폴트 포트는 9443이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;4) 그림 7.1에는 빨간 박스로 표시 되어 있지만 &quot;nifi.web.https.host&quot; 프로퍼티의 값으로 NiFi 서버와 매핑된 도메인을 입력해야지만 다음 글에서 설명하는 OpenID Connect 방식의 로그인을 사용할 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;b. KeyStore 및 Truststore 설정: CA 인증서를 이용해서 생성한 Truststore와 서버 인증서 설정을 (그림 7.2. KeyStore 및 Truststore 설정)과 같이 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그림 7.2. KeyStore 및 Truststore 설정)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;955&quot; data-origin-height=&quot;323&quot; data-filename=&quot;K-018.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGPVlR/btq9bF8pJTt/ra6Aj76gyYmYjOTvg6x5IK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGPVlR/btq9bF8pJTt/ra6Aj76gyYmYjOTvg6x5IK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGPVlR/btq9bF8pJTt/ra6Aj76gyYmYjOTvg6x5IK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGPVlR%2Fbtq9bF8pJTt%2Fra6Aj76gyYmYjOTvg6x5IK%2Fimg.png&quot; data-origin-width=&quot;955&quot; data-origin-height=&quot;323&quot; data-filename=&quot;K-018.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;1) &quot;nifi.security.keystore&quot; 프로퍼티의 값으로 서버 인증서의 경로를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) &quot;nifi.security.keystoreType&quot; 프로퍼티의 값으로 &quot;PKCS12&quot;를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) &quot;nifi.security.keystorePasswd&quot; 프로퍼티의 값으로 TinyCert에서 Sign up 시, 등록한 passphrase 값을 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) &quot;nifi.security.truststore&quot; 프로퍼티의 값으로 CA 인증서를 이용해서 생성한 Truststore의 경로를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5) &quot;nifi.security.truststoreType&quot; 프로퍼티의 값으로 &quot;JKS&quot;를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6) &quot;nifi.security.truststorePasswd&quot; 프로퍼티의 값으로 Truststore를 생성할 때 설정한 패스워드를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(5) hosts 파일 설정 : NiFi 서버에 접속하는 사용자는 (6)에서 설명할 도메인을 통해서 NiFi 서버에 접속을 할 수 있는데, 이 도메인은 EC2에서 추가한 외부 IP인 탄력적 IP와 매핑이 되어 있다. 하지만 NiFi 서버 입장에서는 EC2 인스턴스 내부 IP와 매핑이 되어야하므로 hosts 파일에 매핑 정보를 추가해주어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a. hosts 파일의 경로 : /etc/hosts&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;b.&amp;nbsp;내부 IP는 &quot;hostname -I&quot; 명령어로 확인 가능하며, NiFi 서버가 설치된 EC2 인스턴스의 내부 IP는 &quot;172.31.34.156&quot; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;b. (그림 8. 도메인과 내부 IP 매핑 정보)와 같이 매핑 정보를 추가해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;264&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQPP6G/btq8932Yhqa/LnF7KhYZM8LknOJV49hAd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQPP6G/btq8932Yhqa/LnF7KhYZM8LknOJV49hAd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQPP6G/btq8932Yhqa/LnF7KhYZM8LknOJV49hAd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQPP6G%2Fbtq8932Yhqa%2FLnF7KhYZM8LknOJV49hAd1%2Fimg.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;264&quot; data-filename=&quot;blob&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;1) EC2 인스턴스의 내부 IP와 도메인 매핑 정보를 hosts 파일에 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(6) 접속 테스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a. nifi.sh 명령을 사용해 NiFi 서버를 가동 시킨다. nifi.sh 전체 명령어는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ubuntu@ip:~/app/nifi-1.11.4$ pwd&lt;br /&gt;/home/ubuntu/app/nifi-1.11.4&lt;br /&gt;ubuntu@ip:~/app/nifi-1.11.4$&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;bin/nifi.sh start&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;b. 아래 주소로 접속을 해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https://도메인:9443/nifi&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #172b4d;&quot;&gt;1) NiFi 서버쪽에 정상적으로 접속이 되지 않는 이유는 NiFi 서버는 SSL 설정이 완료되면 추가적으로 로그인 설정을 해주어야 한다. 접속 시, 발생하는 에러는 SSL 설정은 되어 있으나 로그인 설정이 되어 있지 않았기때문에 발생하는 에러이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #172b4d;&quot;&gt;다음 글에서는 NiFi 로그인 인증 방식중에 하나인 OpenID Connect 설정을 해보겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Open source/Apache NiFi</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/29</guid>
      <comments>https://itvillage.tistory.com/entry/Apache-NiFi%EC%97%90-SSL-%EC%A0%81%EC%9A%A9#entry29comment</comments>
      <pubDate>Fri, 9 Jul 2021 15:33:27 +0900</pubDate>
    </item>
    <item>
      <title>Hello Reactor 들여다 보기</title>
      <link>https://itvillage.tistory.com/entry/Hello-Reactor-%EB%93%A4%EC%97%AC%EB%8B%A4-%EB%B3%B4%EA%B8%B0</link>
      <description>&lt;p&gt;안녕하세요? &lt;a href=&quot;https://inf.run/VYRg&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Kevin의 알기 쉬운 RxJava]&lt;/a&gt; 강의를 진행하고 있는 Kevin입니다.&lt;/p&gt;
&lt;p&gt;이번 시간에는 'Hello, Reactor' 코드를 가지고 Reactor의 기본 구조를 들여다보는 시간을 가져보겠습니다.&lt;/p&gt;
&lt;p&gt;개발에 처음 입문하실때 대부분 'Hello World!' 코드를 실행해보셨을텐데요. Reactor 역시 마찬가지로 'Hello, Reactor' 메시지를 출력해보면서 Reactor의 기본 구조를 간단하게 살펴보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;어렵지 않으니 가벼운 마음으로 글을 읽어주시면 감사드리겠습니다.^^&lt;/p&gt;
&lt;p&gt;먼저 아래의 코드를 보시죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;K-004.png&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;529&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wjcS0/btqSEMbwMdu/4tcACItcsyUlMbokwk3l31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wjcS0/btqSEMbwMdu/4tcACItcsyUlMbokwk3l31/img.png&quot; data-alt=&quot;[Hello Reactor 코드]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wjcS0/btqSEMbwMdu/4tcACItcsyUlMbokwk3l31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwjcS0%2FbtqSEMbwMdu%2F4tcACItcsyUlMbokwk3l31%2Fimg.png&quot; data-filename=&quot;K-004.png&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;529&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[Hello Reactor 코드]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여러분들이 아시다 시피 Reactor는 RxJava와 마찬가지로 Reactive Streams라는 표준 사양을 구현한 구현체입니다. 따라서 Reactive Streams에서 정의하고 있는 생산자 즉, Publisher와 소비자 즉, Subscriber를 구현하고 있는데요.&lt;/p&gt;
&lt;p&gt;위의 코드에서 보시는것처럼 Reactor에서는 Flux가 대표적인 생산자 중에 하나입니다.&lt;br /&gt;위의 코드에서 소비자라고 되어 있는 부분은 람다 베이스 Subscriber가 되겠습니다.&lt;/p&gt;
&lt;p&gt;생산자 쪽에서는 just라는 Operator(연산자)를 사용해서 두 개의 데이터를 소비자 쪽으로 통지를 하는데요. Reactor에서는 생산자 쪽에서 통지할 데이터를 정의하는 과정 자체를 묶어서 'Sequence(시퀀스)'라고 합니다. 이 Sequence라는 용어는 Reactor를 접하다보면 자주 나오는 용어이니 기억을 해두시길 바래볼게요.&lt;/p&gt;
&lt;p&gt;생산자 쪽에서 소비자 쪽으로 데이터를 통지하는것이 맞긴하지만 최종 소비자까지 데이터가 전달 되기까지는 중간에 여러 Operator를 만날 수 있기때문에 엄밀히 말하자면 생산자가 소비자에게 데이터를 통지한다라고 말하기보다는 생산자가 Downstream으로 데이터를 내보낸다라는 표현이 더 정확할 것 같네요.&lt;/p&gt;
&lt;p&gt;용어에 대한 부분은 아래쪽에서 다시 정의를 해보도록 하구요.&lt;/p&gt;
&lt;p&gt;아무튼 생산자 쪽에서 데이터를 통지하게 되면 최종 소비자 쪽에 바로 데이터가 전달되는 것이 아니라 map( )이라는 Operator에서 원본 데이터가 가공 처리 된 후에 최종 소비자 쪽으로 전달이 되는것을 볼 수 있는데요.&lt;/p&gt;
&lt;p&gt;여기서는 통지된 String 데이터를 소문자로 변환을 한 후에 최종 소비자쪽으로 전달하고 있네요.&lt;/p&gt;
&lt;p&gt;자, RxJava 강의 시간에도 말씀을 드렸지만 리액티브 프로그래밍은 크게 세가지의 step으로 이루어져있습니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1 step : 생산자가 데이터를 통지한다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;2 step : 통지된 데이터를 Operator로 가공한다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;3 step : 가공 처리된 데이터를 최종 소비자에게 전달한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;이 세가지 큰 흐름은 기본적으로 꼭 기억을 해두시고, 나머지는 데이터를 통지하는 다양한 방법, 데이터를 가공하는 다양한 방법, 비동기 프로그래밍을 위한 쓰레드를 할당하는 방법, 에러 처리 방법 등을 단계적으로 적용 하는 식으로 학습을 진행해 나가면 리액티브 프로그래밍을 조금 더 수월하게 이해하실 수 있을거라는 생각이 드네요. ^^;&lt;/p&gt;
&lt;p&gt;자, 그럼 마지막으로 Reactor에 대한 에피소드를 계속 진행가기 전에 Reactor에서 사용되는 여러가지 용어를 정의하고 에피소드를 마무리 하도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;먼저 아래 용어들을 보시죠.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;Publisher&lt;/b&gt; : 발행자, 게시자, 생산자, 방출자(Emitter)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;Subscriber&lt;/b&gt; : 구독자, 소비자&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;Emit&lt;/b&gt; : Publisher가 데이터를 내보내는 것(방출하다. 내보내다. 통지하다.)&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;Sequence&lt;/b&gt; : Publisher가 emit하는 데이터의 연속적인 흐름. 스트림과 같은 의미라고 보면 됨&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Subscribe&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; : Subscriber&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Sequence&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;를 구독하는 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;Dispose&lt;/b&gt; : Suscriber가 Sequence 구독을 해지 하는 것&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;Downstream&lt;/b&gt; : 현재 Operator 체인의 위치에서 봤을때 데이터가 전달 되는 하위 Operator 및 method 체인&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;Upstream&lt;/b&gt; : 현재 Operator 체인의 위치에서 봤을때 상위 Operator 및 method 체인&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;여태껏 제가 데이터를 통지하는 쪽은 생산자로 지칭했는데요. 보시는대로 생산자 이외에 발행자, 게시자, 방출자 등 다양하게 사용이 되고 있습니다. 모두 같은 의미라고 보시면 되겠지만 앞으로 에피소드를 진행할 때는 그냥 영어로 Publisher라고 지칭하도록 하겠습니다. ^^;&lt;/p&gt;
&lt;p&gt;따라서 소비자 역시 영어로 Subscriber라고 지칭하면 되겠구요.&lt;/p&gt;
&lt;p&gt;데이터를 통지하는 행위 역시 영어 그대로 Emit 한다라고 하겠습니다.&lt;/p&gt;
&lt;p&gt;Sequence는 위에서 잠깐 말씀 드렸죠? Publisher쪽에서 데이터를 emit하는 흐름 자체를 통칭하는 것을 의미하는데, 예를 들어서 Flux라는 Publisher가 데이터의 Emit을 정의하는 부분을 설명할때 Sequence를 생성한다 라고 표현할 수 있겠습니다.&lt;/p&gt;
&lt;p&gt;Subscribe와 Dispose는 구독하다, 구독을 해지하다라고 표현하도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;마지막으로 Downstream과 Upstream이라는 용어인데요. Reactor를 학습하다보면 종종 나오는 용어인데 개념적으로 이해하기 모호한 면이 있는 용어중에 하나이기도 합니다.&lt;/p&gt;
&lt;p&gt;이럴때는 그냥 단순하게 이해를 하는게 제일 좋을거라고 생각을 해보게되네요. ^^;&lt;br /&gt;예를 들어 위의 코드에서 just( )의 위치에서 Downstream은 map( )부터 subscribe( )까지 하위 체인이 되겠습니다. 그리고 subscribe( ) 입장에서는 map( )과 just( )가 Upstream이 되겠구요.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;just( )의 내부를 들여다보면 return 값으로 Flux를 반환하는데요. just( )에서 반환하는 Flux를 원본 Flux가 되겠습니다. 그리고 map( )에서 반환하는 return 값 역시 Flux인데요. 여기서의 Flux는 원본 Flux가 아닌 가공 처리된 데이터를 가지고 있는 새로운 Flux입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Flux(또는 Mono)의 이러한 처리 흐름때문에 Downstream이나 Upstream이라는 용어가 생겨난것 같은데 위에서 언급드린것처럼 단순하게 생각해주시면 될것 같아요.&lt;/p&gt;
&lt;p&gt;자, 오늘은 'Hello, Reactor' 코드를 통해서 Reactor 의 기본 구조를 살펴보았는데요.&amp;nbsp;&lt;br /&gt;다음 시간에 다른 에피소드를 가지고 다시 찾아뵙도록 하겠습니다. ^^&lt;/p&gt;
&lt;p&gt;감사합니다!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://inf.run/VYRg&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Kevin의 알기 쉬운 RxJava 1부] 강의 바로가기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1609895590526&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Kevin의 알기 쉬운 RxJava 1부 - 인프런&quot; data-og-description=&quot;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://inf.run/VYRg&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-1?utm_source=inflearn&amp;amp;utm_medium=social&amp;amp;utm_campaign=share&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ca5C9n/hyIPxz8HHh/DK5OUfRhMyXQQGqm6kPQK1/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/l4i93/hyIPDAlaMp/76DoBZkJRKATN96H5njSH1/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/n3Pxy/hyIPJm27t6/6b6k8IGuqWCyjgH6IxjCF0/img.png?width=757&amp;amp;height=384&amp;amp;face=0_0_757_384&quot;&gt;&lt;a href=&quot;https://inf.run/VYRg&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inf.run/VYRg&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ca5C9n/hyIPxz8HHh/DK5OUfRhMyXQQGqm6kPQK1/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/l4i93/hyIPDAlaMp/76DoBZkJRKATN96H5njSH1/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/n3Pxy/hyIPJm27t6/6b6k8IGuqWCyjgH6IxjCF0/img.png?width=757&amp;amp;height=384&amp;amp;face=0_0_757_384');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Kevin의 알기 쉬운 RxJava 1부 - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://inf.run/JMPs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Kevin의 알기 쉬운 RxJava 2부] 강의 바로가기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1609895635655&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Kevin의 알기 쉬운 RxJava 2부 - 인프런&quot; data-og-description=&quot;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://inf.run/JMPs&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-2?utm_source=inflearn&amp;amp;utm_medium=social&amp;amp;utm_campaign=share&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bwJAYe/hyIPv3oEnC/3na2pbZHT2gV1qfEF9MxU0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/doxdfn/hyIPBoY9Qf/hRXiSFEQskcJaKxe1TrEV1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/nEm50/hyIPGRm43g/COfQ0ssJtWTDlaglz6oL71/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://inf.run/JMPs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inf.run/JMPs&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bwJAYe/hyIPv3oEnC/3na2pbZHT2gV1qfEF9MxU0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/doxdfn/hyIPBoY9Qf/hRXiSFEQskcJaKxe1TrEV1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/nEm50/hyIPGRm43g/COfQ0ssJtWTDlaglz6oL71/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Kevin의 알기 쉬운 RxJava 2부 - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>리액티브 프로그래밍/Reactor</category>
      <category>Hello Reactor</category>
      <category>Reactive Programming</category>
      <category>reactor</category>
      <category>리액터</category>
      <category>리액티브 프로그래밍</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/28</guid>
      <comments>https://itvillage.tistory.com/entry/Hello-Reactor-%EB%93%A4%EC%97%AC%EB%8B%A4-%EB%B3%B4%EA%B8%B0#entry28comment</comments>
      <pubDate>Wed, 6 Jan 2021 10:30:56 +0900</pubDate>
    </item>
    <item>
      <title>이벤트 루프란?</title>
      <link>https://itvillage.tistory.com/entry/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EB%9E%80</link>
      <description>&lt;p&gt;수강생 여러분 안녕하세요. RxJava 강의를 진행하고있는 Kevin입니다. 오늘은 리액티브 프로그래밍과 밀접한 관련이 있는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;이벤트 루프&lt;/b&gt;에대해서 잠시 알아보도록하겠습니다.&lt;/p&gt;
&lt;p&gt;이벤트 루프에 대해서 먼저 이야기 하기 전에 저희는 서블릿에 대한 언급을 먼저 하고 넘어가야되는데, 그 이유는 이벤트 루프라는 개념이 나오게 된 배경에는 서블릿이 있기때문이에요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그럼 서블릿에 대한 이야기를 먼저 해볼까요?&lt;/p&gt;
&lt;h2&gt;서블릿의 기본 개념&lt;/h2&gt;
&lt;p&gt;서블릿은 뭘까요? 자바를 사용하는 개발자이거나 자바를 배우고 계신 분들이 웹 애플리케이션을 만들어 보셨다면 이미 서블릿을 사용해보셨을겁니다. 여러분들이 JSP를 사용해보셨거나 아니면 Spring framework을 사용해보셨다면 여러분들은 알게 모르게 서블릿이란 놈을 사용해보셨다는 말씀. ^^&lt;/p&gt;
&lt;p&gt;서블릿은 웹에서 클라이언트의 요청을 동적으로 처리하기 위해 서블릿 규약이라는&amp;nbsp;일정한 규칙에 맞춰서 작성되는 클래스인데요. 이 클래스들이 클라이언트로 부터 들어오는 요청을 받아서 응답으로 되돌려주는 역할을 하죠.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;개발자가 작성한 서블릿 클래스가 HttpServlet을 상속 받고, doGet( ), doPost()를 구현하는 것이 서블릿 클래스의 기본이라는 사실은&amp;nbsp; 서블릿 클래스를 사용해보신분들은 너무나 잘 알고들 계실겁니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2&gt;Spring에서의 서블릿&lt;/h2&gt;
&lt;p&gt;어쨌든 저희가 HttpServlet 같은 상위 클래스를 상속 받아서 개발을 진행할건&amp;nbsp;아니기때문에 서블릿에 대해서 더 자세한 이야기를 할 필요는 없을 것을 같네요. 다만, 이것 하나만은 기억을 하셔야 될 것 같습니다. Spring MVC 역시 내부적으로는 Servlet 기술을 사용을 한다는것을요. DispatcherServlet 이라는 녀석을 기억하시죠? Spring MVC에서는 이 DispatchServlet이라는 녀석이 최앞단에서 클라이언트의 요청을 전달 받아서 해당 요청에 매핑되는 Controller 에게 요청을 전달한다는 사실은 Spring MVC로 개발을 해보신분들은 잘 알고 계실겁니다.&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;서블릿 방식의 문제점&lt;/h2&gt;
&lt;p&gt;그런데 이러한 서블릿을 사용하여 클라이언트의 요청을 처리하는 방식에는 큰 문제점이 하나 있는데요. 서블릿 방식은 클라이언트에서 요청이 들어오면 해당 요청을 처리하기 위해서 요청 당 쓰레드 하나씩을 생성해서 처리합니다. 하나의 쓰레드에서 클라이언트의 요청을 다 처리하면 이 쓰레드는 쓰레드풀이라는 쓰레드의 저장소에 다시 반납이 되는데,&amp;nbsp; 참고로 이 쓰레드 풀은 톰캣 같은 서블릿 컨테이너가 관리를 해줍니다. 사실 클라이언트의 요청이 폭주만 하지 않으면 지금과 같은 서블릿 방식이 큰 문제가 되지는 않겠지만 클라이언트의 요청이 쓰레드의 갯수에 비해 기하급수적으로 늘어나는 순간 문제가 시작됩니다. 요청을 처리하기 위한 쓰레드가 쓰레드 풀에 남아있지 않을 경우 기존에 사용되던 쓰레드가 쓰레드 풀에 다시 반납이 되어 다른 요청을 처리하기 위해서 재사용할 수 있 때까지 클라이언트로 부터 들어온 요청들은 대기를 해야되니까요. 이런 성능상의 문제점을 상당 부분 해소할 수 있는 방식이 바로 이벤트 루프 방식인데요. 그럼 이벤트 루프 방식이 무엇인지, 어떻게 클라이언트의 요청을 효과적으로 처리하는지 간단하게 살펴볼까요?&lt;/p&gt;
&lt;h2&gt;이벤트 루프란&lt;/h2&gt;
&lt;p&gt;아래 그림을 보면서 이벤트 루프에 대해서 설명 드리겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;880&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WfVK8/btqSNlRh0Ab/9uiyzNMOASsI3FLyyYtOC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WfVK8/btqSNlRh0Ab/9uiyzNMOASsI3FLyyYtOC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WfVK8/btqSNlRh0Ab/9uiyzNMOASsI3FLyyYtOC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWfVK8%2FbtqSNlRh0Ab%2F9uiyzNMOASsI3FLyyYtOC1%2Fimg.png&quot; width=&quot;880&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;클라이언트가 서버로 요청을 보내면 요청 핸들러가 클라이언트의 요청이 들어왔다는 이벤트를 이벤트 루프에 푸시합니다.&lt;/p&gt;
&lt;p&gt;이벤트 루프는 그림과 같이 계속해서 루프를 돌면서(한마디로 무한루프) 요청 핸들러로부터 푸시된 이벤트를 감지합니다. 그리고 나서 해당 이벤트에 대한 콜백을 등록한 후, 클라이언트가 요청한 작업을 처리합니다. 작업이 끝나면 작업이 끝났다는 이벤트를 이벤트 루프에 푸시하고 등록된 콜백을 호출한 후, 요청 핸들러를 다시 거쳐서 클라이언트에게 최종적으로 응답을 되돌려줍니다. 어떠신가요? 이벤트 루프가 무엇인지 대충 감이 오시는지 모르겠네요.&lt;/p&gt;
&lt;p&gt;이벤트 루프 방식의 장점은 쓰레드 하나로 굉장히 많은 요청을 병렬로 처리하기때문에 기존의 서블릿 방식의 성능상의 문제를 해결한다는것입니다.&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;WebFlux와 이벤트 루프&lt;/h2&gt;
&lt;p&gt;&amp;nbsp; Spring WebFlux가 바로 이벤트 루프 방식을 사용하기때문에 대량의 클라이언트 요청을 효과적으로 처리할 수 가 있게되었는데요. Spring WebFlux를 사용해서 대량의 클라이언트 요청을 효과적으로 처리하는 애플리케이션을 한번 만들어보고 싶어지지 않으셨나요? ^^;&lt;/p&gt;
&lt;h2&gt;Spring MVC냐 Spring WebFlux냐&lt;/h2&gt;
&lt;p&gt;자, 그러면 무조건적으로 Spring MVC 대신에 Spring WebFlux를 사용하면 좋은것이냐하면 사실 그건 아닌것같습니다. Spring MVC로 개발을 진행해보셨다면 아시겠지만 생산성이 대단하다는것을 느끼실 수가 있을겁니다. 그리고 RxJava 2부 강의에서 비동기 방식의 디버깅에 대한 이야기를 드리겠지만 비동기 방식보다는 동기 방식이 디버깅도 용이하고 여태껏 사용해온 익숙한 명령형 프로그래밍 방식으로 코드를 짜면 되니 코드를 짜는 부담 역시 줄어드는게 사실일테구요. 하지만 분명한것은 사물 인터넷, 자동차, 인공 지능 등등 우리가 생각하는 전통적인 클라이언트 이외에 다양한 클라이언트들이 계속해서 생겨날텐데 이런 종류의 클라이언트들이 계속해서 생겨나면 생겨날수록 서버가 감당해야 되는 요청의 수는 점점 늘어나겠죠. 마이크로 서비스로 인해서 또한 서비스들 간의 요청도 점점 늘어나고 있기도 하구요.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;결론은 지금 당장은 아니더라도 개발자로서 미래를 준비하고 싶으신 분은 리액티브 프로그래밍 기법을 꼭 익혀두셨으면 하는 바램입니다. ^^ 이야기는 길었는데 결론은 꽤 간단하다는.. ^^;;&lt;/p&gt;
&lt;p&gt;오늘은 이벤트 루프에 대한 이야기를 간단하게 해보았는데요. Spring WebFlux에 대한 이야기로 또 찾아뵙도록 할게요.&lt;/p&gt;
&lt;p&gt;감사합니다~&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://inf.run/o4En&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Kevin의 알기 쉬운 RxJava 1부 강의] 바로 가기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1609892865152&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Kevin의 알기 쉬운 RxJava 1부 - 인프런&quot; data-og-description=&quot;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://inf.run/o4En&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-1?utm_source=inflearn&amp;amp;utm_medium=social&amp;amp;utm_campaign=share&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yZl4M/hyIPB3yO7a/tQWoFUXCel560ZxWfYja51/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/kyudD/hyIPIhjZFd/t4EpclswM2AMHWKkt4KXsK/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/Z2L0Y/hyIPwOHYSk/764XJlQCEmZ6xh55glNKpK/img.png?width=927&amp;amp;height=517&amp;amp;face=0_0_927_517&quot;&gt;&lt;a href=&quot;https://inf.run/o4En&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inf.run/o4En&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yZl4M/hyIPB3yO7a/tQWoFUXCel560ZxWfYja51/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/kyudD/hyIPIhjZFd/t4EpclswM2AMHWKkt4KXsK/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/Z2L0Y/hyIPwOHYSk/764XJlQCEmZ6xh55glNKpK/img.png?width=927&amp;amp;height=517&amp;amp;face=0_0_927_517');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Kevin의 알기 쉬운 RxJava 1부 - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://inf.run/aTtz&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Kevin의 알기 쉬운 RxJava 2부 강의] 바로 가기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1609892926755&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Kevin의 알기 쉬운 RxJava 2부 - 인프런&quot; data-og-description=&quot;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://inf.run/aTtz&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-2?utm_source=inflearn&amp;amp;utm_medium=social&amp;amp;utm_campaign=share&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cH5eF2/hyIPBCto4e/Ci8e15VG6mBDtZWoxBvUn1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bifIZb/hyIPGcJzsv/NJcXidDGj0MPXhayqv12uK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bvrmT8/hyIPBoWGIV/g1KkfGqRckEYgkppVS2lOK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://inf.run/aTtz&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inf.run/aTtz&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cH5eF2/hyIPBCto4e/Ci8e15VG6mBDtZWoxBvUn1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bifIZb/hyIPGcJzsv/NJcXidDGj0MPXhayqv12uK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bvrmT8/hyIPBoWGIV/g1KkfGqRckEYgkppVS2lOK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Kevin의 알기 쉬운 RxJava 2부 - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>리액티브 프로그래밍</category>
      <category>event loop</category>
      <category>Reactive Programming</category>
      <category>reactor</category>
      <category>RxJava</category>
      <category>리액터</category>
      <category>리액티브 프로그래밍</category>
      <category>이벤트 루프</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/27</guid>
      <comments>https://itvillage.tistory.com/entry/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EB%9E%80#entry27comment</comments>
      <pubDate>Wed, 6 Jan 2021 09:29:55 +0900</pubDate>
    </item>
    <item>
      <title>리액터와 WebFlux</title>
      <link>https://itvillage.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%84%B0%EC%99%80-WebFlux</link>
      <description>&lt;p&gt;여러분 안녕하세요? &lt;a href=&quot;https://inf.run/iUef&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Kevin의 알기 쉬운 RxJava 강의]&lt;/a&gt;를 진행하고 있는 Kevin이라고 합니다. 지난번 공지에서 잠깐 말씀을 드렸다시피 매주 한두번 정도 시간을 내서 강의에서 제대로 얘기하지 못했던 리액티브 프로그래밍에 대한 뒷 얘기들을 조금씩 해볼까 합니다. 리액티브 프로그래밍을 배우고자 하시는 분들께 조금이나마 더 도움이 되길 바래보겠습니다.&lt;/p&gt;
&lt;h2&gt;&lt;span&gt;리액터(Reactor)란 무엇일까요?&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;리액티브 프로그래밍에 대해서 처음 들으시는 분들은 리액터에 대해서 당연히 모르실거 같은데 아마도 스프링을 학습하면서 들어 보신분도 있지 않을까하는 생각도 들긴하네요. ^^예상하신분도 계시겠지만 리액터는 Java로 구현 된 리액티브 프로그래밍의 한 종류입니다. 더 정확히 얘길 하자면 Reactive Streams를 구현한 구현체라고 볼 수 있는데요.강의를 수강하신 분들은 이미 알고 계시지만 RxJava 역시 Reactive Streams를 구현한 구현체의 하나라고 볼 수 있습니다. 일반적으로 Reactive Streams를 구현한 구현체들은 대부분 Rx~로 시작을 하는데 Reactor는 그것보다 더 확실하게 &quot;나는 리액티브 프로그래밍을 사랑하는 Reactor야&quot; 라고 얘길 하는것 같군요.^^Reactor라는 이름에서 리액티브 프로그래밍의 느낌이 팍팍 오지 않으신가요?ㅎ&lt;/p&gt;
&lt;h2&gt;&lt;span&gt;리액터(Reactor)를 배워야 할까 말아야 할까&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;결론부터 말씀드리자면 리액티브 프로그래밍을 배우기로 하셨다면 리액터는 배우는것이 맞다고 생각합니다.&amp;nbsp;두가지 이유만 얘길 드리자면 먼저 리액터를 통해서 저희는 명령형 프로그래밍의 한계를 넘어설 수 있는 그리고 시대에 맞는 개발 패러다임을 이해하고 실무에 점차적으로 사용을 하게 될 것입니다. 끊임없이 생산되는 데이터를 효과적인 비동기 상태로 처리할 수 있어야 한다는 사실은 필연적이라는 생각이 드니까요. 그렇기 때문에 Reactor는 꼭 학습을 하시는게 좋을 것이라는 생각이 드네요.&lt;/p&gt;
&lt;h2&gt;&lt;span&gt;굳이 왜 리액터일까?&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;근데 왜 굳이 리액터를 배워야 되는가.. 라고 생각을 하실수도 있겠네요.^^; 저희는 지금 RxJava를 배우고 있는데 말이죠.&amp;nbsp;음.. Java 애플리케이션을 개발해보신 분들은 아시겠지만 Java 애플리케이션을 개발하면서 Spring Framework을 빼놓고 얘기하는게 어려워진 것은 오래되었습니다.물론 Spring을 사용하지 않고, 애플리케이션을 만들 수는 있지만 Spring을 일단 한 번 사용하게되면 Spring 없이 개발하겠다고 얘기하는 분들은 많지 않을거 같아요.Spring 얘기를 왜 하느냐하면 Reactor가 바로 Spring5에서 지원하는 Reactive 프로그래밍의 기본이 되기 때문인데요. Spring을 사용하시는 분들, 그리고 앞으로 Spring을 접하실 분들이라면 Spring과 궁합이 딱 맞는 Reactor를 배우지 않을 이유가 있을까 싶습니다. Spring과 Reactor를 사용해서 Spring MVC 기반의 애플리케이션 대신에 리액티브 애플리케이션을 만들어보는것. 재미있을 것 같지 않나요? ^^&lt;/p&gt;
&lt;h2&gt;&lt;span&gt;그럼 RxJava는 배우지 말라는거?&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;그건 당연히 아닙니다. 제가 RxJava 강의를 오픈한 가장 큰 이유 중에 한 가지는 RxJava라는 기술 자체 보다는 리액티브 프로그래밍에 대해서 알려드리고 싶어서이기 때문인데요. 리액티브 프로그래밍은 처음 접하게 되면 이해하기가 쉽지 않기때문에 리액티브 프로그래밍에 대한 기본 개념을 잡을 수 있도록 하는것이 제 강의의 가장 큰 목표라고 볼 수 있습니다. 그런 의미에서 제 강의는 다른 의미로 여러분들한테 충분히 의미있는 강의가 될거라고 믿어보겠습니다.^^제 강의를 듣고 Reactor를 접하게 된 후에 생각했던 것보다 Reactor가 어렵지 않구나라는 얘기를 꼭 들었으면 좋겠네요. ^^&lt;/p&gt;
&lt;h2&gt;&lt;span&gt;Reactor는 이제 알겠는데 WebFlux는 뭐지?&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;저희 강의를 조금이라도 수강을 하신 분들은 알고 계실텐데요. RxJava에서 데이터를 통지(또는 발행, 방출)하는 생산자의 역할을 하는 놈이 Observable, Flowable, Single, Maybe 등인데, Reactor에서 이러한 역할을 하는 놈이 바로 Flux와 Mono라는 놈입니다.RxJava에서는 데이터를 한 건만 통지할 때에는 Single을 사용하고, 데이터를 한 건도 통지하지 않거나 한 건만 통지할 때는 Maybe를 사용 하죠. 그리고 데이터를 한 건 이상 통지할 때에는 Observable 또는 Flowable을 사용을 합니다. 뭔가 좀 복잡하죠? Reactor에서는 이걸 조금 단순화해서 Mono라는 놈은 딱 한 건의 데이터만 처리하고, Flux는 한건도 통지하지않거나 한건 이상 통지하는데 사용을 하도록했습니다.Flux에 대해서 말씀을 드렸으니 WebFlux는 무얼하는 놈인지 대충 감이 오시죠?&lt;br /&gt;'혹시 Web에서 사용하는 Flux?' 맞습니다.^^ Spring MVC가 웹 계층에 특화된 웹프레임워크인 것과 마찬가지로 WebFlux는 웹 계층에서 비동기 처리를 하는데 특화된 리액티브 프로그래밍 기반의 웹 프레임워크라고 할 수 있습니다.&amp;nbsp;그러니까 우리는 WebFlux라는 저놈을 Spring MVC 처럼 효과적으로 잘 사용 하기 위해서 Reactor를 먼저 배우게 되는 것입니다. 구체적으로 어떻게 사용하는지는 아직 자세히 모르더라도 WebFlux가 대충 뭐하는 놈인지는 대충 이해가 되시죠?그러면 Reactor의 Flux를 사용하는 코드 예시를 조금 살펴보고 오늘 이야기를 마무리 하도록할게요.&amp;nbsp;먼저 RxJava에서 사용하는 코드부터 간단하게 살펴보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1609891020535&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Test
public void ObservableFilterTest() {
	Observable&amp;lt;Integer&amp;gt; observable = Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
		.filter(n -&amp;gt; n % 2 == 0);

    observable.test()
    		.assertValues(2, 4, 6, 8, 10)
    		.assertComplete();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;RxJava 2부 강의에서 만나보시겠지만 Observable에서 통지하는 데이터들에 대한 Unit Test 코드입니다. Observable의 just() 함수를 사용해서 1부터 10까지 10개의 데이터를 통지하면 filter() 함수에서 통지된 데이터를 2로 나눈 나머지가 0인 숫자 즉, 짝수인 숫자만 필터링을 해서 최종적으로 소비자 쪽에 통지를 하는 간단한 코드인데요.이렇게 통지된 데이터가 정상적으로 소비자 쪽에 전달이 되는지 검증하기 위해서 Observable의 test() 함수를 이용해서 검증을 합니다.자, 그러면 이번에는 Reactor에서 사용하는 코드를 한번 보실까요?&lt;/p&gt;
&lt;pre id=&quot;code_1609891066123&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Test
public void fluxFilterTest() {
  Flux&amp;lt;Integer&amp;gt; flux = Flux.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
  		.filter(n -&amp;gt; n % 2 == 0);

  StepVerifier.create(flux)
  		.expectNext(2, 4, 6, 8, 10)
 	 	.verifyComplete();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Flux의 just()함수를 사용해서 1부터 10까지 10개의 데이터를 통지하면 filter()함수에서 역시 짝수인 숫자만 필터링을 해서 소비자 쪽에 통지를 합니다. 그러면 StepVerifier 클래스를 이용해서 소비자 쪽에 전달되는 데이터를 검증을 하고 있습니다.RxJava와 거의 똑같다는거 느껴지시죠? 그렇기때문에 여러분들은 저희 강의를 통해서 리액티브 프로그래밍의 기본 개념을 확실하게 자기 것으로 만든 후에 Reactor의 코드를 보면 '아.. 어렵지 않네?'라고 말씀하실수 있을거라고 생각합니다. ^^그럼 저는 다음 시간에 리액티브 프로그래밍의 또 다른 에피소드를 가지고 다시 뵙도록 할게요. 감사합니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://inf.run/gXcN&quot;&gt;Kevin의 알기 쉬운 RxJava 1부 강의 바로가기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1609891269280&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Kevin의 알기 쉬운 RxJava 1부 - 인프런&quot; data-og-description=&quot;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://inf.run/gXcN&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-1?utm_source=inflearn&amp;amp;utm_medium=social&amp;amp;utm_campaign=share&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/63PBl/hyIPIav2D9/xlm2Z8rfjxlihVXY5Ma3pk/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bEXKsE/hyIPGX5GnE/YRKPYk80rnTpkeHC9lZIDk/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bw5mGt/hyIPyZ3CIj/KOQY54B723ajilnhEkQCG0/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://inf.run/gXcN&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inf.run/gXcN&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/63PBl/hyIPIav2D9/xlm2Z8rfjxlihVXY5Ma3pk/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bEXKsE/hyIPGX5GnE/YRKPYk80rnTpkeHC9lZIDk/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bw5mGt/hyIPyZ3CIj/KOQY54B723ajilnhEkQCG0/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Kevin의 알기 쉬운 RxJava 1부 - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://inf.run/3SG2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Kevin의 알기 쉬운 RxJava 2부 강의 바로가기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1609891328726&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Kevin의 알기 쉬운 RxJava 2부 - 인프런&quot; data-og-description=&quot;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://inf.run/3SG2&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-2?utm_source=inflearn&amp;amp;utm_medium=social&amp;amp;utm_campaign=share&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1auGx/hyIPLkM9uP/uaibXesh15UuVtXnpAr0mK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/gFLlO/hyIPyZ3Gb1/Gom9IBwejMrwO3XMD5o5A1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/Upk2w/hyIPIInqlA/z4OkTT0L9KkU2gJoaauHd0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://inf.run/3SG2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inf.run/3SG2&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1auGx/hyIPLkM9uP/uaibXesh15UuVtXnpAr0mK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/gFLlO/hyIPyZ3Gb1/Gom9IBwejMrwO3XMD5o5A1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/Upk2w/hyIPIInqlA/z4OkTT0L9KkU2gJoaauHd0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Kevin의 알기 쉬운 RxJava 2부 - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>리액티브 프로그래밍/Reactor</category>
      <category>Reactive Programming</category>
      <category>reactor</category>
      <category>RxJava</category>
      <category>리액터</category>
      <category>리액티브 프로그래밍</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/26</guid>
      <comments>https://itvillage.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%84%B0%EC%99%80-WebFlux#entry26comment</comments>
      <pubDate>Wed, 6 Jan 2021 09:02:41 +0900</pubDate>
    </item>
    <item>
      <title>현실적인 개발자 로드맵(8) - Angular 그리고 최종 로드맵</title>
      <link>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B58-Angular-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%B5%9C%EC%A2%85-%EB%A1%9C%EB%93%9C%EB%A7%B5</link>
      <description>&lt;h1&gt;현실적인 개발자 로드맵(8) - Angular 그리고 최종 로드맵&lt;/h1&gt;&lt;div&gt;&lt;div&gt;# 포스팅에서 사용된 코드의 전체 코드는 &lt;a href=&quot;https://github.com/ITVillage-Kevin/roadmap-angular&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://github.com/ITVillage-Kevin/roadmap-angular&lt;/a&gt;&amp;nbsp;에서 다운로드 받으실 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 해당 포스팅은 IT Village 유튜브 채널(&lt;a href=&quot;https://www.youtube.com/channel/UCSIvsntWA8aJ3Apoc7kTxig&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://www.youtube.com/channel/UCSIvsntWA8aJ3Apoc7kTxig&lt;/a&gt;)에서도 시청하실 수 있습니다.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;안녕하세요, Kevin입니다. 계속되는 육아로 인해서 포스팅을 짧은 주기로 할수는 없는 상황이 반복이 되고 있네요. ㅡ,.ㅡ 그래도 여전히 짬을 내어 최선을 다해보도록하겠습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;자, 이번 시간은 개발자 로드맵 시리즈의 마지막 시간입니다. 사실 로드맵 시리즈를 진행을 하고자한다면 얼마든지 더 시리즈를 만들어낼 수 있는데요. 8회에서 마무리 하려는 이유는.. 로드맵은 단지 로드맵일 뿐이기때문에 어떤 언어나 기술, 방법론 등에 대해서 구체적인 지식을 전하기가 힘이 들죠. 구체적인 지식을 전하는것이 주목적이 아니기때문에 로드맵인것이기도 하구요. 결론은 전하고 싶은 지식들이 많이 있는데 언제까지 로드맵 시리즈만 이야기하고싶지 않기때문입니다.^^;&lt;/p&gt;&lt;p&gt;그렇기때문에 이번 시간에는 Front-end쪽 이야기를 살짝하고 이때까지 마인드맵으로 그려온 자바 개발자 로드맵을 최종적으로 정리하는 시간을 가져보겠습니다.&lt;/p&gt;&lt;p&gt;자, 그럼 먼저 Front-end쪽 기술을 로드맵에 추가하기 위한 이야기를 먼저 해보도록 하겠습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h3&gt;SPA란?&lt;/h3&gt;&lt;p&gt;Front-end 쪽 개발을 하시는 분이라면 SPA라는 용어를 들어보셨을겁니다. 아직 들어보지 못하신분은 당연히 SPA 방식으로 개발을 해본적이 없으신 개발자분들이실테구요. SPA란 Single Page Application의 약자인데요. 우리말로 하면 단일 페이지 방식의 애플리케이션 정도로 해석을 할 수 있을텐데요. 초보 분들에게는 단일 페이지 방식이라는게 선뜻 와닿지 않을수도 있습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;더 이해하기 쉽게 얘기하자면 한마디로 웹브라우저에서 보여지는 화면들이 바뀔때 웹브라우저 자체가 refresh가 되지 않는 애플리케이션이라고 말할 수 있겠습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;refresh 되지 않는다.. 즉, 화면이 새로고침하지 않는다라는 것인데 개발자 로드맵 시리즈에서 Ajax에 대한 이야기를 한적이 있는데 Ajax 기술이 바로 화면이 새로고침 되지 않는 방식이라고 말씀을 드렸더랬습니다. 그렇기때문에&amp;nbsp;SPA 방식의 애플리케이션에 필수적인 통신 기술이 바로 Ajax라고 할 수 있겠습니다.&lt;/p&gt;&lt;p&gt;JSP, ASP, PHP 같은 웹 애플리케이션은 기본적으로 서버와 통신하면서 화면이 바뀔때 화면이 새로고침되는 방식입니다. 웹브라우저에 보이는 화면 전체에 대한 HTML 코드들을 서버로부터 한번에 내려받기때문인데요.&amp;nbsp;&lt;/p&gt;&lt;p&gt;물론 이전 로드맵 포스팅에서도 말씀드린적 있지만 JSP 같은 서버사이드 언어에서도 jQuery의 Ajax 통신을 사용해서 화면의 일부분만 갱신되도록 할 수 있습니다. 개발자 로드맵 jQuery 편에서 실제로 TODO 애플리케이션 전체 화면에서 할일을 등록하면 화면의 새로고침 없이 할일 목록만 바뀌도록 했으니까요.&lt;/p&gt;&lt;p&gt;하지만 jQuery만 사용을 해서 SPA를 구현한다는것은 사실 무리가 있습니다. SPA를 구현하는것에 무리가 있다는것이지 jQuery에 문제점이 아주 많아서 사용하지 말아야한다는것은 아닙니다. 현재까지 Front-end 기술의 트렌드는 SPA로 바뀌고 있는 추세이긴하지만 여전히 심플하고 작은 애플리케이션을 만드는데는 jQuery가 많이 사용되고 있고 성능상으로도 큰 무리가 없는게 사실이니까요.&lt;/p&gt;&lt;p&gt;그렇긴하지만 기술 트렌드를 따라가다보면 필연적으로 SPA라는 용어를 만나게될것이고 꼭 익혀야되는 개발 방식이기도합니다. 기술 트렌드를 쫓아서 끊임없이 시도하고 시행착오를 거치는 기업은&amp;nbsp;그렇지 않은 기업보다 개발자가 일하기 더 좋은 기업일 가능성이 높다는 사실 꼭 염두에 두시면 좋으실것 같네요.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h3&gt;SPA 구현을 위한 프레임워크 또는 라이브러리 선택&lt;/h3&gt;&lt;p&gt;자, 그럼 SPA를 구현하기 위한 자바스크립트 프레임워크나 라이브러리를 선택하여 TODO 애플리케이션을 SPA로 바꿔보도록 하겠는데요. 현재 가장 많이 사용하고 있는 기술은 Angular, Vue.js, React 라고 볼 수 있겠습니다. 이 중에서 Angular와 Vue.js는 프레임워크이고, React는 라이브러리입니다. 세가지 기술 모두다 각각의 장점과 단점을 가지고 있는데요. 이 세가지 기술에 대한 비교 설명이 다음 링크에 잘 나와있습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Angular vs Vue.js vs React 비교 :&amp;nbsp;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/comparison.html#Angular-Formerly-known-as-Angular-2&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://kr.vuejs.org/v2/guide/comparison.html#Angular-Formerly-known-as-Angular-2&lt;/a&gt;&lt;/p&gt;&lt;p&gt;그중에서 저는 Angular를 사용하여 TODO 애플리케이션을 구현하였습니다. Angular를 선택한 이유는 많은 개발자들이 Angular는 Vue.js나 React보다 유연성이 떨어진다고들 얘기들하시는데요. 그 떨어지는 유연성이 오히려 생산성 측면에서는 도움이 될 수도 있다는 사실. ^^ (사실 Angular 말고 Vue.js와 React는 사용을 해본적이 없다는것도 큰 이유중에 하나입니다. ^^;) 여기서 말하는 유연성은 애플리케이션의 구조와도 관계가 있는데요. Angular의 경우 애플리케이션의 구조가 자바스크립트 class를 통해서 구조화가 잘되어 있습니다. 그렇기때문에 생산성이나 유지보수 측면에서 더 나은 결과를 가져다 준다는게 개인적인 생각이기도 합니다.&lt;/p&gt;&lt;p&gt;그리고 Angular의 단점 중에 하나가 가파른 학습곡선이다라고 말씀을 하시기도 하는데요. 학습에 대한 시간을 일정 부분 투자를 해야하는건 사실이긴하지만 이건 개개인마다 약간씩 틀리지 않을까 싶네요. 설령 학습 곡선이 가파르다고 하더라도 학습 자체가 상당히 재미있다는게 제 개인적인 생각이며 또한 Google에서 주도하는 오픈 소스이기때문에 학습을 위한 컨텐츠도 상당히 많이 제공되고 있는편입니다.&lt;/p&gt;&lt;p&gt;혹시 시간이 되시는 분들은 Vue.js나 React까지 공부하셔서 이 세가지 기술을 비교해보는것 역시 개인적인 성장에 도움이 되지 않을까 싶네요.&lt;/p&gt;&lt;p&gt;자, 그럼 Angular를 사용하여 이전 시간에 jQuery로 만든 TODO 애플리케이션을 SPA 방식으로 바꾸어 볼까요?&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h3&gt;jQuery vs Angular 구현 방식 비교&lt;/h3&gt;&lt;p&gt;jQuery와 Angular는 구현방식이 근본적으로 다릅니다. 어떻게 다른지 소스코드를 통해서 간단히 살펴보도록 하겠습니다.&lt;/p&gt;&lt;p&gt;먼저 아래는 jQuery로 구현한 TODO 애플리케이션의 소스 코드인데 Angular 구현 코드와 어떻게 다른지 보시겠습니다.&lt;/p&gt;&lt;p&gt;==== front_jquery.js ====&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 900px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99AE48455C641ACC1A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99AE48455C641ACC1A&quot; width=&quot;900&quot; height=&quot;1013&quot; filename=&quot;K-002.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;jQuery 구현 코드에서는 하나의 화면에 할일 등록 폼과 할일 목록이 같이 표시되기때문에 할일을 등록하는 기능과 할일을 서버로부터 조회해서 목록으로 표시하는 기능을 하나의 자바스크립트 파일에서 함수화 하여 구현하였는데 할일을 등록하고, 조회하기 위해서 서버와 통신을 하기도 하고&amp;nbsp;가져온 할일 데이터를 화면에 렌더링하기도 합니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;이 말은 달리 표현하면 하나의 자바스크립트 파일 안에서 너무 많은 일을 한다라고 말씀 드릴 수 있겠습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;또한 서버로부터 가져온 할 일 데이터를 목록으로 화면에 표시하기 위해서 HTML DOM을 조작하기때문에 jQuery 코드에 HTML 코드가 뒤섞여 있는것을 보실 수 있습니다.&amp;nbsp;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;자, 그럼 Angular에서는 할일을 등록하고 등록한 할일을 조회하여 표시하는 기능을 어떻게 구현했을까요? 아래는 Angular로 구현한 TODO 애플리케이션의 디렉토리 구조인데요.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 471px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99ABD73D5C641E9C09&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99ABD73D5C641E9C09&quot; width=&quot;471&quot; height=&quot;677&quot; filename=&quot;K-003.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Angular 애플리케이션에서 하나의 화면은 기본적으로 여러개의 컴포넌트의 조합으로 구성이 되어있습니다. 이말의 의미는 구현해야할 코드가 기능 단위로 각각 분리가 되어있다고도 말할 수 있습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;TODO 애플리케이션의 디렉토리 구조에서 todo 디렉토리 하위에&amp;nbsp; todo-list 디렉토리와 todo-register 디렉토리로 분리되어 있는 것을 보실 수가 있습니다. 할일을 등록하는 기능과 할일을 조회하는 기능을 각각 컴포넌트화하여 분리한것이죠.&amp;nbsp;&lt;/p&gt;&lt;p&gt;그렇기때문에 Angular에서는 하나의 컴포넌트에서는 대부분 하나의 기능에 대해서만 구현하므로&amp;nbsp;소스코드도 매우 간결하게구성할 수 있고, 컴포넌트라는 정해진 구조내에서 코드를 구현하기 때문에 유지 보수측면에서도 매우 뛰어나다고 볼수있습니다.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;자, 그럼 Angular로 구현된 TODO 애플리케이션의 컴포넌트의 소스코드는 어떻게 구성되어있는지 간단하게 살펴보도록 하겠습니다.&lt;/p&gt;&lt;p&gt;==== todo.component.html ====&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 900px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9913533C5C655F2507&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9913533C5C655F2507&quot; width=&quot;900&quot; height=&quot;71&quot; filename=&quot;K-004.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;TODO 컴포넌트는 위와 같이 크게 할일을 등록하는 컴포넌트와 할일을 조회하여 목록으로 표시하는 컴포넌트로 나누어져있습니다. 이 각각의 하위 컴포넌트들이 HTML로 렌더링되어 화면에 보여지게 되는것입니다. 이렇게 해놓으니까 코드가 무척 단순해 보이지 않나요? 실제로 복잡한 코드들 즉, 해당 컴포넌트들의 기능들에 대한 코드는 각각의 컴포넌트안에서 독립적으로 구현이 됩니다. 다시 말하면 컴포넌트들간의 관계가 느슨해진다라고도 할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;그럼 하위 컴포넌트들은 어떻게 구현되어있는지 한번 볼까요?&lt;/p&gt;&lt;p&gt;==== todo-register.component.ts ====&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 900px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99EA6C3F5C6560A801&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99EA6C3F5C6560A801&quot; width=&quot;900&quot; height=&quot;609&quot; filename=&quot;K-001.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;할일을 등록하는 컴포넌트인데요. Angular의 컴포넌트들은 위와 같이 @Component 애노테이션을 가지는 클래스로 구성이 되어 있으며, 특정 이벤트가 발생했을때 클래스내의 함수(또는 메서드)를 호출 하도록 구성이 되어있습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;위의 TodoRegisterComponent 클래스에서는 할일을 등록하는 버튼 이벤트가 발생했을 때 registerTodo() 함수가 호출이 되어 서버측에 할일 데이터를 전송하게됩니다.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;TodoRegisterComponent는 할일 등록에 대한 부분에 대해서만 처리하는데 해당 클래스내에서 서버쪽에 할일 데이터를 전송하는 로직은 포함하고 있지 않으며 서버측으로의 전송 자체는 자신의 일이 아니기때문에 TodoService 에 서버쪽으로의 전송을 위임을 합니다.&lt;/p&gt;&lt;p&gt;Angular를 사용하면 각각의 기능들을 Component 단위로 쪼개서 각자의 할일만 처리하도록 구현을 할 수 있습니다. 이와 더불어 컴포넌트간의 관계는 느슨하기때문에 컴포넌트들은 서로가 무슨 일을 하는지 알지 못하며 알필요가 없습니다.&lt;/p&gt;&lt;p&gt;할일을 등록하고난 후, 등록된 할일 데이터를 할일 목록에 갱신을 하게 되는데요. 이 경우 등록된 할일 데이터를 할일 목록을 표시하는 컴포넌트에 넘겨주는데 그 부분이 바로 코드 24번 라인에 나와 있습니다.&lt;/p&gt;&lt;p&gt;TodoRegisterComponent는 TodoListComponent라는 존재를 알지 못한채 그저 등록된 할일 데이터를 이벤트에 담아TodoService에게 전달하기만 하고, 누가 그 데이터를 전달받아서 사용하는지에 대해서는 관심이 없습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;TodoListComponent는 등록된 할일 데이터를 담고 있는 이벤트가 발생하는지 귀 기울이고 있다가&amp;nbsp;이벤트가&amp;nbsp;발생하면 할일 데이터를 전달받아서 할일 목록을 갱신하게 됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;어떠신가요? 제가 Angular 문법 자체를 강의하는게 아니라서 어렵게 느껴지실수 있겠지만 Angular가 각각의 컴포넌트들을 클래스화 하여 객체지향적인 코드를 쉽게 구현할 수 있도록 해주고 있다는 사실을 조금만 더 학습해보시면 충분히 느끼실 수 있을것입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;자, 다음으로 앞서 말씀드린것처럼 TodoRegisterComponent에서 등록된 할일 데이터를 이벤트에 담아 내보냈을 때, 이를 받아서 할일 목록을 갱신하는 기능을 하는 TodoListComponent의 코드를 간단히 살펴보도록 하겠습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;==== todo-list.component.ts&amp;nbsp;====&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 900px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99BF9D355C6567AC42&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99BF9D355C6567AC42&quot; width=&quot;900&quot; height=&quot;584&quot; filename=&quot;K-002.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;TodoListComponent 의 코드는 복잡하지 않습니다. 단순히 할일 데이터를 전달 받아서 화면에 표시하는 기능만 담당하기때문에 코드가 복잡할래야 복잡할수가 없는것이죠.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Angular의 컴포넌트들은 대부분 한가지 기능에 대해서만 독립적으로 구현을 하기때문에 코드 자체가 단순해질 수 밖에 없으며, 만약 하나의 컴포넌트내에서 코드가 복잡해진다면 무언가 잘못 구현을 하고 있으며 이럴 경우 컴포넌트를 세분화 하는 등의 리팩토링이 필요할 것입니다.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;19 - 23 라인의 ngOnInit() 함수에서는 Angular 애플리케이션의 구성이 완료되었을 때 초기화 작업을 진행할 수 있는데 여기서는 서버측으로부터 할일 데이터를 가져와서 할일 목록을 표시하는 작업을 처리합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;11 - 16라인의 생성자에서는 TodoRegisterComponent 에서 할일을 등록하고 이벤트를 내보내면 이 이벤트가 발생했는지에 대해서 귀 기울이고 있다가 이벤트가 발생하면 등록된 할일 데이터를 받아서 할일 목록을 갱신하는 작업을 처리합니다.&lt;/p&gt;&lt;p&gt;제가 이벤트 발생 여부에 대해서 귀 기울인다고 쉽게 표현을 했지만 더 정확하게는 해당 이벤트를 구독한다라고 할 수 있습니다. 이벤트 발행/구독에 대한 부분은 &lt;a href=&quot;http://reactivex.io/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Reactive프로그래밍&lt;/a&gt;과 관련이 있으므로 더 자세한 내용은 관련 자료를 확인하시기 바랍니다.&lt;/p&gt;&lt;p&gt;그런데 신기한건 jQuery 처럼 DOM을 조작하여 등록된 할일 데이터를 화면에 추가하는것이 아닌 단순히 todoData라는 배열에 등록된 할일 데이터를 추가하는것 뿐인데 화면에서는 할일 목록이 자동으로 갱신이 됩니다. 이는 Angular의 컴포넌트 내에서 모델 데이터와 Template간에 데이터가 동기화 되는것인데 이를 바인딩이라고 합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Angular로 구현한&amp;nbsp;Todo 애플리케이션에 대해서 대략적으로나마 설명을 드렸는데 어떠신가요? 물론 Angular에 대한 학습 곡선이 조금 있는 편이기때문에 처음에는 어렵게 느껴지실수도 있습니다. 하지만 그만큼 학습 자체가 상당히 재미있기때문에 코딩하는 재미도 그만큼 크다는것이 제 개인적인 생각입니다. ^^;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;자.. 이제 웹 개발자(Java 개발자) 로드맵 시리즈를 슬슬 마무리 할 시간인데요. 이때까지 로드맵 시리즈를 진행하면서 그려왔던 로드맵을 마지막으로 한번 정리해보는 시간을 가지고 이번 포스팅을 마무리 하도록 하겠습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h3&gt;최종 로드맵&lt;/h3&gt;&lt;p&gt;웹 개발자(Java 개발자) 로드맵 시리즈를 시작한지 5개월만에 이제야 마무리를 할 시간이 다가왔네요. ^^; 5개월이면 매일 하나씩 포스팅을 할 수 있다고 쳐도 100개가 넘는 포스팅을 할 수 있었을텐데 겨우 7개밖에 할 수 없었던 이유는 직장 + 유부남 + 아기 탄생 + 육아때문이라고 볼 수 있겠습니다. ^^ 아무튼 늦게나마 최종 로드맵을 올릴 수 있어서 개인적으로는 보람이 있습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;첫번째 시간에서도 말씀을 드린바 있지만 웹 개발자(Java 개발자)가 되기 위해서, 그리고 되고난 후에 배워야할 것들은 상당히 많습니다. 하지만 대한민국에서 웹 개발자(Java 개발자)로써 현실적으로 필요한 것들만 제가 경험한 기술들을 바탕으로 말씀을 드리고 싶었더랬습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;여기서 현실적이라는 말은 이 글을 읽으시는 분들이 한국에서 생활하기 괜찮은 직장(연봉, 복지, 기업문화)에서 좋은 개발자로써 일을할 수 있었으면 하는 제 바램이 담겨있다고 보시면 되겠는데요. 개발자 입문자들에게 부디 조금이라도 도움이 될 수 있길 바래보면서 그럼 최종 로드맵을 소개하도록 하겠습니다.&lt;/p&gt;&lt;p&gt;짜잔!&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 621px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99F31C415C5BAA5139&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99F31C415C5BAA5139&quot; width=&quot;621&quot; height=&quot;1017&quot; filename=&quot;K-001.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;[Angular + 추가 기술 포함 최종 로드맵]&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;색깔이 채워져 있지 않은 놈들은 이미 이전시간까지 소개를 해드렸던 기술들이며, 노란색으로 채워져 있는 기술은 오늘 말씀드린 Angular가 추가 되었습니다. Angular 이외에 Vue.js나 React는 Angular 대신에 학습하면 되는 기술들이기에 같이 적어놓았는데요. 시간이 되신다면 Angular나 Vue.js 둘중에 하나를 학습하시고, React를 추가적으로 학습하시면 좋을듯 하네요. Angular와 Vue.js는 상대적으로 학습할 내용이 비슷한 부분이 있다고 생각되지만 React는 개발 방식이 많이 다른걸로 알고 있습니다.(저도 안써봐서 어떻게 다른지에 대해서는 말씀을 못드리겠네요. ^^;)&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;자, 그리고 녹색으로 채워져 있는 부분은 개발자 로드맵에서 추가적으로 설명을 하고 싶지만 시간 관계상 모두 설명하기 힘든 녀석들을 모아봤습니다. 하지만 언젠가 꼭 학습을 하게된다면 개발자로써 한단계 더 성장할 수 있는 기술들만 추려보았으므로 가급적이면 시간과 계획을 세워서 꼭 학습해보길 바래봅니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;이로써 5개월 동안의 웹 개발자(Java 개발자) 로드맵 시리즈는 이렇게 마무리를 짓도록하겠습니다. 다음 시간부터는 조금 구체적인 특정 내용들로 육아 후, 짬나는대로 찾아뵙도록 할게요.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;그럼 전 이만.. 새해 복 많이 받으세요~&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;★ &lt;a href=&quot;https://inf.run/xezD&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;[Kevin의 알기 쉬운 Java 로드맵 이야기] 인프런 강의 바로가기&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>현실적인 개발자 로드맵</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/25</guid>
      <comments>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B58-Angular-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%B5%9C%EC%A2%85-%EB%A1%9C%EB%93%9C%EB%A7%B5#entry25comment</comments>
      <pubDate>Tue, 29 Jan 2019 21:31:36 +0900</pubDate>
    </item>
    <item>
      <title>현실적인 개발자 로드맵(7) - Spring Boot으로 Spring을 더욱 편리하게..</title>
      <link>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B57-Spring-Boot%EC%9C%BC%EB%A1%9C-Spring%EC%9D%84-%EB%8D%94%EC%9A%B1-%ED%8E%B8%EB%A6%AC%ED%95%98%EA%B2%8C</link>
      <description>&lt;h1&gt;현실적인 개발자 로드맵(7) - Spring Boot으로 Spring을 더욱 편리하게..&lt;/h1&gt;
&lt;div&gt;
&lt;div&gt;# 포스팅에서 사용된 코드의 전체 코드는 &lt;b&gt;&lt;a class=&quot;tx-link&quot; href=&quot;https://github.com/ITVillage-Kevin/roadmap-springboot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/ITVillage-Kevin/roadmap-springboot&lt;/a&gt;&lt;/b&gt; 에서 다운로드 받으실 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;# 해당 포스팅은 IT Village 유튜브 채널(&lt;b&gt;&lt;a class=&quot;tx-link&quot; href=&quot;https://www.youtube.com/channel/UCSIvsntWA8aJ3Apoc7kTxig&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.youtube.com/channel/UCSIvsntWA8aJ3Apoc7kTxig&lt;/a&gt;&lt;/b&gt;)에서도 시청하실 수 있습니다.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;안녕하세요, 오랜만에 찾아뵙게 되었네요. Kevin이라고 합니다. ^^&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;거의 석 달만에 올리는 글이 되겠습니다. 저희 아기가 탄생하시는 바램에 석 달 동안 참 바빴고 여러가지 많은 일이 있었더랬습니다. 새 생명이 탄생하는 경이로움을 경험했다고나 할까요. 아무튼 직장 다니랴 아기 돌보느라 이래 저래 바빠서 포스팅 한다는것은 꿈에도 생각하지 못했네요. 다만 짬짬이 유튜브 강의 영상만 녹화 하면서 간간히 소식을 전해드렸는데요. 포스팅 한 내용만큼 유튜브 영상 업로드가 끝나서 다시 포스팅을 진행하게 되었네요. Term이 조금 길긴했지만 그래도 이 다음 이야기를 궁금해 하실 초보 개발자분들이 있을거라고 생각하기에 다시 이야기를 진행해보도록 하겠습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;지난 시간에는 Spring 프레임워크에 대한 얘기를 나누어보았는데요. 앞서 보셨듯이 Spring은 그야말로 자바 개발자들에게는 없어서는 안될 그런 대세가 되어버렸습니다. 하지만 그 대단한 Spring 프레임워크에도 개발자들이 아주 난색을 표하는 단점이 하나 있었는데요. 그것은 바로 Spring 프레임워크를 사용하기 위한 설정의 어려움이었습니다. 프레임워크라는 편리한 도구를 사용해서 비즈니스 로직의 개발에 집중을 해야될 판에 정작 Spring Framework를 사용하기 위한 설정에 개발자들이 꽤나 상당한 시간을 투자해야하는 아이러니한 상황이 발생하게 되었던 것입니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;물론 Spring이 버전업되면서 이런 문제들이 개선이 되긴했지만 여전히 설정의 복잡함은 남아 있었는데요.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;이러한 문제를 해결하기 위해서 등장한것이 바로 &lt;b&gt;&lt;a class=&quot;tx-link&quot; href=&quot;https://spring.io/projects/spring-boot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Spring Boot&lt;/a&gt;&lt;/b&gt; 이라는 놈입니다. 이 Spring Boot이 등장함으로 인해서 개발자들은 더이상 Spring의 설정을 위해 머리 싸매면서 고민하던 시간을 접고 비즈니스 로직을 구현하는데 집중할 수 있게 해주었습니다.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;그럼, Spring Boot 이라는 놈은 어떤 놈인지 지금부터 한번 살펴 볼까요?&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;우리가 Spring을 사용하여 웹 애플리케이션을 만들기위해서는 어떤 것들이 필요할까요?&amp;nbsp;&lt;/div&gt;
&lt;div&gt;우선 메이븐이나 그레이들을 사용하는 프로젝트 구조를 사용하기 위해서 Spring에 필요한 의존 관계 라이브러리들을 지정해야합니다.&lt;/div&gt;
&lt;div&gt;그리고 DispatcherServlet 설정을 위한 web.xml 설정도 해야하구요.&lt;/div&gt;
&lt;div&gt;또한 Spring MVC를 사용하기 위해서는 핸들러 매핑이나 핸들러 어댑터, 뷰 리졸버 등의 설정을 최소한 해주어야합니다.&lt;/div&gt;
&lt;div&gt;마지막으로 웹 애플리케이션을 배포하기 위한 Tomcat 등의 웹 애플리케이션 서버가 필요하겠죠.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;이러한 준비 작업들이 끝나고나면 자 드디어 컨트롤러 클래스를 작성하면서 개발을 시작하면 되겠습니다.&lt;/div&gt;
&lt;div&gt;자, 어떻게 된게 개발을 시작하기도 전에 해야할 일이 너무 많다는 생각이 들지 않으신가요? ^^; 개발자가 개발에 집중해야함에도 불구하고 개발을 하기 위한 사전 작업을 너무나 많이 하게되는 셈이 되는데요.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Spring Boot의 자동 구성과 스타터 의존성이라는 방법으로 이러한 문제들을 해결 해주게되었습니다. 짝!짝!짝! 자 그럼 Spring Boot의 자동 구성과 스타터 의존성이 무엇인지 잠깐 알아볼까요?&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자동 구성&lt;/h3&gt;
&lt;div&gt;우선 Spring Boot은 Tomcat을 내장하고 있기때문에 별도의 웹 애플리케이션 서버를 설치할 필요가 없습니다. 내장된 Tomcat에 빠르게 웹 애플리케이션을 배포할 수 있습니다.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;그리고 자동 구성 덕택에 DispatcherServlet이나 핸들러 매핑, 핸들러 어댑터 등의 Spring MVC 설정도 따로 필요가 없습니다. Spring Boot이 애플리케이션의 클래스 패스에서 관련 클래스들을 발견하고는 알아서 척척 구성을 해주는 것이죠. 또한 &lt;b&gt;&lt;a class=&quot;tx-link&quot; href=&quot;http://www.h2database.com/html/main.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;H2&lt;/a&gt;&lt;/b&gt; 같은 특정 데이터베이스 라이브러리를 발견한다면 해당 라이브러리를 사용하기위한 데이터베이스 설정을 자동으로 구성합니다. &lt;b&gt;&lt;a class=&quot;tx-link&quot; href=&quot;https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94_%ED%8D%BC%EC%8B%9C%EC%8A%A4%ED%84%B4%EC%8A%A4_API&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;JPA&lt;/a&gt;&lt;/b&gt;를 사용하고 싶다면? 네, 맞습니다. Spring Boot이 JPA를 사용하기 위한 설정을 역시 자동으로 구성해줍니다. 웹 애플리케이션의 보안 설정을 하고 싶다면? 네, 역시 Spring Boot이 &lt;b&gt;&lt;a class=&quot;tx-link&quot; href=&quot;https://spring.io/projects/spring-security&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Spring Security&lt;/a&gt;&lt;/b&gt;를 사용하기 위한 설정을 자동으로 구성해줍니다.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Spring Boot을 통해 Spring이 더 똑똑해졌다는 생각이 드시죠?&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스타터 의존성&lt;/h3&gt;
&lt;div&gt;하나의 애플리케이션을 만들기 위해서 의존 관계에 있는 라이브러리들을 정확하게 추가하는일은 쉽지가 않습니다. JSON 형식의 응답 데이터를 반환하는 Spring MVC 애플리케이션을 만들어서 내장된 Tomcat으로 애플리케이션을 실행해야 한다면&amp;nbsp;대략 다음과 같은 라이브러리들을 직접 메이븐이나 그레이들 설정에 추가해주어야 합니다.&lt;/div&gt;
&lt;div&gt;spring-core, spring-web, spring-webmvc, jackson-databind, tomcat-embed-core, tomcat-embed-el&lt;/div&gt;
&lt;div&gt;하지만 스타터 의존성을 이용하면 개발자가 이런 의존관계에 있는 라이브러리들을 일일이 추가할 필요가 없습니다. 단순히 spring-boot-starter-web 스타터를 추가해주면 알아서 의존 위에 열거한 라이브러리들을 자동으로 추가해줍니다. 개발자들이 어떤 스타터를 추가해야 되는지도 직관적으로 알 수 있습니다.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;웹 애플리케이션을 개발하기 위해서는 spring-boot-starter-web 스타터를 추가하면 되고, JPA 영속성을 사용하려면 spring-boot-starter-jpa를, 애플리케이션 보안이 필요하다면 spring-boot-starter-security를 추가하면 됩니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;말로 설명하면 Spring Boot의 장점이 직접적으로 와닿지 않을수도 있으니 코드를 한번 살펴보도록 하겠습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;지난 시간의 Spring MVC 프로젝트의 구조에서는 설정해야되는 부분이 엄청 많았습니다. 아래 이미지를 보시죠.&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99E771495C2A39CE1A?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99E771495C2A39CE1A?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99E771495C2A39CE1A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99E771495C2A39CE1A&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;900&quot; height=&quot;499&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;text-align: center; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;[전통적인 Spring MVC 설정]&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;스프링 MVC 프로젝트에 필요한 라이브러리들을 설정해놓은 pom.xml, 웹 애플리케이션 개발을 위한 dispatcher-servlet.xml과 web.xml 설정을 개발자가 직접 해주어야 했습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;물론 Spring Boot에서도 설정을 하긴합니다. 아래와 같이 말이죠.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9998BC3E5C2A3B7B32?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9998BC3E5C2A3B7B32?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9998BC3E5C2A3B7B32&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9998BC3E5C2A3B7B32&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;900&quot; height=&quot;478&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;text-align: center; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;[Spring Boot의 설정]&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;build.gradle 파일에서 Spring MVC 웹 애플리케이션 설정을 위해서 dependencies 항목에 필요한 라이브러리들을 설정하는데 단 5줄밖에 되지 않습니다. web.xml이나 dispatcher-servlet.xml 같은 설정은 아예 사라져버린것이죠.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;이게 끝입니다. 더 이상 설정할게 없으며, 이 5줄만으로 기존의 Spring MVC 웹 애플리케이션에 JPA와 H2 데이터베이스 기능까지 포함한 설정이 자동으로 구성이 되는것입니다. 엄청나지 않나요? 저만 그렇게 느끼는건 아닌지.ㅎ ^^;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 추가적으로 기존의 Spring MVC 기반의 Todo 애플리케이션을 Spring Boot 기반으로 변경하면서 구현 코드는 어떻게 변경이 되었는지 잠깐 살펴보도록 하겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;이번 Spring Boot 기반 Todo 애플리케이션에는 할일 목록을 DB에 저장하기 위한 기술로 JPA(Java Persistence API)를 사용하였습니다. JPA는 데이터베이스의 테이블과 자바의 Entity 객체를 매핑하여 데이터를 손쉽게 DB에 저장하는 ORM(Object Relational Mapping) 기술중에 하나로써 Spring을 사용하여 JPA를 손쉽게 사용할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;아래 코드를 잠깐 보시겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;==== Todo.java ====&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;419&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9915A83D5C2A42770E?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9915A83D5C2A42770E?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9915A83D5C2A42770E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9915A83D5C2A42770E&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;629&quot; height=&quot;419&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;419&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;DB의 todo 테이블과 매핑되는 Todo 엔티티 클래스인데요. @Entity 애노테이션을 붙여서 엔티티 클래스라는것을 명시했고, @Id 애노테이션을 붙여서 기본키를 설정했습니다. 그리고 10번 라인에 @Data 애노테이션을 추가해서 Todo 클래스의 멤버 변수들에 대한 getter/setter 메서드를 간편하게 추가하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;참고로 Lombok이라는 라이브러리를 사용하여 클래스의 생성자, toString(), getter/setter 등의 다양한 설정을 간편하게 할 수 있기때문에 Lombok은 개발자들이 많이 사용하는 라이브러리중의 하나입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;==== TodoRepository.java ====&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/990FB5415C2A444E0E?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/990FB5415C2A444E0E?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/990FB5415C2A444E0E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F990FB5415C2A444E0E&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;777&quot; height=&quot;175&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;이 클래스는 Todo 클래스의 객체를 DB에 저장해주는 기능을 하는 Repository 클래스입니다. JpaRepository를 상속받고 있는 인터페이스인데요. 구현된게 아무것도 없음에도 불구하고 할일을 저장해주고 조회해주는 CRUD 기능을 모두 다 할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;마지막으로 할일에 대한 요청을 받아들이는 TodoController 클래스를 한번 보실까요?&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;==== TodoController.java ====&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K-009.png&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eog3ja/btryMZPa24o/Pke5OKvPyo971Wd4PRaXoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eog3ja/btryMZPa24o/Pke5OKvPyo971Wd4PRaXoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eog3ja/btryMZPa24o/Pke5OKvPyo971Wd4PRaXoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feog3ja%2FbtryMZPa24o%2FPke5OKvPyo971Wd4PRaXoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;827&quot; height=&quot;700&quot; data-filename=&quot;K-009.png&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서는 @RequestParam 애노테이션을 사용하여 할일 데이터를 register()의 파라미터로 받았는데 이번에는 todoName과 todoDate 파라미터의 값이 Todo 객체에 바로 매핑이 되도록 한 후, TodoRepository 클래스의 save()를 사용하여 DB에 저장하는 로직으로 변경되었습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;28번 라인에서는 TodoRepository 클래스의 findAll()을 사용하여 저장된 모든 할일 데이터를 조회하도록 변경되었습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;실제 DB에 저장을 하는 로직으로 변경되었지만 여전히 소스코드가 필요한 비즈니스 로직만 포함하는 깔끔함을 유지하는것을 보실 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;자, 간단하게나마 Spring Boot의 편리함에 대해서 살펴보았는데요. Spring Boot이 개발자가 비즈니스 로직 개발에 집중하도록 얼마나 많은 노력을 하였는지 여전히 현실적으로 와닿지 않는 분들께서는 저처럼 Spring Boot을 사용하지 않고 Spring MVC 애플리케이션 구성을 해보신다음 Spring Boot으로 개선해보는 작업을 한번쯤 해보셨으면 하는 바램을 가져보겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;자, 그럼 이번 시간에 새롭게 추출해야 될 기술들에는 무엇이 있는지 살펴보겠습니다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;1013&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9917DD4D5C2B55221A?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9917DD4D5C2B55221A?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9917DD4D5C2B55221A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9917DD4D5C2B55221A&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;733&quot; height=&quot;1013&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;1013&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;text-align: center; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;[Spring Boot이 추가된 로드맵]&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;개발자 로드맵에 새롭게 추가된 기술입니다. ^^ Spring Boot은 당연히 추가가 되었구요. 그 외에 JPA와 H2를 추가하였는데요.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;JPA는 데이터베이스에 데이터를 추가하는 대표적인 ORM 기술로써 괜찮은 회사(여기서 괜찮은 회사라함은 기술 트렌드를 적극적으로 받아들이는 회사로 칭하겠습니다.^^;)에서 일을 하고 싶다면 꼭 알아두셨으면 하는 기술중에 하나입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;H2는 그 자체로는 단순히 데이터베이스의 한 종류입니다. 일반적인 데이터베이스와의 차이점이라면 In memory DB 즉, 애플리케이션 실행을 종료하면 저장된 데이터는 사라지는 내장 DB라는것입니다. 제가 말씀드리고 싶은것은 H2 자체를 배우라기보다는 In memory DB를 활용할 줄 알아야한다는 것입니다. 나중에 Spring으로 개발을 진행하다보면 단위 테스트 코드를 짜야 되는 경우가 많은데 이 경우에 H2 같은 In memory DB를 활용하면 편리하게 단위 테스트 코드를 DB와 연계할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;자, 이번 시간에는 Java 개발자(웹 개발자) 로드맵에 Spring Boot을 추가를 해보았는데요. 다음 시간에는 '현실적인 개발자 로드맵 시리즈'의 마지막 편을 포스팅 해보도록 하겠습니다. 시리즈로 계속해서 포스팅을 할 수 있지만 다른 기술들에 대해서 구체적인 얘기들을 많이 해야되겠기에 어쨌든 다음 시간에 '현실적인 개발자 로드맵 시리즈'를 마무리 하겠습니다. ^^;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;오늘이 2019년의 첫날인데 다들 새해에 목표하신바를 잘 이룰 수 있도록 좋은 출발하시기를 바래보겠습니다. 저도 올 한해 열심히 열심히 살아보도록 하겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 다음 시간에 다시 뵐게요~ 새해 복 많이 받으세요!(Happy New Year!)&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;★ &lt;a class=&quot;tx-link&quot; href=&quot;https://inf.run/xezD&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Kevin의 알기 쉬운 Java 로드맵 이야기] 인프런 강의 바로 가기&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>현실적인 개발자 로드맵</category>
      <category>spring boot</category>
      <category>spring mvc</category>
      <category>Spring 프레임워크</category>
      <category>개발자 로드맵</category>
      <category>스프링 프레임워크</category>
      <category>웹 개발자 로드맵</category>
      <category>자바 개발자 로드맵</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/24</guid>
      <comments>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B57-Spring-Boot%EC%9C%BC%EB%A1%9C-Spring%EC%9D%84-%EB%8D%94%EC%9A%B1-%ED%8E%B8%EB%A6%AC%ED%95%98%EA%B2%8C#entry24comment</comments>
      <pubDate>Mon, 31 Dec 2018 23:11:05 +0900</pubDate>
    </item>
    <item>
      <title>현실적인 개발자 로드맵(6) - Spring의 탄생</title>
      <link>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B56-Spring%EC%9D%98-%ED%83%84%EC%83%9D</link>
      <description>&lt;h1&gt;현실적인 개발자 로드맵(6) - Spring의 탄생&lt;/h1&gt;&lt;div&gt;# 포스팅에서 사용된 코드의 전체 코드는 &lt;b&gt;&lt;a href=&quot;https://github.com/ITVillage-Kevin/roadmap-spring-mvc&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://github.com/ITVillage-Kevin/roadmap-spring-mvc&lt;/a&gt;&lt;/b&gt;&amp;nbsp;에서 다운로드 받으실 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 해당 포스팅은 IT Village 유튜브 채널(&lt;b&gt;https://www.youtube.com/channel/UCSIvsntWA8aJ3Apoc7kTxig&lt;/b&gt;)에서도 시청하실 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;안녕하세요, Kevin입니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;지난 시간까지 Ajax와 jQuery가 등장함으로 인해서 웹 애플리케이션 개발이 Front-end와 Back-end로 나누어 지기 시작했다고 말씀을 드렸는데요. 지난 시간에 Front-end쪽을 잠깐 살펴보았으니 이번 시간에는 Servlet에서 멈춰 있는 Back-end 쪽의 기술의 변화를 다시 살펴 보도록 하겠습니다.&lt;/p&gt;&lt;p&gt;Servlet을 사용하여 Back-end쪽을 불편하고 복잡하게 개발을 하던 시절의 무렵, 혜성 같이 등장한 구원투수가 있었으니 그것은 바로 Spring이라는 놈이었습니다. 마치 야근을 밥 먹듯이 하는 개발자들에게 봄날이 오길 바라는 마음에서 였을까 아무튼 자바 개발자들에게 Spring은 봄(spring)이 오듯이 찾아왔습니다. ^^;&lt;/p&gt;&lt;p&gt;Java 웹 개발의 이전과 이후는 Spring을 기준으로 나뉘어 진다고해도 과언이 아니라고 볼 수 있습니다. 마치 예능 프로그램이 무한도전 이전과 이후로 나뉜다고 볼 수 있는것 처럼 말이죠. ^^&lt;/p&gt;&lt;p&gt;아무튼 Spring이라는 프레임워크가 등장한 이후로 웹 애플리케이션을 개발에 있어서 커다란 변화가 생겼음은 Java 개발자라면 누구나 부정할 수 없을것이라고 봅니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Spring 1.0 버전이 2004년에 나온것으로 알고 있는데 저는 개인적으로 2009년에 Spring 2.5를 처음 접하게 되었습니다.&lt;/p&gt;&lt;p&gt;2018년 10월 현재 Spring 5 버전이 나오면서 Spring 프레임워크의 기술에도 많은 변화가 있었으나 IOC, DI, AOP 같은 Spring의 근본 기술들은 Spring의 핵심 기술로 변함없이 자리를 지키고 있습니다.&lt;/p&gt;&lt;p&gt;이 포스팅은 Spring에 대한 구체적인 사용방법 등에 대해서 이야기를 하는것이 아니기때문에 Spring에 대한 이야기들은 추후에 다시 포스팅할 수 있는 기회가 있었으면 좋겠습니다.&lt;/p&gt;&lt;p&gt;Java 개발자 로드맵에 이미 Spring이 추가가 될 것이라는 결론은 난 셈이 되지만 그래도 이대로 글을 마치게되면 너무 싱거우니 여태까지 해온대로 이전 포스팅에서 Servlet 기반으로 구현했던 TODO 애플리케이션을 Spring 프레임워크를 사용하여 개선을 해보도록 하겠습니다. Servlet 기반의 TODO 애플리케이션 구현에 대한 상세한 내용은 아래 포스팅을 참고해주세요.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://itvillage.tistory.com/entry/현실적인-개발자-로드맵-JSP-Model-2&quot; target=&quot;_blank&quot;&gt;2018/09/07 - [현실적인 개발자 로드맵] - 현실적인 개발자 로드맵(3) - JSP 모델2&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;자, 그럼 결론적으로 Servlet으로 구현한 TODO 애플리케이션의 Servlet 코드와 Spring으로 구현한 Spring Controller의 코드를 비교해서 잠깐 살펴보겠습니다. 아래는 TodoAjaxServlet의 코드입니다.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;==== ToDoAjaxServlet.java ====&lt;/p&gt;&lt;pre style=&quot;background-color:#2b2b2b;color:#a9b7c6;font-family:'Consolas';font-size:14.3pt;&quot;&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;package &lt;/span&gt;com.itvillage.servlet&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;com.google.gson.Gson&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;com.itvillage.vo.ToDo&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;javax.servlet.ServletException&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;javax.servlet.annotation.&lt;span style=&quot;color:#bbb529;&quot;&gt;WebServlet&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;javax.servlet.http.HttpServlet&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;javax.servlet.http.HttpServletRequest&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;javax.servlet.http.HttpServletResponse&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;java.io.IOException&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;java.util.ArrayList&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;java.util.List&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#bbb529;&quot;&gt;@WebServlet&lt;/span&gt;(&lt;span style=&quot;color:#d0d0ff;&quot;&gt;name &lt;/span&gt;= &lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;ToDoAjaxServlet&quot;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#d0d0ff;&quot;&gt;value &lt;/span&gt;= &lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;/todoAjax&quot;&lt;/span&gt;)&lt;br /&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;public class &lt;/span&gt;ToDoAjaxServlet &lt;span style=&quot;color:#cc7832;&quot;&gt;extends &lt;/span&gt;HttpServlet {&lt;br /&gt;    &lt;span style=&quot;color:#808080;&quot;&gt;// Database&lt;/span&gt;&lt;span style=&quot;color:#808080;font-family:'굴림체';&quot;&gt;를 대신한다&lt;/span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;.&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;private &lt;/span&gt;List&amp;lt;ToDo&amp;gt; &lt;span style=&quot;color:#9876aa;&quot;&gt;todoList&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#bbb529;&quot;&gt;@Override&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#bbb529;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;public void &lt;/span&gt;&lt;span style=&quot;color:#ffc66d;&quot;&gt;init&lt;/span&gt;() &lt;span style=&quot;color:#cc7832;&quot;&gt;throws &lt;/span&gt;ServletException {&lt;br /&gt;        &lt;span style=&quot;color:#cc7832;&quot;&gt;super&lt;/span&gt;.init()&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        this&lt;/span&gt;.&lt;span style=&quot;color:#9876aa;&quot;&gt;todoList &lt;/span&gt;= &lt;span style=&quot;color:#cc7832;&quot;&gt;new &lt;/span&gt;ArrayList&amp;lt;&amp;gt;()&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    &lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;    &lt;span style=&quot;color:#cc7832;&quot;&gt;protected void &lt;/span&gt;&lt;span style=&quot;color:#ffc66d;&quot;&gt;doPost&lt;/span&gt;(HttpServletRequest request&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;HttpServletResponse response) &lt;br /&gt;            &lt;span style=&quot;color:#cc7832;&quot;&gt;throws &lt;/span&gt;ServletException&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;IOException {&lt;br /&gt;        request.setCharacterEncoding(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;response.setContentType(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;text/html;charset=UTF-8&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;String todoName = request.getParameter(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoName&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;String todoDate = request.getParameter(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoDate&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#9876aa;&quot;&gt;todoList&lt;/span&gt;.add(&lt;span style=&quot;color:#cc7832;&quot;&gt;new &lt;/span&gt;ToDo(todoName&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;todoDate))&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;Gson gson = &lt;span style=&quot;color:#cc7832;&quot;&gt;new &lt;/span&gt;Gson()&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;String json = gson.toJson(&lt;span style=&quot;color:#9876aa;&quot;&gt;todoList&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;response.setContentType(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;application/json&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;response.setCharacterEncoding(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;response.setHeader(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;Access-Control-Allow-Origin&quot;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;*&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;response.getWriter().write(json)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    &lt;/span&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;다음은 Spring으로 구현한 ToDoController입니다.&lt;/p&gt;&lt;p&gt;==== ToDoController.java ====&lt;/p&gt;&lt;pre style=&quot;background-color:#2b2b2b;color:#a9b7c6;font-family:'Consolas';font-size:14.3pt;&quot;&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;package &lt;/span&gt;com.itvillage&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;com.itvillage.vo.ToDo&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;org.springframework.stereotype.&lt;span style=&quot;color:#bbb529;&quot;&gt;Controller&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;org.springframework.web.bind.annotation.&lt;span style=&quot;color:#bbb529;&quot;&gt;RequestMapping&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;org.springframework.web.bind.annotation.RequestMethod&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;org.springframework.web.bind.annotation.&lt;span style=&quot;color:#bbb529;&quot;&gt;RequestParam&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;org.springframework.web.bind.annotation.&lt;span style=&quot;color:#bbb529;&quot;&gt;ResponseBody&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;java.util.List&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#bbb529;&quot;&gt;@Controller&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;public class &lt;/span&gt;ToDoController {&lt;br /&gt;    &lt;span style=&quot;color:#bbb529;&quot;&gt;@RequestMapping&lt;/span&gt;(&lt;span style=&quot;color:#d0d0ff;&quot;&gt;value &lt;/span&gt;= &lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;/todoAjax&quot;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#d0d0ff;&quot;&gt;method &lt;/span&gt;= RequestMethod.&lt;span style=&quot;color:#9876aa;font-style:italic;&quot;&gt;POST&lt;/span&gt;)&lt;br /&gt;    &lt;span style=&quot;color:#bbb529;&quot;&gt;@ResponseBody&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#bbb529;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;public &lt;/span&gt;List&amp;lt;ToDo&amp;gt; &lt;span style=&quot;color:#ffc66d;&quot;&gt;todoAjax&lt;/span&gt;(&lt;span style=&quot;color:#bbb529;&quot;&gt;@RequestParam&lt;/span&gt;(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoName&quot;&lt;/span&gt;)String todoName&lt;span style=&quot;color:#cc7832;&quot;&gt;,&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;                               &lt;/span&gt;&lt;span style=&quot;color:#bbb529;&quot;&gt;@RequestParam&lt;/span&gt;(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoDate&quot;&lt;/span&gt;)String todoDate) {&lt;br /&gt;        ToDo.&lt;span style=&quot;color:#9876aa;font-style:italic;&quot;&gt;todoList&lt;/span&gt;.add(&lt;span style=&quot;color:#cc7832;&quot;&gt;new &lt;/span&gt;ToDo(todoName&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;todoDate))&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        return &lt;/span&gt;ToDo.&lt;span style=&quot;color:#9876aa;font-style:italic;&quot;&gt;todoList&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    &lt;/span&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ToDoAjaxServlet 클래스의 doPost() 메서드와 ToDoController 클래스의 todoAjax() 메서드는 클라이언트측으로부터 Ajax 요청을 처리하는 메서드인데요. 일단 눈으로 보이는 소스 코드의 길이만 봐도 ToDoController의 todoAjax() 메서드가 확연히 짧고 간결한것이 보이시죠?&lt;/p&gt;&lt;p&gt;ToDoAjaxSevlet의 doPost()에서는 캐릭터 셋 설정이나 JSON으로의 포맷팅 설정, response의 설정까지 직접 해주어야 했으나 ToDoController의 todoAjax() 메서드에는 그런 설정들이 전혀 없습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Spring 설정파일에 별도의 설정을 하긴 하지만 구현 코드상에서는 모두 제거되어 Servlet 보다 실제로 깔끔한 소스 코드를 유지하는것이 가능해졌습니다.&lt;/p&gt;&lt;p&gt;사실 왜 Spring을 사용하여야 되는지에 대해서 구체적으로 설명하려면 저는 내일 출근을 못합니다.^^;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Spring에 대해서는 다음에 더 많은 얘기를 나눌 수 있는 기회가 있을것이라고 생각을 하고, 우선 지금은 이것 하나만 기억하시면 되겠습니다.&lt;/p&gt;&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(255, 0, 0);&quot;&gt;Spring은 Java 개발자가 되기 위해서 반드시 알아야되는 핵심 기술중에 하나이다&lt;/span&gt;&lt;/b&gt; 라는것을 말이죠.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;자, 이미 앞에서 이미 다 결론이 난 상태이지만 그래도 로드맵을 정리하는 차원에서 다시 한번 로드맵을 그려보면 다음과 같습니다.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 664px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/998D5C415BB411BB0D&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F998D5C415BB411BB0D&quot; width=&quot;664&quot; height=&quot;647&quot; filename=&quot;K-002.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;[Spring 프레임워크가 추가된 개발자 로드맵]&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;짠~ 예상했던대로 Spring 프레임워크가 개발자 로드맵에 추가가 되었습니다.^^;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Spring에 대해서는 몇십번을 강조해도 지나치지 않을만큼 Java 개발자가 되기위해서 필수적으로 알아야 되는 기술입니다.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;더 정확하게 말하자면 Spring을 몰라도 당연히 Java 개발자가 될 수 있지만 조금 더 스마트하고 미래지향적인 Java 개발자가 되기위해서는 배워야 될 기술입니다.&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;이렇게 Spring에 대한 칭찬만 하다가 끝나도 사실 상관은 없지만 그래도 Spring에 대한 단점에 대해서 언급은 하고 가는것이 맞는거 같아 제가 Spring을 처음 접하면서 겪었던 불편함을 잠시 말씀드리겠습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;처음 Spring을 접하면서 가장 불편했던점은 바로 Spring의 개발 환경을 구축하는것이었습니다.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Spring의 기술을 사용하기 위해서 해야되는 설정이 생각보다 복잡했고, Spring에 대한 근본 지식이 없는 상태에서 설정을 하다가 에러를 만나게 되면 해결하는것이 그리 만만치 않았다는 것입니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;이번 포스팅을 위해서 오랜만에 Spring 개발 환경 설정을 해보았는데 역시나 설정하는데 오래 걸리더군요. 보통 밤 12시 전에 잠을 자는데 설정하고 정상적으로 동작하는지 확인하느라 12시를 넘겨버리고 말았습니다. ^^;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;그럼 Spring 프레임워크를 사용하여 개발하는데 있어서 어떤 설정을 해주어야 하는지에 대해서 간단하게만 말씀 드리고 이번 포스팅을 마치도록 하겠습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;==== dispatcher-servlet.xml ===&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;
&lt;/p&gt;&lt;pre class=&quot;brush: xml&quot;&gt;&lt;!--?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?--&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot; xmlns:mvc=&quot;http://www.springframework.org/schema/mvc&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:context=&quot;http://www.springframework.org/schema/context&quot; xsi:schemalocation=&quot;
    http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd&quot;&gt;
    &lt;mvc:annotation-driven&gt;
        &lt;mvc:message-converters&gt;
            &lt;bean class=&quot;org.springframework.http.converter.json.MappingJackson2HttpMessageConverter&quot;&gt;
        &lt;/bean&gt;&lt;/mvc:message-converters&gt;
    &lt;/mvc:annotation-driven&gt;
    &lt;context:component-scan base-package=&quot;com.itvillage&quot;&gt;


    &lt;bean id=&quot;viewResolver&quot; class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;&gt;
        &lt;property name=&quot;prefix&quot; value=&quot;/WEB-INF/views/&quot;&gt;
        &lt;property name=&quot;suffix&quot; value=&quot;.jsp&quot;&gt;
    &lt;/property&gt;&lt;/property&gt;&lt;/bean&gt;
&lt;/context:component-scan&gt;&lt;/beans&gt;
&lt;/pre&gt;
&lt;p&gt;dispatcher-servlet.xml에는 웹 프리젠테이션 계층의 빈(Bean)을 등록하는 설정 파일입니다. ToDo 애플리케이션의 경우 샘플 애플리케이션이다 보니 많은 빈(Bean)이 등록되지 않았으나 애플리케이션의 기능이 늘어날수록 빈 설정의 수는 늘어날 것입니다. 현재는 응답 데이터를 JSON으로 변환해주는 Converter와 JSP 뷰를 리졸빙해주는 Resolver만 설정되어 있습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;==== web.xml ====&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;
&lt;/p&gt;&lt;pre class=&quot;brush: xml&quot;&gt;&lt;!--?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?--&gt;
&lt;web-app xmlns=&quot;http://xmlns.jcp.org/xml/ns/javaee&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xsi:schemalocation=&quot;http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd&quot; version=&quot;4.0&quot;&gt;
    &lt;context-param&gt;
        &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
        &lt;param-value&gt;/WEB-INF/spring-config/applicationContext.xml&lt;/param-value&gt;
    &lt;/context-param&gt;
    &lt;listener&gt;
        &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;
    &lt;/listener&gt;
    &lt;servlet&gt;
        &lt;servlet-name&gt;dispatcher&lt;/servlet-name&gt;
        &lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet&lt;/servlet-class&gt;
        &lt;init-param&gt;
            &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
            &lt;param-value&gt;/WEB-INF/spring-config/dispatcher-servlet.xml&lt;/param-value&gt;
        &lt;/init-param&gt;
        &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
    &lt;/servlet&gt;
    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;dispatcher&lt;/servlet-name&gt;
        &lt;url-pattern&gt;/&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;
    &lt;filter&gt;
        &lt;filter-name&gt;CORSFilter&lt;/filter-name&gt;
        &lt;filter-class&gt;com.itvillage.filter.CORSFilter&lt;/filter-class&gt;
    &lt;/filter&gt;
    &lt;filter-mapping&gt;
        &lt;filter-name&gt;CORSFilter&lt;/filter-name&gt;
        &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    &lt;/filter-mapping&gt;

&lt;/web-app&gt;
&lt;/pre&gt;
&lt;p&gt;Servlet 사용 시 필요한 web.xml 파일에 Spring 연동을 위한 설정들이 추가로 포함되었습니다. ContextLoaderListener를 통해 Root Web Application Context를 등록하고, 앞서 보았던 dispatcher-servlet.xml 파일의 경로를 추가함으로써 Servlet Web Application Context를 등록하였습니다. 그리고 추가적으로 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;CORS&lt;/a&gt;(Cross Origin Resource Sharing) 문제를 해결하기 위해서 Filter 설정이 포함되었습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;이 포스팅에서는 이런 설정들이 정확하게 무엇을 하는지 몰라도 상관없습니다. 이런 설정들을 해야지만 Spring을 사용할 수 있다라는 정도만 알고 계시면 될것 같습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;설정이 간단해 보이지만 직접 한번 해보시면 다양한 에러를 만나실 수 도 있을것입니다. ^^; 제 경우는 Spring 설정 + IDE 설정 + CORS 문제 등등으로 시간이 좀 걸렸습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;아무튼 Spring의 이런 설정상의 불편함때문에 개발자들이 정작 핵심 개발에 집중을 하지 못하고 Spring 설정에서부터 벽에 부딪치는 경우도 종종 있다고 알고 있는데요. 아시는 분들은 아시겠지만 이런 불편함이 추후에 개선이 되었더랬죠.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;이 부분에 대해서는 다음 시간에 말씀을 드리도록하고, 오늘은 이만 여기서 포스팅을 마무리해야겠습니다. 그럼 다음 시간에 다시 찾아뵐게요~&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;/span&gt;&lt;a href=&quot;https://inf.run/xezD&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;[Kevin의 알기 쉬운 Java 개발자 로드맵 이야기] 인프런 강의 바로가기&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>현실적인 개발자 로드맵</category>
      <category>ajax</category>
      <category>java</category>
      <category>Java 개발자 로드맵</category>
      <category>javascript</category>
      <category>jQuery</category>
      <category>servlet</category>
      <category>spring</category>
      <category>spring mvc</category>
      <category>개발자 로드맵</category>
      <category>스프링 프레임워크</category>
      <category>웹 개발</category>
      <category>자바</category>
      <category>자바 개발자 로드맵</category>
      <category>자바스크립트</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/23</guid>
      <comments>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B56-Spring%EC%9D%98-%ED%83%84%EC%83%9D#entry23comment</comments>
      <pubDate>Wed, 3 Oct 2018 10:54:13 +0900</pubDate>
    </item>
    <item>
      <title>현실적인 개발자 로드맵(5) - jQuery의 등장</title>
      <link>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5-jQuery%EC%9D%98-%EB%93%B1%EC%9E%A5</link>
      <description>&lt;h1&gt;현실적인 개발자 로드맵 - jQuery의 등장&lt;/h1&gt;&lt;div&gt;&lt;div&gt;# 포스팅에서 사용된 코드의 전체 코드는&amp;nbsp;&lt;a href=&quot;https://github.com/ITVillage-Kevin/roadmap-front-javascript&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;b&gt;https://github.com/ITVillage-Kevin/roadmap-front-javascript&lt;/b&gt;&lt;/a&gt;&amp;nbsp;에서 다운로드 받으실 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 해당 포스팅은 IT Village 유튜브 채널(&lt;b&gt;https://www.youtube.com/channel/UCSIvsntWA8aJ3Apoc7kTxig&lt;/b&gt;)에서도 시청하실 수 있습니다.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;안녕하세요, Kevin입니다. 추석을 앞두고 비가 오더니 집에 올때 쯤 그쳤네요. 오늘은 조금 일찍 퇴근해서 이렇게 글을 적고 있습니다. 오늘도 아직 끝나지 않은 개발자 로드맵에 관한 이야기를 해볼까 합니다.&lt;/div&gt;&lt;div&gt;지난 시간에는 Ajax 기술이 등장함으로 인해서 Front-end와 Back-end가 나누어지는 기술의 추세를 잠깐 언급을 했었는데요.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;제가 Ajax 기술을 처음으로 접했던게 2007년이었던가 2008년 이었던가 아무튼 오래되서 기억이 가물 가물한데 아무튼 제 기억으로는 Ajax 기술을 처음 접한 이후,&amp;nbsp;오래지 않아서 순수 자바스크립트 Ajax 기술의 복잡함을 단순화 시킬 수 있는 기술이 등장을 하게됩니다.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;그것은 바로.. jQuery입니다. 두둥~&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;처음 jQuery를 접했을땐 조금 충격적이었습니다. 그 당시만해도 자바스크립트는 이해하기 쉽지 않은 언어였었고, 자바스크립트 Ajax 통신을 통해서 가져온 데이터를 이용하여&amp;nbsp;DOM을 조작해서 UI를 업데이트 하는것이 결코 쉬운 작업이 아니었습니다. 자바스크립트로 그런 복잡한 작업을 하다가 jQuery를 사용하여 똑같은 작업을 하면서 어떻게 이렇게 간결하게 작성할 수 있을까라며 놀라움을 금하지 못하던 때가 분명히 있었네요. ^^;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;그럼 지난 시간에 순수 자바 스크립트 Ajax 기술을 사용해서 서버와 통신을 하던 부분을 jQuery로 똑같은 기능을 하도록 작성을 해보겠습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;먼저 순수 자바 스크립트로 작성한 것을 다시 한번 보도록 하겠습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;pre class=&quot;brush: js&quot;&gt;        function registerTodo(){
            var todoName = document.getElementById(&quot;todoName&quot;).value;
            var todoDate = document.getElementById(&quot;todoDate&quot;).value;

            if(!todoName){
                alert(&quot;할일을 입력해주세요.&quot;);
                return false;
            }

            if(!todoDate){
                alert(&quot;날짜를 입력해주세요.&quot;);
                return false;
            }

            var xhr = new XMLHttpRequest();
            xhr.open('POST', 'http://localhost:8080/todoAjax', true);
            xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

            xhr.onload = function(){
                if(xhr.status == 200 || xhr.status == 201){

                    // 화면에 반영
                    var todoList = JSON.parse(xhr.responseText);
                    var html = &quot;&quot;;
                    for (var i in todoList) {
                        var todo = todoList[i];
                        html += '' + todo.todoName + '' + todo.todoDate + '';
                    }
                    var todoContents = document.getElementById(&quot;todoContents&quot;);
                    todoContents.innerHTML = '';
                    todoContents.innerHTML = html;
                }else{
                    // 에러 처리
                }
            };
            xhr.send('todoName=' + todoName + '&amp;amp;todoDate=' + todoDate);
        }
&lt;/pre&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;div&gt;
15번 라인에서 36번 라인까지가 자바스크립트 Ajax 기술을 사용하여 서버와 데이터를 주고 받는 부분인데요. 소스 코드 자체가 복잡해보이는건 어쩔 수 없습니다.&amp;nbsp;&lt;/div&gt;&lt;div&gt;그럼 이제 위 소스코드를 jQuery로 다시 한번 구현을 해보도록 하겠습니다. jQuery로 구현한 코드는 아래와 같습니다.&lt;/div&gt;&lt;div&gt;
&lt;pre class=&quot;brush:js&quot;&gt;$(document).ready(function(){
   $(&quot;#btnReg&quot;).click(function(){
       var todoName = $(&quot;#todoName&quot;).val();
       var todoDate = $(&quot;#todoDate&quot;).val();

       if(!todoName){
           alert(&quot;할일을 입력해주세요.&quot;);
           return false;
       }

       if(!todoDate){
           alert(&quot;날짜를 입력해주세요.&quot;);
           return false;
       }

       $.post(&quot;http://localhost:8080/todoAjax&quot;, {
           todoName: todoName,
           todoDate: todoDate
       }, function(todoList) {
           var contents = &quot;&quot;;
           for (var i in todoList) {
               var todo = todoList[i];
               contents += '' + todo.todoName + '' + todo.todoDate + '';
           }

           $(&quot;#todoContents&quot;).html(contents);
       }, 'json');
   });
});
&lt;/pre&gt;
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;저희의 TODO 프로젝트 UI가 단순하다보니 UI쪽 코드는 코드 길이도 그렇게 차이나지 않고 복잡함에 있어서도 별 차이가 나지 않네요. 하지만 Ajax 통신하는 부분을 한번 보십시요. 소스 코드 길이도 물론 반으로 줄었고, 한눈에 봐도 되게 단순해 보이지 않나요?&lt;/div&gt;&lt;p&gt;하지만 이렇게 승승장구 할것만 같았던 jQuery도 세월이 흐르면서 서서히 한계를 드러내기 시작을 했더랬습니다. 그 이야기는 나중에 다시 하도록하고, 어쨌든 오늘은 보시면 아시겠지만 저희가 개선한 코드를 통해 추출할 수 있는 기술은 딱 한가지입니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;바로 jQuery이지요. jQuery의 사용 빈도가 점점 줄어들고 있는건 사실이긴하지만 그래도 한국에서는 여전히 많이 사용되고 있는 기술중에 하나이기때문에 익혀두는것이 결코 나쁘지는 않습니다. jQuery 자체가 간결함을 지향하는 기술이기 때문에 학습 곡선 역시도 크게 가파르지 않으므로 조금만 시간 투자를 하시면 금방 익숙하게 사용하실 수 있을거라고 생각합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;자, 그럼 오늘 추출한 기술을 포함한 지금까지의 로드맵을 마인드 맵으로 한번 그려 볼까요?&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 546px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99AF96395BA4C9F905&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99AF96395BA4C9F905&quot; width=&quot;546&quot; height=&quot;612&quot; filename=&quot;K-013.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;[jQuery 기술이 추가된 개발자 로드맵]&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;기술이 점점 늘어가고 있군요. ^^&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;이번 시간까지는 Front-end쪽의 기술의 변화를 살펴봤으니 다음 시간에는 다시&amp;nbsp; Back-end쪽의 기술의 변화를 살펴보도록 하겠습니다.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;선선한 가을 바람이 부는 금요일 밤인데 저는 잠깐 조깅을 하러 나가봐야겠습니다. 추석 연휴 잘들 보내시길 Kevin이 진심으로 기원하겠습니다. 다음 시간에 뵐게요~&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: square;&quot;&gt;&lt;li&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://inf.run/xezD&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;[ Kevin의 알기 쉬운 Java 개발자 로드맵 이야기 ] 인프런 강의 바로가기&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>현실적인 개발자 로드맵</category>
      <category>ajax</category>
      <category>Java 개발</category>
      <category>JAVA 개발자</category>
      <category>javascript</category>
      <category>jQuery</category>
      <category>개발자 로드맵</category>
      <category>웹 개발</category>
      <category>웹개발</category>
      <category>제이쿼리</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/22</guid>
      <comments>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5-jQuery%EC%9D%98-%EB%93%B1%EC%9E%A5#entry22comment</comments>
      <pubDate>Fri, 21 Sep 2018 19:46:09 +0900</pubDate>
    </item>
    <item>
      <title>현실적인 개발자 로드맵(4) - Ajax의 등장</title>
      <link>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5-Ajax%EC%9D%98-%EB%93%B1%EC%9E%A5</link>
      <description>&lt;h1&gt;현실적인 개발자 로드맵 - Ajax의 등장&lt;/h1&gt;&lt;div&gt;# 포스팅에서 사용된 코드의 전체 코드는&amp;nbsp;&lt;a href=&quot;https://github.com/ITVillage-Kevin/roadmap-front-javascript&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;b&gt;https://github.com/ITVillage-Kevin/roadmap-front-javascript&lt;/b&gt;&lt;/a&gt;&amp;nbsp;에서 다운로드 받으실 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 해당 포스팅은 IT Village 유튜브 채널(&lt;b&gt;https://www.youtube.com/channel/UCSIvsntWA8aJ3Apoc7kTxig&lt;/b&gt;)에서도 시청하실 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;안녕하세요, 오늘도 여전히 금요일 저녁에 찾아뵙네요. 평일엔 일 하느라 바빠서 포스팅을 할 수 없어서 금요일만 되면 포스팅 할 수 있다는 설레는 마음으로 이렇게 컴퓨터 앞에 앉아있습니다. ^^;&lt;/p&gt;&lt;p&gt;자, 그럼 지난 시간에 이어서 개발자 로드맵에 대해서 계속 이야기 해볼까요?&lt;/p&gt;&lt;p&gt;지난 시간에는 JSP 모델1을 개선한 모델2까지에서 추출한 로드맵을 그려보았습니다. 바로 아래와 같죠?&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 611px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/996655395B9BA9DA27&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F996655395B9BA9DA27&quot; width=&quot;611&quot; height=&quot;483&quot; filename=&quot;99CBF43A5B9284341A.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;[JSP 모델2 방식에서 추가된 개발자 로드맵]&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;자, 그런데 JSP를 사용해보신 분들이라면 다들 알고 계시겠지만 JSP에는 한가지 문제점이 있습니다. 사실 문제점이라기 보다는 JSP 등의 서버 사이드 언어를 사용하면 당연시 여기던 것이었는데요. 저희는 아래와 같은 TODO 애플리케이션을 만들고 있습니다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 580px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9960203B5B9BAAC82C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9960203B5B9BAAC82C&quot; width=&quot;580&quot; height=&quot;593&quot; filename=&quot;9937A8345B8B261A0E.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;[TODO 애플리케이션]&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;그림과 같이 할일과 날짜를 입력한 후 [등록] 버튼을 클릭하게 되면 할일 데이터를 서버로 전송하게 되는데요. 이때 화면이 깜빡 거리는것을 볼 수 있습니다. 깜빡임의 이유는 [등록] 버튼을 클릭하게 되면 서버에 데이터를 전송한 후, 서버에서 todo_model1.jsp로 다시 포워딩을 시키면서 화면이 바뀌게 되기 때문입니다. 이해 되셨죠?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;TODO 애플리케이션의 경우 서버에서 처리하는 시간이 얼마 되지 않기 때문에 잠시 깜빡거리겠지만 실무에서 많은 데이터를 처리할 경우에는 [등록] 버튼을 누른후에도 한참을 화면이 바뀌지 않거나 화면이 바뀌고 난 후에 서버에서 처리가 끝날때까지 브라우저에서&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;한참 동안&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;흰색 화면만 표시되는&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;&amp;nbsp;경우도 있다는걸 아시는분들은 아실겁니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;이렇게 페이지 전체를 refresh 하게되면 사용자들은 서버의 처리가 끝날때까지 아무것도 하지 못한채 기다려야 하죠. 이런 문제점을 개선하기 위해서 나온 기술이 바로 &lt;a href=&quot;https://ko.wikipedia.org/wiki/Ajax&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Ajax(Asynchronous Javascript and XML)&lt;/a&gt; 기술입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px; text-align: center;&quot;&gt;Ajax 기술을 통해 페이지 전체를 refresh 하지 않고, 페이지의 일부분에 표시되는 데이터를 서버측으로 부터 받아서 화면의 깜빡임 없이 데이터를 갱신하는것이죠. Ajax 기술로 인해 페이지의 특정 부분의 데이터 또는 UI만 갱신 되므로 페이지 전체가 refresh 될때까지 기다릴 필요가 없게 되었습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;font face=&quot;Nanum Gothic, Noto Sans Korean&quot;&gt;&lt;span style=&quot;font-size: 15px;&quot;&gt;JSP가 처음 등장할 당시 클라이언트측에 표시되는 UI 화면을 구성하는 HTML + Javascript + CSS 소스코드들이 서버측 소스들과 함께 서버측에 위치하는 경우가 많았는데 Ajax 기술이 등장함으로 인해서 슬슬&amp;nbsp;&lt;/span&gt;&lt;/font&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px;&quot;&gt;HTML + Javascript + CSS 등의 소스 코드들은 웹서버에 위치시키고 서버측에는 순수하게 서버측 언어로 구성된 소스 코드들만 위치 시키는 소위 말하는 Front-end와 Back-end로 역할 구분을 하게됩니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-size: 15px; font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;;&quot;&gt;자기 역할에 충실할 수 있도록 구성되는것은 어찌보면 너무나 당연한것이라고 볼 수 있는건데 말이죠.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-size: 15px; font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;;&quot;&gt;자, 그럼&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; font-size: 15px;&quot;&gt;HTML + Javascript + CSS로 구성된 Front-end 의 소스 코드를 보시겠습니다. Back-end 소스 코드는 여전히 서블릿으로 구성되어 있으므로 손대지 않겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt; 
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;Todo List&lt;/title&gt;
    &lt;style&gt;
        #todoList {
            border: 1px solid #8F8F8F;
            width: 500px;
            border-collapse: collapse;
        }
        th, td {
            padding: 5px;
            border: 1px solid #8F8F8F;
        }
    &lt;/style&gt;
    &lt;script&gt;
        function registerTodo(){
            var todoName = document.getElementById(&quot;todoName&quot;).value;
            var todoDate = document.getElementById(&quot;todoDate&quot;).value;
            if(!todoName){
                alert(&quot;할일을 입력해주세요.&quot;);
                return false;
            }
            if(!todoDate){
                alert(&quot;날짜를 입력해주세요.&quot;);
                return false;
            }
            var xhr = new XMLHttpRequest();
            xhr.open('POST', 'http://localhost:8080/todoAjax', true);
            xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
            xhr.onload = function(){
                if(xhr.status == 200 || xhr.status == 201){
                    // 화면에 반영
                    var todoList = JSON.parse(xhr.responseText);
                    var html = &quot;&quot;;
                    for (var i in todoList) {
                        var todo = todoList[i];
                        html += '&lt;tr&gt;&lt;td&gt;' + todo.todoName + '&lt;/td&gt;&lt;td&gt;' + todo.todoDate + '&lt;/td&gt;&lt;/tr&gt;';
                    }
                    var todoContents = document.getElementById(&quot;todoContents&quot;);
                    todoContents.innerHTML = '';
                    todoContents.innerHTML = html;
                }else{
                    // 에러 처리
                }
            };
            xhr.send('todoName=' + todoName + '&amp;todoDate=' + todoDate);
        }
    &lt;/script&gt;


    &lt;h3&gt;TO DO 등록&lt;/h3&gt;
    &lt;div&gt;
        &lt;form id=&quot;todoForm&quot; method=&quot;POST&quot; action=&quot;/todo&quot;&gt;
            &lt;input type=&quot;text&quot; name=&quot;todoName&quot; id=&quot;todoName&quot; value=&quot;&quot;&gt;
            &lt;input type=&quot;date&quot; name=&quot;todoDate&quot; id=&quot;todoDate&quot; value=&quot;&quot;&gt;
            &lt;input type=&quot;button&quot; id=&quot;btnReg&quot; value=&quot;등록&quot; onclick=&quot;registerTodo()&quot;&gt;
        &lt;/form&gt;
    &lt;/div&gt;
    &lt;div&gt;
        &lt;h4&gt;TO DO List&lt;/h4&gt;
        &lt;table id=&quot;todoList&quot;&gt;
            &lt;thead&gt;
            &lt;tr&gt;
                &lt;td align=&quot;center&quot;&gt;todo name&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;todo date&lt;/td&gt;
            &lt;/tr&gt;
            &lt;/thead&gt;

            &lt;tbody id=&quot;todoContents&quot;&gt;
            &lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;할 일이 없습니다.&lt;/td&gt;&lt;/tr&gt;
            &lt;/tbody&gt;
        &lt;/table&gt;
    &lt;/div&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;[front_javascript.html]&lt;/p&gt;
26번 라인부터 45번 라인까지가 Ajax 기술을 이용해 Back-end 쪽에 할일 데이터를 전송하고 응답으로 할일 목록 데이터를 전송 받는 부분입니다. 조금 복잡해 보이지 않나요?&amp;nbsp;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;어쨌든 여기서 저희는 로드맵에 하나의 기술을 더 추가할 수 있게되었네요.^^ 바로 아래와 같겠죠?&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 531px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/997B00475B9BB5272D&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F997B00475B9BB5272D&quot; width=&quot;531&quot; height=&quot;528&quot; filename=&quot;K-010.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;[Ajax 기술이 추가된 개발자 로드맵]&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;개발자 로드맵에 Ajax 기술이 추가되었습니다. ^^;&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;사실 요즘에는 Javascript로 Ajax 기술을 직접적으로 사용하는일은 많지 않습니다. 소스 코드를 보시면 아시겠지만 처음 보는 분들한테는 소스코드가 결코 단순하지는 않습니다.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;그럼에도 불구하고 Ajax 기술을 로드맵에 포함시킨것은 순수 Javascript의 Ajax 기술을 사용하라는 의미는 아니고 최소한 Ajax의 원리는 이해를 해야한다는 의미에서 로드맵에 포함을 시킨거였습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;그럼, 다음 시간에는 순수 Javascript의 Ajax 기술의 복잡함을 극복하도록 Front-end 측 소스 코드를 개선해 보도록 하겠습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;모두들 즐거운 불금 보내시고 저는 이만 다른 공부를 좀 해야겠습니다. 그럼 다음시간에 뵐게요~&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: square;&quot;&gt;&lt;li&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://inf.run/xezD&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;[ Kevin의 알기 쉬운 Java 개발자 로드맵 이야기 ] 인프런 강의 바로가기&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>현실적인 개발자 로드맵</category>
      <category>ajax</category>
      <category>CSS</category>
      <category>HTML</category>
      <category>IDE</category>
      <category>java</category>
      <category>javascript</category>
      <category>개발자</category>
      <category>로드맵</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/21</guid>
      <comments>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5-Ajax%EC%9D%98-%EB%93%B1%EC%9E%A5#entry21comment</comments>
      <pubDate>Fri, 14 Sep 2018 22:36:38 +0900</pubDate>
    </item>
    <item>
      <title>현실적인 개발자 로드맵(3) - JSP 모델2</title>
      <link>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5-JSP-Model-2</link>
      <description>&lt;h1&gt;&lt;span style=&quot;box-sizing: border-box; background-color: rgb(241, 241, 241);&quot;&gt;&lt;font face=&quot;Nanum Gothic, Noto Sans Korean&quot;&gt;현실적인 개발자 로드맵 - JSP 모델2&lt;/font&gt;&lt;/span&gt;&lt;/h1&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;# 포스팅에서 사용된 코드의 전체 코드는&amp;nbsp;&lt;a href=&quot;https://github.com/ITVillage-Kevin/roadmap-jsp&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;b&gt;https://github.com/ITVillage-Kevin/roadmap-jsp&lt;/b&gt;&lt;/a&gt;&amp;nbsp;에서 다운로드 받으실 수 있습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;# 해당 포스팅은 IT Village 유튜브 채널(&lt;b&gt;https://www.youtube.com/channel/UCSIvsntWA8aJ3Apoc7kTxig&lt;/b&gt;)에서도 시청하실 수 있습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;안녕하세요, Kevin입니다. 지난 시리즈에 이어 개발자 로드맵 이야기를 계속 해보겠습니다. 지난 시간에 어떤 이야기를 했는지 기억나시나요?^^ 지난 시간에는 Java 개발자로 입문하면서 필수적으로 배워야 할 것들이 무엇인지 JSP Model1 방식으로 구현한 TO DO 애플리케이션을 통해서 알아보았습니다. 지난 시간까지 알아본 바로는 아래와 같은 기술들을 배워야 한다고 했죠?&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 481px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99A21E505B9277A62F&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99A21E505B9277A62F&quot; width=&quot;481&quot; height=&quot;405&quot; filename=&quot;995D61365B8B2AAB1A.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; background-color: rgb(241, 241, 241); font-size: 12pt;&quot;&gt;&lt;span style=&quot;font-size: 15px; background-color: rgb(255, 255, 255);&quot;&gt;[JSP 모델1 방식에서 추출한 기술]&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-family: &amp;quot;Nanum Gothic&amp;quot;, &amp;quot;Noto Sans Korean&amp;quot;; background-color: rgb(241, 241, 241); font-size: 12pt;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;자, 그럼 오늘은 JSP Model1 방식으로 만든 TODO 애플리케이션을 조금 더 개선 해보면서 어떤 기술들을 더 알아야하는지 알아보겠습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;h2&gt;클라이언트 + 서버 통합(JSP 모델2)&lt;/h2&gt;&lt;div&gt;지난 시간과 비슷하게 오늘은 JSP 모델2 방식의 구성도를 먼저 보도록 하겠습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 900px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9955A44D5B9279012A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9955A44D5B9279012A&quot; width=&quot;900&quot; height=&quot;374&quot; filename=&quot;K-007.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;[JSP 모델2 구성도]&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;JSP 모델1의 구성도와 어떤 차이가 있는지 느껴지시나요? 모델1의 경우 JSP에서 클라이언트의 요청을 직접 받고 클라이언트에게 응답으로 돌려줄 View 까지 생성했더랬습니다. 그야말로 JSP가 모든 일을 다 하는거라고 보시면 됩니다. 그렇기 때문에 소스 코드 상에서도 요청을 처리하는 Java 코드가 뷰를 구성하는 JSP 코드에 구현 되어 있는것을 보실 수 있었는데요.&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;모델1에서 요청을 처리하는 코드와 뷰를 구성하는 코드가 JSP에 모두 구성되어 있는 반면에 JSP 모델2 방식은&amp;nbsp;&amp;nbsp;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94_%EC%84%9C%EB%B8%94%EB%A6%BF&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Servlet&lt;/a&gt;이라는 자바 클래스가 요청을 받아서 뷰에 표시될 데이터들만 JSP에게 넘겨 주고, JSP는 이 데이터들을 HTML 코드에 잘 보이도록 적절하게 랜더링한 후, 클라이언트에게 넘겨줍니다.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;한마디로 말해서&amp;nbsp;각자의 할 일은 각자 하자는 구성 방식이라고 볼 수 있습니다. 요청을 처리하는 역할은 Servlet이 맡고 , JSP는 뷰로써의 역할만 하자는 역할 분리가 이루어진셈입니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;실제 코드를 보면 조금 더 이해 하시기 쉬울겁니다. 소스 코드를 한번 볼까요?&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;==== web.xml ====&lt;/p&gt;&lt;pre style=&quot;background-color:#2b2b2b;color:#a9b7c6;font-family:'Consolas';font-size:14.3pt;&quot;&gt;&lt;p&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;?&lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;xml version&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;=&quot;1.0&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;encoding&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;=&quot;UTF-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;?&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;web-app &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;=&quot;2.4&quot;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;         &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;xmlns&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;=&quot;http://java.sun.com/xml/ns/j2ee&quot;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;         &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;xmlns:&lt;/span&gt;&lt;span style=&quot;color:#9876aa;&quot;&gt;xsi&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;         &lt;/span&gt;&lt;span style=&quot;color:#9876aa;&quot;&gt;xsi&lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;:schemaLocation&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;=&quot;http://java.sun.com/xml/ns/j2ee&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;         http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;display-name&amp;gt;&lt;/span&gt;To Do JSP&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/display-name&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;welcome-file-list&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;welcome-file&amp;gt;&lt;/span&gt;index.html&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/welcome-file&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;welcome-file&amp;gt;&lt;/span&gt;index.jsp&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/welcome-file&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;/welcome-file-list&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;servlet&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;servlet-name&amp;gt;&lt;/span&gt;Todo&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;servlet-class&amp;gt;&lt;/span&gt;com.itvillage.servlet.TodoServlet&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/servlet-class&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;/servlet&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;servlet-mapping&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;servlet-name&amp;gt;&lt;/span&gt;Todo&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;url-pattern&amp;gt;&lt;/span&gt;/todo&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;/servlet-mapping&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/web-app&amp;gt;&lt;/span&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;모델2 방식에서는 클라이언트의 요청을 처리하게 되는 Servlet 설정 정보를 위와 같이 web.xml에 설정해주어야합니다. 이렇게 Servlet 클래스와 URL의 매핑을 해주어야지만 클라이언트에서 /todo URL로 요청을 보냈을때 /todo URL에 매핑되는 TodoServlet 클래스가 요청을 처리할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;==== ToDoServlet ====&lt;/p&gt;&lt;pre style=&quot;background-color:#2b2b2b;color:#a9b7c6;font-family:'Consolas';font-size:14.3pt;&quot;&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;package &lt;/span&gt;com.itvillage.servlet&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;com.itvillage.vo.ToDo&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;javax.servlet.RequestDispatcher&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;javax.servlet.ServletException&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;javax.servlet.annotation.WebServlet&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;javax.servlet.http.HttpServlet&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;javax.servlet.http.HttpServletRequest&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;javax.servlet.http.HttpServletResponse&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;java.io.IOException&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;java.util.ArrayList&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;java.util.HashMap&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;java.util.List&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;java.util.Map&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;@WebServlet(name = &lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;TodoServlet&quot;&lt;/span&gt;)&lt;br /&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;public class &lt;/span&gt;TodoServlet &lt;span style=&quot;color:#cc7832;&quot;&gt;extends &lt;/span&gt;HttpServlet {&lt;br /&gt;    &lt;span style=&quot;color:#808080;&quot;&gt;// Database&lt;/span&gt;&lt;span style=&quot;color:#808080;font-family:'굴림체';&quot;&gt;를 대신한다&lt;/span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;.&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;private &lt;/span&gt;List&amp;lt;ToDo&amp;gt; todoList&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    &lt;/span&gt;@Override&lt;br /&gt;    &lt;span style=&quot;color:#cc7832;&quot;&gt;public void &lt;/span&gt;init() &lt;span style=&quot;color:#cc7832;&quot;&gt;throws &lt;/span&gt;ServletException {&lt;br /&gt;        &lt;span style=&quot;color:#cc7832;&quot;&gt;super&lt;/span&gt;.init()&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        this&lt;/span&gt;.todoList = &lt;span style=&quot;color:#cc7832;&quot;&gt;new &lt;/span&gt;ArrayList&amp;lt;&amp;gt;()&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    &lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;    &lt;span style=&quot;color:#cc7832;&quot;&gt;protected void &lt;/span&gt;doPost(HttpServletRequest request&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;HttpServletResponse response)&lt;br /&gt;            &lt;span style=&quot;color:#cc7832;&quot;&gt;throws &lt;/span&gt;ServletException&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;IOException {&lt;br /&gt;        request.setCharacterEncoding(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;response.setContentType(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;text/html;charset=UTF-8&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;System.out.println(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;Hello Servlet doPost!&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;String todoName = request.getParameter(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoName&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;String todoDate = request.getParameter(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoDate&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;todoList.add(&lt;span style=&quot;color:#cc7832;&quot;&gt;new &lt;/span&gt;ToDo(todoName&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;todoDate))&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;RequestDispatcher dispatcher =&lt;br /&gt;                request.getRequestDispatcher(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;/todo_model2.jsp&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;request.setAttribute(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoList&quot;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;todoList)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;dispatcher.forward(request&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;response)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    &lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;    &lt;span style=&quot;color:#cc7832;&quot;&gt;protected void &lt;/span&gt;doGet(HttpServletRequest request&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;HttpServletResponse response)&lt;br /&gt;            &lt;span style=&quot;color:#cc7832;&quot;&gt;throws &lt;/span&gt;ServletException&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;IOException {&lt;br /&gt;        System.out.println(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;Hello Servlet doGet!&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;RequestDispatcher dispatcher =&lt;br /&gt;                request.getRequestDispatcher(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;/todo_model2.jsp&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;request.setAttribute(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoList&quot;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;todoList)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;dispatcher.forward(request&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;response)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    &lt;/span&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;클라이언트에게서 받은 요청을 처리하는 TodoServlet 클래스입니다. doPost()에서 할 일을 등록한 후, 등록된 할 일 데이터를 JSP로 넘겨주고&amp;nbsp;doGet()에서는 단순히 등록된 할일을 JSP로 넘겨줍니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;doPost() 안에 있는 소스 코드들은 이전 시간에 봤던 모델1 방식에서 result_model1.jsp 안에 포함되어 있던 코드들인데 이렇게 TodoServlet 클래스 안으로 분리되었습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;==== todo_model2.jsp ====&lt;/p&gt;&lt;pre style=&quot;background-color:#2b2b2b;color:#a9b7c6;font-family:'Consolas';font-size:14.3pt;&quot;&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;%@ &lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;page &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;contentType=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;text/html;charset=UTF-8&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; language=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;java&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; %&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;%@ &lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;taglib &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;uri=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;http://java.sun.com/jsp/jstl/core&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; prefix=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot;%&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;%@ &lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;taglib &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;uri=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;http://java.sun.com/jsp/jstl/functions&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; prefix=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; %&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;meta &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;http-equiv=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;Content-Language&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;content=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;ko&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;meta &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;http-equiv=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;Content-Type&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;content=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;text/html; charset=UTF-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;title&amp;gt;&lt;/span&gt;TODO &lt;span style=&quot;font-family:'굴림체';&quot;&gt;등록&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/title&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;style&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        #todoList &lt;/span&gt;{&lt;br /&gt;            &lt;span style=&quot;color:#bababa;&quot;&gt;border&lt;/span&gt;: &lt;span style=&quot;color:#6897bb;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;px solid &lt;/span&gt;&lt;span style=&quot;color:#6897bb;&quot;&gt;#8F8F8F&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;width&lt;/span&gt;: &lt;span style=&quot;color:#6897bb;&quot;&gt;500&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;border-collapse&lt;/span&gt;: &lt;span style=&quot;color:#a5c261;&quot;&gt;collapse&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;        &lt;span style=&quot;color:#cc7832;&quot;&gt;th, td &lt;/span&gt;{&lt;br /&gt;            &lt;span style=&quot;color:#bababa;&quot;&gt;padding&lt;/span&gt;: &lt;span style=&quot;color:#6897bb;&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;border&lt;/span&gt;: &lt;span style=&quot;color:#6897bb;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;px solid &lt;/span&gt;&lt;span style=&quot;color:#6897bb;&quot;&gt;#8F8F8F&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;}&lt;br /&gt;    &lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/style&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;script&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;function &lt;/span&gt;&lt;span style=&quot;color:#ffc66d;&quot;&gt;registerTodo&lt;/span&gt;(){&lt;br /&gt;            &lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;var &lt;/span&gt;todoName = document.&lt;span style=&quot;color:#ffc66d;&quot;&gt;getElementById&lt;/span&gt;(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoName&quot;&lt;/span&gt;).&lt;span style=&quot;color:#9876aa;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;var &lt;/span&gt;todoDate = document.&lt;span style=&quot;color:#ffc66d;&quot;&gt;getElementById&lt;/span&gt;(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoDate&quot;&lt;/span&gt;).&lt;span style=&quot;color:#9876aa;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;if&lt;/span&gt;(!todoName){&lt;br /&gt;                &lt;span style=&quot;color:#ffc66d;&quot;&gt;alert&lt;/span&gt;(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;font-family:'굴림체';&quot;&gt;할일을 입력해주세요&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;.&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;                &lt;/span&gt;&lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;return false&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;}&lt;br /&gt;            &lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;if&lt;/span&gt;(!todoDate){&lt;br /&gt;                &lt;span style=&quot;color:#ffc66d;&quot;&gt;alert&lt;/span&gt;(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;font-family:'굴림체';&quot;&gt;날짜를 입력해주세요&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;.&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;                &lt;/span&gt;&lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;return false&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;            &lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;var &lt;/span&gt;form = document.&lt;span style=&quot;color:#ffc66d;&quot;&gt;getElementById&lt;/span&gt;(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoForm&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;form.&lt;span style=&quot;color:#ffc66d;&quot;&gt;submit&lt;/span&gt;()&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;}&lt;br /&gt;    &lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;h3&amp;gt;&lt;/span&gt;TO DO &lt;span style=&quot;font-family:'굴림체';&quot;&gt;등록&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/h3&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;div&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;form &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;id=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoForm&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;method=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;POST&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;action=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;/todo&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;input &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;type=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;text&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;name=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoName&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;id=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoName&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;input &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;type=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;date&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;name=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoDate&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;id=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoDate&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;input &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;type=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;button&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;id=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;btnReg&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#a5c261;font-family:'굴림체';&quot;&gt;등록&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;onclick=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#ffc66d;&quot;&gt;registerTodo&lt;/span&gt;()&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;/form&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;div&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;h4&amp;gt;&lt;/span&gt;TO DO List&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/h4&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;table &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;id=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoList&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;thead&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                &amp;lt;tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                    &amp;lt;td &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;align=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;center&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;/span&gt;todo name&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                    &amp;lt;td &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;align=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;center&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;/span&gt;todo date&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                &amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;/thead&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;tbody&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:choose&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                    &lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:when &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;=&quot;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;fn:length(todoList) == &lt;/span&gt;&lt;span style=&quot;color:#6897bb;background-color:#232525;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                        &lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                            &amp;lt;td &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;align=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;center&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;colspan=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-family:'굴림체';&quot;&gt;할 일이 없습니다&lt;/span&gt;.&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                        &amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                    &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:when&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                    &lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:otherwise&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                        &lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:forEach &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;items&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;=&quot;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;todoList&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;&quot; &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;=&quot;todo&quot;&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                            &lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                                &amp;lt;td&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;todo.todoName&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                                &amp;lt;td &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;align=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;center&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;todo.todoDate&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                            &amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                        &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:forEach&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                    &lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:otherwise&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                &lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:choose&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;            &lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/tbody&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;/table&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;todo_model2.jsp는 todo_model1.jsp와 변한게 없으므로 넘어가겠습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;자, 그럼 지난 시간과 마찬가지로 이번에도 우리가 배워야 할 기술들을 추출해 보겠습니다. 눈치채신 분도 계시겠지만 이번 시간에 변화 된것은 딱 한가지 밖에 없습니다. 바로 Servlet입니다. 저희가 배워야 될 기술에 Servlet만 포함시키면 됩니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&quot;오래된 방식인데 꼭 배워야 해?&quot;라고 말씀하시는 분들이 많겠지만 일단 포함을 시켜보겠습니다. Servlet이 포함된&amp;nbsp;추가된 로드맵은 다음과 같겠죠?&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 611px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99CBF43A5B9284341A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99CBF43A5B9284341A&quot; width=&quot;611&quot; height=&quot;483&quot; filename=&quot;K-009.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;[JSP 모델2 방식에서 추가된 개발자 로드맵]&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;자, 그럼 Servlet은 과연 배워야 할까요? Servlet을 사용하고 있는&amp;nbsp;회사라면 당연히 배워야됩니다. 하지만 아직도 Servlet을 사용하고 있는 기업이라면.. 음.. 저 멀리 아주 멀리, 시대에 동 떨어진 회사라고 봐도 될 것 같습니다.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Servlet을 여전히 메인 방식으로 사용하고 있는 회사라면 과감하게 입사를 포기하든가 아니면 입사하셔서 여러분이 직접 회사를 바꾸시면 되겠습니다. ^^;&amp;nbsp;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;오늘은 이쯤에서 포스팅을 마치기로 하고, 다음 시간에는 오늘 포스팅 한 소스 코드를 살짝만&amp;nbsp;더 개선해보도록 하겠습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;그럼 좋은 주말 보내세요~&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: square;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://inf.run/xezD&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;[ Kevin의 알기 쉬운 Java 개발자 로드맵 이야기 ] 인프런 강의 바로가기&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>현실적인 개발자 로드맵</category>
      <category>CSS</category>
      <category>HTML</category>
      <category>java</category>
      <category>javascript</category>
      <category>JSP</category>
      <category>MODEL1</category>
      <category>model2</category>
      <category>개발자</category>
      <category>로드맵</category>
      <category>모델1</category>
      <category>모델2</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/20</guid>
      <comments>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5-JSP-Model-2#entry20comment</comments>
      <pubDate>Fri, 7 Sep 2018 23:15:30 +0900</pubDate>
    </item>
    <item>
      <title>현실적인 개발자 로드맵(2) - 무엇부터 배워야 할까요?</title>
      <link>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5-%EB%AC%B4%EC%97%87%EB%B6%80%ED%84%B0-%EB%B0%B0%EC%9B%8C%EC%95%BC-%ED%95%A0%EA%B9%8C%EC%9A%94</link>
      <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h1&gt;현실적인 개발자 로드맵 - 무엇부터 배워야 할까요?&lt;/h1&gt;&lt;p style=&quot;line-height: 1.5;&quot;&gt;# 포스팅에서 사용된 코드의 전체 코드는&amp;nbsp;&lt;a href=&quot;https://github.com/ITVillage-Kevin/roadmap-jsp&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;b&gt;https://github.com/ITVillage-Kevin/roadmap-jsp&lt;/b&gt;&lt;/a&gt;&amp;nbsp;에서 다운로드 받으실 수 있습니다.&lt;/p&gt;&lt;p style=&quot;line-height: 1.5;&quot;&gt;# 해당 포스팅은 IT Village 유튜브 채널(&lt;b&gt;&lt;a href=&quot;https://www.youtube.com/channel/UCSIvsntWA8aJ3Apoc7kTxig&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://www.youtube.com/channel/UCSIvsntWA8aJ3Apoc7kTxig&lt;/a&gt;&lt;/b&gt;)에서도 시청하실 수 있습니다.&lt;/p&gt;&lt;p style=&quot;line-height: 1.5;&quot;&gt;&lt;span style=&quot;font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.5;&quot;&gt;&lt;span style=&quot;font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.5;&quot;&gt;&lt;span style=&quot;font-size:12pt;&quot;&gt;안녕하세요, Kevin입니다. 지난 시리즈에 이어 오늘은 개발자가 되려면 무얼 배워야 하는지에 대한 이야기부터 하겠습니다. 당연한 이야기이겠지만 개발자가 되기위해서 배워야 할 기술들은 상당히 다양하고 많습니다. 그리고 여기서 말하는 개발자는 주로 &lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9B%B9_%EA%B0%9C%EB%B0%9C%EC%9E%90&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;웹 개발자&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;입니다. 아래 로드맵을 먼저 살펴보시면 &quot;우와, 이걸 언제 다 배워??&quot; 라고 한숨부터 나올지도 모르겠네요. ^^&lt;/span&gt;&lt;span style=&quot;font-size:12pt;&quot;&gt; 그래도 일단 스크롤을 내려서 끝까지 주욱 살펴보시길 권해드립니다.ㅎ&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.5;&quot;&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;2018 웹 개발자 로드맵&lt;/h2&gt;&lt;p style=&quot;line-height: 1.5; text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 900px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9965344F5B8A4D4029&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9965344F5B8A4D4029&quot; width=&quot;900&quot; height=&quot;388&quot; alt=&quot;웹 개발자 Intro&quot; filename=&quot;intro.png&quot; filemime=&quot;image/jpeg&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.5; text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 900px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/999BBF485B8A4D7B24&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F999BBF485B8A4D7B24&quot; width=&quot;900&quot; height=&quot;2559&quot; alt=&quot;프론트엔드 로드맵&quot; filename=&quot;frontend-v2.png&quot; filemime=&quot;image/jpeg&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.5; text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 900px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99A09A4D5B8A4D942C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99A09A4D5B8A4D942C&quot; width=&quot;900&quot; height=&quot;2530&quot; alt=&quot;백엔드 로드맵&quot; filename=&quot;backend-v2.png&quot; filemime=&quot;image/jpeg&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.5; text-align: center; clear: none; float: none;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.5; text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 900px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/997FAE465B8A4DD733&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F997FAE465B8A4DD733&quot; width=&quot;900&quot; height=&quot;1111&quot; alt=&quot;데브옵스 로드맵&quot; filename=&quot;devops.png&quot; filemime=&quot;image/jpeg&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.5; text-align: center;&quot;&gt;[출처: &lt;a href=&quot;https://github.com/devJang/developer-roadmap&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://github.com/devJang/developer-roadmap&lt;/a&gt;]&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h2&gt;현실적인 개발자 로드맵&lt;/h2&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;span style=&quot;font-size:12pt;&quot;&gt;쭈욱 살펴 보셨나요? 참고로 위에서 보셨던 로드맵은 번역본이고 실제 작성자는 해외에서 활동하시는분으로 추정되며(작성하신분은 현재 독일 베를린에서 활동하시는 개발자인것 같습니다.), 해외에서 개발자로 일을 하고 싶으신 분에게 추천 드리고 싶은&amp;nbsp;좋은 로드맵이라고 생각됩니다. 물론 이 로드맵의 작성자분도 필수적으로 알아야 할 부분과 선택적으로 배우길 추천하는 부분을 색깔로 잘 표시해놨지만 저는 일단 이 로드맵 안에 있는 항목들 중에서 &lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9B%B9_%EA%B0%9C%EB%B0%9C%EC%9E%90&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;웹 개발자&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;로써 취업을 하기 위해서 최소한으로 배워야 할 기술들만 추스려서 말씀을 드릴생각입니다. 특히나 한국의 특수한 상황을 고려해서 한국에서 취업하기에 조금이라도 더 용이한 기술들 위주로&amp;nbsp;말씀드리겠습니다. 이건 어디까지나 저의 개인적인 의견일 뿐이니 다른 의견을 가지시는 분이 있으시면 언제든지 댓글로 남겨주시면 감사드리겠습니다.^^&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;여러분들의 이해를 쉽게 하기 위해서 우선 간단한 코드를 먼저 보여드리고, 해당 코드에서 저희가 배워야 할 기술들을 추출해서 간단하게 로드맵을 한번 그려보겠습니다.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;h2&gt;클라이언트 + 서버(JSP Model 1)&lt;/h2&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;먼저 아래의 구성도를 한번 보도록 하겠습니다. 아래 구성도는 JSP Model1 방식의 아키텍처인데요.&amp;nbsp;웹 애플리케이션을 만들기 위한&amp;nbsp;서버측 언어로는 &lt;a href=&quot;https://namu.wiki/w/JSP&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;JSP&lt;/a&gt;, &lt;a href=&quot;https://ko.wikipedia.org/wiki/PHP&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;PHP&lt;/a&gt;, &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%95%A1%ED%8B%B0%EB%B8%8C_%EC%84%9C%EB%B2%84_%ED%8E%98%EC%9D%B4%EC%A7%80&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;ASP&lt;/a&gt; 등이 있는데, 제가 Java 개발자이기때문에 JSP를 기준으로 우선 설명을 드리도록 하겠습니다.&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: center;&quot;&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 900px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99EA30345B8B24E603&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99EA30345B8B24E603&quot; width=&quot;900&quot; height=&quot;363&quot; alt=&quot;JSP 모델 1 구성도&quot; filename=&quot;K-004.png&quot; filemime=&quot;image/jpeg&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;[JSP 모델1 구성도]&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;JSP Model1 방식은 아주 오래된 개발 방식인데 이를 설명하는 이유는 오래된 기술을 간단하게 리뷰함으로 인해서 오늘날 사용하고 있는 최신 기술들이 얼마나 잘 발전되어 왔는지를 조금 더 체감을 할 수 있지 않을까라고 생각하기때문입니다. 이 기술을 어떻게 사용하는지도 중요하지만 이 기술을 왜 사용해야 하는지에 대해서 알고 난 후에 해당 기술을 사용하는것이 훨씬 중요하다는 생각때문에 JSP 까지 설명을 하고 있네요. ^^&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;구성도를 보시면 웹 브라우저에서 요청을 받은 서버에서 JSP를 통해 해당 요청을 가공한 후, HTML 페이지를 응답으로 돌려줍니다. 실제로 JSP 코드를 보시면 HTML 코드와 JSP 코드가 뒤섞여있는것을 보실 수 있습니다.&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;여러분들의 이해를 조금 더 돕기 위해서 초간단 TODO 애플리케이션을 만들었습니다. 아래 화면을 보시죠.&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: center;&quot;&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 580px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9937A8345B8B261A0E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9937A8345B8B261A0E&quot; width=&quot;580&quot; height=&quot;593&quot; alt=&quot;TO DO 웹 애플리케이션&quot; filename=&quot;K-005.png&quot; filemime=&quot;image/jpeg&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;[TO DO 웹 애플리케이션]&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;할일과 날짜를 입력하고 서버측으로 전송하는 간단한 애플리케이션인데요. 이 애플리케이션의 소스 코드를 보면서 저희가 배워야 할 기술들을 한번 추출해보도록 하겠습니다.&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;그럼, JSP Model1 방식으로 구성된 TO DO 애플리케이션의 소스 코드를 보시죠.&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;==== todo_model1.jsp ====&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;&lt;pre style=&quot;background-color:#2b2b2b;color:#a9b7c6;font-family:'Consolas';font-size:14.3pt;&quot;&gt;&lt;p&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;%@ &lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;page &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;contentType=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;text/html;charset=UTF-8&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; language=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;java&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; %&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;%@ &lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;taglib &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;uri=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;http://java.sun.com/jsp/jstl/core&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; prefix=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot;%&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;%@ &lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;taglib &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;uri=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;http://java.sun.com/jsp/jstl/functions&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; prefix=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; %&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;meta &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;http-equiv=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;Content-Language&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;content=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;ko&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;meta &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;http-equiv=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;Content-Type&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;content=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;text/html; charset=UTF-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;title&amp;gt;&lt;/span&gt;TODO &lt;span style=&quot;font-family:'굴림체';&quot;&gt;등록&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/title&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;style&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        #todoList &lt;/span&gt;{&lt;br /&gt;            &lt;span style=&quot;color:#bababa;&quot;&gt;border&lt;/span&gt;: &lt;span style=&quot;color:#6897bb;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;px solid &lt;/span&gt;&lt;span style=&quot;color:#6897bb;&quot;&gt;#8F8F8F&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;width&lt;/span&gt;: &lt;span style=&quot;color:#6897bb;&quot;&gt;500&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;border-collapse&lt;/span&gt;: &lt;span style=&quot;color:#a5c261;&quot;&gt;collapse&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;        &lt;span style=&quot;color:#e8bf6a;&quot;&gt;th&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;td &lt;/span&gt;{&lt;br /&gt;            &lt;span style=&quot;color:#bababa;&quot;&gt;padding&lt;/span&gt;: &lt;span style=&quot;color:#6897bb;&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;border&lt;/span&gt;: &lt;span style=&quot;color:#6897bb;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;px solid &lt;/span&gt;&lt;span style=&quot;color:#6897bb;&quot;&gt;#8F8F8F&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;}&lt;br /&gt;    &lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/style&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;script&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;function &lt;/span&gt;registerTodo(){&lt;br /&gt;            &lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;var &lt;/span&gt;todoName = document.getElementById(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoName&quot;&lt;/span&gt;).value&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;var &lt;/span&gt;todoDate = document.getElementById(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoDate&quot;&lt;/span&gt;).value&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;if&lt;/span&gt;(!todoName){&lt;br /&gt;                alert(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;font-family:'굴림체';&quot;&gt;할일을 입력해주세요&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;..&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;                &lt;/span&gt;&lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;return false&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;}&lt;br /&gt;            &lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;if&lt;/span&gt;(!todoDate){&lt;br /&gt;                alert(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;font-family:'굴림체';&quot;&gt;날짜를 입력해주세요&lt;/span&gt;&lt;span style=&quot;color:#6a8759;&quot;&gt;.&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;                &lt;/span&gt;&lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;return false&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;            &lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;var &lt;/span&gt;form = document.getElementById(&lt;span style=&quot;color:#6a8759;&quot;&gt;&quot;todoForm&quot;&lt;/span&gt;)&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;            &lt;/span&gt;form.submit()&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        &lt;/span&gt;}&lt;br /&gt;    &lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;h3&amp;gt;&lt;/span&gt;TO DO &lt;span style=&quot;font-family:'굴림체';&quot;&gt;등록&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/h3&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;div&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;form &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;id=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoForm&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;method=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;POST&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;action=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;/result_model1.jsp&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;input &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;type=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;text&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;name=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoName&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;id=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoName&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;input &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;type=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;date&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;name=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoDate&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;id=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoDate&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;input &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;type=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;button&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;id=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;btnReg&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#a5c261;font-family:'굴림체';&quot;&gt;등록&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;onclick=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#ffc66d;&quot;&gt;registerTodo&lt;/span&gt;()&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;/form&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;div&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;h4&amp;gt;&lt;/span&gt;TO DO List&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/h4&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;table &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;id=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;todoList&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;thead&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                &amp;lt;tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                    &amp;lt;td &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;align=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;center&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;/span&gt;todo name&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                    &amp;lt;td &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;align=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;center&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;/span&gt;todo date&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                &amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;/thead&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;            &amp;lt;tbody&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:choose&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                    &lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:when &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;=&quot;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;fn:length(todoList) == &lt;/span&gt;&lt;span style=&quot;color:#6897bb;background-color:#232525;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                        &lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                            &amp;lt;td &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;align=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;center&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;colspan=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-family:'굴림체';&quot;&gt;할 일이 없습니다&lt;/span&gt;.&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                        &amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                    &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:when&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                    &lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:otherwise&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                        &lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:forEach &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;items&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;=&quot;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;todoList&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;&quot; &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;=&quot;todo&quot;&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                            &lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                                &amp;lt;td&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;todo.todoName&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                                &amp;lt;td &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;align=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;center&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;todo.todoDate&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                            &amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;                        &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:forEach&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                    &lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:otherwise&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;                &lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#9876aa;background-color:#232525;font-weight:bold;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;:choose&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;            &lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/tbody&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;        &amp;lt;/table&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;할일과 날짜를 입력 받아서 리스트로 표시하는 뷰 페이지입니다. 앞에서 말씀드린대로 &amp;lt;tbody&amp;gt;&amp;lt;/tbody&amp;gt; 부분에 보면 HTML 코드와 JSP 코드가 뒤섞여 있는것이 보이시나요?&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;==== result_model1.jsp ====&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;&lt;pre style=&quot;background-color:#2b2b2b;color:#a9b7c6;font-family:'Consolas';font-size:14.3pt;&quot;&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;%@ &lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;page &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;contentType=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;text/html;charset=UTF-8&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; language=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;java&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; %&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;%@ &lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;taglib &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;uri=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;http://java.sun.com/jsp/jstl/core&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; prefix=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot;%&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;%@ &lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;taglib &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;uri=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;http://java.sun.com/jsp/jstl/functions&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; prefix=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; %&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&amp;lt;%@ &lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;page &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;import=&quot;&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;com.itvillage.vo.ToDo&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;&quot; %&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;&amp;lt;%&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;request.setCharacterEncoding(&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;response.setContentType(&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;&quot;text/html;charset=UTF-8&quot;&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;System.out.println(&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;&quot;Hello Servlet doPost!&quot;&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;String todoName = request.getParameter(&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;&quot;todoName&quot;&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;String todoDate = request.getParameter(&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;&quot;todoDate&quot;&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;ToDo.todoList.add(&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;new &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;ToDo(todoName&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;todoDate))&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;RequestDispatcher dispatcher = request.getRequestDispatcher(&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;&quot;/todo_model1.jsp&quot;&lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;request.setAttribute(&lt;/span&gt;&lt;span style=&quot;color:#6a8759;background-color:#232525;&quot;&gt;&quot;todoList&quot;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;ToDo.todoList)&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;dispatcher.forward(request&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;background-color:#232525;&quot;&gt;response)&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;background-color:#232525;font-weight:bold;&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;font-weight:bold;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;meta &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;http-equiv=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;Content-Language&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;content=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;ko&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;meta &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;http-equiv=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;Content-Type&quot; &lt;/span&gt;&lt;span style=&quot;color:#bababa;&quot;&gt;content=&lt;/span&gt;&lt;span style=&quot;color:#a5c261;&quot;&gt;&quot;text/html; charset=UTF-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;title&amp;gt;&lt;/span&gt;TODO &lt;span style=&quot;font-family:'굴림체';&quot;&gt;등록&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/title&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;script&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;    &amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#e8bf6a;&quot;&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;todo_model1.jsp에서 랜더링된 뷰 페이지, 즉 할 일 등록 폼에서 사용자가 할 일을 입력하면 result_model1.jsp에서 해당 데이터를 저장 한 후에 다시 todo_model1.jsp로 포워딩 시킵니다. 여기서는 간단한 애플리케이션인지라 DB를 사용하지 않고, ArrayList에 할 일 데이터를 저장했습니다.&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;==== ToDo.java ====&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;&lt;pre style=&quot;background-color:#2b2b2b;color:#a9b7c6;font-family:'Consolas';font-size:14.3pt;&quot;&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;package &lt;/span&gt;com.itvillage.vo&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;java.util.ArrayList&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;import &lt;/span&gt;java.util.List&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;public class &lt;/span&gt;ToDo {&lt;br /&gt;    &lt;span style=&quot;color:#cc7832;&quot;&gt;private &lt;/span&gt;String todoName&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    private &lt;/span&gt;String todoDate&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    public static &lt;/span&gt;List todoList = &lt;span style=&quot;color:#cc7832;&quot;&gt;new &lt;/span&gt;ArrayList&amp;lt;&amp;gt;()&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    public &lt;/span&gt;ToDo(String todoName&lt;span style=&quot;color:#cc7832;&quot;&gt;, &lt;/span&gt;String todoDate) {&lt;br /&gt;        &lt;span style=&quot;color:#cc7832;&quot;&gt;this&lt;/span&gt;.todoName = todoName&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;        this&lt;/span&gt;.todoDate = todoDate&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    &lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;    &lt;span style=&quot;color:#cc7832;&quot;&gt;public &lt;/span&gt;String getTodoName() {&lt;br /&gt;        &lt;span style=&quot;color:#cc7832;&quot;&gt;return &lt;/span&gt;todoName&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    &lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;    &lt;span style=&quot;color:#cc7832;&quot;&gt;public &lt;/span&gt;String getTodoDate() {&lt;br /&gt;        &lt;span style=&quot;color:#cc7832;&quot;&gt;return &lt;/span&gt;todoDate&lt;span style=&quot;color:#cc7832;&quot;&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color:#cc7832;&quot;&gt;    &lt;/span&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: left;&quot;&gt;사용자가 입력한 할 일 데이터를 저장하는 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94%EB%B9%88%EC%A6%88&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Java Bean&lt;/a&gt; 클래스입니다. 이 Java Bean에 담아서 DB에 저장하는것이 일반적이나 여기서는 ArrayList에 담아서 메모리에서 가지고 있습니다. 물론 서버를 restart 시키면 해당 데이터는 사라지겠죠.&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;자, 그럼 이 JSP 모델 1 방식의 소스 코드로부터 저희가 배워야 할 기술들이 뭐가 있는지 한번 추출 해 볼까요?&lt;/div&gt;&lt;div style=&quot;line-height: 1.5; text-align: center;&quot;&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 481px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/995D61365B8B2AAB1A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F995D61365B8B2AAB1A&quot; width=&quot;481&quot; height=&quot;405&quot; alt=&quot;JSP 모델1 방식에서 추출한 기술&quot; filename=&quot;웹 개발자 로드맵.png&quot; filemime=&quot;image/jpeg&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;[JSP 모델1 방식에서 추출한 기술]&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;짜잔~ 여러가지 기술들이 추출 되었습니다. ^^ 웹 브라우저에서 사용자들에게 보여지는 부분은 &lt;a href=&quot;https://www.w3schools.com/html/default.asp&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;HTML&lt;/a&gt;로 구성이 됩니다. 그리고 HTML로 구성된 화면을 조금 더 예쁘게 보여주기 위해서 &lt;a href=&quot;https://www.w3schools.com/html/html_css.asp&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;CSS&lt;/a&gt;가 사용되며, 폼을 전송하는 부분에서는 &lt;a href=&quot;https://www.w3schools.com/js/default.asp&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Javascript&lt;/a&gt;가 사용이 됩니다.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;사실 Javascript를 TODO 애플리케이션에서 사용하지 않고, 데이터를 전송할 수 있으나 실무에서는 폼 데이터를 전송하기 전에 클라이언트측에서 데이터를 전송하기 전에&amp;nbsp;해당 데이터가 올바른 데이터인지 검사를 하는데 그 검사를 Javascript가 맡아서 하게 되는겁니다. 물론 Javascript로 검사한다고 해서 그 검사를 우회해서 데이터를 변조하는 방법이 있기때문에 서버측에서 반드시 유효성 검사를 다시 해야되나 이런 부분은 주제를 벗어나기때문에 우선은 생략하도록 하겠습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;HTML과 Javascript, CSS는 개발자가 되기 위한 필수 기술이기때문에 꼭 학습을 해야되는 부분입니다.&lt;/b&gt; 다만 Javascript를 공부하다보면 굉장히 방대하고 공부하면 할수록 점점 어려워진다는 사실을 알 수 있을텐데 Javascript를 많이 알고 능숙하게 사용하실 수 있다면 그만큼 자신의 경쟁력을 향상시키는것이라고 보시면 될것 같네요. ^^&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;그럼 JSP는 배워야 할까요 배우지 말아야 할까요? 이건 어떤 기업에 입사를 하느냐에 많이 좌우되는데 &lt;a href=&quot;https://ko.wikipedia.org/wiki/Ajax&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Ajax&lt;/a&gt; 기술이 발달함으로 인해서 JSP의&amp;nbsp;사용 빈도는 점점 줄어들고 있는 추세입니다. 기술 트렌드를 잘 따라가는 기업일수록 JSP 사용 빈도가 작지 않을까 생각합니다.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Java는 Java 개발자가 되기위해서는 반드시 알아야 될 기술&lt;/b&gt;이기에 따로 설명을 드리지 않아도 되겠죠? ^^&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;데이터베이스의 경우 클라이언트 측으로부터 전달 받은 데이터를 저장하는 가장 범용적인 방법이기때문에 반드시 배우셔야 하는데&lt;/b&gt; MySQL(또는 MariaDB) 등의 오픈 소스 DB에 대한 사용법과 SQL 쿼리 작성에 대한 학습을 꾸준히 하시는게 좋을것 같습니다. 데이터베이스 종류가 다양하지만 한국에서 가장 많이 사용되는 데이터베이스는 MySQL(또는 MariaDB)과 Oracle + MSSQL 이지 않을까 하는데요. 기업에서는 PHP + MySQL(또는&amp;nbsp;MariaDB), JSP + Oracle, ASP + MSSQL 의 조합으로 많이들 사용해왔습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;마지막으로 말씀드릴게 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%ED%86%B5%ED%95%A9_%EA%B0%9C%EB%B0%9C_%ED%99%98%EA%B2%BD&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;IDE(Integrated Development Enviroment)&lt;/a&gt;인데요.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;코딩, 디버깅, 테스트, 컴파일, 배포를 하나의 환경에서 할 수 있도록 제작된 한마디로 말해서 맥가이버 같은 개발 도구라고 보시면 됩니다. 자바 개발에 있어서 오픈 소스 IDE인 &lt;a href=&quot;https://www.eclipse.org/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;이클립스&lt;/a&gt;가 전통적으로 가장 많이 사용되고 있을것으로 생각됩니다. 저는 개인적으로 몇년전부터 &lt;a href=&quot;https://www.jetbrains.com/idea/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;IntelliJ&lt;/a&gt;를 사용하고 있는데 현재까지는 최고의 IDE라는 생각이 듭니다. 기업에서 사용하려면 유료라는게 단점이긴 하지만 개인이 사용 가능한 커뮤니티 버전이나 학생용 라이센스를 잘 활용할 수 있으니 참고하시길 바랍니다. IDE 이외에 &lt;a href=&quot;https://www.sublimetext.com/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;서브라임텍스트&lt;/a&gt;, &lt;a href=&quot;https://atom.io/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Atom&lt;/a&gt; 등의 Editor가 있으니 참고하시길요.&amp;nbsp;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;이번 시간에는 JSP Model1 방식을 통해 웹 개발자로 입문하기 위해서 무엇부터 배워야 할지를 알아보았는데요. 제가 Java&amp;nbsp;개발자인 관계로 서버측 언어로 Java를 사용하고 있으니 이 점 염두에 두시고 읽어주시면 감사드리겠습니다.&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;다음 시간에는 JSP Model1 방식의 문제점을 살펴보고 이를 개선한 방식에서 저희가 알아야 할 기술에는 어떤게 있는지 살펴보도록 하겠습니다.&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;그럼 좋은 하루 보내시길 바라면서..&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;line-height: 1.5;&quot;&gt;&lt;ul style=&quot;list-style-type: square;&quot;&gt;&lt;li&gt;&lt;font face=&quot;Nanum Gothic, Noto Sans Korean&quot;&gt;&lt;span style=&quot;font-size: 14px; background-color: rgb(241, 241, 241);&quot;&gt;&lt;a href=&quot;https://inf.run/xezD&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;[ Kevin의 알기 쉬운 Java 개발자 로드맵 이야기 ] 인프런 강의 바로&lt;/a&gt;&lt;a href=&quot;https://inf.run/xezD&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;가기&lt;/a&gt;&lt;/span&gt;&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;[참고 사이트]&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/kamranahmedse/developer-roadmap&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;https://github.com/kamranahmedse/developer-roadmap&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;&lt;a href=&quot;https://github.com/devJang/developer-roadmap&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://github.com/devJang/developer-roadmap&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://namu.wiki/w/JSP&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://namu.wiki/w/JSP&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/PHP&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://ko.wikipedia.org/wiki/PHP&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%95%A1%ED%8B%B0%EB%B8%8C_%EC%84%9C%EB%B2%84_%ED%8E%98%EC%9D%B4%EC%A7%80&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://ko.wikipedia.org/wiki/%EC%95%A1%ED%8B%B0%EB%B8%8C_%EC%84%9C%EB%B2%84_%ED%8E%98%EC%9D%B4%EC%A7%80&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94%EB%B9%88%EC%A6%88&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94%EB%B9%88%EC%A6%88&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.w3schools.com/html/default.asp&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://www.w3schools.com/html/default.asp&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.w3schools.com/css/default.asp&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://www.w3schools.com/css/default.asp&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.w3schools.com/js/default.asp&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://www.w3schools.com/js/default.asp&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/Ajax&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://ko.wikipedia.org/wiki/Ajax&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.eclipse.org/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://www.eclipse.org/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.jetbrains.com/idea/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://www.jetbrains.com/idea/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.sublimetext.com/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://www.sublimetext.com/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://atom.io/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://atom.io/&lt;/a&gt;&lt;/p&gt;</description>
      <category>현실적인 개발자 로드맵</category>
      <category>개발자</category>
      <category>로드맵</category>
      <category>백엔드</category>
      <category>웹</category>
      <category>웹개발자</category>
      <category>프론트엔드</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/19</guid>
      <comments>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5-%EB%AC%B4%EC%97%87%EB%B6%80%ED%84%B0-%EB%B0%B0%EC%9B%8C%EC%95%BC-%ED%95%A0%EA%B9%8C%EC%9A%94#entry19comment</comments>
      <pubDate>Sat, 1 Sep 2018 17:54:38 +0900</pubDate>
    </item>
    <item>
      <title>현실적인 개발자 로드맵(1) - Intro</title>
      <link>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5-Intro</link>
      <description>&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;안녕하세요? Kevin 이라고 합니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;font-size: 18.6667px; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;블로그 개설한지 4년이나 지났는데 바쁘다는 핑계로 거의 방치하다시피 했네요. 아주 아주 새로운 마음으로 이제 다시 시작하려 합니다. ^^&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;제 소개를 잠깐 드리자면, 저는 올해로 개발 10년차가 되는 자바 개발자로 대한민국에서 꿋꿋하게 개발자로 버티고 살아가고 있는 평범한(?) 40대 현업 개발자입니다. ㅡ.,ㅡ;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;개발자로 억지로 버티면서 살아가고 싶은 마음은 눈꼽만큼도 없지만 주위에서 저를 버티고 살아가게 만들더군요. 개발자로 즐겁게 살아갈 수 있는 날이 올 수 있으려나 모르겠지만&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;저 나름대로 개발을 천직으로 알고 즐겁게 살아가기 위해 노력중이죠. 아무튼 제 얘기는 글을 쓰면서 종종 들려드리도록 하구요.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;IT Village 라는 블로그를 시작하게 된 계기, 그리고 앞으로 블로그를 어떤식으로 운영할 것인가에 대해서 짤막하게 나마 말씀을 드리는것이 제 블로그를 찾아주시는 분들이&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif; font-size: 14pt; font-style: inherit; font-variant-ligatures: inherit; font-variant-caps: inherit; font-weight: inherit;&quot;&gt;조금 더 관심을 가지고 제가 올린 글들을 흥미롭게 읽어주시고 그 분들에게 작은 도움이나마 드릴 수 있지 않을까 하네요.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;h2 style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.2; font-size: 38px; font-weight: normal; margin: 0px; outline: 0px; padding: 0px 0px 18px; vertical-align: baseline; color: rgb(51, 51, 51); visibility: visible; font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;블로그 시작 계기&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;그럼 먼저 이 블로그를 시작하게 된 계기를 짤막하게 얘기 드려볼까요? ^^;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;사실 이 블로그 이전에 제가 10년전부터 운영을 했던&amp;nbsp;IT Village 카페(&lt;/span&gt;&lt;a href=&quot;http://cafe.daum.net/ITVillage&quot; target=&quot;_blank&quot; rel=&quot;noopener nofollow&quot; class=&quot;external external_icon&quot; data-slimstat=&quot;5&quot; style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 18.6667px; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px 13px 0px 0px; vertical-align: baseline; color: rgb(86, 86, 86); background: url(&amp;quot;external.png&amp;quot;) right center no-repeat;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;http://cafe.daum.net/ITVillage&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;)가 현재도 존재합니다. 회원수가 무려 500명이 넘는다는.. ^^; 회원수가 너무 작죠?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;뭐 어쨌든간에 이 카페에 나름 제가 10년동안 개발자 생활을 해오면서 나름 개발 입문자분들에게 도움을 주고자 많은 글들을 포스팅했는데,&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;기술이 하루가 다르게 변해가는데도 불구하고 이직, 결혼 등등으로 인해 3년전부터 거의 관리를 못하게 되었더랬습니다. 이렇게 관리를 못하고 방치를 할 바에는 차라리&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;카페를 폐쇄할까라는 생각도 했었는데 그러기에는 가끔씩 찾아주시는 회원분들의 “좋은 정보 알게되어서 감사합니다”라는 한마디에 차마 그럴 수 없었네요. ㅠㅠ&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;결론은 여전히 카페에 남아 있는 단 하나의 유용한 정보라도 찾아주시는 분들에게 제공할 수 있기때문에 카페는 회원제가 아닌 누구나 정보를 확인할 수 있도록 오픈할 예정입니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;그리고 카페에 남기지 못했던 나날이 변해가는 기술들에 대한 이야기는 이곳 IT Village 블로그에서 모두 하려합니다. 부디 이곳에서도 많은 분들에게서 도움이 되었다는 한마디를&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;들을 수 있길 바래보겠습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;h2 style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.2; font-size: 38px; font-weight: normal; margin: 0px; outline: 0px; padding: 0px 0px 18px; vertical-align: baseline; color: rgb(51, 51, 51); visibility: visible; font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;블로그 포스팅 방식 및 순서&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;어제, 오늘 한강 시민 공원을 뛰면서 블로그를 어떤 식으로 운영하는것이 좋을까라고 숨을 헐떡헐떡 거리면서 생각을 해봤었는데요.ㅎ&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;기존의 카페와 블로그에 포스팅한 글들은 거의 대부분이 독자들을 배려했다기 보다는 제 위주의 글들이었고, 소프트웨어 개발에 처음 입문하는분들에 대한 배려가 거의 없었다고 생각합니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;의도된건 아니지만 아무튼 그랬습니다. 어쨌거나 이 블로그에서는 소프트웨어 개발 입문자들이나 초급 개발자분들한테 실질적으로 도움을 줄 수 있는 현실적인 이야기들을 많이 하려고합니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;블로그도 블로그이지만 블로그로 나누기에 부족한 내용들의 경우 다른 SNS나 유튜브를 통해서 소통을 할 수 있도록 노력할 생각입니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;현재까지 생각한 포스팅 순서는 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;&lt;h3 style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.2; font-size: 34px; font-weight: normal; margin: 0px; outline: 0px; padding: 0px 0px 18px; vertical-align: baseline; color: rgb(51, 51, 51); visibility: visible; font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;Intro&lt;/span&gt;&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;Intro에서는 소프트웨어 개발 입문자들이 현시점에서 알아야하는 기술들이 어떤것들이 있는지 간단한 프로젝트를 같이 수행해보면서 입문자분들이&amp;nbsp;&lt;/span&gt;&lt;strong style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 18.6667px; font-style: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;개발자로서 바른길을 걸어갈 수 있는 방향을 제시할 수 있는 로드맵&lt;/span&gt;&lt;/strong&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;을 그려보는것을 위주로 포스팅을 할까 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;그 외에 입문자들이 실무에서 반드시 알았으면 좋겠다 싶은 내용이 있으면 제가 알고 있는것들은 모두 알려드려볼까 합니다. 회사 생활하면서 겪은 내용들이 많으니 작은 도움이라도 드릴 수 있지 않을까 생각하게 되네요.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;제가 자바 개발자이다 보니 주로 자바 개발 위주로 알려드리긴 하겠지만 프로그래밍 언어와 상관없는 공통 부분도 존재한다는것 또한 미리 말씀을 드리겠습니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;h3 style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.2; font-size: 34px; font-weight: normal; margin: 0px; outline: 0px; padding: 0px 0px 18px; vertical-align: baseline; color: rgb(51, 51, 51); visibility: visible; font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible;&quot;&gt;&lt;strong style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 18.6667px; font-style: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;40대 개발자 생존 전략&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;말씀 드렸다시피 저는 현재 40대 초중반을 바라보는 현업 자바 개발자입니다. ^^;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;대한민국에서 40대 개발자로 살아간다는게 흔한 일인지 드문 일인지 솔직히 잘은 모르겠지만 다만, 제 주위의 현실을 객관적으로 들여다보면 40대 개발자가 그리 많지 않은것 같다가 제 결론입니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;저희 회사만 해도 저랑 비슷한 나이대 분들중에 개발하시는 분들은 한분도 못봤습니다.&amp;nbsp; 저빼고 모두 팀장, 부서장, 임원급이라는.. ㅡ.ㅡ; 한마디로 관리직이나 영업직이라는 얘기죠.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;여러분들은 어떠세요? 40대가 되어서도 여전히 개발을 하고 싶으신가요? 아니면 개발을 하고 싶은데 어쩔 수 없이 관리직이나 영업직으로 전환하고 싶으신가요? 저는 백발이 희끗해도(물론 지금도 흰머리가 셀 수 없이 많긴하지만..ㅎ) 여전히 개발이 하고 싶기때문에 저와 같은 생각을 가지고 계신분들에게&amp;nbsp;&lt;/span&gt;&lt;strong style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 18.6667px; font-style: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;어떻게 하면 40대에도 여전히 개발을 할 수 있는지에 대해 제 경험과 생각을 들려드릴까합니다.&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;저와 비슷한 생각을 가지는 분들에게 조금이나마 도움이 될 수 있길 바라면서 조심스럽게 글을 써 볼 예정이니 조금만 기다려주시길 부탁드려볼게요.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;h3 style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.2; font-size: 34px; font-weight: normal; margin: 0px; outline: 0px; padding: 0px 0px 18px; vertical-align: baseline; color: rgb(51, 51, 51); visibility: visible; font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible;&quot;&gt;&lt;strong style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 18.6667px; font-style: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;프로그래밍 강좌&lt;/span&gt;&lt;/strong&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;현업에 종사하시는 많은 분들, 그리고 현업에 종사하지 않더라도 프로그래밍에 관심있는 멋진 분들이 프로그래밍 강좌를 많이들 올려주셔서 저도 그런 강좌들을 보면서 많은 것을 배워왔더랬습니다. 그렇기때문에 저 역시도 제가 알고 있는 프로그래밍 언어들에 대한 강좌를 진행해볼까 합니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;Intro 섹션에서 나중에 말씀드리겠지만 거기서 만든 로드맵에 있는 기술들중에서 제가 알고 있는 기술들에 한해서 강좌를 진행해볼 생각입니다. 10년차 개발자이지만 여전히 부족한것이 많은 놈이니 강좌를 보시고 부족한 부분이 보이면 언제든지 말씀해주시면 감사드리겠습니다. 그래야 저도 더 배우고 성장할 수 있으니까요.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;이게 끝나면 뭘 할까요?&amp;nbsp; 끝날리가 없죠. ^^ 저 3가지만해도 많은 시간이 흘러갈테고, 그때가 되면 또 새로운 이야기들을 할 수 있는 기회가 계속 생길꺼라고 봐요. ^^&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;개인적으로 맥주를 사랑하는 저이기때문에 기회가 되면 맥주에 대한 포스팅도 번외로 진행을 해볼까 합니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; font-size: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68); font-family: &amp;quot;Nanum Gothic&amp;quot; !important;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; border: 0px; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-size: 14pt; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;그럼 다음 시간에는 간단한 프로젝트를 진행 해보면서 개발 입문자들을 위한 앞으로의 로드맵을 그려보도록 하겠습니다.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68);&quot;&gt;&lt;font face=&quot;Gulim, 굴림&quot;&gt;&lt;span style=&quot;font-size: 18.6667px; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;그럼 다음 시간에 뵙도록 하겠습니다. Coming soon..&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68);&quot;&gt;&lt;font face=&quot;Gulim, 굴림&quot;&gt;&lt;span style=&quot;font-size: 18.6667px; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: square;&quot;&gt;&lt;li&gt;&lt;p style=&quot;box-sizing: border-box; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-stretch: inherit; line-height: 1.6; margin-right: 0px; margin-bottom: 15px; outline: 0px; padding: 0px; vertical-align: baseline; visibility: visible; color: rgb(68, 68, 68);&quot;&gt;&lt;font face=&quot;Gulim, 굴림&quot;&gt;&lt;span style=&quot;font-size: 18.6667px; font-family: &amp;quot;맑은 고딕&amp;quot;, sans-serif;&quot;&gt;[&lt;a href=&quot;https://inf.run/xezD&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;kevin의 알기 쉬운 Java 개발자 로드맵 이야기]&amp;nbsp;인프런 강의 바로가기&lt;/a&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>현실적인 개발자 로드맵</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/18</guid>
      <comments>https://itvillage.tistory.com/entry/%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5-Intro#entry18comment</comments>
      <pubDate>Fri, 31 Aug 2018 12:44:52 +0900</pubDate>
    </item>
    <item>
      <title>소프트 키보드 오픈 시, 리스트뷰의 아이템(체크박스, 버튼 등) 상태 유지 안됨</title>
      <link>https://itvillage.tistory.com/entry/%EC%86%8C%ED%94%84%ED%8A%B8-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%98%A4%ED%94%88-%EC%8B%9C-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%B7%B0%EC%9D%98-%EC%95%84%EC%9D%B4%ED%85%9C%EC%B2%B4%ED%81%AC%EB%B0%95%EC%8A%A4-%EB%B2%84%ED%8A%BC-%EB%93%B1-%EC%83%81%ED%83%9C-%EC%9C%A0%EC%A7%80-%EC%95%88%EB%90%A8</link>
      <description>&lt;p&gt;안드로이드 학습을 하다가 다시 그만두다가를 반복했는데 필요에 의해서 다시 학습을 시작하고 있다.&lt;/p&gt;&lt;p&gt;1차 목표는 리스트 뷰를 사용하여 아이템을 등록하고, 해당 아이템의 상태를 변경하는 간단한 앱을 만들어서 마켓에 올리는 것이다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 413px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/224B1F3858980DE718&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F224B1F3858980DE718&quot; width=&quot;413&quot; height=&quot;810&quot; filename=&quot;K-002.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;[그림1] 아이템의 상태 변경&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 413px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/251B443358980E4B2A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F251B443358980E4B2A&quot; width=&quot;413&quot; height=&quot;810&quot; filename=&quot;K-001.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;[그림2] 아이템의 상태 변경&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;개발중에 가장 문제가 되었던것중에 하나가&amp;nbsp;&lt;/p&gt;&lt;p&gt;우선,&amp;nbsp;[그림1]에서와 같이 아이템의 체크박스에 체크를 하고, 구매 상태를 변경한 후에 EditText에 포커싱이 되어서 소프트 키보드가 오픈이 되는 순간&lt;/p&gt;&lt;p&gt;[그림1]에서 변경했던 상태가 [그림2]에서처럼 유지가 되지 않는것이었다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Android/Listview 관련</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/15</guid>
      <comments>https://itvillage.tistory.com/entry/%EC%86%8C%ED%94%84%ED%8A%B8-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%98%A4%ED%94%88-%EC%8B%9C-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%B7%B0%EC%9D%98-%EC%95%84%EC%9D%B4%ED%85%9C%EC%B2%B4%ED%81%AC%EB%B0%95%EC%8A%A4-%EB%B2%84%ED%8A%BC-%EB%93%B1-%EC%83%81%ED%83%9C-%EC%9C%A0%EC%A7%80-%EC%95%88%EB%90%A8#entry15comment</comments>
      <pubDate>Fri, 3 Feb 2017 13:15:19 +0900</pubDate>
    </item>
    <item>
      <title>How to use ng-cloak</title>
      <link>https://itvillage.tistory.com/entry/How-to-use-ng-cloak</link>
      <description>&lt;p&gt;Users may see expressions which is defined at inline template, when they use a device with low speed.&lt;/p&gt;&lt;p&gt;In that case, We can use &lt;span style=&quot;color: rgb(255, 0, 0);&quot;&gt;&lt;b&gt;ng-cloak&lt;/b&gt;&lt;/span&gt; directive.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;txc-textbox&quot; style=&quot;border: 1px dashed rgb(159, 211, 49); padding: 10px; background-color: rgb(231, 253, 181);&quot;&gt;&lt;p&gt;&amp;lt;div class=&quot;well&quot;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;radio&quot; ng-repeat=&quot;button in ['None', 'Table', 'List']&quot;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;label &lt;span style=&quot;color: rgb(255, 0, 0);&quot;&gt;&lt;b&gt;ng-cloak&lt;/b&gt;&lt;/span&gt;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;input type=&quot;radio&quot; ng-model=&quot;data.mode&quot; value=&quot;{{button}}&quot; ng-checked=&quot;$first&quot;/&amp;gt; {{button}}&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/label&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div ng-switch on=&quot;data.mode&quot; &lt;b&gt;&lt;span style=&quot;color: rgb(255, 0, 0);&quot;&gt;ng-cloak&lt;/span&gt;&lt;/b&gt;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div ng-switch-when=&quot;Table&quot;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;table class=&quot;table&quot;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;thead&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;tr&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;th&amp;gt;#&amp;lt;/th&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;th&amp;gt;Action&amp;lt;/th&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;th&amp;gt;Done&amp;lt;/th&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/tr&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/thead&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;tr ng-repeat=&quot;item in todos&quot; ng-class=&quot;$odd ? 'odd' : 'even'&quot;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;td&amp;gt;{{$index + 1}}&amp;lt;/td&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;td ng-repeat=&quot;prop in item&quot;&amp;gt;{{prop}}&amp;lt;/td&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/tr&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/table&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div ng-switch-when=&quot;List&quot;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;ol&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;li ng-repeat=&quot;item in todos&quot;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {{item.action}}&amp;lt;span ng-if=&quot;item.complete&quot;&amp;gt;(Done)&amp;lt;/span&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/li&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/ol&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div ng-switch-default&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Select another option to display a layout&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/p&gt;&lt;/div&gt;</description>
      <category>AngularJS/Template Directives</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/14</guid>
      <comments>https://itvillage.tistory.com/entry/How-to-use-ng-cloak#entry14comment</comments>
      <pubDate>Tue, 7 Jun 2016 10:16:18 +0900</pubDate>
    </item>
    <item>
      <title>Listing up branch</title>
      <link>https://itvillage.tistory.com/entry/Listing-up-branch</link>
      <description>&lt;p&gt;* Listing up just&amp;nbsp;local branch&lt;/p&gt;&lt;div class=&quot;txc-textbox&quot; style=&quot;border: 1px dashed rgb(159, 211, 49); padding: 10px; background-color: rgb(231, 253, 181);&quot;&gt;&lt;p&gt;$ git branch&lt;/p&gt;&lt;p&gt;* RB_1.0.1&lt;/p&gt;&lt;p&gt;&amp;nbsp; master&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Git/branch</category>
      <author>Kevin's IT Village</author>
      <guid isPermaLink="true">https://itvillage.tistory.com/13</guid>
      <comments>https://itvillage.tistory.com/entry/Listing-up-branch#entry13comment</comments>
      <pubDate>Wed, 12 Aug 2015 08:10:19 +0900</pubDate>
    </item>
  </channel>
</rss>