<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>댕댕아 코딩하자</title>
    <link>https://sedangdang.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 8 Jun 2026 22:54:02 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>세댕댕이</managingEditor>
    <image>
      <title>댕댕아 코딩하자</title>
      <url>https://tistory1.daumcdn.net/tistory/4569167/attach/7a7c7ef601fa4037b914a709a1519084</url>
      <link>https://sedangdang.tistory.com</link>
    </image>
    <item>
      <title>신입 백엔드 개발자 면접 경험하며 받았던 질문 목록</title>
      <link>https://sedangdang.tistory.com/309</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;올해 초부터 본격적으로 개발자 취업 시장에 뛰어들어서 다양한 IT 기업에 이력서를 넣기 시작했고, 감사하게도 면접도 꽤 많이 볼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;면접은 많이 봐야 는다&quot;&lt;/b&gt;고 이야기 많이 들었는데 이거 진짜 100000% 맞는 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접 볼 수 있는 기회가 생긴다면 꼭 가보는 것을 추천하고, 몇번 가보다 보면 생각보다 면접 질문들이 거기서 거기구나 하는 느낌을 꽤 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 만약 다대다 면접이라면 다른 지원자는 어떻게 준비했구나 직접 들어볼 수 있는 절호의 기회이기 때문에 면접이 잡힌다면 꼭 가보는 것이 좋다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접비나 면접 선물? 키트를 주는 곳들도 꽤 있는데 그런걸 비교해 보는 것도 나름의 소소한 재미가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운이 좋게도 한 기업에 최종 합격하게 되었고, 그동안 취준하면서 받았던 질문 목록들에 대해 블로그에 남겨보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 물어보는 필수 질문에 대해서는 &lt;b&gt;굵게 강조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;[인성 질문]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 흔하게 물어보는 기본적인 인성 질문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;간단한 자기소개&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;- 정말 짧게 해달라, 개발자가 되기 위해 어떤 공부를 해왔는지 위주로 해달라, 지원동기와 함께 말해달라 등.. 여러 방식으로 변형해서 요청한다&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;자신이 생각하는 성격의 장단점과 단점을 개선하기 위해 노력하는 방법 (사례와 함께 소개해달라)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;개발자로서의 목표가 있는지, 5년 후/10년 후 목표&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 회사에 지원하게 된 동기, 우리 회사를 선택한 이유는?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&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;span&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;성공 경험이 있다면? 인생에서 무엇인가를 이뤄본 경험이 있으면 소개해달라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 스트레스를 해소하는 방법은 무엇인지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 취미가 뭔지, 주말에 뭐하는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 도전 경험이 있다면? 인생에서 가장 큰 고비를 겪어본 경험이 있다면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 존경하는 사람이 있는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 개발자, 백엔드 개발에 관심을 갖게된 이유가 있는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 개발이 적성에 잘 맞는다고 생각하는지? 그 이유는 무엇인지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 학과로 진학한 이유는 무엇인지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자신의 어떤 역량이 우리 회사와/직무와 가장 잘 맞는다고 생각하는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다른 지원자와 비교했을 때 내가 더 뛰어나다고 생각하는 부분이 있다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 회사에 대해 궁금한 점?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;[인성 질문++]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 업무를 진행하면서 어려움이 생긴다면 어떻게 대응할 것인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 우리 회사 말고도 다른 회사에도 면접을 꽤 봤을 것 같은데, 여러 곳에 합격하면 어디를 갈 것인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 주니어 개발자와 시니어 개발자의 차이가 뭐라고 생각하나?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자신이 생각하는 개발자에게 가장 중요한 덕목이 뭐라고 생각하나?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 회사 생활에 있어 동료와의 화합, 내 기술적인 성장, 회사의 성장 중 무엇이 가장 중요하다고 생각하나?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 코더와 개발자의 차이가 뭐라고 생각하나?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사수가 소극적인 경우 어떻게 대응할 것인지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 선배가 잘못된 지시를 할 경우(잘못된 정보를 전달하는 경우) 어떻게 할 것인지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 긍정적 피드백과 부정적 피드백의 장점과 단점을 각각 설명해봐라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자신은 리더형이라고 생각하는지 팔로워형이라고 생각하는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;- 야근에 대해 어떻게 생각하는지? / 워라밸에 대해 어떻게 생각하는지?&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;&amp;lt; 이거 은근 자주 물어봄&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사업 할 생각 있는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 생각해둔 연봉 수준이 있다면 솔직하게 말해달라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(부트캠프 들은 사람에 한해)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 전공자인데 부트캠프를 추가로 들은 이유가 있는지?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 회사와 부트캠프의 차이가 뭐라고 생각하는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 이외에도 비전공자라면 &lt;b&gt;개발 직무를 선택한 이유&lt;/b&gt; 이런 질문은 꼭 한다고 생각하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;[기술 질문]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술 질문은 신입이기 때문에 진심으로 듣도보도 못한 기술에 대한 질문을 던지지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 개발 공부 하면서 한번 이상 듣고 사용해봤던 정도 / 포폴에 적힌 내용에 대해서만 물어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;자료구조&amp;amp;알고리즘&amp;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;- 탐색 알고리즘에는 어떤게 있고, 특징은 무엇인지? (BFS, DFS...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 컬렉션 프레임워크의 종류에 대해 각각 설명해달라 (List, Set, Map)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 프로젝트를 진행하면서 특정 자료구조/알고리즘을 사용해 문제를 효율적으로 해결한 경험이 있다면 소개해달라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;Java &amp;amp; 스프링&amp;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;- Java 11을 사용했다고 하는데, Java 11의 특징은 무엇인지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 람다식과 스트림을 답변, 람다식과 스트림은 무엇인지 다시 질문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 함수형 프로그래밍 어쩌고 답변, 그럼 함수형 프로그래밍이 무엇인지 다시 질문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 스트림 사용해 봤는지? 반복문을 사용했을 때랑 성능 차이를 직접 비교해봤나? &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;&amp;lt; 말문 막힘..&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;- Call by Value와 Call by Reference에 대해 설명해달라&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; - DI(의존성 주입)에 대해 답변 &amp;gt; 의존성 주입이 무엇인지? 왜 쓰는지 설명해달라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 코드 내에서 new 키워드 안써도 된다 &amp;gt; 코드 내에서 new 키워드 쓰면 안좋은 이유가 뭔지?&lt;span style=&quot;color: #9d9d9d;&quot;&gt; (내가 답변을 잘못했다..)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- JPA를 사용했던데 Mybatis 사용해본 경험이 있는지? JPA와 Mybatis는 무엇이 다른지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- JPA의 특징이 무엇인지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 동기, 비동기의 차이를 아는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;웹&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Rest API란 무엇인지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- HTTP 메서드에 대해 아는대로 설명해봐라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- TCP/UDP의 차이에 대해 아는대로 설명해봐라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;DB&amp;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;- Join의 종류에 대해 아는대로 모두 설명해봐라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 관계형 데이터베이스와 NoSQL의 차이가 무엇인지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Redis를 사용해봤다고 하는데, 왜 사용했는지 이유를 말해달라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 인덱스가 뭔지, 직접 사용해 본 적이 있는지?&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 동적 쿼리 사용해본 경험이 있는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 테이블 키 설정은 어떻게 구성했는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;nbsp; &amp;nbsp; - (참고) 실무에서는 외래키를 사용하지 않는 경우가 많다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;서버&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 리눅스를 다룰 줄 안다고 했는데, 리눅스 명령어를 아는대로 모두 이야기 해봐라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - screen 명령어 언급 &amp;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;- AWS EC2를 직접 구축했다고 했는데 초기 설정을 어떻게 했는지 모두 이야기 해봐라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Docker 사용해 봤는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- AWS에 배포 해보셨다고 하는데 로드 밸런싱 해봤는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;프로젝트 관련 및 기타&amp;gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- &lt;span style=&quot;color: #333333;&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: #ef5369;&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: #9d9d9d;&quot;&gt;&amp;nbsp; &amp;nbsp; - 기술 면접의 핵심 중에 핵심 질문.. 무수한 꼬리질문을 만날 수 있다&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;nbsp; &amp;nbsp; - 이건 왜 이렇게 구현하셨나요? 그냥 이렇게 하면 되는거 아니에요? 하면 당황하게 될 확률이 높으니 준비를 잘...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 프로젝트 중에서 가장 도전적인, 어려웠던 부분은 어떤 부분이었는지. 그리고 이를 어떻게 해결했는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 프로젝트를 하면서 가장 큰 실수를 한 것이 있다면 무엇인지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 프로젝트를 하면서 어떤 부분이 가장 힘들었는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 원활한 프로젝트 진행을 위해 희생하거나 양보했던 경험이 있는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 회사에서 사용해보고 싶은 기술이 있는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 현재 관심을 갖고 있는 기술이 있다면?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 가장 최근에 공부한 내용은 무엇인가? 가장 최근에 읽은 개발 서적은 무엇인지 내용을 간단하게 설명해봐라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 가장 최근에 봤던 IT 이슈 또는 신기술은 무엇인지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기타 대외활동에 관한 질문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 학교에서 들었던 과목 중 가장 기억에 남는 강의가 있다면 무엇인지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&lt;b&gt; (코테 전형이 있었다면)&lt;/b&gt; 코딩 테스트는 난이도가 어땠는지? 할만 했는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 생각보다 인성 질문을 많이 한다. 개발자라고 기술 면접만 준비하면 광탈의 지름길이다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 면접은 대부분 &lt;b&gt;일대다&lt;/b&gt; 면접이었고 간혹 다대다 면접을 보는 경우도 있었다. 화상 면접을 볼 수도 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;소요시간은 30분~1시간 정도, &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: #6164c6;&quot;&gt; &lt;b&gt;면접 복장&lt;/b&gt;&lt;/span&gt;이 진짜 처음에 고민이 많이 되는데 남자의 경우 &lt;b&gt;정장&lt;/b&gt; 입고 가는게 진심으로 속 편하다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 아마 대부분 기업들이&lt;b&gt; 단정한 복장, 편안한 복장&lt;/b&gt;으로 명시를 해놓을 것..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 회사 바이 회사겠지만 면접 대기실에 딱 들어갔는데 나 빼고 다 정장 입고있는거 보면 괜히 그때가서 후회하게 된다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 복장이 크게 당락에 영향을 주는 것 같지는 않지만 심리적으로 편안..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* &lt;b&gt;&quot;아는대로 전부&quot;&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;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;성능 테스트&lt;/b&gt;&lt;/span&gt;를 해보고 &quot;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;수치적으로&lt;/b&gt;&lt;/span&gt;&quot; 얼마만큼 개선되었다고 말할 수 있도록 하자!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;** 잘 모르는거 괜히 이야기 꺼내면 큰코다친다&lt;/b&gt;&lt;/span&gt;... 질문폭격...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;** 포폴/이력서에 면접관이 질문 던질만한 요소를 적절히 배치하는 것이 중요!!&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 프로젝트 중 겪었던 문제상황과 이를 해결한 방법을 정리해서 포폴에 담으면 굉장히 좋아함&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** 사실 신입 수준에서 기술적으로 차별화를 준다는게 쉽지 않습니다.. 면접 보러가면 부트캠프 출신에 프로젝트 경험 갖춰온 지원자 굉장히 많습니다. 실무에서 일하는 면접관 시점에서는 그냥 고만고만한 프로젝트, 고만고만한 기술스택..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;합격 후 나중에 면접관이었던 팀장님과 얘기해봤을때&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt; 목표가 뚜렷한 사람&lt;/b&gt;&lt;/span&gt;을 굉장히 인상깊게 봤다고 얘기합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;나는 구체적인 목표가 있고&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-&amp;gt; 그 목표를 위해 이러한 준비를 해왔고&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-&amp;gt; 이 회사에서 이러한 것을 통해 내 목표를 이루고싶다는 것&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보여주면 정말 좋은 인상을 남길 수 있을거라고 생각합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;★ 자신의 목표가 무엇인지&lt;/b&gt;&lt;/span&gt;를 꼭꼭 생각해보고 가시길 바랍니다.. 꼭!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 &lt;i&gt;&quot;음 그냥 자바 스프링 백엔드 개발 아무거나 하고싶은데요?&quot;&amp;nbsp; &lt;/i&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: #333333;&quot;&gt;그리고 마지막으로..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;어려운 취업 시장속 백엔드 개발자를 목표로 하는 모두에게 건승을 빕니다.&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;02_모코코콘2_30_영차.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QwQwS/btr95pvyMnI/Yifjmtbs7UIgHQ2XsuSSTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QwQwS/btr95pvyMnI/Yifjmtbs7UIgHQ2XsuSSTk/img.png&quot; data-alt=&quot;할 수 있다!!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QwQwS/btr95pvyMnI/Yifjmtbs7UIgHQ2XsuSSTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQwQwS%2Fbtr95pvyMnI%2FYifjmtbs7UIgHQ2XsuSSTk%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;152&quot; height=&quot;152&quot; data-filename=&quot;02_모코코콘2_30_영차.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;할 수 있다!!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>나머지</category>
      <author>세댕댕이</author>
      <guid isPermaLink="true">https://sedangdang.tistory.com/309</guid>
      <comments>https://sedangdang.tistory.com/309#entry309comment</comments>
      <pubDate>Thu, 15 Dec 2022 19:37:37 +0900</pubDate>
    </item>
    <item>
      <title>ObjectMapper는 어떻게 setter 없이도 동작할까?</title>
      <link>https://sedangdang.tistory.com/307</link>
      <description>&lt;div class=&quot;alert alert-danger&quot; role=&quot;alert&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의역/오역이 있을 수 있고 잘못된 부분이 다수 존재할 수 있습니다&lt;/p&gt;
&lt;/div&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;요약&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;1. JSON을 파싱함&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;2. &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;기본 생성자&lt;/b&gt;&lt;/span&gt;를 이용해 반환할 객체(Object)를 생성한다&lt;br /&gt;3. 키값을 기준으로 &lt;b&gt;반복문&lt;/b&gt;을 돈다&lt;br /&gt;3-1. 객체에서 &lt;b&gt;getter 또는 setter를 통해 얻은 &lt;span style=&quot;color: #6164c6;&quot;&gt;프로퍼티&lt;/span&gt;들 중&lt;/b&gt; &lt;b&gt;키값의 이름이랑 같은거&lt;/b&gt;를 찾음&lt;br /&gt;3-2. &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;리플렉션&lt;/b&gt;&lt;/span&gt;을 이용해 빈 객체에 값을 채워넣는다&lt;br /&gt;3-3. 반복&lt;br /&gt;4. 결과(Object) 리턴&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;b&gt;# Jackson&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- the Java Json Library / &lt;b&gt;the best JSON parser for Java&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 스프링 부트에서 기본적으로 내장하고 있음&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;# ObjectMapper&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- com.fasterxml.jackson.databind&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;POJO &amp;lt;--&amp;gt; JSON 변환하는 역할&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;수행&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669818899646&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MyValue value = mapper.readValue(new File(&quot;data.json&quot;), MyValue.class);
// or:
value = mapper.readValue(new URL(&quot;http://some.com/api/entry.json&quot;), MyValue.class);
// or:
value = mapper.readValue(&quot;{\&quot;name\&quot;:\&quot;Bob\&quot;, \&quot;age\&quot;:13}&quot;, MyValue.class);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1669818714083&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mapper.writeValue(new File(&quot;result.json&quot;), myResultObject);
// or:
byte[] jsonBytes = mapper.writeValueAsBytes(myResultObject);
// or:
String jsonString = mapper.writeValueAsString(myResultObject);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- 특징으로는 신기하게도 DTO에 &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&quot;getter&quot; 만 있어도 바인딩이 된다는 것이다&lt;/span&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&quot;setter&quot; 없어도 된다)&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- getter/setter가 둘 다 필요했던 ModelAttribute와 차이!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대체 어떻게 이게 가능할까?? 리플렉션을 이용한다고 하는데 직접 찾아보고 싶어서 공식 문서를 한참동안 찾아봤는데 공식 문서로는 잘 알 수가 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 할까말까 진짜 고민 많이했는데 안하기는 또 아쉬워서 직접 코드를 일일히 뒤져보기로...한다......&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;시간이......정말 많이 걸렸다..........&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;# 코드 뜯어보기&lt;/b&gt;&lt;/h3&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;&lt;b&gt;1. Json 데이터를 역직렬화 해줄 Root Deserializer 를 찾는다&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sxREq/btrSyTUETuQ/i1Rkdw7w5Yni4uhtdaHh4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sxREq/btrSyTUETuQ/i1Rkdw7w5Yni4uhtdaHh4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sxREq/btrSyTUETuQ/i1Rkdw7w5Yni4uhtdaHh4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsxREq%2FbtrSyTUETuQ%2Fi1Rkdw7w5Yni4uhtdaHh4K%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;740&quot; height=&quot;540&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&lt;b&gt;2. Deserializer를 통해 Deserialize 한다&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WHvUZ/btrSveflVtN/ybPHk6ayldlVua5CkOrW3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WHvUZ/btrSveflVtN/ybPHk6ayldlVua5CkOrW3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WHvUZ/btrSveflVtN/ybPHk6ayldlVua5CkOrW3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWHvUZ%2FbtrSveflVtN%2FybPHk6ayldlVua5CkOrW3k%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;567&quot; height=&quot;277&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;저 &lt;b&gt;deser.deserialize()&lt;/b&gt; 메서드를 통해 역직렬화를 진행한다.&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;+ 한단계 더 들어가면 저 &lt;b&gt;Root Deserializer(JsonDeserializer)&lt;/b&gt;로서 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;BeanDeserializer&lt;/b&gt;&lt;/span&gt;가 들어간다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;(Bean이 Java Bean 할 때 그 Bean을 의미하는 것 같다.)&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;BeanDeserializer 내부에서는 JSON을 파싱한 다음 키값에 대해 반복문을 돌면서 해당하는 키값과 맞는 프로퍼티를 찾고, 해당 프로퍼티에 값을 채워넣는 방식으로 진행된다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 기본 생성자를 이용해 Object를 생성한다&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYIYlm/btrSy7MmhmZ/lUPkJIuAD5AjJuKGUe05G1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYIYlm/btrSy7MmhmZ/lUPkJIuAD5AjJuKGUe05G1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYIYlm/btrSy7MmhmZ/lUPkJIuAD5AjJuKGUe05G1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYIYlm%2FbtrSy7MmhmZ%2FlUPkJIuAD5AjJuKGUe05G1%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;944&quot; height=&quot;624&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;624&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 파싱한 JSON을 반복문을 돌면서 값을 하나씩 채운다&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;613&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba1bt4/btrSz8cYiXG/ItW2O0mKlZhbyN7wyBSVbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba1bt4/btrSz8cYiXG/ItW2O0mKlZhbyN7wyBSVbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba1bt4/btrSz8cYiXG/ItW2O0mKlZhbyN7wyBSVbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba1bt4%2FbtrSz8cYiXG%2FItW2O0mKlZhbyN7wyBSVbK%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;613&quot; height=&quot;399&quot; data-origin-width=&quot;613&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;639&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4Vo4F/btrSwTaeacF/MT7d4sQAA0j2yFvAYCkzSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4Vo4F/btrSwTaeacF/MT7d4sQAA0j2yFvAYCkzSk/img.png&quot; data-alt=&quot;FieldProperty - set() 메서드를 통해 필드에 값을 채워넣는다!!!!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4Vo4F/btrSwTaeacF/MT7d4sQAA0j2yFvAYCkzSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4Vo4F%2FbtrSwTaeacF%2FMT7d4sQAA0j2yFvAYCkzSk%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;792&quot; height=&quot;639&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;639&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FieldProperty - set() 메서드를 통해 필드에 값을 채워넣는다!!!!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글 다 쓰고 나니 이거 알아서 뭐에 써먹나... 싶긴 한데&amp;nbsp;나름 좋은 경험 했다 생각하려고 한다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 일일히 뜯어보면서 느낀건데 진짜 이런 &lt;span&gt;라이브러리 &lt;/span&gt;어떻게 만들었는지 존경스러울 따름이다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 특히 인터페이스, 추상클래스 이런것들을 진짜 기똥차게 잘 사용하는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향 냄새 펄펄 풍기고 다형성의 참맛을 제대로 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나도 언젠가 이런거 만들어볼 수 있을까..아마 공부를 한참 해야할 것 같다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;참고&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669818749845&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - FasterXML/jackson-databind: General data-binding package for Jackson (2.x): works on streaming API (core) implementatio&quot; data-og-description=&quot;General data-binding package for Jackson (2.x): works on streaming API (core) implementation(s) - GitHub - FasterXML/jackson-databind: General data-binding package for Jackson (2.x): works on strea...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/FasterXML/jackson-databind&quot; data-og-url=&quot;https://github.com/FasterXML/jackson-databind&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ruYnn/hyQJx7RlDt/tyhrqnMuFqNmxvFkETqbu1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/FasterXML/jackson-databind&quot; data-source-url=&quot;https://github.com/FasterXML/jackson-databind&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ruYnn/hyQJx7RlDt/tyhrqnMuFqNmxvFkETqbu1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - FasterXML/jackson-databind: General data-binding package for Jackson (2.x): works on streaming API (core) implementatio&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;General data-binding package for Jackson (2.x): works on streaming API (core) implementation(s) - GitHub - FasterXML/jackson-databind: General data-binding package for Jackson (2.x): works on strea...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1669820156793&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;ObjectMapper는 Property를 어떻게 찾을까 ?&quot; data-og-description=&quot;최근에 Restful API 에 POST Method로 요청 시에 Body의 Json을 requestDto로 매핑하는데 있어서 @Setter가 필요없다는 것을 알게 되었다.&quot; data-og-host=&quot;bactoria.github.io&quot; data-og-source-url=&quot;https://bactoria.github.io/2019/08/16/ObjectMapper%EB%8A%94-Property%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%B0%BE%EC%9D%84%EA%B9%8C/&quot; data-og-url=&quot;https://bactoria.github.io/2019/08/16/ObjectMapper%EB%8A%94-Property%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%B0%BE%EC%9D%84%EA%B9%8C/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/CTNaf/hyQKYCMlzT/PgfgRguX1MiBEvtYKGkNU0/img.jpg?width=460&amp;amp;height=460&amp;amp;face=0_0_460_460&quot;&gt;&lt;a href=&quot;https://bactoria.github.io/2019/08/16/ObjectMapper%EB%8A%94-Property%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%B0%BE%EC%9D%84%EA%B9%8C/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bactoria.github.io/2019/08/16/ObjectMapper%EB%8A%94-Property%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%B0%BE%EC%9D%84%EA%B9%8C/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/CTNaf/hyQKYCMlzT/PgfgRguX1MiBEvtYKGkNU0/img.jpg?width=460&amp;amp;height=460&amp;amp;face=0_0_460_460');&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;ObjectMapper는 Property를 어떻게 찾을까 ?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;최근에 Restful API 에 POST Method로 요청 시에 Body의 Json을 requestDto로 매핑하는데 있어서 @Setter가 필요없다는 것을 알게 되었다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;bactoria.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1669823083209&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;@RequestBody 모델에 기본생성자, setter/getter가 필요한가?&quot; data-og-description=&quot;서론 최근에 Entity 모델의 속성의 성격을 잘 생각해보고 불변해야 하는 것들은 final로 선언해 명확하게 하라는 코드리뷰를 받았다. 한번 생성되면 변하면 안되는 것들, 이 속성 없이 생성되면 안&quot; data-og-host=&quot;bbbicb.tistory.com&quot; data-og-source-url=&quot;https://bbbicb.tistory.com/46&quot; data-og-url=&quot;https://bbbicb.tistory.com/46&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cZWqkV/hyQJAXQAaz/hEdlK7iF5fAVL6vIQsJpbk/img.png?width=467&amp;amp;height=355&amp;amp;face=0_0_467_355,https://scrap.kakaocdn.net/dn/bpU7dx/hyQKOGYr9T/AaXsiptKSlKVuhKi8AZ8Gk/img.png?width=467&amp;amp;height=355&amp;amp;face=0_0_467_355,https://scrap.kakaocdn.net/dn/dypuag/hyQJJtJkHW/5MlExUTbd0lkzRbGOiLDm1/img.png?width=1306&amp;amp;height=653&amp;amp;face=0_0_1306_653&quot;&gt;&lt;a href=&quot;https://bbbicb.tistory.com/46&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bbbicb.tistory.com/46&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cZWqkV/hyQJAXQAaz/hEdlK7iF5fAVL6vIQsJpbk/img.png?width=467&amp;amp;height=355&amp;amp;face=0_0_467_355,https://scrap.kakaocdn.net/dn/bpU7dx/hyQKOGYr9T/AaXsiptKSlKVuhKi8AZ8Gk/img.png?width=467&amp;amp;height=355&amp;amp;face=0_0_467_355,https://scrap.kakaocdn.net/dn/dypuag/hyQJJtJkHW/5MlExUTbd0lkzRbGOiLDm1/img.png?width=1306&amp;amp;height=653&amp;amp;face=0_0_1306_653');&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;@RequestBody 모델에 기본생성자, setter/getter가 필요한가?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;서론 최근에 Entity 모델의 속성의 성격을 잘 생각해보고 불변해야 하는 것들은 final로 선언해 명확하게 하라는 코드리뷰를 받았다. 한번 생성되면 변하면 안되는 것들, 이 속성 없이 생성되면 안&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;bbbicb.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1669830954059&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;@RequestBody에 왜 기본 생성자는 필요하고, Setter는 필요 없을까? #3&quot; data-og-description=&quot;이전 글에서는 RestController에서 @RequestBody 바인딩을 Jackson 라이브러리의 ObjectMapper가 하는 것을 확인했습니다.그리고 RequestBody를 생성할 때, DTO가 Property기반이 아니거나 Delegate를 한 상태가 아니라&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@conatuseus/RequestBody%EC%97%90-%EC%99%9C-%EA%B8%B0%EB%B3%B8-%EC%83%9D%EC%84%B1%EC%9E%90%EB%8A%94-%ED%95%84%EC%9A%94%ED%95%98%EA%B3%A0-Setter%EB%8A%94-%ED%95%84%EC%9A%94-%EC%97%86%EC%9D%84%EA%B9%8C-3-idnrafiw&quot; data-og-url=&quot;https://velog.io/@conatuseus/RequestBody에-왜-기본-생성자는-필요하고-Setter는-필요-없을까-3-idnrafiw&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/braVfY/hyQK0HlGIx/i8jVDqLkKzx47BgSUafKTk/img.png?width=1554&amp;amp;height=884&amp;amp;face=0_0_1554_884,https://scrap.kakaocdn.net/dn/bkCMvc/hyQKYJwAj4/Qe85FJB6lOyLvdKkXsaOUk/img.png?width=1554&amp;amp;height=884&amp;amp;face=0_0_1554_884,https://scrap.kakaocdn.net/dn/VUAVs/hyQKRDElUU/rvBIkbcASLFGf6dy7FloYk/img.png?width=2432&amp;amp;height=1146&amp;amp;face=0_0_2432_1146&quot;&gt;&lt;a href=&quot;https://velog.io/@conatuseus/RequestBody%EC%97%90-%EC%99%9C-%EA%B8%B0%EB%B3%B8-%EC%83%9D%EC%84%B1%EC%9E%90%EB%8A%94-%ED%95%84%EC%9A%94%ED%95%98%EA%B3%A0-Setter%EB%8A%94-%ED%95%84%EC%9A%94-%EC%97%86%EC%9D%84%EA%B9%8C-3-idnrafiw&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@conatuseus/RequestBody%EC%97%90-%EC%99%9C-%EA%B8%B0%EB%B3%B8-%EC%83%9D%EC%84%B1%EC%9E%90%EB%8A%94-%ED%95%84%EC%9A%94%ED%95%98%EA%B3%A0-Setter%EB%8A%94-%ED%95%84%EC%9A%94-%EC%97%86%EC%9D%84%EA%B9%8C-3-idnrafiw&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/braVfY/hyQK0HlGIx/i8jVDqLkKzx47BgSUafKTk/img.png?width=1554&amp;amp;height=884&amp;amp;face=0_0_1554_884,https://scrap.kakaocdn.net/dn/bkCMvc/hyQKYJwAj4/Qe85FJB6lOyLvdKkXsaOUk/img.png?width=1554&amp;amp;height=884&amp;amp;face=0_0_1554_884,https://scrap.kakaocdn.net/dn/VUAVs/hyQKRDElUU/rvBIkbcASLFGf6dy7FloYk/img.png?width=2432&amp;amp;height=1146&amp;amp;face=0_0_2432_1146');&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;@RequestBody에 왜 기본 생성자는 필요하고, Setter는 필요 없을까? #3&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이전 글에서는 RestController에서 @RequestBody 바인딩을 Jackson 라이브러리의 ObjectMapper가 하는 것을 확인했습니다.그리고 RequestBody를 생성할 때, DTO가 Property기반이 아니거나 Delegate를 한 상태가 아니라&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.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>웹/Spring</category>
      <author>세댕댕이</author>
      <guid isPermaLink="true">https://sedangdang.tistory.com/307</guid>
      <comments>https://sedangdang.tistory.com/307#entry307comment</comments>
      <pubDate>Thu, 1 Dec 2022 02:46:13 +0900</pubDate>
    </item>
    <item>
      <title>@RequestBody 공식문서를 읽어보자 + HttpMessageConverter</title>
      <link>https://sedangdang.tistory.com/305</link>
      <description>&lt;div class=&quot;alert alert-danger&quot; role=&quot;alert&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의역/오역이 있을 수 있고 잘못된 부분이 존재할 수 있습니다&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;요약&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;1. @RequestBody는 &lt;b&gt;HttpMessageConverter&lt;/b&gt;가&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt; HTTP Request Body&lt;/b&gt; &lt;/span&gt;내의 데이터를 객체로 변환(역직렬화)하도록 시키는 애노테이션이다.&lt;br /&gt;&lt;br /&gt;2. @Valid를 붙이면 검증을 할 수 있고, 실패 시 &lt;b&gt;MethodArgumentNotValidException&lt;/b&gt;을 던진다.&lt;br /&gt;&lt;br /&gt;3.&lt;b&gt; application/json&lt;/b&gt; 타입의 경우 &lt;b&gt;MappingJackson2HttpMessageConverter&lt;/b&gt;가 데이터를 객체로 역직렬화 해준다.&lt;br /&gt;&lt;br /&gt;4. MappingJackson2HttpMessageConverter는 &lt;b&gt;ObjectMapper&lt;/b&gt;를 사용하는데, &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;프로퍼티&lt;/b&gt;&lt;/span&gt;를 이용하기 때문에 &lt;b&gt;getter / setter 둘 중 하나&lt;/b&gt;만 있으면 된다&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@RequestBody&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;- Annotation indicating a method parameter should be bound to the body of the web request.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;(메서드 파라미터가 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;web request(HTTP Request)의 body 에 있는 데이터를 통해 bind 될 것&lt;/b&gt;&lt;/span&gt;을 나타내는 애노테이션)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;(굉장히 정직한 이름이다;;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;- The body of the request is passed through an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/HttpMessageConverter.html&quot;&gt;HttpMessageConverter&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to resolve the method argument depending on the content type of the request.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;(http body는 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;HttpMessageConverter&lt;/b&gt;&lt;/span&gt;에게 전달되어 처리된다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;- Optionally, automatic validation can be applied by annotating the argument with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@Valid&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(&lt;span style=&quot;color: #6164c6;&quot;&gt;@Valid&lt;/span&gt;&lt;/b&gt; 애노테이션과 함께 사용하면 자동으로 검증이 적용된다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669622779410&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping(&quot;/accounts&quot;)
public void handle(@RequestBody Account account) {
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;You can use the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@RequestBody&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;annotation to have the request body&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt; read and deserialized into an&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Object&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;through an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#rest-message-conversion&quot;&gt;HttpMessageConverter&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(request Body를 읽고 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;역직렬화&lt;/b&gt;&lt;/span&gt;를 통해 &lt;b&gt;데이터를 객체로 만드는 과정&lt;/b&gt;을 &lt;b&gt;HttpMessageConverter&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;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;By default, validation errors cause a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;MethodArgumentNotValidException&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;, which is turned into a 400 (BAD_REQUEST) response. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;(@Valid를 사용할 경우, 검증 실패 시 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;MethodArgumentNotValidException&lt;/b&gt;&lt;/span&gt;을 발생시킨다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;Alternatively, you can handle validation errors locally within the controller through an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Errors&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;or&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;BindingResult&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;argument&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(예외를 바로 던지지 않고 컨트롤러에서 처리하고 싶다면 BindingResult를 뒤에 바로 붙여주면 된다)&lt;/p&gt;
&lt;pre id=&quot;code_1669622999959&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping(&quot;/accounts&quot;)
public void handle(@Valid @RequestBody Account account, BindingResult result) {
    // ...
}&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;b&gt;# HttpMessageConverter&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;- The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;spring-web&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;module contains the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;HttpMessageConverter&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;contract for reading and writing the body of HTTP requests and responses through&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;InputStream&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;OutputStream&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&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;(spring-web 모듈은 HttpMessageConverter를 기본적으로 포함하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; InputStream에서&lt;span style=&quot;color: #6164c6;&quot;&gt; &lt;b&gt;HTTP request의 body를 읽는 역할&lt;/b&gt;&lt;/span&gt; &amp;amp; OutputStream에서&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt; HTTP response의 body에 데이터를 쓰는 역할&lt;/b&gt;&lt;/span&gt;을 담당한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- HttpMessageConverter&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;instances are used on the client side (for example, in the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;RestTemplate&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;) and on the server side (for example, in Spring MVC REST controllers).&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;(클라이언트 사이드에서는 RestTemplate, 서버 사이드에서는 &lt;b&gt;RestController&lt;/b&gt; 구현 할 때 주로 사용된다.)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;/span&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;- Concrete implementations for the main media (MIME) types are provided in the framework and are, by default, registered with the&lt;span&gt;&amp;nbsp;&lt;/span&gt;RestTemplate&lt;span&gt;&amp;nbsp;&lt;/span&gt;on the client side and with&lt;span&gt;&amp;nbsp;&lt;/span&gt;RequestMappingHandlerAdapter&lt;span&gt;&amp;nbsp;&lt;/span&gt;on the server side (see&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-message-converters&quot;&gt;Configuring Message Converters&lt;/a&gt;).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(HttpMessageConverter(인터페이스)의 구현체는&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt; MIME 타입에 의해 정해진다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; &lt;b&gt;RequestMappingHandlerAdapter&lt;/b&gt;가 이를 판단하고 적절한 HttpMessageConverter를 사용할 수 있게 해준다)&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&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;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;&amp;lt;MIME 3대장&amp;gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;text/* -&amp;gt; StringHttpMessageConverter&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;application/x-www-form-urlencoded -&amp;gt; FormHttpMessageConverter&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;b&gt;application/json -&amp;gt;&lt;/b&gt; &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;MappingJackson2HttpMessageConverter&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;# MappingJackson2HttpMessageConverter&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Implementation of org.springframework.http.converter.HttpMessageConverter that can read and write JSON using Jackson 2.x's&amp;nbsp;&amp;nbsp;ObjectMapper.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Jackson 라이브러리의 ObjectMapper&lt;/b&gt;&lt;/span&gt;를 사용해서&lt;b&gt; JSON을 읽거나 쓰도록 만들어주는 HttpMessageConverter 구현체&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;- This&amp;nbsp;converter&amp;nbsp;can&amp;nbsp;be&amp;nbsp;used&amp;nbsp;to&amp;nbsp;bind&amp;nbsp;to&amp;nbsp;typed&amp;nbsp;beans,&amp;nbsp;or&amp;nbsp;untyped&amp;nbsp;HashMap&amp;nbsp;instances.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(typed beans(??) 또는 untyped HashMap 인스턴스를 바인딩 하는데 사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;&amp;lt;결론&amp;gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 파라미터에 @RequestBody가 붙어있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; HTTP Request Body 내부에 데이터가 담겨있음을 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; RequestMappingHandlerAdapter가 데이터의 MIME 타입을 확인하고 적절한 HttpMessageConverter 구현체 선정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; (application/json의 경우 MappingJackson2HttpMessageConverter 사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 해당 Converter가 Request Body 내부의 데이터를 객체(Object)로 변환!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; (&lt;span&gt;MappingJackson2HttpMessageConverter의 경우 내부적으로 ObjectMapper를 이용해 변환)&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;* RequestBody의 DTO는 setter 없이 getter만 있으면 getter를 이용해 값을 바인딩할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** 어떻게 가능할까? 후속 포스팅 ==&amp;gt; &lt;a href=&quot;https://sedangdang.tistory.com/307&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sedangdang.tistory.com/307&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;+ HTTP Request 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVyADE/btrSc9rZjQM/BhZCox54lLcX5LIn5fi4k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVyADE/btrSc9rZjQM/BhZCox54lLcX5LIn5fi4k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVyADE/btrSc9rZjQM/BhZCox54lLcX5LIn5fi4k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVyADE%2FbtrSc9rZjQM%2FBhZCox54lLcX5LIn5fi4k0%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;376&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* Start Line (시작 줄)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- HTTP 메서드 (GET, PUT, POST, DELETE, HEAD, OPTIONS 등....)&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;-&lt;b&gt; HTTP Version&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oSbWF/btrSlk6B66G/TVlKUp2AieFwEQCuSMukt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oSbWF/btrSlk6B66G/TVlKUp2AieFwEQCuSMukt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oSbWF/btrSlk6B66G/TVlKUp2AieFwEQCuSMukt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoSbWF%2FbtrSlk6B66G%2FTVlKUp2AieFwEQCuSMukt1%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;748&quot; height=&quot;286&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* Body (본문)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt; 필수 요소 X &lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(GET, HEAD, DELETE, OPTIONS와 같이 리소스를 가져오는 요청은 보통 본문이 필요하지 않다)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- POST와 같이 서버에 데이터를 전송하고자 할 때 주로 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;b&gt;단일 리소스 바디&lt;/b&gt; (Content-Type, Content-Length로 정의)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;b&gt;다중 리소스 바디&lt;/b&gt; (Multipart form 데이터)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(* 참고 ) &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/HTTP/Messages#http_%EC%9A%94%EC%B2%AD&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/HTTP/Messages#http_%EC%9A%94%EC%B2%AD&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜로 파면 팔수록 더 어렵다;;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;참고&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669620191488&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;RequestBody (Spring Framework 6.0.2 API)&quot; data-og-description=&quot;Whether body content is required. Default is true, leading to an exception thrown in case there is no body content. Switch this to false if you prefer null to be passed when the body content is null.&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html&quot; data-og-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html&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;RequestBody (Spring Framework 6.0.2 API)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Whether body content is required. Default is true, leading to an exception thrown in case there is no body content. Switch this to false if you prefer null to be passed when the body content is null.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.spring.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1669620208479&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;Web on Servlet Stack&quot; data-og-description=&quot;This part of the reference documentation covers support for Servlet stack, WebSocket messaging that includes raw WebSocket interactions, WebSocket emulation through SockJS, and publish-subscribe messaging through STOMP as a sub-protocol over WebSocket. = I&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-requestbody&quot; data-og-url=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-requestbody&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cgUoev/hyQH0aZdvK/PIKKw7cnMtK8gkcNPkGro1/img.png?width=1022&amp;amp;height=415&amp;amp;face=0_0_1022_415,https://scrap.kakaocdn.net/dn/evj8HB/hyQJDkXaD8/QpVSk1BvkiFiRm7ULxzOX1/img.png?width=1000&amp;amp;height=392&amp;amp;face=0_0_1000_392,https://scrap.kakaocdn.net/dn/bt7ZL7/hyQJCfhKUd/R83SPAVbJKpjKKvCz98xg0/img.png?width=582&amp;amp;height=536&amp;amp;face=0_0_582_536&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-requestbody&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-requestbody&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cgUoev/hyQH0aZdvK/PIKKw7cnMtK8gkcNPkGro1/img.png?width=1022&amp;amp;height=415&amp;amp;face=0_0_1022_415,https://scrap.kakaocdn.net/dn/evj8HB/hyQJDkXaD8/QpVSk1BvkiFiRm7ULxzOX1/img.png?width=1000&amp;amp;height=392&amp;amp;face=0_0_1000_392,https://scrap.kakaocdn.net/dn/bt7ZL7/hyQJCfhKUd/R83SPAVbJKpjKKvCz98xg0/img.png?width=582&amp;amp;height=536&amp;amp;face=0_0_582_536');&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;Web on Servlet Stack&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This part of the reference documentation covers support for Servlet stack, WebSocket messaging that includes raw WebSocket interactions, WebSocket emulation through SockJS, and publish-subscribe messaging through STOMP as a sub-protocol over WebSocket. = I&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.spring.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹/Spring</category>
      <author>세댕댕이</author>
      <guid isPermaLink="true">https://sedangdang.tistory.com/305</guid>
      <comments>https://sedangdang.tistory.com/305#entry305comment</comments>
      <pubDate>Mon, 28 Nov 2022 17:13:12 +0900</pubDate>
    </item>
    <item>
      <title>@ModelAttribute 공식 문서를 읽어보자</title>
      <link>https://sedangdang.tistory.com/304</link>
      <description>&lt;div class=&quot;alert alert-danger&quot; role=&quot;alert&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의역/오역이 있을 수 있고 잘못된 부분이 존재할 수 있습니다&lt;/p&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;핵심 요약&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;1. @ModelAttribute는&lt;span style=&quot;color: #6164c6;&quot;&gt;&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;&quot;&lt;span style=&quot;color: #6164c6;&quot;&gt;가장 적절한&lt;/span&gt;&quot;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt; 생성자를 찾아 객체를 생성 및 초기화한다&lt;br /&gt;2. 객체 생성 및 초기화 -&amp;gt; Data Binding -&amp;gt; Validation 순서로 진행된다&lt;br /&gt;3. Data Binding은 getter / setter가 존재하는 변수에 한해서 이루어진다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@ModelAttribute&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;Annotation that binds a method parameter or method return value to a named model attribute, exposed to a web view.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;(&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;메서드 파라미터를 Bind&lt;/b&gt; &lt;/span&gt;해주거나, &lt;b&gt;웹 뷰에 노출되는&lt;/b&gt; &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;모델을 지정된 name 속성으로 담아 리턴&lt;/b&gt;&lt;/span&gt;해주는 애노테이션)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;- &lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;Supported for controller classes with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html&quot;&gt;@RequestMapping&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;methods.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;(@RequestMapping 애노테이션이 달려있는 컨트롤러 클래스와 함께 사용됨)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Data binding can lead to security issues by exposing parts of the object graph that are not meant to be accessed or modified by external clients.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;(데이터 바인딩은 보안 이슈를 불러올 수도 있음 - 외부 클라이언드가 접근(get)하거나 수정(set)하면 안되는 객체의 일부분을 노출하게 됨으로써..= &lt;b&gt;DTO를 쓰자&lt;/b&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;- &lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;Note however that reference data and all other model content are not available to web views when request processing results in an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Exception&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;since the exception could be raised at any time making the content of the model unreliable.&lt;span&gt;&amp;nbsp;&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 style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;(처리 과정 중에 Exception이 터지면 (대표적으로 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Type mismatch&lt;/b&gt;&lt;/span&gt;), 해당 모델은 신뢰할 수 없는 것이기 때문에 View에 모델을 리턴하지 않는다)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;*&amp;nbsp; Data Binding 중 발생하는 BindException을 처리하고 싶으면 @ModelAttribute 뒤에 BindingResult를 붙여줘야 한다 (아래 참고)&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 모델을 지정된 name 속성으로 뷰에 리턴해준다는 것은&lt;/p&gt;
&lt;pre id=&quot;code_1669173276628&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping
public String registerAdmin(@ModelAttribute(&quot;admin&quot;) AdminRegisterDto registerDTO) {
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@ModelAttribute(&quot;이름&quot;)을 받았을 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;registerDTO의 값을 getter/setter로 바인딩 해준 이후에&lt;/p&gt;
&lt;pre id=&quot;code_1669173161021&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model.addAttribute(&quot;admin&quot;, registerDTO);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 자동으로 만들어서 넣어준다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;# 스프링에서 ModelAttribute가 객체를 생성하는 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(우선순위 순 정렬인지는 모르겠으나, 이 중 한가지 방법을 이용해 객체를 생성/초기화 한다)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Retrieved from the model where it may have been added by a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-modelattrib-methods&quot;&gt;@ModelAttribute method&lt;/a&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Retrieved from the HTTP session if the model attribute was listed in the class-level&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-sessionattributes&quot;&gt;@SessionAttributes&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;annotation&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Obtained through a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Converter&lt;span&gt;&amp;nbsp;&lt;/span&gt;where the model attribute name matches the name of a request value such as a path variable or a request parameter (see next example).&lt;/p&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: #6164c6;&quot;&gt;&lt;b&gt;- Instantiated using its default constructor. = 기본 생성자(NoArgsConstructor) 이용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;- Instantiated through a &lt;span style=&quot;color: #ef6f53;&quot;&gt;&amp;ldquo;primary constructor&amp;rdquo;&lt;/span&gt; with arguments that match to Servlet request parameters&lt;/b&gt;.&lt;/span&gt; Argument names are determined through JavaBeans&lt;span&gt;&amp;nbsp;&lt;/span&gt;@ConstructorProperties&lt;span&gt;&amp;nbsp;&lt;/span&gt;or through runtime-retained parameter names in the bytecode. &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;= request parameter와 가장 잘 맞는(?) 생성자를 이용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@ModelAttribute의 Data Binding&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;- After the model attribute instance is obtained, data binding is applied.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;(&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;모델 객체가 초기화 된 이후에&lt;/b&gt;&lt;/span&gt; 데이터 바인딩을 적용한다)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Only&amp;nbsp;public&amp;nbsp;properties following the&amp;nbsp;&lt;a href=&quot;https://www.oracle.com/java/technologies/javase/javabeans-spec.html&quot;&gt;JavaBeans naming conventions&lt;/a&gt;&amp;nbsp;are exposed for data binding&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;자바빈 명명 규칙을 따르는 public 프로퍼티가 존재하는 데이터에 대해서만&lt;/b&gt; &lt;/span&gt;바인딩이 이루어진다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;= 각 변수마다 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;public getter / public setter&lt;/b&gt;&lt;/span&gt; 가 있어야 한다는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- It is extremely important to properly configure allowed and disallowed field patterns when exposing your domain model directly for data binding purposes. Otherwise, it is a big security risk.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;((DTO 없이) 도메인 모델을 곧바로 데이터 바인딩 하고자 할 때는, &lt;b&gt;노출할 변수와 숨길 변수를 잘 설정&lt;/b&gt;하는 것이 매우 중요하다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 외부로부터 숨기고 싶은 변수는 getter/setter를 뗀 채로 사용하면 OK&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;- Furthermore, it is strongly recommended that you do not use types from your domain model such as JPA or Hibernate entities as the model object in data binding scenarios.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(JPA(Hibernate) 엔티티(도메인) 모델을 곧바로 바인딩 받는 것은 매우 권장하지 않는다 = &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;DTO를 사용해라&lt;/b&gt;&lt;/span&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;Data binding can result in errors. By default, a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;BindException&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;is raised. However, to check for such errors in the controller method, you can add a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;BindingResult&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;argument immediately next to the&amp;nbsp;&lt;/span&gt;@ModelAttribute&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Data Binding에 실패하면, BindException이 발생한다. 예외를 바로 던지지 않고 컨트롤러에서 별도로 처리하고 싶으면 &lt;b&gt;@ModelAttribute 바로 옆에 BindingResult&lt;/b&gt;를 붙이면 된다)&lt;/p&gt;
&lt;pre id=&quot;code_1669175504987&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping(&quot;/owners/{ownerId}/pets/{petId}/edit&quot;)
public String processSubmit(@ModelAttribute(&quot;pet&quot;) Pet pet, BindingResult result) {
    if (result.hasErrors()) {
        return &quot;petForm&quot;;
    }
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;You can automatically apply &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;validation&lt;/b&gt; &lt;b&gt;after data binding&lt;/b&gt; &lt;/span&gt;by adding the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;jakarta.validation.Valid&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;annotation or Spring&amp;rsquo;s&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@Validated&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;annotation&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Data Binding이 끝난 이후에는 @Valid, @Validated 애노테이션을 이용해 값을 검증하도록 할 수 있다)&lt;/p&gt;
&lt;pre id=&quot;code_1669175678173&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping(&quot;/owners/{ownerId}/pets/{petId}/edit&quot;)
public String processSubmit(@Valid @ModelAttribute(&quot;pet&quot;) Pet pet, BindingResult result) { (1)
    if (result.hasErrors()) {
        return &quot;petForm&quot;;
    }
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;객체 생성 및 초기화 -&amp;gt; Data Binding -&amp;gt; Validation 순서로 진행&lt;/b&gt;&lt;/span&gt;된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;Note that &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;using&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;@ModelAttribute&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;&amp;nbsp;is optional&lt;/b&gt; &lt;/span&gt;(for example, to set its attributes). By default, any argument that is not a &lt;b&gt;simple value type&lt;/b&gt; &lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;and&lt;b&gt; is not resolved by any other argument resolver&lt;/b&gt; is treated as if it were annotated with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@ModelAttribute&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #191e1e;&quot;&gt;(simple value type 또는 다른 argument resolver가 처리하는 것이 아닌 경우는 모두 @ModelAttribute가 적용되기 때문에 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;생략해도 된다&lt;/b&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #191e1e;&quot;&gt;&lt;span style=&quot;color: #191e1e;&quot;&gt;* &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;simple value type&lt;/span&gt;:&lt;/b&gt; &lt;/span&gt;&lt;span style=&quot;color: #474747;&quot;&gt;a primitive or primitive wrapper, an enum, a String or other CharSequence, a Number, a Date, a Temporal, a URI, a URL, a Locale, or a Class.&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: #191e1e;&quot;&gt;&lt;span style=&quot;color: #191e1e;&quot;&gt;&lt;span style=&quot;color: #474747;&quot;&gt;(원시 자료형 및 Wrapper / Enum / String / Number / Date 등)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;참고&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669176064243&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;ModelAttribute (Spring Framework 6.0.0 API)&quot; data-og-description=&quot;The name of the model attribute to bind to. The default model attribute name is inferred from the declared attribute type (i.e. the method parameter type or method return type), based on the non-qualified class name: e.g. &amp;quot;orderAddress&amp;quot; for class &amp;quot;mypackag&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html&quot; data-og-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html&quot; data-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html&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;ModelAttribute (Spring Framework 6.0.0 API)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The name of the model attribute to bind to. The default model attribute name is inferred from the declared attribute type (i.e. the method parameter type or method return type), based on the non-qualified class name: e.g. &quot;orderAddress&quot; for class &quot;mypackag&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;figure id=&quot;og_1669176058563&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;Web on Servlet Stack&quot; data-og-description=&quot;This part of the reference documentation covers support for Servlet stack, WebSocket messaging that includes raw WebSocket interactions, WebSocket emulation through SockJS, and publish-subscribe messaging through STOMP as a sub-protocol over WebSocket. = I&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-modelattrib-method-args&quot; data-og-url=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-modelattrib-method-args&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cGIy8w/hyQGw6R6AW/sE1ThIjr6ynctKMyB2KMp1/img.png?width=1022&amp;amp;height=415&amp;amp;face=0_0_1022_415,https://scrap.kakaocdn.net/dn/b8ZWQA/hyQE0V2EA8/MeJafZnMR2kPDhkOG8DLTk/img.png?width=1000&amp;amp;height=392&amp;amp;face=0_0_1000_392,https://scrap.kakaocdn.net/dn/VpjoZ/hyQGrxIVvF/k739SWJS3jqNCoO1o12j3k/img.png?width=582&amp;amp;height=536&amp;amp;face=0_0_582_536&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-modelattrib-method-args&quot; data-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-modelattrib-method-args&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cGIy8w/hyQGw6R6AW/sE1ThIjr6ynctKMyB2KMp1/img.png?width=1022&amp;amp;height=415&amp;amp;face=0_0_1022_415,https://scrap.kakaocdn.net/dn/b8ZWQA/hyQE0V2EA8/MeJafZnMR2kPDhkOG8DLTk/img.png?width=1000&amp;amp;height=392&amp;amp;face=0_0_1000_392,https://scrap.kakaocdn.net/dn/VpjoZ/hyQGrxIVvF/k739SWJS3jqNCoO1o12j3k/img.png?width=582&amp;amp;height=536&amp;amp;face=0_0_582_536');&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;Web on Servlet Stack&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This part of the reference documentation covers support for Servlet stack, WebSocket messaging that includes raw WebSocket interactions, WebSocket emulation through SockJS, and publish-subscribe messaging through STOMP as a sub-protocol over WebSocket. = I&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;figure id=&quot;og_1669176053718&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;Web on Servlet Stack&quot; data-og-description=&quot;This part of the reference documentation covers support for Servlet stack, WebSocket messaging that includes raw WebSocket interactions, WebSocket emulation through SockJS, and publish-subscribe messaging through STOMP as a sub-protocol over WebSocket. = I&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design&quot; data-og-url=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cO1rdd/hyQGjzEXK5/ngtPFEfpyBtlyOuk4ilmsK/img.png?width=1022&amp;amp;height=415&amp;amp;face=0_0_1022_415,https://scrap.kakaocdn.net/dn/bTSwvp/hyQGqMjs0c/tyqmPmLQ6PDTPtrMUYyec0/img.png?width=1000&amp;amp;height=392&amp;amp;face=0_0_1000_392,https://scrap.kakaocdn.net/dn/d5PXzR/hyQGxxT211/NAUlWljqIHhTkqkp7yMo00/img.png?width=582&amp;amp;height=536&amp;amp;face=0_0_582_536&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design&quot; data-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cO1rdd/hyQGjzEXK5/ngtPFEfpyBtlyOuk4ilmsK/img.png?width=1022&amp;amp;height=415&amp;amp;face=0_0_1022_415,https://scrap.kakaocdn.net/dn/bTSwvp/hyQGqMjs0c/tyqmPmLQ6PDTPtrMUYyec0/img.png?width=1000&amp;amp;height=392&amp;amp;face=0_0_1000_392,https://scrap.kakaocdn.net/dn/d5PXzR/hyQGxxT211/NAUlWljqIHhTkqkp7yMo00/img.png?width=582&amp;amp;height=536&amp;amp;face=0_0_582_536');&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;Web on Servlet Stack&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This part of the reference documentation covers support for Servlet stack, WebSocket messaging that includes raw WebSocket interactions, WebSocket emulation through SockJS, and publish-subscribe messaging through STOMP as a sub-protocol over WebSocket. = I&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;figure id=&quot;og_1669171686225&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;@ModelAttribute와 @RequestBody 그리고 Setter&quot; data-og-description=&quot;@RequestBody와 Setter 본 주제에 대해 이야기하기 전에 먼저 @ModelAttribute에 대해 이야기해보려합니다. @ModelAttribute 우리는 Spring에서 Reqeust Parameter를 얻기 위해 @ModelAttribute를 사용하곤합니다. 값을 바&quot; data-og-host=&quot;minchul-son.tistory.com&quot; data-og-source-url=&quot;https://minchul-son.tistory.com/546&quot; data-og-url=&quot;https://minchul-son.tistory.com/546&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/3MgpY/hyQGugTI1i/l20ITZbv1uGbXV0pg44tlK/img.png?width=400&amp;amp;height=279&amp;amp;face=0_0_400_279,https://scrap.kakaocdn.net/dn/wamDq/hyQGkyAsSk/qZJk8Qm6INPbjXtgnRoLPK/img.png?width=400&amp;amp;height=279&amp;amp;face=0_0_400_279,https://scrap.kakaocdn.net/dn/b0lTal/hyQGj7vIJT/kKkvPzLkt3YgNNKQiZBk20/img.png?width=924&amp;amp;height=791&amp;amp;face=0_0_924_791&quot;&gt;&lt;a href=&quot;https://minchul-son.tistory.com/546&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://minchul-son.tistory.com/546&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/3MgpY/hyQGugTI1i/l20ITZbv1uGbXV0pg44tlK/img.png?width=400&amp;amp;height=279&amp;amp;face=0_0_400_279,https://scrap.kakaocdn.net/dn/wamDq/hyQGkyAsSk/qZJk8Qm6INPbjXtgnRoLPK/img.png?width=400&amp;amp;height=279&amp;amp;face=0_0_400_279,https://scrap.kakaocdn.net/dn/b0lTal/hyQGj7vIJT/kKkvPzLkt3YgNNKQiZBk20/img.png?width=924&amp;amp;height=791&amp;amp;face=0_0_924_791');&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;@ModelAttribute와 @RequestBody 그리고 Setter&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;@RequestBody와 Setter 본 주제에 대해 이야기하기 전에 먼저 @ModelAttribute에 대해 이야기해보려합니다. @ModelAttribute 우리는 Spring에서 Reqeust Parameter를 얻기 위해 @ModelAttribute를 사용하곤합니다. 값을 바&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;minchul-son.tistory.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;읽는 시간은 확실히 오래걸리는데 그만큼 이해가 엄청 잘 되는 것 같다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로도 공식문서 읽는 습관을 들여야지..&lt;/p&gt;</description>
      <category>웹/Spring</category>
      <author>세댕댕이</author>
      <guid isPermaLink="true">https://sedangdang.tistory.com/304</guid>
      <comments>https://sedangdang.tistory.com/304#entry304comment</comments>
      <pubDate>Wed, 23 Nov 2022 12:00:02 +0900</pubDate>
    </item>
    <item>
      <title>@WebMvcTest 에서 Spring Security 적용, 401/403 에러 해결하기 - csrf</title>
      <link>https://sedangdang.tistory.com/303</link>
      <description>&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;요약&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-size=&quot;size26&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;401 Unauthorized -&amp;gt; @WithMockUser, @WithMockUserDetails 사용&lt;br /&gt;403 Forbidden -&amp;gt; with(csrf()) 추가&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;@WebMvcTest&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;Annotation that can be used for a Spring MVC test that focuses&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;only&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;on Spring MVC components.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;@Controller&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@ControllerAdvice&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@JsonComponent&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Converter&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;/&lt;/span&gt;GenericConverter&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Filter&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;WebMvcConfigurer&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;HandlerMethodArgumentResolver&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;beans&lt;/b&gt; but not&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;@Component&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@Service&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;or&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@Repository&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;beans&lt;/b&gt;).&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747;&quot;&gt;By default, &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;tests annotated with&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;@WebMvcTest&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;will also auto-configure Spring Security and MockMvc&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669025823163&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;WebMvcTest (Spring Boot 2.7.5 API)&quot; data-og-description=&quot;Annotation that can be used for a Spring MVC test that focuses only on Spring MVC components. Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. @Controller, @ControllerAdvice, @JsonC&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTest.html&quot; data-og-url=&quot;https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTest.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTest.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTest.html&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;WebMvcTest (Spring Boot 2.7.5 API)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Annotation that can be used for a Spring MVC test that focuses only on Spring MVC components. Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. @Controller, @ControllerAdvice, @JsonC&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;MVC와 관련된 애노테이션(Controller, ControllerAdvice, Filter, WebMvcConfigurer 등..)만 불러오고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Component, @Service, @Repository와 같은 빈들은 불러오지 않는다고 되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고! @WebMvcTest는 &lt;b&gt;Spring Security를 auto-configure&lt;/b&gt; 한다고 하는데, 이것도 뭔 소리인지 아래에서 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. WebMvcTest와 SpringBootTest의 차이?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서로만 들으면 사실 잘 감이 안오는데 진짜 안불러올까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- SpringBootTest 에서의 ApplicationContext Bean&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;343&quot; data-origin-height=&quot;25&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oLAuN/btrRNTOw55n/sFUJmFqQw1aNioRGuIuRtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oLAuN/btrRNTOw55n/sFUJmFqQw1aNioRGuIuRtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oLAuN/btrRNTOw55n/sFUJmFqQw1aNioRGuIuRtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoLAuN%2FbtrRNTOw55n%2FsFUJmFqQw1aNioRGuIuRtk%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;370&quot; height=&quot;27&quot; data-origin-width=&quot;343&quot; data-origin-height=&quot;25&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;251&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUyum8/btrRH6I02HT/oJxxL7NvAX6KpsFUPcElRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUyum8/btrRH6I02HT/oJxxL7NvAX6KpsFUPcElRk/img.png&quot; data-alt=&quot;@Repository 애노테이션도 다 불러옴&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUyum8/btrRH6I02HT/oJxxL7NvAX6KpsFUPcElRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUyum8%2FbtrRH6I02HT%2FoJxxL7NvAX6KpsFUPcElRk%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;251&quot; height=&quot;176&quot; data-origin-width=&quot;251&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;@Repository 애노테이션도 다 불러옴&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; @Controller, @Service, @Component, @Bean 등등 모두 다 불러와서 실제 환경과 동일하게 테스트 하고자 할 때 사용하는 @SpringBootTest를 사용한다&lt;/p&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;- WebMvcTest에서의 ApplicationContext Bean&lt;/p&gt;
&lt;pre id=&quot;code_1669022017514&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@WebMvcTest(TownApiController.class)
class TownApiControllerTest {

    @Autowired private MockMvc mvc;
    
    @MockBean private UserService userService;
    ....
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;28&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJ8R0w/btrRGkAQtaI/XPv2KK2FrfZDMBoPVkhN8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJ8R0w/btrRGkAQtaI/XPv2KK2FrfZDMBoPVkhN8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJ8R0w/btrRGkAQtaI/XPv2KK2FrfZDMBoPVkhN8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJ8R0w%2FbtrRGkAQtaI%2FXPv2KK2FrfZDMBoPVkhN8k%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;401&quot; height=&quot;28&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;28&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말로 WebMvcTest가 &lt;b&gt;절반 가랑&lt;/b&gt;의 스프링 빈만 불러와서 사용하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 beanDefinitionMap을 일일히 뒤져봐도 repository나 component같이 내가 설정해둔 빈들을 불러오지 않은 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC8zj3/btrRMKraFP2/wbjPHpLQEXGIZoeWiTWVik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC8zj3/btrRMKraFP2/wbjPHpLQEXGIZoeWiTWVik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC8zj3/btrRMKraFP2/wbjPHpLQEXGIZoeWiTWVik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC8zj3%2FbtrRMKraFP2%2FwbjPHpLQEXGIZoeWiTWVik%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;459&quot; height=&quot;122&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신에 내가 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;@MockBean&lt;/b&gt;&lt;/span&gt;으로 주입해준 UserService 빈은 #0이 뒤에 붙어서 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;스프링 빈으로 저장&lt;/b&gt;&lt;/span&gt;되어있는 것을 확인할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beFXMB/btrRNBUNB1M/yoxwwq5x7nuM8fpWJGSKd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beFXMB/btrRNBUNB1M/yoxwwq5x7nuM8fpWJGSKd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beFXMB/btrRNBUNB1M/yoxwwq5x7nuM8fpWJGSKd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeFXMB%2FbtrRNBUNB1M%2Fyoxwwq5x7nuM8fpWJGSKd1%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;646&quot; height=&quot;125&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;125&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 UserService 빈은 MockitoMock을 통해 반들어져있고 내부는 다 null로 채워져있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러니까 이 텅 비어있는 userService 빈을 실제 동작하는 것처럼 흉내내기 위해서 (Mocking)&lt;/p&gt;
&lt;pre id=&quot;code_1669022293593&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;given(userService.findById(1L)).willReturn(user);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 given() 메서드를 대신 활용하는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Spring Security의 Auto Congifure&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebMvcTest는 컨트롤러 테스트를 하기 위해 꼭 필요한 빈들만을 불러와서 사용한다는 것을 직접 확인해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 문제가 되는 것은 &lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;내가 만들어둔 Spring Security Configuration, Bean들도 불러오지 않는다&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;그 대신에 스프링 시큐리티가 자동으로 구성하는 Configuration 파일들을 불러와서 사용한다.&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;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;SpringBootWebSecurityConfiguration&lt;/b&gt; &lt;/span&gt;&amp;lt;&amp;lt; 이 친구가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1669023263059&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration(proxyBeanMethods = false)
@ConditionalOnDefaultWebSecurity
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {

	@Bean
	@Order(SecurityProperties.BASIC_AUTH_ORDER)
	SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
		http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
		return http.build();
	}

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거 딱 스프링 시큐리티 맨 처음 적용했을 때 페이지 들어가면 바로 로그인 창 뜨게 만드는 그것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;모든 요청에 대해 아무 권한이나 갖고있으면 되도록&lt;/b&gt;&lt;/span&gt; 간단하게만 설정되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러 계층 테스트를 하기 위해&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669026594881&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mvc.perform(delete(&quot;/api/towns/1&quot;));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 요청을 해보면 스프링 시큐리티가 적용되어 있기 때문에 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;401 Unauthorized&lt;/b&gt;&lt;/span&gt;에러를 반환하는 것을 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 요청에 대해 권한이 필요하도록 기본적으로 적용이 되어있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dC74uR/btrRKKzbjOJ/x6u09IJGKxXfx5kQokLDbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dC74uR/btrRKKzbjOJ/x6u09IJGKxXfx5kQokLDbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dC74uR/btrRKKzbjOJ/x6u09IJGKxXfx5kQokLDbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdC74uR%2FbtrRKKzbjOJ%2Fx6u09IJGKxXfx5kQokLDbk%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;512&quot; height=&quot;122&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러니까 결국 &lt;b&gt;요청을 할 때 권한을 같이 넘겨줘야 한다는 것&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. ExcludeFilter를 이용해서 Security를 회피하는 방법&lt;span&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;@WithMockUser, @WithUserDetails&lt;/b&gt;&lt;/span&gt; 와 같은 애노테이션을 이용해 권한을 넘겨주는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;&lt;b&gt;(+) @TestConfiguration 파일을 별도로 설정하는 방법도 있다!&lt;/b&gt;&lt;/i&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;1번의 경우 일단 되긴 하나 근본적인 해결이 되지 못하는 방법이다. 패스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 방법을 사용해 임의의 UserDetails를 만들어서 이를 같이 넘겨주는 방법을 사용하면 된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@WithMockUser&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@WithAnonymousUser - 미인증 사용자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@WithUserDetails - 메서드가 principal 내부의 값을 직접 사용하는 경우 (별도의 사전 설정 필요)&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;pre id=&quot;code_1669040622189&quot; class=&quot;kotlin&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequiredArgsConstructor
@Validated
@RestController
@RequestMapping(value = &quot;/api/towns&quot;)
public class TownApiController {

    private final TownService townService;
    private final UserService userService;
    private final AdminService adminService;
    
    ...

    @Secured({&quot;ROLE_SUPER&quot;})
    @DeleteMapping(&quot;/{townId}&quot;)
    public ResponseEntity&amp;lt;String&amp;gt; remove(@PathVariable @Positive Long townId) {
        townService.delete(townId);
        return ResponseEntity.ok(&quot;성공적으로 삭제했습니다&quot;);
    }
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1669040560551&quot; class=&quot;pgsql&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Test
    @WithMockUser(username = &quot;테스트_최고관리자&quot;, roles = {&quot;SUPER&quot;})
    void test() throws Exception {
        ResultActions actions = mvc.perform(
                delete(&quot;/api/towns/1&quot;)
                        .with(csrf()));

        actions.andExpect(status().isOk());
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는 것으로 알고 있었고 해결이 되었다면 블로깅을 안했을 것인데 추가적인 문제가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@WithMockUser를 사용하고 나니 401 에러가 아니라 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;403 Forbidden 에러&lt;/b&gt;&lt;/span&gt;가 떴다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;508&quot; data-origin-height=&quot;113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf3p9k/btrRMJ6Yo0J/kojsViKSBG0Y6E8EkpKrk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf3p9k/btrRMJ6Yo0J/kojsViKSBG0Y6E8EkpKrk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf3p9k/btrRMJ6Yo0J/kojsViKSBG0Y6E8EkpKrk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf3p9k%2FbtrRMJ6Yo0J%2FkojsViKSBG0Y6E8EkpKrk0%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;508&quot; height=&quot;113&quot; data-origin-width=&quot;508&quot; data-origin-height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;401 Unauthorized&lt;/b&gt;는 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;아예 비로그인 상태&lt;/b&gt;&lt;/span&gt;에서 권한이 필요한 요청을 했을 때 발생하는 에러&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;403 Forbidden&lt;/b&gt;은&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt; 로그인은 했으나 권한이 맞지 않는 경우&lt;/b&gt;&lt;/span&gt;에 발생하는 에러라고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;283&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tt6Y3/btrRMV0vJoP/z4iW3dyKjFNOaJLAO432Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tt6Y3/btrRMV0vJoP/z4iW3dyKjFNOaJLAO432Uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tt6Y3/btrRMV0vJoP/z4iW3dyKjFNOaJLAO432Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftt6Y3%2FbtrRMV0vJoP%2Fz4iW3dyKjFNOaJLAO432Uk%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;283&quot; height=&quot;192&quot; data-origin-width=&quot;283&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;67&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdombh/btrRNA2KDLT/z9LkY0UI8LnrSiYu0zABVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdombh/btrRNA2KDLT/z9LkY0UI8LnrSiYu0zABVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdombh/btrRNA2KDLT/z9LkY0UI8LnrSiYu0zABVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdombh%2FbtrRNA2KDLT%2Fz9LkY0UI8LnrSiYu0zABVK%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;1030&quot; height=&quot;67&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;67&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 request 봐도 세션에 SpringContext 내부에 WithMockUser로 만든 권한들이 다 잘 들어가있는 걸 확인할 수 있고, Authorities도 &quot;ROLE_SUPER&quot;으로 잘 담겨있는데 대체 왜 안된다는걸까??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글링 해보니 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;csrf&lt;/b&gt;&lt;/span&gt;와 관련된 문제라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf&quot;&lt;/b&gt; 를 추가해주면 된다고 하는데,,&lt;/p&gt;
&lt;pre id=&quot;code_1669025194744&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ResultActions actions = mvc.perform(
                delete(&quot;/api/towns/1&quot;)
                        .with(csrf()));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말로&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt; with.(csrf())&lt;/b&gt; &lt;/span&gt;를 추가해주니 참 어처구니 없게도 잘 된다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대체 csrf가 뭔데 그러는 걸까?&lt;/p&gt;
&lt;pre id=&quot;code_1669041114285&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf().disable()
     ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 맨 처음에 Spring Security를 얕게 배울 때부터 그냥&lt;b&gt; csrf().disable()&lt;/b&gt; 이거는 솔직히 뭔지도 잘 모르고 관례적으로;;;&amp;nbsp; 그러려니 하고 기본으로 깔고 사용하고 있었는데.... 이 참에 뭔지 알아보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# csrf(Cross-Site Request Forgery)&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 공격자가 악의적인 코드를 심어놓은 사이트를 만들어놓고, 로그인 된 사용자가 클릭하게 만들어 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;사용자 의지와 무관한 요청을 발생&lt;/b&gt;&lt;/span&gt;시키는 공격&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;b&gt;사용자는 로그인 한 상태&lt;/b&gt;고 쿠키, 권한을 갖고있기 때문에 공격자가 위조한 웹사이트에 방문하게 되면 사용자 모르게 악의적인 POST, DELETE 요청을 정상 수행하도록 만들어버리는 공격&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이를 해결하기 위해 스프링 시큐리티에서는 &quot;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;CSRF 토큰&lt;/b&gt;&lt;/span&gt;&quot; 을 이용해 토큰 값을 비교해서 일치하는 경우에만 메서드를 처리하도록 만든다. (&lt;b&gt;Synchronizer Token Pattern&lt;/b&gt; 이라고 한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# &lt;b&gt;Synchronizer Token Pattern&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서버가 뷰를 만들어줄 때 &lt;b&gt;사용자 별 랜덤값을 만들어 세션에 저장&lt;/b&gt;한 다음 이를 뷰 페이지에 같이 담아 넘겨주게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;클라이언트는 HTTP 요청마다 숨겨진 csrf 토큰을 같이 넘겨줘야 하는 방식.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서버는 HTTP Request에 있는 csrf 토큰값과 세션에 저장되어있는 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;토큰값을 비교해 일치하는 경우에만 처리를 진행&lt;/b&gt;&lt;/span&gt;하는 방식이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 위조된 사이트의 경우&lt;b&gt; csrf 토큰값이 일치하지 않기 때문에&lt;/b&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;* GET 요청에 대해서는 csrf 검증을 수행하지 않는다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* with(csrf()) 를 추가한 경우 파라미터로 _csrf 값을 같이 보내주는 것을 실제로 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1rp6Z/btrRMLRtRuk/GKfk4GcoBwjHDagVYjLlak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1rp6Z/btrRMLRtRuk/GKfk4GcoBwjHDagVYjLlak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1rp6Z/btrRMLRtRuk/GKfk4GcoBwjHDagVYjLlak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1rp6Z%2FbtrRMLRtRuk%2FGKfk4GcoBwjHDagVYjLlak%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;967&quot; height=&quot;190&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* with(csrf())를 사용하지 않은 경우 -&amp;gt; 세션에 저장된 CSRF 값과 매치되지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 요청을 수행하지 않고 403 에러 반환&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1309&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duYZAH/btrRMU8Ao6c/9p3TKkXxyZ4DXv0ZBtn6F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duYZAH/btrRMU8Ao6c/9p3TKkXxyZ4DXv0ZBtn6F1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duYZAH/btrRMU8Ao6c/9p3TKkXxyZ4DXv0ZBtn6F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduYZAH%2FbtrRMU8Ao6c%2F9p3TKkXxyZ4DXv0ZBtn6F1%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;1309&quot; height=&quot;190&quot; data-origin-width=&quot;1309&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# 그럼 왜 안전한 csrf 기능을 disable 한걸까??&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csrf 토큰 방식을 살펴보면 각 사용자에 대한 세션을 이용하는 방식이라는 것을 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 웹 브라우저를 통한 접근을 하는 경우, &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;세션/쿠키를 사용해 상태를 유지하려고 하는 경우&lt;/b&gt;&lt;/span&gt; csrf를 사용하는 것이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 REST API의 경우는 대개 무상태성을 유지하며 JWT와 같은 토큰 방식으로 인증하게 되면 요청이 세션에 의존하지 않기 때문이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcAee3/btrROqFH8J1/RLKFvEgUWw07HfpYVGxQtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcAee3/btrROqFH8J1/RLKFvEgUWw07HfpYVGxQtk/img.png&quot; data-alt=&quot;https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/csrf.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcAee3/btrROqFH8J1/RLKFvEgUWw07HfpYVGxQtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcAee3%2FbtrROqFH8J1%2FRLKFvEgUWw07HfpYVGxQtk%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;1206&quot; height=&quot;115&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/csrf.html&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;* 타임리프와 같은 템플릿 엔진을 통해 View를 같이 제공하는 애플리케이션 / 웹 브라우저를 통해 요청을 받는 애플리케이션 -&amp;gt;&amp;nbsp; csrf 사용 권장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*&amp;nbsp; Rest API만 제공하는 애플리케이션 = csrf 사용 안해도 무방.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어째 알면 알수록 더 어려운 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;01_모코코콘1_18_슬퍼.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjBGdy/btrRMKrgpPl/TJxafpnSzCFQkNa8jSrKK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjBGdy/btrRMKrgpPl/TJxafpnSzCFQkNa8jSrKK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjBGdy/btrRMKrgpPl/TJxafpnSzCFQkNa8jSrKK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjBGdy%2FbtrRMKrgpPl%2FTJxafpnSzCFQkNa8jSrKK1%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;193&quot; height=&quot;193&quot; data-filename=&quot;01_모코코콘1_18_슬퍼.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;Security 참고&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-09-30-spring-security-test/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-09-30-spring-security-test/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669046178308&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring Security가 적용된 곳을 효율적으로 테스트하자.&quot; data-og-description=&quot;Spring Security와 관련된 기능을 테스트하다보면 인증 정보를 미리 주입해야 하는 경우가 종종 발생한다. 기본적으로 생각할 수 있는 가장 간단한 방법은 테스트 전에 SecurityContext에 직접 Authenticatio&quot; data-og-host=&quot;tecoble.techcourse.co.kr&quot; data-og-source-url=&quot;https://tecoble.techcourse.co.kr/post/2020-09-30-spring-security-test/&quot; data-og-url=&quot;https://post/2020-09-30-spring-security-test/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bSW7pX/hyQE1MYubj/IvoGij7kKYjuL3jrvbVKCK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot;&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-09-30-spring-security-test/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tecoble.techcourse.co.kr/post/2020-09-30-spring-security-test/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bSW7pX/hyQE1MYubj/IvoGij7kKYjuL3jrvbVKCK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720');&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 Security가 적용된 곳을 효율적으로 테스트하자.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Security와 관련된 기능을 테스트하다보면 인증 정보를 미리 주입해야 하는 경우가 종종 발생한다. 기본적으로 생각할 수 있는 가장 간단한 방법은 테스트 전에 SecurityContext에 직접 Authenticatio&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tecoble.techcourse.co.kr&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;lt;CSRF 참고&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://codevang.tistory.com/282&quot;&gt;https://codevang.tistory.com/282&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669044233589&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring Security_CSRF Token의 개념과 사용 방법&quot; data-og-description=&quot;- Develop OS : Windows10 Ent, 64bit - WEB/WAS Server : Tomcat v9.0 - DBMS : MySQL 5.7.29 for Linux (Docker) - Language : JAVA 1.8 (JDK 1.8) - Framwork : Spring 3.2.9 Release - Build Tool : Maven 3.6.3 - ORM : Mybatis 3.2.8 [ CSRF(Cross Site Request Forgery&quot; data-og-host=&quot;codevang.tistory.com&quot; data-og-source-url=&quot;https://codevang.tistory.com/282&quot; data-og-url=&quot;https://codevang.tistory.com/282&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/biSdNb/hyQE4QrKaa/8ZjsyvrK47qVzx9bFieByK/img.png?width=611&amp;amp;height=572&amp;amp;face=0_0_611_572,https://scrap.kakaocdn.net/dn/cihqcu/hyQE2SCwa1/I5t2IB9G2ZrDYCME9z8N20/img.png?width=611&amp;amp;height=572&amp;amp;face=0_0_611_572,https://scrap.kakaocdn.net/dn/xrSTh/hyQE5PlMNl/vERRn7CUj2Z0qf4dy2QEPK/img.png?width=545&amp;amp;height=510&amp;amp;face=0_0_545_510&quot;&gt;&lt;a href=&quot;https://codevang.tistory.com/282&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://codevang.tistory.com/282&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/biSdNb/hyQE4QrKaa/8ZjsyvrK47qVzx9bFieByK/img.png?width=611&amp;amp;height=572&amp;amp;face=0_0_611_572,https://scrap.kakaocdn.net/dn/cihqcu/hyQE2SCwa1/I5t2IB9G2ZrDYCME9z8N20/img.png?width=611&amp;amp;height=572&amp;amp;face=0_0_611_572,https://scrap.kakaocdn.net/dn/xrSTh/hyQE5PlMNl/vERRn7CUj2Z0qf4dy2QEPK/img.png?width=545&amp;amp;height=510&amp;amp;face=0_0_545_510');&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 Security_CSRF Token의 개념과 사용 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;- Develop OS : Windows10 Ent, 64bit - WEB/WAS Server : Tomcat v9.0 - DBMS : MySQL 5.7.29 for Linux (Docker) - Language : JAVA 1.8 (JDK 1.8) - Framwork : Spring 3.2.9 Release - Build Tool : Maven 3.6.3 - ORM : Mybatis 3.2.8 [ CSRF(Cross Site Request Forgery&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;codevang.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-security/reference/features/exploits/csrf.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-security/reference/features/exploits/csrf.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669044150194&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;Cross Site Request Forgery (CSRF) :: Spring Security&quot; data-og-description=&quot;When should you use CSRF protection? Our recommendation is to use CSRF protection for any request that could be processed by a browser by normal users. If you are only creating a service that is used by non-browser clients, you will likely want to disable &quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-security/reference/features/exploits/csrf.html&quot; data-og-url=&quot;https://docs.spring.io/spring-security/reference/features/exploits/csrf.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-security/reference/features/exploits/csrf.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-security/reference/features/exploits/csrf.html&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;Cross Site Request Forgery (CSRF) :: Spring Security&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;When should you use CSRF protection? Our recommendation is to use CSRF protection for any request that could be processed by a browser by normal users. If you are only creating a service that is used by non-browser clients, you will likely want to disable&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>웹/Spring</category>
      <author>세댕댕이</author>
      <guid isPermaLink="true">https://sedangdang.tistory.com/303</guid>
      <comments>https://sedangdang.tistory.com/303#entry303comment</comments>
      <pubDate>Tue, 22 Nov 2022 00:38:46 +0900</pubDate>
    </item>
    <item>
      <title>Nginx를 이용해 HTTPS 웹 애플리케이션 무중단 배포 환경 만들기</title>
      <link>https://sedangdang.tistory.com/301</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;사용 기술 스택&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- AWS EC2 (Amazon Linux2 -&amp;gt; CentOS 기반)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Nginx -&amp;gt; &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;리버스 프록시&lt;/b&gt;&lt;/span&gt; 용도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Let's Encrypt(Certbot) -&amp;gt; &lt;b&gt;SSL 인증서 발급&lt;/b&gt; 용도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- SpringBoot 및 쉘 스크립트&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;631&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJGEYT/btrQPD7zqJA/fkfbdKV4ahp9IXPEed05Hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJGEYT/btrQPD7zqJA/fkfbdKV4ahp9IXPEed05Hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJGEYT/btrQPD7zqJA/fkfbdKV4ahp9IXPEed05Hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJGEYT%2FbtrQPD7zqJA%2FfkfbdKV4ahp9IXPEed05Hk%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;605&quot; height=&quot;631&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;631&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;0. 사전 개념 정리!&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;# 프록시, 리버스 프록시란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;&quot;프록시 = 중개&quot; &lt;/b&gt;&lt;/span&gt;해주는 역할로 이해하면 쉽다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사용자를 대신해 서버에 접근 해주는 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; &lt;b&gt;보안, 성능, 안정성 향상&lt;/b&gt; + &lt;b&gt;캐시 서버&lt;/b&gt; 역할 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 포워드 프록시와 리버스 프록시로 구분할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;&amp;lt;포워드 프록시&amp;gt;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 내부 망에서 사용해서 외부 인터넷 망으로 연결하기 전에 걸러내는 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 클라이언트가 특정 웹 사이트에 접근하는 것을 사전 차단하는 역할 (학교에서 유해사이트 차단 등... ㅋㅋ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 포워드 프록시 서버의 IP를 대신 사용하는 것으로 클라이언트가 누구인지를 감추는 역할도 할 수 있다 &lt;b&gt;(IP 우회)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 동일한 요청이 들어올 경우 캐싱된 데이터를 곧바로 전달해주는 역할도 할 수 있다 &lt;b&gt;(캐싱)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;&amp;lt;리버스 프록시&amp;gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서버 단의 맨 앞에 위치해서 요청을 분배해주는 역할 (&lt;b&gt;로드 밸런싱&lt;/b&gt; 등...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;애플리케이션 서버는 프록시 뒤에 감춰버리는 효과&lt;/b&gt;&lt;/span&gt;를 얻을 수 있다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsduDY/btrQSZveNcy/bnMe8kON8TYUGaCGo9y5M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsduDY/btrQSZveNcy/bnMe8kON8TYUGaCGo9y5M0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsduDY/btrQSZveNcy/bnMe8kON8TYUGaCGo9y5M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsduDY%2FbtrQSZveNcy%2FbnMe8kON8TYUGaCGo9y5M0%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;703&quot; height=&quot;402&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;# Nginx란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 비동기 이벤트 기반으로 &lt;b&gt;동시접속 처리에 특화&lt;/b&gt;된 &quot;웹 서버&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Apache보다 단순하고, 전달자 역할에 특화. &lt;b&gt;가볍고 빠르다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 정적 파일을 제공하는 웹 서버의 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 리버스 프록시 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 비동기 이벤트 처리 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;# HTTPS는 어떻게 적용하는지?&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4nGwL/btrQQjHMNSq/BG2yynq1pNOKye4eUgHMYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4nGwL/btrQQjHMNSq/BG2yynq1pNOKye4eUgHMYk/img.png&quot; data-alt=&quot;https://aws-hyoh.tistory.com/38 - 미닉스 김인성님의 블로그&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4nGwL/btrQQjHMNSq/BG2yynq1pNOKye4eUgHMYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4nGwL%2FbtrQQjHMNSq%2FBG2yynq1pNOKye4eUgHMYk%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;563&quot; height=&quot;802&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://aws-hyoh.tistory.com/38 - 미닉스 김인성님의 블로그&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. Nginx 서버에 SSL 인증서를 발급해둔다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Nginx 서버가 443 포트로 들어오는 요청을 모두 받는다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 뿐만 아니라 HTTP(80) 포트로 들어오는 요청도 HTTPS(443)으로 강제 리다이렉트도 시켜줄 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Nginx 단에서 세션키(대칭키)를 이용해 데이터를 복호화 -&amp;gt; 복호화된 데이터를 애플리케이션 서버로 전송해준다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;336&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKPRRe/btrQN6QEW1e/ZaOe46NSZjw6twHH1T5ue0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKPRRe/btrQN6QEW1e/ZaOe46NSZjw6twHH1T5ue0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKPRRe/btrQN6QEW1e/ZaOe46NSZjw6twHH1T5ue0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKPRRe%2FbtrQN6QEW1e%2FZaOe46NSZjw6twHH1T5ue0%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;336&quot; height=&quot;221&quot; data-origin-width=&quot;336&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리 코드 및 순서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. EC2 서버에 Certbot 이용해 인증서 발급받기 (Nginx 방식)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1668044023471&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo certbot --nginx&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;b&gt;2. Nginx 환경 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;/etc/nginx/nginx.conf&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1668044183760&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;
	
    #클라이언트가 보낼 수 있는 requst의 총 크기
    client_max_body_size 30M; 

    server {
        # server_name www.smarttownnotice.gq;
        server_name [내 도메인 주소];

        # 무중단 배포 시 교체할 url이 담긴 파일
        include /etc/nginx/conf.d/service-url.inc; 

        location / {
            # 해당 주소로 요청 전송 (service-url.inc에서 가져온다)
            proxy_pass $service_url;

            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
        }

        listen 443 ssl; # 443 포트로 요청이 들어왔을 때
        ssl_certificate /etc/letsencrypt/live/www.smarttownnotice.gq/fullchain.pem; # 공개키
        ssl_certificate_key /etc/letsencrypt/live/www.smarttownnotice.gq/privkey.pem; # 비밀키
		
        ...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 Nginx reload (!= restart)&lt;/p&gt;
&lt;pre id=&quot;code_1668044243342&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo systemctl reload nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* restart = 끊김 O / reload = 끊김 X&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 배포 스크립트 만들기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 기반으로 만들어진 스크립트입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. start.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1668044383366&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
source ${ABSDIR}/profile.sh

REPOSITORY=/home/ec2-user/app/smartnotice_build
PROJECT_NAME=smartnotice

echo &quot;&amp;gt; Build 파일 복사&quot;
echo &quot;&amp;gt; cp ${REPOSITORY}/zip/*.jar ${REPOSITORY}/&quot;

cp ${REPOSITORY}/zip/build/libs/*.jar ${REPOSITORY}/

echo &quot;&amp;gt; 새 애플리케이션 배포&quot;
JAR_NAME=$(ls -tr ${REPOSITORY}/*.jar | tail -n 1)

echo &quot;&amp;gt; JAR Name: ${JAR_NAME}&quot;

echo &quot;&amp;gt; ${JAR_NAME} 에 실행 권한 추가&quot;

chmod +x ${JAR_NAME}

echo &quot;&amp;gt; ${JAR_NAME} 실행&quot;

IDLE_PROFILE=$(find_idle_profile) # SubShell을 호출해 함수의 결과값을 리턴받는다

echo &quot;&amp;gt; ${JAR_NAME} 을 profile=${IDLE_PROFILE} 로 실행합니다&quot;

nohup java -jar \
        -Dspring.config.location=classpath:/application.properties,/home/ec2-user/app/application-real-db.properties,classpath:/application-$IDLE_PROFILE.properties,/home/ec2-user/app/application-api.properties \
        -Dspring.profiles.active=${IDLE_PROFILE} \
        ${JAR_NAME} &amp;gt; ${REPOSITORY}/nohup.out 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. stop.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1668044403229&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH) # 현재 stop.sh가 속해있는 경로를 찾는다
source ${ABSDIR}/profile.sh # java import 생각하면 됨 - 다른 .sh 파일의 function을 사용할 수 있게 해줌

IDLE_PORT=$(find_idle_port) #subShell

echo &quot;&amp;gt; $IDLE_PORT 에서 구동중인 애플리케이션 pid 확인&quot;
IDLE_PID=$(lsof -ti tcp:${IDLE_PORT})

if [ -z ${IDLE_PID} ]
then
  echo &quot;&amp;gt; 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다&quot;
else
  echo &quot;&amp;gt; kill -15 $IDLE_PID&quot;
  kill -15 ${IDLE_PID}
  sleep 5
fi&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. profile.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1668044426326&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

# 쉬고있는 profile 찾기
function find_idle_profile() {

    # 현재 애플리케이션이 몇번 포트로 실행되고 있는지 확인 (Nginx가 443으로 받아서 포워드)
    RESPONSE_CODE=$(curl -s -o /dev/null -w &quot;%{http_code}&quot; https://www.smarttownnotice.gq/profile)

    if [ &quot;${RESPONSE_CODE}&quot; -ge 400 ] || [ &quot;${RESPONSE_CODE}&quot; -eq 000 ] # 400보다 크거나, 000(TimeOut)이면 -&amp;gt; Error 발생
    then
      CURRENT_PROFILE=real1 # 에러 발생 시 real1 포트로 보내도록 세팅
    else # 정상 상태(200) 이라면
      CURRENT_PROFILE=$(curl -s https://www.smarttownnotice.gq/profile) # 사이트에서 현재 사용중인 포트를 응답해줌(real1/real2)
    fi

    if [ &quot;${CURRENT_PROFILE}&quot; == real1 ]
    then
      IDLE_PROFILE=real2;
    else
      IDLE_PROFILE=real1
    fi

    echo &quot;${IDLE_PROFILE}&quot;
}

# 쉬고 있는 profile의 port 찾기
function find_idle_port() {
  IDLE_PROFILE=$(find_idle_profile) # SubShell 호출

  if [ &quot;${IDLE_PROFILE}&quot; == real1 ]
  then
    echo &quot;8081&quot;
  else
    echo &quot;8082&quot;
  fi
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. health.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1668044447477&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
source ${ABSDIR}/profile.sh
source ${ABSDIR}/switch.sh

IDLE_PORT=$(find_idle_port)

echo &quot;&amp;gt; Health Check Start!&quot;
echo &quot;&amp;gt; IDLE_PORT: $IDLE_PORT&quot;
echo &quot;&amp;gt; curl -s http://localhost:$IDLE_PORT/profile&quot;
sleep 10

for RETRY_COUNT in {1..10}
do
  RESPONSE=$(curl -s http://localhost:${IDLE_PORT}/profile)
  UP_COUNT=$(echo ${RESPONSE} | grep 'real' | wc -l)

  if [ ${UP_COUNT} -ge 1 ] # real 문자열이 있는지 검증
  then
    echo &quot;&amp;gt; Health Check 성공&quot;
    switch_proxy # 정상적으로 배포 성공하면 다음 배포시 다른 포트를 사용하기 위해 포트 변경!!!
    break
  else
    echo &quot;&amp;gt; Health Check의 응답을 알 수 없거나, 실행 상태가 아닙니다&quot;
    echo &quot;&amp;gt; Health Check: ${RESPONSE}&quot;
  fi

  if [ ${RETRY_COUNT} -eq 10 ]
  then
    echo &quot;&amp;gt; Health Check 실패&quot;
    echo &quot;&amp;gt; Nginx에 연결하지 않고 배포를 종료합니다&quot;
    exit 1
  fi

  echo &quot;&amp;gt; Health Check 연결 실패. 재시도...&quot;
  sleep 10
done&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. switch.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1668044482838&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
source ${ABSDIR}/profile.sh

function switch_proxy() {
  IDLE_PORT=$(find_idle_port)

  echo &quot;&amp;gt; 전환할 Port: $IDLE_PORT&quot;
  echo &quot;&amp;gt; Port 전환&quot;
  # Nginx가 변경할 프록시 주소 생성해서 파이프라인으로 넘김 | 덮어씌우기
  echo &quot;set \$service_url http://127.0.0.1:${IDLE_PORT};&quot; | sudo tee /etc/nginx/conf.d/service-url.inc
  echo &quot;&amp;gt; Nginx Reload&quot;
  sudo service nginx reload # restart(끊김 O) != reload(끊김 X)
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 배포!&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스프링부트 애플리케이션 내 appsepc.yml&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668044518696&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# CodeDeploy Version
version: 0.0
os: linux
files:
  - source: / # CodeDeploy에서 전달해준 파일 중 destination으로 이동시킬 대상 지정 (/ = 루트, 전체 파일)
    destination: /home/ec2-user/app/smartnotice_build/zip # source에서 지정된 파일을 받을 위치. 이후 Jar은 destination에서 옮겨진 파일들로 진행.
    overwrite: yes

# CodeDeploy에서 EC2 서버로 넘겨준 파일들 모두 ec2-user 권한을 갖도록 한다
permissions:
  - object: /
    pattern: &quot;**&quot;
    owner: ec2-user
    group: ec2-user

# CodeDeploy 배포 단계에서 실행할 명령어 지정
hooks:
  AfterInstall: # 제일 먼저 수행
    - location: /scripts/stop.sh # nginx와 연결되어있지 않는 스프링부트 종료
      timeout: 60
      runas: ec2-user
  ApplicationStart: # 그 다음 수행
    - location: /scripts/start.sh # nginx와 연결되어있지 않은 포트로 새 버전의 스프링부트 실행
      timeout: 60
      runas: ec2-user
  ValidateService: # 마지막으로 수행
    - location: /scripts/health.sh # 새 스프링 부트가 정상적으로 실행됬는지 확인
      timeout: 60
      runas: ec2-user&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OhJbb/btrQPktzn7r/9MvLlP3iaKF17i14MtKrF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OhJbb/btrQPktzn7r/9MvLlP3iaKF17i14MtKrF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OhJbb/btrQPktzn7r/9MvLlP3iaKF17i14MtKrF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOhJbb%2FbtrQPktzn7r%2F9MvLlP3iaKF17i14MtKrF1%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;516&quot; height=&quot;379&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹</category>
      <author>세댕댕이</author>
      <guid isPermaLink="true">https://sedangdang.tistory.com/301</guid>
      <comments>https://sedangdang.tistory.com/301#entry301comment</comments>
      <pubDate>Thu, 10 Nov 2022 10:42:15 +0900</pubDate>
    </item>
    <item>
      <title>2022 윈터 코딩 스타트업 인턴 프로그램 코딩테스트 후기</title>
      <link>https://sedangdang.tistory.com/300</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;22.11.05 10:00 ~ 12:00, 4문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;결과부터 말하자면 시원하게 말아먹었다......&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;사실 3번 빼고 다 풀긴 했는데 나머지 문제들 난이도가 크게 어렵지 않은 것 같아서 여지없이 컷 당할 것 같다.&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;(300/400 코테 합격)&lt;/b&gt;&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;01_모코코콘1_18_슬퍼.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cw9QKN/btrQrtYUqRT/MH0FR7sapT1XM7MsG6YOXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cw9QKN/btrQrtYUqRT/MH0FR7sapT1XM7MsG6YOXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cw9QKN/btrQrtYUqRT/MH0FR7sapT1XM7MsG6YOXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcw9QKN%2FbtrQrtYUqRT%2FMH0FR7sapT1XM7MsG6YOXK%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;161&quot; height=&quot;161&quot; data-filename=&quot;01_모코코콘1_18_슬퍼.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 -&amp;gt; &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;문자열 &lt;/b&gt;&lt;/span&gt;이용하는 문제로 생각보다 매우 금방 풀렸음 (예상 1렙)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 -&amp;gt; &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;우선순위 큐&lt;/b&gt;&lt;/span&gt;로 풀 수 있었던 문제였는데 시간을 너무 허비했다 (예상 2렙)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번 -&amp;gt; &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;그래프 탐색(DFS/BFS)&lt;/b&gt; &lt;/span&gt;보였는데 솔직히 읽어보지도 못했다 (예상 3렙?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4번 -&amp;gt; &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;SQL&lt;/b&gt; &lt;/span&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;SQL 문제가 생각보다 꽤 난이도가 있었다.&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;YEAR(datetime), MONTH(datetime) 이용한 년/월 추출(?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SUM, COUNT 랑 GROUP BY 사용한 그룹 함수&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2중 ORDER BY DESC 정렬 등등..&lt;/p&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;&lt;b&gt;SUM(IF~)&lt;/b&gt; &lt;/span&gt;이거 찾아서 사용할 수 있는지가 킬포였다. 나도 물론 오늘 처음 알았다 ㅎㅎ;;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 검색이 가능해서 푸는데는 크게 지장은 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 2번 문제..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 우선순위 큐에 대해 잘못된 지식을 갖고있었던 문제로 쉽게 풀 수 있던 문제를 1시간동안 붙잡고 앉아있었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문을 돌면서 우선순위큐에 담긴 원소의 최솟값을 별도의 변수에 담아 계속 갱신하려고 했는데 여기서 문제가 있었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전반적인 로직은 정말로 어렵지 않았는데.... 진짜 어휴...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어째저째 결국 풀기는 했는데 이거 붙잡고 풀고나니까 3번문제를 풀 시간이 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업 지원 대상을 훑어봤을 때 스타트업 위주라서 그런지 자바+스프링 포지션으로 지원받는 회사가 애초에 드물었기에 별 기대가 없었긴 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 코테에서 탈락할듯 ㅠㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아휴 공부 열심히 해야할 것 같다&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(+) 11.07 오늘 이메일로 코테 합불 결과를 받았는데 의외로 합격했다는 결과??!?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별 기대 안하고 있었는데 괜히 기분이 좋다 ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접이라도 보러갈 수 있었으면 좋겠따 :D&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ADFZg/btrQw2U3gQg/8YcoA4MzxGRXwHnZdQdrf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ADFZg/btrQw2U3gQg/8YcoA4MzxGRXwHnZdQdrf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ADFZg/btrQw2U3gQg/8YcoA4MzxGRXwHnZdQdrf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FADFZg%2FbtrQw2U3gQg%2F8YcoA4MzxGRXwHnZdQdrf1%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;622&quot; height=&quot;556&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11.08 오늘 지원한 모 기업 중 한군데에서 지원서를 확인했다는 문자가 왔다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 몇분 지났을까 또 정말 감사하게도 서류 통과했다는 메일이 왔다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요 근래 면접 준비를 하면서 쪼오금 자존감이 떨어졌었는데 그래도 서류라도 붙으니 위안이 되는 기분이다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열심히 해야지 뭐 답이 없다 ㅜㅜ&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QEqbF/btrQFZRMQ4n/XRHI1tkuaDtYbIKfIZIVfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QEqbF/btrQFZRMQ4n/XRHI1tkuaDtYbIKfIZIVfK/img.png&quot; data-alt=&quot;담당자가 지원서를 확인했다는 것도 다 남는다.. ㄷㄷ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QEqbF/btrQFZRMQ4n/XRHI1tkuaDtYbIKfIZIVfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQEqbF%2FbtrQFZRMQ4n%2FXRHI1tkuaDtYbIKfIZIVfK%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;459&quot; height=&quot;240&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;담당자가 지원서를 확인했다는 것도 다 남는다.. ㄷㄷ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>나머지</category>
      <author>세댕댕이</author>
      <guid isPermaLink="true">https://sedangdang.tistory.com/300</guid>
      <comments>https://sedangdang.tistory.com/300#entry300comment</comments>
      <pubDate>Sat, 5 Nov 2022 12:42:10 +0900</pubDate>
    </item>
    <item>
      <title>인생 첫 코딩테스트를 보고나서.. 후기</title>
      <link>https://sedangdang.tistory.com/299</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;10월 24일 지원한 모 회사의 인턴십 모집에 감사하게도 서류 합격한지 어언 1주일이 지나고..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;11월 3일&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;두 문제에 110분의 시간이 주어졌다.&lt;/p&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;그래서 2번 문제를 먼저 볼까 싶었는데 2번 문제도 처음에는 딱 어떻게 풀어야겠다는 해결방식이 안떠오르다보니 갑자기 불길한 예감이 싹 스쳐지나가는 느낌이 들었다..... 아 지금 생각해도 아찔하다..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;01_모코코콘1_10_재.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WVL12/btrQku4MtOk/fD7R4Yswok7LULPO4zGf10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WVL12/btrQku4MtOk/fD7R4Yswok7LULPO4zGf10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WVL12/btrQku4MtOk/fD7R4Yswok7LULPO4zGf10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWVL12%2FbtrQku4MtOk%2FfD7R4Yswok7LULPO4zGf10%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;158&quot; height=&quot;158&quot; data-filename=&quot;01_모코코콘1_10_재.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 어째저째 마음을 다잡고 풀었고 결과는 다행히 좋았다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;607&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blYSrF/btrQiixA7cV/r9bLkmR6dugefKK1r8iqR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blYSrF/btrQiixA7cV/r9bLkmR6dugefKK1r8iqR0/img.png&quot; data-alt=&quot;대박!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blYSrF/btrQiixA7cV/r9bLkmR6dugefKK1r8iqR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblYSrF%2FbtrQiixA7cV%2Fr9bLkmR6dugefKK1r8iqR0%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;607&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;607&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;대박!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번문제는 구현문제였고 2번 문제는 완전탐색? 같은데 재귀로 풀 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머스 난이도로 따지면 2레벨 정도일 듯 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 2~3레벨 정도 풀면 대부분 기업의 코테는 통과한다던데 직접 경험해보니 맞는 말인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;1. 평소에 너무 IDE에 의존해서 연습문제를 풀어왔다보니 직접 일일히 타이핑 하는게 힘들었다..&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- IDE의 자동완성 기능이 얼마나 위대하고 소중한 기능인지를 다시한번 느꼈다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 앞으로도 코딩테스트 문제를 풀때에는 IDE 없이 사이트에서 직접 타이핑해서 푸는 습관을 들이는게 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 주석을 달면서 코드를 짜는 것이 중요하다고 느꼈다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자꾸 두개의 테스트케이스에서 실패가 뜨길래 죽는 줄 알았다. 종료시간이 다가올수록 더 조여오는 느낌 ㅠㅜ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 코드의 주요한 부분에 주석을 달아서 &lt;b&gt;어디쯤에서 코드에 헛점이 있는지를 빨리 파악&lt;/b&gt;할 수 있도록 하는 것이 중요한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 대부분의 예외는&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt; 경계값 근처에서 발생&lt;/b&gt;&lt;/span&gt;한다. 일부 테케만 틀리는 경우 이러한 예외처리를 빼먹었을 확률이 매우 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 2와 연결되는 부분이기도 한데&lt;b&gt; 반례를 찾을 줄 알아야겠다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 근데 사실 입력값이 복잡한 경우에는 테스트케이스를 만드는 것 부터가 시간이 많이 걸리고 골치아픈 일이라 어렵다ㅜㅜ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 정말 마지막으로 제일 중요한 점은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;할 수 있다는 자신감을 갖는 것인 것 같다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;솔직히 서류통과도 못할 줄 알았는데 서류도 합격하고 어느덧 코테에서도 좋은 결과를 받아 여기까지 온 것 처럼!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 면접이라는 커다란 산이 남아있지만 여기까지 왔다는 것만 해도 굉장한 성과인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 요즘 느끼는거지만 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;&quot;시작이 반이다&quot;&lt;/b&gt;&lt;/span&gt; 라는 말을 너무 체감하고 있다. 아니 반도 아니고 한 70%는 되는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가만히 시간만 보내고 있으면 진짜 아무도 내 인생을 책임져주는 사람/기업이 없다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 질러보고 안되면 그때 가서 후회하던가 하자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;01_모코코콘1_01_따봉.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D4T53/btrQj6i4IvJ/3j5rUSWSkKXaSRuJujcmK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D4T53/btrQj6i4IvJ/3j5rUSWSkKXaSRuJujcmK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D4T53/btrQj6i4IvJ/3j5rUSWSkKXaSRuJujcmK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD4T53%2FbtrQj6i4IvJ%2F3j5rUSWSkKXaSRuJujcmK0%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;220&quot; height=&quot;220&quot; data-filename=&quot;01_모코코콘1_01_따봉.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화이팅!!!&lt;/p&gt;</description>
      <category>나머지</category>
      <author>세댕댕이</author>
      <guid isPermaLink="true">https://sedangdang.tistory.com/299</guid>
      <comments>https://sedangdang.tistory.com/299#entry299comment</comments>
      <pubDate>Thu, 3 Nov 2022 21:51:37 +0900</pubDate>
    </item>
    <item>
      <title>스프링 + Thymeleaf로 게시글 비밀번호 기능 구현하기</title>
      <link>https://sedangdang.tistory.com/298</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;작은 게시판 프로젝트를 진행하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중에서 소소하게 비밀글 기능을 추가해서 맞는 비밀번호를 입력했을 때만 게시글 조회가 가능하도록 하는 기능을 추가하려고 했는데,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 처음 계획으로는 대충 게시글 엔티티에 비밀번호 컬럼 하나 추가하고 대충 비교만 하면 되겠지? 싶은 생각으로 한두시간 정도면 끝낼 수 있는 작업으로 생각했었는데,,, 생각보다 훨씬 더 어려웠던 작업이었기에 기록으로 남겨본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;중요하지 않은 부분은 빠르게 넘어가고 핵심 작업만 다룬다!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;작업 1. 게시글 생성 시 비밀글 유무 선택 / 암호화 저장&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1667029575459&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Board extends AuditingFields {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 50, nullable = false)
    private String title;

    @Column(length = 15, nullable = false)
    private String editor;

    @Column(columnDefinition = &quot;TEXT&quot;, nullable = false)
    private String content;

    private String password; // 비밀글 조회 비밀번호 (null 유무로 비밀글 유무 체크)

	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(비밀글 유무는 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Password 컬럼이 null인지 아닌지&lt;/b&gt;&lt;/span&gt;를 이용함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;게시글 생성 시 사용할 PostDTO&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667029843849&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @AllArgsConstructor
    @NoArgsConstructor(access = AccessLevel.PROTECTED) // ObjectMapper(jackson) 는 NoArgs 생성자가 반드시 필요하다
    @Getter // 리플렉션을 이용해 주입하기 때문에 @Setter 필요 없음
    public class BoardPostDto {
        ...
        
        private String password; // 비밀글 비밀번호

        private Boolean secret; // 비밀글 유무
        
        public Boolean getSecret() {
            return secret != null; // secret 체크를 하지 않았을 경우 false / 이외 true
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 비밀글 유무 체크박스에 체크한 경우에만 비밀글 설정을 하기 위해 secret 필드를 추가 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** html 체크박스 특성 상 체크를 하지 않으면 값을 아예 보내지 않음(null)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt;&lt;b&gt; get 메서드를 별도로 만들어 기본 값(false)을 넣어주면 된다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;게시글 생성 html 일부&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNfPHv/btrPTjPhXb5/gMaxgxwFyzk3KikDDqokxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNfPHv/btrPTjPhXb5/gMaxgxwFyzk3KikDDqokxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNfPHv/btrPTjPhXb5/gMaxgxwFyzk3KikDDqokxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNfPHv%2FbtrPTjPhXb5%2FgMaxgxwFyzk3KikDDqokxk%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;610&quot; height=&quot;60&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;60&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1667030112399&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;input-group&quot;&amp;gt;
    &amp;lt;div class=&quot;input-group-text&quot;&amp;gt;
        &amp;lt;span class=&quot;me-2&quot;&amp;gt;비밀글 선택&amp;lt;/span&amp;gt;
        &amp;lt;input class=&quot;form-check-input mt-0&quot; type=&quot;checkbox&quot; name=&quot;secret&quot; value=&quot;true&quot;&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;input type=&quot;password&quot; class=&quot;form-control&quot; id=&quot;password&quot; name=&quot;password&quot; placeholder=&quot;비밀글 비밀번호를 입력하세요...&quot;&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;응답 전용 DTO&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667029716858&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@Builder
public class BoardResponse {

    private final Long id;
    private final String title;
    ...
    private final boolean secret; // 비밀글 유무
    
    public static BoardResponse from(Board board) {
    ...
        return BoardResponse.builder()
                .id(board.getId())
                .title(board.getTitle())
                ...
                .secret(board.getPassword() != null)
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;추가 작업) 클라이언트로부터 받은 비밀번호를 암호화 해서 저장하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 회원가입 기능 만들면서 사용하고 있던 &lt;b&gt;BCryptPasswordEncoder&lt;/b&gt; (스프링 시큐리티가 제공) 를 사용&lt;/p&gt;
&lt;pre id=&quot;code_1667030516162&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰 --&amp;gt; (평문/DTO) --&amp;gt; 컨트롤러 --&amp;gt; (평문/DTO) --&amp;gt;&lt;b&gt; &lt;/b&gt;&amp;nbsp;&lt;b&gt;서비스(암호화 &amp;amp;&amp;amp; DTO -&amp;gt; 엔티티)&lt;/b&gt;&amp;nbsp;--&amp;gt; 저장!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667030725779&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
@Transactional
@Service
public class BoardService {

    private final BoardRepository boardRepository;
    private final BoardMapper boardMapper;
    private final PasswordEncoder passwordEncoder; // BCryptEncoder

    public Long create(BoardRequest.BoardPostDto boardPostDto, UserAccountDto userAccountDto) {
        // (1) Dto -&amp;gt; Entity 변환
        Board board = boardMapper.BoardPostDtoToBoardEntity(boardPostDto, userAccountDto, passwordEncoder);

        // (2) Entity 저장
        Board savedBoard = boardRepository.save(board);

        ...

        // (4) 생성된 게시글의 ID 반환
        return savedBoard.getId();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1667030637010&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Mapper(componentModel = &quot;spring&quot;)
public interface BoardMapper  {
    // DTOs -&amp;gt; Board Entity
    default Board BoardPostDtoToBoardEntity(BoardRequest.BoardPostDto boardPostDto, UserAccountDto userAccountDto, PasswordEncoder passwordEncoder) {
        UserAccount userAccount = userAccountDtoToUserAccountEntity(userAccountDto);
        // 비밀글 체크를 한 경우에만 비밀번호 설정 (암호화 후 저장)
        String password = boardPostDto.getSecret() ? passwordEncoder.encode(boardPostDto.getPassword()) : null;

        return Board.builder()
                .title(boardPostDto.getTitle())
                .editor(boardPostDto.getEditor())
                .content(boardPostDto.getContent())
                .password(password)
                .userAccount(userAccount)
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0oY00/btrPS0bdVBw/0DSmd2s1XvgzQaTEVDgER1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0oY00/btrPS0bdVBw/0DSmd2s1XvgzQaTEVDgER1/img.png&quot; data-alt=&quot;암호화가 잘 된다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0oY00/btrPS0bdVBw/0DSmd2s1XvgzQaTEVDgER1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0oY00%2FbtrPS0bdVBw%2F0DSmd2s1XvgzQaTEVDgER1%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;326&quot; height=&quot;202&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;암호화가 잘 된다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;작업 2. 클라이언트로부터 받은 비밀번호를 검증하는 작업&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 생각보다 난해했는데..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 일단 게시글을 클릭했을 때 &lt;b&gt;해당 게시글이 비밀글인지/아닌지를 확인&lt;/b&gt;해야 하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 해당 게시글이 비밀글이라면 &lt;b&gt;비밀번호를 입력받는 페이지를 먼저 보여준 다음&lt;/b&gt;에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3, 검증에 실패하면 &lt;b&gt;실패했다는 응답&lt;/b&gt;을 보내주고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 검증에&lt;b&gt; 성공한 경우에만 게시글&lt;/b&gt;을 보여줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 CSR 방식으로 클라이언트 측에서 동적으로 페이지 요청을 두방 보내면 사실 별 어려움이 없었을 것 같은데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 본인은 혼자서 스프링부트와 타임리프를 이용해 SSR 방식으로 개발하고 있기 때문에 뭔가 새로운 방법을 고민했는데..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WTRw1/btrPRAqkwRb/YxBrDgO4PM9yzVtUmgeZw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WTRw1/btrPRAqkwRb/YxBrDgO4PM9yzVtUmgeZw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WTRw1/btrPRAqkwRb/YxBrDgO4PM9yzVtUmgeZw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWTRw1%2FbtrPRAqkwRb%2FYxBrDgO4PM9yzVtUmgeZw1%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;674&quot; height=&quot;419&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;571&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 흐름으로 &lt;b&gt;게시글 비밀번호 입력 페이지를 별도로&lt;/b&gt; 만들 수 밖에 없었다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트에서 동적으로 페이지를 만드는게 아니다보니 한계가 있는 것 같은데 더 좋은 방법이 있는지 잘 모르겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667031700316&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @GetMapping(&quot;/{boardId}&quot;)
    public String boardDetail(@PathVariable Long boardId,
                              HttpSession session,
                              Model model) {
                              
        // (1) 해당 게시글이 비밀글인지 먼저 확인
        boolean secretBoard = boardService.isSecretBoard(boardId);

        // (2) 세션에 해당 게시글의 비밀번호 정보가 있는지 확인
        String savedPassword = (String) session.getAttribute(&quot;secret_board_&quot; + boardId);

        // (3) 비밀글이면서 비밀번호 정보가 없이 GET 요청했다면 비밀번호 입력 페이지로 포워드
        if(secretBoard &amp;amp;&amp;amp; savedPassword == null) {
            return &quot;forward:/boards/&quot; + boardId + &quot;/auth&quot;;
        }

        // (4) 검증 통과 시 페이지 정상 응답
        BoardWithRepliesResponseDto dto = boardService.readWithRepliesById(boardId, savedPassword);
        model.addAttribute(&quot;board&quot;, dto);
        model.addAttribute(&quot;replies&quot;, dto.getReplies());

        return &quot;board/board-detail&quot;;
    }
    
    @GetMapping(&quot;/{boardId}/auth&quot;)
    public String getSecretBoardForm(@PathVariable Long boardId, Model model) {
        boolean secretBoard = boardService.isSecretBoard(boardId);
        if(!secretBoard) { // 해당 글이 비밀글이 아닌 경우 리다이렉트로 바로 보내버리기
            return &quot;redirect:/boards/&quot; + boardId;
        }
        model.addAttribute(&quot;boardId&quot;, boardId);
        return &quot;board/board-secret-auth&quot;;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러에서 해당 게시글이 비밀글인지 유무를 먼저 확인하고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀글인 경우 세션에서 해당 게시글에 대한 비밀번호를 가지고 있는지 확인한 다음에 비밀번호 정보가 없다! 하면 비밀번호 입력 페이지로 &quot;forward&quot; 시켜버린다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWtBVK/btrPRdowXLY/dY2Uw2j4IQsPpL5wN5QyE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWtBVK/btrPRdowXLY/dY2Uw2j4IQsPpL5wN5QyE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWtBVK/btrPRdowXLY/dY2Uw2j4IQsPpL5wN5QyE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWtBVK%2FbtrPRdowXLY%2FdY2Uw2j4IQsPpL5wN5QyE1%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;780&quot; height=&quot;662&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 클라이언트는 내가 /boards/{boardId} 페이지를 요청한 결과로 비밀번호 입력 페이지를 곧바로 응답받게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(URL 뒤에 /boards/{boardId}/auth 가 붙어있지 않음!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* forward와 redirect는 다르다!!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- forward를 하게되면 요청 url이 그대로 남아있다&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;229&quot; data-origin-height=&quot;38&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yP9yP/btrPTjofSf5/nNScAfcJjXaKH2lfpk7g5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yP9yP/btrPTjofSf5/nNScAfcJjXaKH2lfpk7g5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yP9yP/btrPTjofSf5/nNScAfcJjXaKH2lfpk7g5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyP9yP%2FbtrPTjofSf5%2FnNScAfcJjXaKH2lfpk7g5K%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;229&quot; height=&quot;38&quot; data-origin-width=&quot;229&quot; data-origin-height=&quot;38&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 근데 &lt;b&gt;redirect를 하게 되면 새로운 페이지로 다시 요청하기 때문에 새로운 url으로 아예 이동&lt;/b&gt;하게 된다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXhrtw/btrPPXG5KvQ/VilMyFZ8tqgVTb4e1xOzQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXhrtw/btrPPXG5KvQ/VilMyFZ8tqgVTb4e1xOzQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXhrtw/btrPPXG5KvQ/VilMyFZ8tqgVTb4e1xOzQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXhrtw%2FbtrPPXG5KvQ%2FVilMyFZ8tqgVTb4e1xOzQk%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;206&quot; height=&quot;34&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 이렇게 게시글을 클릭하면 비밀번호 입력 페이지를 먼저 띄우는 것까지 완료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 비밀번호를 입력 시 검증하는 RestController를 만든다&lt;/p&gt;
&lt;pre id=&quot;code_1667033036693&quot; class=&quot;aspectj&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
@Component
public class BoardPasswordValidator implements Validator {

    private final BoardService boardService;

    @Override
    public boolean supports(Class&amp;lt;?&amp;gt; clazz) {
        return BoardRequest.BoardPasswordDto.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        BoardRequest.BoardPasswordDto boardPasswordDto = (BoardRequest.BoardPasswordDto) target;

        boolean matchPassword
                = boardService.isMatchSecretPassword(boardPasswordDto.getBoardId(), boardPasswordDto.getPassword());

        if(!matchPassword) {
            errors.rejectValue(&quot;password&quot;, &quot;비밀번호 검증 오류&quot;, &quot;비밀번호가 일치하지 않습니다&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1667032587151&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
@RequestMapping(&quot;/api/boards&quot;)
@RestController
public class BoardApiController {

    private final BoardService boardService;
    private final BoardPasswordValidator boardPasswordValidator;

    @InitBinder(&quot;boardPasswordDto&quot;) // BoardPassword에 대해서만 검증해라!!
    protected void initBinder(WebDataBinder dataBinder) {
        dataBinder.addValidators(boardPasswordValidator);
    }

    ...

    @PostMapping(&quot;/{boardId}/auth&quot;)
    public ResponseEntity&amp;lt;?&amp;gt; loginSecretBoard(@PathVariable Long boardId,
                                              @Valid @RequestBody BoardRequest.BoardPasswordDto boardPasswordDto,
                                              HttpServletRequest request) {
        HttpSession session = request.getSession();
        session.setAttribute(&quot;secret_board_&quot; + boardId, boardPasswordDto.getPassword());
        return ResponseEntity.ok(boardId);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증에 통과한 경우 세션에다가 &quot;secret_board_[게시글 번호]&quot; 이름으로 비밀번호를 담아놓는다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(근데 이래도 되나 모르겠다. 보안 이슈가 있을 것 같긴 한데)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세션에 담긴 정보를 가지고 다시 게시글 GET 요청을 하면, 세션에서 비밀번호를 꺼내와서 비밀번호가 일치하는지 다시 검증한 다음에 일치하는 경우 게시글 페이지를 보여주는 방식이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;못 담은 내용들이 꽤 많은데 블로그에 다 적으려면 정말 한세월이 걸릴 것 같아 이정도만 쓰고 급하게 마무리..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 사항은 이슈 및 커밋 내역을 참고!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/eheh12321/MySimpleBoardService/issues/37&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/eheh12321/MySimpleBoardService/issues/37&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1667032995388&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;비밀글 기능 구현 &amp;middot; Issue #37 &amp;middot; eheh12321/MySimpleBoardService&quot; data-og-description=&quot;게시글에 비밀번호를 걸어 비밀글을 설정할 수 있는 기능을 만들어본다&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/eheh12321/MySimpleBoardService/issues/37&quot; data-og-url=&quot;https://github.com/eheh12321/MySimpleBoardService/issues/37&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buZCh4/hyQnRc0smS/d1sYPhYKjihMdAXku2YDm1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/eheh12321/MySimpleBoardService/issues/37&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/eheh12321/MySimpleBoardService/issues/37&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buZCh4/hyQnRc0smS/d1sYPhYKjihMdAXku2YDm1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;비밀글 기능 구현 &amp;middot; Issue #37 &amp;middot; eheh12321/MySimpleBoardService&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;게시글에 비밀번호를 걸어 비밀글을 설정할 수 있는 기능을 만들어본다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹/Spring</category>
      <author>세댕댕이</author>
      <guid isPermaLink="true">https://sedangdang.tistory.com/298</guid>
      <comments>https://sedangdang.tistory.com/298#entry298comment</comments>
      <pubDate>Sat, 29 Oct 2022 16:56:58 +0900</pubDate>
    </item>
    <item>
      <title>DTO는 대체 어디서 변환하는 것이 좋을까?</title>
      <link>https://sedangdang.tistory.com/296</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. DTO를 왜 사용하는지?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;DTO(Data Transfer Object)&lt;/b&gt;&lt;/span&gt;는 말 그대로 데이터를 전송하는 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벨덩에서는 &lt;b&gt;&quot;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;They &lt;/span&gt;are flat data structures that contain no business logic.&quot;&lt;/b&gt; 이라고 정의해뒀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;요청 시 DTO를 사용하는 이유&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. @RequestParam으로 데이터를 일일히 받을 필요 없이 객체 하나로 한꺼번에 받을 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;Bean Validation,&lt;/b&gt;&lt;/span&gt; Contoller에서 검증 기능을 분리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 엔티티 내부를 캡슐화 할 수 있다 (뜬금없는 곳에서 엔티티의 값이 변경되지 않도록 한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;응답 시 DTO를 사용하는 이유&amp;gt;&lt;/b&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. 순환참조를 예방할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 엔티티 내부를 캡슐화 할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 참고 (&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-08-31-dto-vs-entity/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-08-31-dto-vs-entity/&lt;/a&gt;)&lt;/p&gt;
&lt;pre id=&quot;code_1666770513883&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ToString
@Getter @Setter
public class BoardCreateDto {
    @Size(max = 50)
    @NotBlank
    private String title;

    @Size(max = 15)
    @NotBlank
    private String editor;

    @NotBlank
    private String content;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1666761826438&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
@RequestMapping(&quot;/api/boards&quot;)
@RestController
public class BoardApiController {

    private final BoardService boardService;

    @PostMapping
    public ResponseEntity&amp;lt;?&amp;gt; create(@Valid BoardCreateDto dto) {
        Long boardId = boardService.create(dto);
        return ResponseEntity.ok(boardId);
    }
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View &amp;lt;-&amp;gt; Controller 에서는 DTO를 왜 사용해야 하는지는 아주 잘 이해가 된다.&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;#2. DTO를 대체 어디서 Entity로 변환할 것인가??&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. &lt;span style=&quot;color: #006dd7;&quot;&gt;Controller&lt;/span&gt;에서 DTO를 Entity로 변환하고, Service에는 Entity 객체를 파라미터로 넘기자&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cI7AZ1/btrPGd2G7UP/Iahj1DJk0VQ2QjidntFph1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cI7AZ1/btrPGd2G7UP/Iahj1DJk0VQ2QjidntFph1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cI7AZ1/btrPGd2G7UP/Iahj1DJk0VQ2QjidntFph1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcI7AZ1%2FbtrPGd2G7UP%2FIahj1DJk0VQ2QjidntFph1%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;1217&quot; height=&quot;258&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;장점&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서비스 계층 입장에서는 특정 DTO에 얽매이지 않고, 엔티티에만 의존하기 때문에&amp;nbsp;&lt;b&gt;유연하고, 재사용성이 높아진다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;단점/의문점&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;b&gt;하나의 DTO 만으로 완전한(값이 다 들어있는) 엔티티를 만들지 못하는 경우가 대부분이다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 예를들어 게시글 등록을 위한 DTO의 경우, 게시글 ID값이 아직 부여되지 않은 상태..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 여러 DTO를 모아 하나의 엔티티를 만들어야 하는 경우도 빈번하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; -&amp;gt; &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;여러 DTO를 모아 복잡한 엔티티를 만드는 것 자체가 비즈니스 로직&lt;/b&gt;&lt;/span&gt;일 확률이 매우 높다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 파라미터로 받은 Entity는 &lt;b&gt;영속된 상태가 아니라는 것&lt;/b&gt;도&amp;nbsp;조심해야 한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;*&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.inflearn.com/questions/53023&quot;&gt;https://www.inflearn.com/questions/53023&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Controller에서 받은 DTO를 Service에 그대로 넘기고, &lt;span style=&quot;color: #006dd7;&quot;&gt;Service&lt;/span&gt;에서 Entity로 변환하자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책이나 강의를 보면서 만드는 프로젝트는 대부분 이 방법을 사용했었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1213&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w6aMv/btrPFciUPoj/9w2psGfIcsvI13Rb7OP2y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w6aMv/btrPFciUPoj/9w2psGfIcsvI13Rb7OP2y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w6aMv/btrPFciUPoj/9w2psGfIcsvI13Rb7OP2y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw6aMv%2FbtrPFciUPoj%2F9w2psGfIcsvI13Rb7OP2y0%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;1213&quot; height=&quot;246&quot; data-origin-width=&quot;1213&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;장점&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 컨트롤러는 엔티티를 알지 못하고&lt;b&gt; 오직 DTO에 대해서만 알고있도록 &lt;/b&gt;만들 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;단점&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 컨트롤러와 서비스간 결합도가 높아지는 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;*&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://stackoverflow.com/questions/16866102/using-dto-to-transfer-data-between-service-layer-and-ui-layer/16872129#16872129&quot;&gt;https://stackoverflow.com/questions/16866102/using-dto-to-transfer-data-between-service-layer-and-ui-layer/16872129#16872129&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 서비스 DTO와 Mapper를 별도로 만들자!&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 요청/응답 시 서비스 계층의 메서드가 사용하는 서비스 DTO를 별도로 만들고, DTO와 서비스 DTO를 매핑하는 Mapper를 끼워넣는 방법이 있다고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1327&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kDASc/btrPGOItTjc/TFm7suowqtKUf25yKOtIak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kDASc/btrPGOItTjc/TFm7suowqtKUf25yKOtIak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kDASc/btrPGOItTjc/TFm7suowqtKUf25yKOtIak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkDASc%2FbtrPGOItTjc%2FTFm7suowqtKUf25yKOtIak%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;1327&quot; height=&quot;249&quot; data-origin-width=&quot;1327&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mapper를 사용하면 얻을 수 있는 장점으로는 RequestDTO가 변경되더라도 ServiceRequest 객체는 변하지 않기 때문에 Mapper만 수정해주면 서비스 계층에는 아무런 영향을 미치지 않는다는 것이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 매퍼가 중간다리 역할을 해주면서 &lt;b&gt;컨트롤러와 서비스 계층이 완전히 분리되는 효과&lt;/b&gt;를 얻을 수 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* &lt;a href=&quot;https://techblog.woowahan.com/2711/&quot;&gt;https://techblog.woowahan.com/2711/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번이 이상적으로 보면 가장 바람직한 구조로 보이나,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View -&amp;gt; Controller 에서 RequestDto 하나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller -&amp;gt; Service 에서 ServiceRequestDto 하나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service -&amp;gt; Controller 에서 ServiceResponseDto 하나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller -&amp;gt; View 에서 ResponseDto 하나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;하나의 요청에 4개의 Dto&lt;/b&gt;&lt;/i&gt;를 사용해야 한다는 것일까..?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음...... 장단점이 있는 것으로 보인다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;이 글을 쓰면서 개인적으로 든 생각은 2번 방법, &lt;b&gt;서비스 계층에서&lt;/b&gt; DTO를 그대로 파라미터로 받아 내부에서 엔티티로 변환하는 방법을 기본으로 사용하다가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 프로젝트 규모가 커지고, 컨트롤러와 서비스 계층의 분리/모듈화가 필요하다고 느낄 때 Mapper와 서비스 DTO를 만들어 3번으로 넘어가는 것이 좋지 않을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 프로젝트 규모에 따라 유동적으로 선택하는 것이 이번 포스팅의 결론이 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진리의 케바케..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://kafcamus.tistory.com/12&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kafcamus.tistory.com/12&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(+)&amp;nbsp; &lt;a href=&quot;https://www.baeldung.com/java-dto-pattern&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.baeldung.com/java-dto-pattern &lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1666762869730&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;The DTO Pattern (Data Transfer Object) | Baeldung&quot; data-og-description=&quot;Explore the definition of the DTO Pattern and its reason for existing, and how to implement it.&quot; data-og-host=&quot;www.baeldung.com&quot; data-og-source-url=&quot;https://www.baeldung.com/java-dto-pattern&quot; data-og-url=&quot;https://www.baeldung.com/java-dto-pattern&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wCGCr/hyQlzRHNn8/acGkTdvZh9WK3HXZHkTypk/img.jpg?width=952&amp;amp;height=498&amp;amp;face=0_0_952_498,https://scrap.kakaocdn.net/dn/dRrKoB/hyQlBBZRG9/TN81HfQ4E6ifi8jq51yK60/img.jpg?width=952&amp;amp;height=498&amp;amp;face=0_0_952_498&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/java-dto-pattern&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.baeldung.com/java-dto-pattern&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wCGCr/hyQlzRHNn8/acGkTdvZh9WK3HXZHkTypk/img.jpg?width=952&amp;amp;height=498&amp;amp;face=0_0_952_498,https://scrap.kakaocdn.net/dn/dRrKoB/hyQlBBZRG9/TN81HfQ4E6ifi8jq51yK60/img.jpg?width=952&amp;amp;height=498&amp;amp;face=0_0_952_498');&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;The DTO Pattern (Data Transfer Object) | Baeldung&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Explore the definition of the DTO Pattern and its reason for existing, and how to implement it.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.baeldung.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;아주 좋아하는 벨덩에서 역시나 DTO에 대한 내용이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#6.Common mistakes &lt;/b&gt;에 관한 내용을 대충 정리해봤다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 매 상황마다 DTO를 각각 새로 만드는 것은 피하는 것이 좋다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존의 DTO를 재사용하는 것과 새로 만드는 것의 trade-off를 고려해 봐야함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 너무 큰 하나의 DTO로 퉁치는 것은 대부분의 변수가 자주 사용되지 않는 상황이 생기기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. &lt;span style=&quot;color: #ef5369;&quot;&gt;DTO 내부에 비즈니스 로직을 넣지 마라!&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;3. &quot;&lt;b&gt;LocalDTOs&lt;/b&gt;&quot; 라고 하는데, 여러개의 entity 정보를 하나의 DTO에 전부 때려박지 마라는 이야기인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이는 DTO 매핑에 대한 유지보수 비용이 발생한다&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래밍에 정답이 없다는 것이 참 매력적이긴 하지만 그만큼 힘들기도 한 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 사람을 어렵게 만드는 것은 정답은 없지만 오답은 있다는 것이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자라는 직업이 최선은 못하더라도 최악은 피하기 위해서 계속 공부를 계속 할 수 밖에 없는 것 같다.. ㅜ&lt;/p&gt;</description>
      <category>웹/Spring</category>
      <author>세댕댕이</author>
      <guid isPermaLink="true">https://sedangdang.tistory.com/296</guid>
      <comments>https://sedangdang.tistory.com/296#entry296comment</comments>
      <pubDate>Thu, 27 Oct 2022 02:03:31 +0900</pubDate>
    </item>
  </channel>
</rss>