<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Humbly</title>
    <link>https://sowhat4.tistory.com/</link>
    <description>Python Back-end 개발자가 Java Back-end 개발자가 되기까지</description>
    <language>ko</language>
    <pubDate>Fri, 19 Jun 2026 01:25:04 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>sowhat92</managingEditor>
    <image>
      <title>Humbly</title>
      <url>https://tistory1.daumcdn.net/tistory/1951483/attach/f1124ebec6894603b995f1e3cd412187</url>
      <link>https://sowhat4.tistory.com</link>
    </image>
    <item>
      <title>Inner Class에 대한 유효성 검사(javax.validation)</title>
      <link>https://sowhat4.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;API를 작성하다 보면 입력 데이터에 대한 유효성 검사가 필요한 상황이 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 코드가 있다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SnfOm/btrGhyoOFhc/y6cV9nhewyCVNeYkBzMEE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SnfOm/btrGhyoOFhc/y6cV9nhewyCVNeYkBzMEE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SnfOm/btrGhyoOFhc/y6cV9nhewyCVNeYkBzMEE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSnfOm%2FbtrGhyoOFhc%2Fy6cV9nhewyCVNeYkBzMEE1%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;1594&quot; height=&quot;710&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 데이터(CreatePersonRequest)를 검증한 후 요건에 맞는 처리 후 응답하는 간단한 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k4E2R/btrGiuGddHn/F9bvsKkf9b0iH9lz6wUoE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k4E2R/btrGiuGddHn/F9bvsKkf9b0iH9lz6wUoE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k4E2R/btrGiuGddHn/F9bvsKkf9b0iH9lz6wUoE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk4E2R%2FbtrGiuGddHn%2FF9bvsKkf9b0iH9lz6wUoE1%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;454&quot; height=&quot;321&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름(name)과 나이(age)를 요청 값으로 받고 이름은 올바른 문자열인지, 나이는 0보다 큰 값인지 판단하는 검증을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1656727964302&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;name&quot; : &quot;Steve&quot;,
    &quot;age&quot; : 15
}
// 201 Success Return&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;pre id=&quot;code_1656728027701&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;name&quot; : &quot;&quot;,
    &quot;age&quot; : 15
}
// 올바른 이름을 입력해주세요.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 유효하지 않은 요청 값을 전달하면 유효성 검사에 실패하여 에러 메시지가 반환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지는 큰 문제없이 사용할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 요청 데이터 클래스 내부에 또 다른 클래스(Inner Class)가 존재하고, 그 클래스 필드에도 유효성 검사가 필요하다면 어떨까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;1438&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ky7eE/btrGgspnKVW/OLFEAcm9ZciM7YLTLezXnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ky7eE/btrGgspnKVW/OLFEAcm9ZciM7YLTLezXnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ky7eE/btrGgspnKVW/OLFEAcm9ZciM7YLTLezXnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fky7eE%2FbtrGgspnKVW%2FOLFEAcm9ZciM7YLTLezXnK%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;1438&quot; height=&quot;810&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;810&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대충 봤을 때 잘 돌아갈 것 같은 코드처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 한번 요청을 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1656728493365&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;name&quot; : &quot;Steve&quot;,
    &quot;age&quot; : 15,
    &quot;job&quot; : {
        &quot;name&quot; : &quot;&quot;,
        &quot;description&quot; : &quot;middle school&quot;
    }
}
// Internal Server Error&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;왜냐하면 유효성 검사에 따른 Exception Handler를 별도로 추가해두었기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;error log를 봐보면 다음과 같은 내용이 출력되어 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1656728611266&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;org.springframework.beans.NotReadablePropertyException: Invalid property 'job' of bean class [com.example.demo_databinder.controller.CreatePersonRequest]: Bean property 'job' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;job 프로퍼티를 읽을 수 없다는 내용으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당황스럽다. Inner Class가 추가되었을 뿐인데 왜 에러가 발생하는 것 일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;has&amp;nbsp;an&amp;nbsp;invalid&amp;nbsp;getter&amp;nbsp;method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;private field라서 접근을 못하는 것인가? 그렇다면 해당 필드의 Getter를 추가하고 다시 호출해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;974&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFky4w/btrGf2dn146/Dkt6hDnoKU1vMXY9t6Oi7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFky4w/btrGf2dn146/Dkt6hDnoKU1vMXY9t6Oi7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFky4w/btrGf2dn146/Dkt6hDnoKU1vMXY9t6Oi7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFky4w%2FbtrGf2dn146%2FDkt6hDnoKU1vMXY9t6Oi7k%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;455&quot; height=&quot;507&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;974&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1656728982878&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;name&quot; : &quot;Steve&quot;,
    &quot;age&quot; : 15,
    &quot;job&quot; : {
        &quot;name&quot; : &quot;&quot;,
        &quot;description&quot; : &quot;middle school&quot;
    }
}
// Internal Server Error&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;pre id=&quot;code_1656729004779&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Invalid property 'job.name' of bean class [com.example.demo_databinder.controller.CreatePersonRequest]: Bean property 'job.name' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Job 내부의 name 프로퍼티에 대한 내용이 출력되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CreatePersonRequest.Job에 대한 접근을 위해 job 프로퍼티에 대한 Getter만 추가해주면 될 것이라 생각했지만 Inner Class의 경우 검증이 필요한 내부 필드에도 Getter 추가가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 name 필드에 Getter를 추가 후 다시 테스트해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;744&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NIzYE/btrGgJj5GE2/DXZMIT3UqWdBuPg32Rjgq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NIzYE/btrGgJj5GE2/DXZMIT3UqWdBuPg32Rjgq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NIzYE/btrGgJj5GE2/DXZMIT3UqWdBuPg32Rjgq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNIzYE%2FbtrGgJj5GE2%2FDXZMIT3UqWdBuPg32Rjgq0%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;422&quot; height=&quot;399&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;744&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1656729160809&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;name&quot; : &quot;Steve&quot;,
    &quot;age&quot; : 15,
    &quot;job&quot; : {
        &quot;name&quot; : &quot;&quot;,
        &quot;description&quot; : &quot;middle school&quot;
    }
}
// 올바른 직업명을 입력해주세요.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 의도한대로 유효성 검사에 따른 에러 메시지가 반환되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면 Inner Class 유효성 검증을 진행하려면 상위 클래스와 해당 클래스(Inner Class)에서 검증을 진행할 필드의 Getter가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 만약 요청 객체의 모든 필드를 public으로 지정하여 외부에서 자유롭게 접근할 수 있도록 한다면 어떨까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yicFp/btrGgJ5wI17/wv1vmUUr30lmeaK3kt2eV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yicFp/btrGgJ5wI17/wv1vmUUr30lmeaK3kt2eV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yicFp/btrGgJ5wI17/wv1vmUUr30lmeaK3kt2eV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyicFp%2FbtrGgJ5wI17%2Fwv1vmUUr30lmeaK3kt2eV0%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;620&quot; height=&quot;374&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;810&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 데이터 전달 목적(로직을 포함하지 않은)으로만 사용한다면 위와 같이 작성하여 사용하는 것도 크게 이상하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 모든 필드에 대한 접근 지정자를 public으로 허용해두었으니 외부에서 자유롭게 접근할 수 있을 것이고 그럼 에러가 발생하지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1656729541957&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;name&quot; : &quot;Steve&quot;,
    &quot;age&quot; : 15,
    &quot;job&quot; : {
        &quot;name&quot; : &quot;&quot;,
        &quot;description&quot; : &quot;middle school&quot;
    }
}
// Internal Server Error&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1656729555051&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;org.springframework.beans.NotReadablePropertyException: Invalid property 'job' of bean class [com.example.demo_databinder.controller.CreatePersonRequest]: Bean property 'job' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안타깝게도 동일한 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 그럴까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이쯤에서 드는 생각은 유효성 검사를 진행할 때 검사가 필요한 대상이 필드라면 직접 접근하여 값을 얻어오는 것이 아니라 Getter를 통해 값을 얻어온다고 추측할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 반대로 필드에 직접 접근하도록 할 수 없을까 찾아보다가 아래의 글을 발견하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/39164077/java-spring-rest-validation-configure-property-access&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/39164077/java-spring-rest-validation-configure-property-access&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1656729798781&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Java Spring Rest validation configure property access&quot; data-og-description=&quot;I have a Spring-JSON/RestAPI that use annotation driven input validation. @Valid I get the following error when I try to validate an object inside another object. java.lang.IllegalStateException:...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/39164077/java-spring-rest-validation-configure-property-access&quot; data-og-url=&quot;https://stackoverflow.com/questions/39164077/java-spring-rest-validation-configure-property-access&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bODwqH/hyOYiP12NT/MXyOWUfD41Ooo3x7Fnaik0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/39164077/java-spring-rest-validation-configure-property-access&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/39164077/java-spring-rest-validation-configure-property-access&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bODwqH/hyOYiP12NT/MXyOWUfD41Ooo3x7Fnaik0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Java Spring Rest validation configure property access&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I have a Spring-JSON/RestAPI that use annotation driven input validation. @Valid I get the following error when I try to validate an object inside another object. java.lang.IllegalStateException:...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1656729821487&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @InitBinder
    private void activateDirectFieldAccess(DataBinder dataBinder) {
        dataBinder.initDirectFieldAccess();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 코드를 ControllerAdvice에 추가하여 설정한다면 DataBinder가 필드에 직접 접근하여 사용한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 검사를 위한 목적으로만 추가된 불필요한 public getter를 제거하고 저 설정을 켜 두는 게 맞을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1568091/why-use-getters-and-setters-accessors&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/1568091/why-use-getters-and-setters-accessors&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1656729932613&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;Why use getters and setters/accessors?&quot; data-og-description=&quot;What's the advantage of using getters and setters - that only get and set - instead of simply using public fields for those variables? If getters and setters are ever doing more than just the simp...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/1568091/why-use-getters-and-setters-accessors&quot; data-og-url=&quot;https://stackoverflow.com/questions/1568091/why-use-getters-and-setters-accessors&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xiiEC/hyOYllHbm8/kZOfGjcU7NJBaB0W26KWkk/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1568091/why-use-getters-and-setters-accessors&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/1568091/why-use-getters-and-setters-accessors&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xiiEC/hyOYllHbm8/kZOfGjcU7NJBaB0W26KWkk/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&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;Why use getters and setters/accessors?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;What's the advantage of using getters and setters - that only get and set - instead of simply using public fields for those variables? If getters and setters are ever doing more than just the simp...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 글은 필드에 직접 접근하지 않고 getter/setter를 사용하였을 때 장점을 이야기하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향적인 관점에서 필드를 숨기는 행위(private)는 여러모로 의미가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 유효성 검증만을 위한 public getter이지만 필드에 직접 접근하는 것을 허용함으로써 여러 설계적 단점이 발생하는 것보다 낫다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 필드에 있는 유효성 검사 어노테이션을 메서드로 옮겨 public getter method의 역할을 분명히 하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;980&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PhRt0/btrGhWC4awE/q0mD3RlW1KRN3TkmiWKKXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PhRt0/btrGhWC4awE/q0mD3RlW1KRN3TkmiWKKXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PhRt0/btrGhWC4awE/q0mD3RlW1KRN3TkmiWKKXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPhRt0%2FbtrGhWC4awE%2Fq0mD3RlW1KRN3TkmiWKKXK%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;1512&quot; height=&quot;980&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;980&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>자바 개발자되기</category>
      <category>@valid</category>
      <category>java</category>
      <category>javax.validation.constraints</category>
      <category>NotReadablePropertyException</category>
      <category>spring 유효성검사</category>
      <category>valid</category>
      <author>sowhat92</author>
      <guid isPermaLink="true">https://sowhat4.tistory.com/83</guid>
      <comments>https://sowhat4.tistory.com/83#entry83comment</comments>
      <pubDate>Sat, 2 Jul 2022 11:53:34 +0900</pubDate>
    </item>
    <item>
      <title>Java NIO - SocketChannel non-blocking mode에서 Read Timeout 설정</title>
      <link>https://sowhat4.tistory.com/81</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;SocketChannel Default &lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;nonBlocking&lt;/span&gt; is false&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;SocketChannel을 사용할 때 기본적으로 &lt;b&gt;Blocking Mode&lt;/b&gt;로 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;970&quot; width=&quot;526&quot; height=&quot;393&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TwQ5J/btrjBHIuLRJ/fHFgAakivbLlc6GcuGfzak/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TwQ5J/btrjBHIuLRJ/fHFgAakivbLlc6GcuGfzak/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TwQ5J/btrjBHIuLRJ/fHFgAakivbLlc6GcuGfzak/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTwQ5J%2FbtrjBHIuLRJ%2FfHFgAakivbLlc6GcuGfzak%2Fimg.jpg&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;970&quot; width=&quot;526&quot; height=&quot;393&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 다음과 같이 사용하는 경우 버퍼에 있는 데이터를 읽기 위한 작업 수행 시 &lt;b&gt;Thread Block&lt;/b&gt; 발생&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;272&quot; width=&quot;527&quot; height=&quot;122&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kKWsC/btrjCRD79NO/5l7MvRaddKvwJ4f0UQitQK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kKWsC/btrjCRD79NO/5l7MvRaddKvwJ4f0UQitQK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kKWsC/btrjCRD79NO/5l7MvRaddKvwJ4f0UQitQK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkKWsC%2FbtrjCRD79NO%2F5l7MvRaddKvwJ4f0UQitQK%2Fimg.jpg&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;272&quot; width=&quot;527&quot; height=&quot;122&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 &lt;b&gt;Read Timeout&lt;/b&gt;(soTimeout)을 설정해도 먹히지 않음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;180&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I2AqO/btrjD3xkcW4/BJPA14cKGM3xd4VGWNJkZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I2AqO/btrjD3xkcW4/BJPA14cKGM3xd4VGWNJkZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I2AqO/btrjD3xkcW4/BJPA14cKGM3xd4VGWNJkZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI2AqO%2FbtrjD3xkcW4%2FBJPA14cKGM3xd4VGWNJkZ1%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;180&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Javadoc을 보면 read() 메서드에 대한 timeout은 InputStream을 사용할 때 적용 가능하다고 되어 있음&lt;/p&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;br /&gt;즉, SocketChannel의 구현체인 &lt;span style=&quot;color: #000000;&quot;&gt;SocketChannelImpl의 read() 메서드를 직접 사용하는 경우 InputStream을 사용하지 않고 &lt;b&gt;IOUtil.read()&lt;/b&gt;를 호출&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;406&quot; width=&quot;526&quot; height=&quot;215&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crf2sD/btrjzmLqls3/P92lMIj23UioWjnDNhcxuK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crf2sD/btrjzmLqls3/P92lMIj23UioWjnDNhcxuK/img.jpg&quot; data-alt=&quot;SocketChannelImpl.read(ByteBuffer buf) 메서드 내부&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crf2sD/btrjzmLqls3/P92lMIj23UioWjnDNhcxuK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcrf2sD%2FbtrjzmLqls3%2FP92lMIj23UioWjnDNhcxuK%2Fimg.jpg&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;406&quot; width=&quot;526&quot; height=&quot;215&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SocketChannelImpl.read(ByteBuffer buf) 메서드 내부&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;InputStream을 사용하여 ReadTimeout 설정&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;310&quot; width=&quot;530&quot; height=&quot;140&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7K2Vz/btrjEU1ddZK/EmkXI7BfU4JB8Rs3W9GkP1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7K2Vz/btrjEU1ddZK/EmkXI7BfU4JB8Rs3W9GkP1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7K2Vz/btrjEU1ddZK/EmkXI7BfU4JB8Rs3W9GkP1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7K2Vz%2FbtrjEU1ddZK%2FEmkXI7BfU4JB8Rs3W9GkP1%2Fimg.jpg&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;310&quot; width=&quot;530&quot; height=&quot;140&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getInputStream()을 통해 실제 &lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ChannelInputStream&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이후 &lt;b&gt;SocketInputStream.read(ByteBuffer bb)&lt;/b&gt;가 호출됨&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;1772&quot; width=&quot;425&quot; height=&quot;744&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kRnAZ/btrjCSpxK3P/3azZ9bNWsPDmzHwis9VBVK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kRnAZ/btrjCSpxK3P/3azZ9bNWsPDmzHwis9VBVK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kRnAZ/btrjCSpxK3P/3azZ9bNWsPDmzHwis9VBVK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkRnAZ%2FbtrjCSpxK3P%2F3azZ9bNWsPDmzHwis9VBVK%2Fimg.jpg&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;1772&quot; width=&quot;425&quot; height=&quot;744&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드에서의 특징은&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;소켓 채널의 설정을 &lt;b&gt;Non-Blocking&lt;/b&gt;으로 변경&lt;br /&gt;
&lt;pre id=&quot;code_1635812665724&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sc.configureBlocking(false);​&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;최초 읽기 시도 시 반환된 데이터가 없으면 이후 timeout 설정만큼 소켓을 &lt;b&gt;poll 하여&lt;/b&gt; 변화를 감지&lt;br /&gt;
&lt;pre id=&quot;code_1635813162613&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int result = sc.poll(Net.POLLIN, to);​&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;만약 0보다 작은 값이 반환되었다면 Running Time을 확인하여 타임아웃이라 판단되면 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;SocketTimeoutException Throw&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;pre id=&quot;code_1635813183627&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;to -= System.currentTimeMillis() - st;
if (to &amp;lt;= 0)
	throw new SocketTimeoutException();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;메서드를 Return 하기 전 설정을 원복 하여 Blocking Mode로 바꿈&lt;br /&gt;
&lt;pre id=&quot;code_1635813277245&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;finally {
	try {
		sc.configureBlocking(true);
	} catch (ClosedChannelException e) { }
}​&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&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;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결론&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SocketChannel Blocking Mode에서는 기본적으로 Read Timeout 기능을 제공하지 않음&lt;/li&gt;
&lt;li&gt;InputStream을 사용하는 형태로 Blocking Mode에서도 Read Timeout 기능을 사용할 수 있지만 실제 내부 로직에서는 Non-Blokcing Mode로 임시 변경하여 사용하는 트릭과 같음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/nio/channels/SocketChannel.html#read-java.nio.ByteBuffer-&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.oracle.com/javase/8/docs/api/java/nio/channels/SocketChannel.html#read-java.nio.ByteBuffer-&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635813423567&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;SocketChannel (Java Platform SE 8 )&quot; data-og-description=&quot;Reads a sequence of bytes from this channel into the given buffer. An attempt is made to read up to r bytes from the channel, where r is the number of bytes remaining in the buffer, that is, dst.remaining(), at the moment this method is invoked. Suppose th&quot; data-og-host=&quot;docs.oracle.com&quot; data-og-source-url=&quot;https://docs.oracle.com/javase/8/docs/api/java/nio/channels/SocketChannel.html#read-java.nio.ByteBuffer-&quot; data-og-url=&quot;https://docs.oracle.com/javase/8/docs/api/java/nio/channels/SocketChannel.html#read-java.nio.ByteBuffer-&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/nio/channels/SocketChannel.html#read-java.nio.ByteBuffer-&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.oracle.com/javase/8/docs/api/java/nio/channels/SocketChannel.html#read-java.nio.ByteBuffer-&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;SocketChannel (Java Platform SE 8 )&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Reads a sequence of bytes from this channel into the given buffer. An attempt is made to read up to r bytes from the channel, where r is the number of bytes remaining in the buffer, that is, dst.remaining(), at the moment this method is invoked. Suppose th&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.oracle.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://bugs.java.com/bugdatabase/view_bug.do?bug_id=4614802&quot;&gt;https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4614802&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1635813457417&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;Bug ID: JDK-4614802 setSoTimeout does not work with nio SocketChannel&quot; data-og-description=&quot;&quot; data-og-host=&quot;bugs.java.com&quot; data-og-source-url=&quot;https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4614802&quot; data-og-url=&quot;https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4614802&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4614802&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4614802&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;Bug ID: JDK-4614802 setSoTimeout does not work with nio SocketChannel&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;bugs.java.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://technfun.wordpress.com/2009/01/29/networking-in-java-non-blocking-nio-blocking-nio-and-io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://technfun.wordpress.com/2009/01/29/networking-in-java-non-blocking-nio-blocking-nio-and-io/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635813474198&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;Networking in Java: non-blocking NIO, blocking NIO and IO&quot; data-og-description=&quot;Standard run-time library in Java provides two interfaces for networking. One, which exists in Java since the beginning, is called &amp;ldquo;basic IO&amp;rdquo;, because it is based on generic framework o&amp;hellip;&quot; data-og-host=&quot;technfun.wordpress.com&quot; data-og-source-url=&quot;https://technfun.wordpress.com/2009/01/29/networking-in-java-non-blocking-nio-blocking-nio-and-io/&quot; data-og-url=&quot;https://technfun.wordpress.com/2009/01/29/networking-in-java-non-blocking-nio-blocking-nio-and-io/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/g8GNm/hyMa1STDE5/JKNvURKlfpCT5M9ACWvPRK/img.jpg?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200&quot;&gt;&lt;a href=&quot;https://technfun.wordpress.com/2009/01/29/networking-in-java-non-blocking-nio-blocking-nio-and-io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://technfun.wordpress.com/2009/01/29/networking-in-java-non-blocking-nio-blocking-nio-and-io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/g8GNm/hyMa1STDE5/JKNvURKlfpCT5M9ACWvPRK/img.jpg?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200');&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;Networking in Java: non-blocking NIO, blocking NIO and IO&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Standard run-time library in Java provides two interfaces for networking. One, which exists in Java since the beginning, is called &amp;ldquo;basic IO&amp;rdquo;, because it is based on generic framework o&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;technfun.wordpress.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>자바 개발자되기</category>
      <category>Block</category>
      <category>java</category>
      <category>Read Timeout</category>
      <category>socketchannel</category>
      <author>sowhat92</author>
      <guid isPermaLink="true">https://sowhat4.tistory.com/81</guid>
      <comments>https://sowhat4.tistory.com/81#entry81comment</comments>
      <pubDate>Tue, 2 Nov 2021 09:39:13 +0900</pubDate>
    </item>
    <item>
      <title>Spring JPA findById() 사용 시 주의점</title>
      <link>https://sowhat4.tistory.com/80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring JPA에서 Entity 조회 시 &lt;span style=&quot;color: #39c8b0;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;@Id&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;어노테이션이 적용되어 있는 필드 메서드 인자 값으로 하여&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #666666;&quot;&gt;Optional&amp;lt;T&amp;gt; findById(ID id)&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;메서드 호출 가능하다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #39c8b0;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JpaRepository를 상속받은 경우 SimpleJpaRepository가 구현체로 주입되며 조회 시 아래와 같은 처리가 진행된다.&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;b&gt;&lt;span style=&quot;color: #39c8b0;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 만약 1차 캐시에 조회하고자 하는 Managed Entity가 존재한다면 이를 반환&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2074&quot; data-origin-height=&quot;448&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVbyR0/btraCkCa0KG/TuP9okphIVezuc9P140Lv0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVbyR0/btraCkCa0KG/TuP9okphIVezuc9P140Lv0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVbyR0/btraCkCa0KG/TuP9okphIVezuc9P140Lv0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVbyR0%2FbtraCkCa0KG%2FTuP9okphIVezuc9P140Lv0%2Fimg.jpg&quot; data-origin-width=&quot;2074&quot; data-origin-height=&quot;448&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #39c8b0;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 만약 1차 캐시에 없다면 DB 조회 후 반환&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;394&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGPTus/btraJoJ58oj/bZ2E91lTlzBy9iaUCpmZ5k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGPTus/btraJoJ58oj/bZ2E91lTlzBy9iaUCpmZ5k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGPTus/btraJoJ58oj/bZ2E91lTlzBy9iaUCpmZ5k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGPTus%2FbtraJoJ58oj%2FbZ2E91lTlzBy9iaUCpmZ5k%2Fimg.jpg&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;394&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DB에서 조회하기 전 트랜잭션을 생성하게 되는데 이때 트랜잭션 속성의 경우 1) method @Transactional 어노테이션이 있는지 확인하고, 만약 없다면 2) class에 &lt;b&gt;@Transactional 어노테이션 존재 확인&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;482&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuuDvP/btraHZDYY1o/YoEEJoUQTYlqnU8hAZ7JN1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuuDvP/btraHZDYY1o/YoEEJoUQTYlqnU8hAZ7JN1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuuDvP/btraHZDYY1o/YoEEJoUQTYlqnU8hAZ7JN1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuuDvP%2FbtraHZDYY1o%2FYoEEJoUQTYlqnU8hAZ7JN1%2Fimg.jpg&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;482&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;findById() 메서드는 SimpleJpaRepository 클래스의 구현체 메서드로 호출되고, 해당 클래스에는 &lt;span style=&quot;color: #000000;&quot;&gt;@Transactional(readOnly = true) 가 적용되어 있음&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;172&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5opWA/btraImzpQjW/vwQTGkGTgfK40NcrVyf4bk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5opWA/btraImzpQjW/vwQTGkGTgfK40NcrVyf4bk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5opWA/btraImzpQjW/vwQTGkGTgfK40NcrVyf4bk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5opWA%2FbtraImzpQjW%2FvwQTGkGTgfK40NcrVyf4bk%2Fimg.jpg&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;172&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;findById()를 호출 한 상위 메서드에서 별도의 트랜잭션이 생성되어 존재하지 않는다면 SImpleJpaRepository에서 생성된 트랜잭션을 물고 들어가기에 findById()는 readOnly 속성이 적용된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RW/RO를 별개의 물리적인 Datasource를 사용하고 있다면 readOnly 속성이 적용된 트랜잭션의 경우 RO에서 데이터를 조회하기 때문에 Replica Lag(복제 지연)이 발생할 수 있다.&lt;/p&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;1. RW에 레코드 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 복제 지연 기간 내 RO에서 1번에서 생성된 레코드 조회(findById) &amp;rarr; NOT_FOUND&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt; findById()는 호출자 상위에서 별도의 트랜잭션이 존재하지 않는다면 readOnly 트랜잭션을 생성하여 조회&lt;/li&gt;
&lt;li&gt;RW/RO를 분리하여 사용하고 있다면 복제 지연으로 인하여 RW에서 생성 한 레코드를 findById()로 조회하였을 때 NOT_FOUND를 응답받을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 방안&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;findById() 호출자에서 트랜잭션(readOnly = false)을 생성하여 RW에서 조회&lt;/li&gt;
&lt;li&gt;Repository에 Custom Method를 정의하여 사용(RW로 접근)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자바 개발자되기</category>
      <category>JPA</category>
      <category>readonly</category>
      <category>simplejparepository</category>
      <category>Spring</category>
      <author>sowhat92</author>
      <guid isPermaLink="true">https://sowhat4.tistory.com/80</guid>
      <comments>https://sowhat4.tistory.com/80#entry80comment</comments>
      <pubDate>Wed, 28 Jul 2021 21:08:55 +0900</pubDate>
    </item>
    <item>
      <title>@MockBean Spring Context Recreated</title>
      <link>https://sowhat4.tistory.com/79</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 현상&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-stringify-type=&quot;unordered-list&quot; data-indent=&quot;0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-stringify-indent=&quot;0&quot;&gt;@SpringBootTest로 통합 테스트를 관리하는 경우 테스트 시작 시 SQL Script를 통해 H2 in-memory-database에 초기화를 진행할 때 스크립트 중복 실행이 발생하여 DB Exception 발생&lt;/li&gt;
&lt;li data-stringify-indent=&quot;0&quot;&gt;기본적으로 Spring boot JDBC Initializer는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/docs/2.0.0.M1/reference/html/howto-database-initialization.html&quot; data-stringify-link=&quot;https://docs.spring.io/spring-boot/docs/2.0.0.M1/reference/html/howto-database-initialization.html&quot; data-sk=&quot;tooltip_parent&quot;&gt;fail-fast&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;전략으로 스크립트 실행 중 에러가 발생하면 중단시킴&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 원인&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;통합테스트를 위해 필요한 Configuration을 정의 후 사용, 일부 테스트에서 개별적으로 @MockBean을 사용하여 Spring Context Recreated를 유발&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1621864246251&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Any existing single bean of the same type defined in the context will be replaced by the mock. If no existing bean is defined a new one will be added.&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@MockBean을 사용할 경우 Spring Context에 이미 Bean이 존재하더라도 대체. 각 테스트마다 @MockBean이 사용된다면 개별적인 Spring Context가 생성.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 해결 방안&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-stringify-type=&quot;unordered-list&quot; data-indent=&quot;0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-stringify-indent=&quot;0&quot;&gt;application.properties에 아래의 옵션 추가. 실행 중 스크립트로 인한 에러가 발생해도 중지하지 않음(Fail-Safe)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1621864050868&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.datasource.continue-on-error=true&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@MockBean 어노테이션을 각 테스트 클래스마다 사용하지 말고 필요 한 부분을 추출하여 공통 Configuration으로 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-boot/issues/10015&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;- https://github.com/spring-projects/spring-boot/issues/10015&lt;/a&gt;&lt;/p&gt;</description>
      <category>자바 개발자되기</category>
      <category>@MockBean</category>
      <category>Spring</category>
      <category>springboot</category>
      <category>TDD</category>
      <author>sowhat92</author>
      <guid isPermaLink="true">https://sowhat4.tistory.com/79</guid>
      <comments>https://sowhat4.tistory.com/79#entry79comment</comments>
      <pubDate>Mon, 24 May 2021 22:53:08 +0900</pubDate>
    </item>
    <item>
      <title>구체적인 Exception Catch를 해야하는 이유</title>
      <link>https://sowhat4.tistory.com/78</link>
      <description>&lt;p&gt;이전 회사 A에서는 서비스에서 발생할 수 있는 Custom Exception을 최대한 구체적으로 작성하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이후 회사 B에서는 Exception을 최소한으로 분류하여 사용하고 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;무조건 '어떤 것이 옳다'는 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다만 각각의 선택지마다 사이드 이펙트는 분명히 존재한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Custom Exception을 구체적으로 작성하여 사용하지 않았을 때 발생할 수 있는 문제 중 하나는 &quot;&lt;b&gt;공통&lt;/b&gt;&quot; 처리로 인한 사이드 이펙트이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 코드&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기존&lt;/h4&gt;
&lt;pre id=&quot;code_1619015953523&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class BlackListChecker {
    public boolean isBlackList(final String ip) {
        try {
            IpLogger.log(new Ip(ip));
            // check blacklist
            if(blackList)
            {
            	return true;
            }
        } catch (final Exception e) {
            return false;
        }
        return false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 ip가 블랙리스트인지 체크한다. 만약 블랙리스트라면 true, 아니면 false를 반환한다.&lt;/li&gt;
&lt;li&gt;처리 중 에러가 발생한다면 블랙리스트가 아닌 것으로 취급(false 반환)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;시간이 지나 Exception이 발생하게 된다면 블랙리스트로 취급하도록 기준이 강화되었다고 생각해보자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이후 기준 강화&lt;/h4&gt;
&lt;pre id=&quot;code_1619017666600&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class BlackListChecker {
    public boolean isBlackList(final String ip) {
        try {
            IpLogger.log(new Ip(ip));
            // check blacklist
            if(blackList)
            {
            	return true;
            }
        } catch (final Exception e) {
            // 기준이 강화되어 에러가 발생한다면 안전하게 블랙리스트로 취급
            return true;
        }
        return false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를 들기 위한 극단적인 코드이지만 기준이 강화된 이후 변경된 코드로 인해 블랙리스트가 아닌 ip에 대하여 블랙리스트로 취급되는 문제가 발생하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;열심히 디버깅하여 문제의 원인을 찾아보았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;왜 문제가 발생하였을까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;문제는 아래와 같이 호출 된 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1619017984427&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;blackListChecker.isBlackList(&quot;&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;어떠한 이유가 됬든 IP로 전달되어야 할 인자 값에 blank string이 전달되었다.&lt;/p&gt;
&lt;p&gt;이렇게 전달된 데이터로 인해 Ip 객체 생성 &lt;i&gt;new Ip(ip) &lt;/i&gt;에서 &lt;b&gt;InvalidStringException&lt;/b&gt;이 발생하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 경우 블랙리스트로 판단하는 것이 맞을까?&lt;/p&gt;
&lt;p&gt;물론 요구 사항에 따라 달라지겠지만 극단적으로 이 서비스에서는 ip가 blank string이면 블랙리스트가 아니어야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;블랙리스트인지 확인하는 코드에서 발생하는 Exception catch를 구체적으로 분류하지 않았다.&lt;/li&gt;
&lt;li&gt;그로 인해 예상치 못한 Exception raise(정상 처리로 반환했어야 할)가 발생하였을 때 의도하지 않은 결과가 반환되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 방안&lt;/h3&gt;
&lt;p&gt;이 글은 Exception Catch를 구체적으로 나열하지 않고 공통된 하나의 Exception으로 다뤘을 때의 문제점을 전달하기 위해 작성하였다.&lt;/p&gt;
&lt;p&gt;결론은 Exception Case를 구체적으로 분류하여 각각의 에러 상황에 개발자가 의도한 로직이 처리되도록 하는 것 이다.&lt;/p&gt;
&lt;pre id=&quot;code_1619018543136&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class BlackListChecker {
    public boolean isBlackList(final String ip) {
        try {
            IpLogger.log(new Ip(ip));
            // check blacklist
            if (blackList) {
                return true;
            }
        } catch (final InvalidStringException e) {
            // 인자로 전달 된 ip가 문제가된 경우 블랙리스트가 아님으로 취급
            return false;
        } catch (final Exception e) {
            // 기준이 강화되어 에러가 발생한다면 안전하게 블랙리스트로 취급
            return true;
        }
        return false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 사실 아래와 같은 편법으로 작성할 때가 많기도 하다.&lt;/p&gt;
&lt;pre id=&quot;code_1619018422520&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class BlackListChecker {
    public boolean isBlackList(final String ip) {
        if(isInvalidString(ip)) {
            return false;
        }
        try {
            IpLogger.log(new Ip(ip));
            // check blacklist
            if(blackList)
            {
            	return true;
            }
        } catch (final Exception e) {
            // 기준이 강화되어 에러가 발생한다면 안전하게 블랙리스트로 취급
            return true;
        }
        return false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인자로 전달된 ip(String)가 적절한 문자인지 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자바 개발자되기</category>
      <category>Catch</category>
      <category>Exception</category>
      <category>Exception handle</category>
      <category>java</category>
      <category>에러 처리</category>
      <category>예외 처리</category>
      <author>sowhat92</author>
      <guid isPermaLink="true">https://sowhat4.tistory.com/78</guid>
      <comments>https://sowhat4.tistory.com/78#entry78comment</comments>
      <pubDate>Thu, 22 Apr 2021 00:25:00 +0900</pubDate>
    </item>
    <item>
      <title>Open Session In View와 트랜잭션, 그리고 영속성 컨텍스트</title>
      <link>https://sowhat4.tistory.com/77</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Open Session In View?&lt;/h2&gt;
&lt;p&gt;Open session in view는 view에서 session을 열겠다는 속성이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그럼 언제 view에서 session을 열고자 할까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Controller에서 객체를 응답으로 보내고, view에서 사용자의 입력에 따라 객체의 값을 수정하고자 할 때 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 수정된 객체는 Open Session in view 설정에 따라 영속성 컨텍스트에 반영이 되고, Datasource(DB)에 기록될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Open Session In View = False&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DgtEd/btq0Yg9j9nn/fkPHSzNKLpZ4dPy6KJogZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DgtEd/btq0Yg9j9nn/fkPHSzNKLpZ4dPy6KJogZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DgtEd/btq0Yg9j9nn/fkPHSzNKLpZ4dPy6KJogZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDgtEd%2Fbtq0Yg9j9nn%2FfkPHSzNKLpZ4dPy6KJogZK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이 옵션이 꺼져 있을 때에는 일반적으로 트랜잭션이 종료될 때 영속성 컨텍스트도 같이 비워진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Open Session In View = True&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EbASx/btq0XPYsqRs/cj7CBWVOJQWawqttFRMafk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EbASx/btq0XPYsqRs/cj7CBWVOJQWawqttFRMafk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EbASx/btq0XPYsqRs/cj7CBWVOJQWawqttFRMafk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEbASx%2Fbtq0XPYsqRs%2Fcj7CBWVOJQWawqttFRMafk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;옵션이 켜져 있다면 트랜잭션이 종료되어도 영속성 컨텍스트는 비워지지 않는다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주의 사항&lt;/h2&gt;
&lt;p&gt;Open Session In View 옵션이 켜져 있는 상태에서는 더티 체킹에 주의해야 한다.&lt;/p&gt;
&lt;p&gt;트랜잭션이 종료되어 객체가 비영속 상태가 되었을 것이라 생각하기 쉽기 때문에 이후 객체의 상태가 변경된다면 더티 체킹이 발생하여 의도치 않은 값 변경이 적용될 수 있다.&lt;/p&gt;</description>
      <category>자바 개발자되기</category>
      <category>JPA</category>
      <category>open session in view</category>
      <category>Spring</category>
      <category>transaction</category>
      <category>영속성 컨텍스트</category>
      <author>sowhat92</author>
      <guid isPermaLink="true">https://sowhat4.tistory.com/77</guid>
      <comments>https://sowhat4.tistory.com/77#entry77comment</comments>
      <pubDate>Wed, 24 Mar 2021 22:12:02 +0900</pubDate>
    </item>
    <item>
      <title>Java Ratelimiter - 초 당 처리량 조절하기</title>
      <link>https://sowhat4.tistory.com/76</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Why?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 담당하고 있는 서비스에서 API, DB 호출에 대한 초 당 처리량 조절 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ratelimiter&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://guava.dev/releases/19.0/api/docs/index.html?com/google/common/util/concurrent/RateLimiter.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Guava&lt;/a&gt;에서 제공하는 모듈로 초 당 처리량을 설정을 통하여 관리될 수 있도록 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1614606380325&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation(&quot;com.google.guava:guava&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gradle 의존성 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 인스턴스 생성&lt;/h3&gt;
&lt;pre id=&quot;code_1614606423051&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private final RateLimiter apiRateLimiter = RateLimiter.create(4);

public static RateLimiter create(double permitsPerSecond) {
    /*
     * The default RateLimiter configuration can save the unused permits of up to one second. This
     * is to avoid unnecessary stalls in situations like this: A RateLimiter of 1qps, and 4 threads,
     * all calling acquire() at these moments:
     *
     * T0 at 0 seconds
     * T1 at 1.05 seconds
     * T2 at 2 seconds
     * T3 at 3 seconds
     *
     * Due to the slight delay of T1, T2 would have to sleep till 2.05 seconds, and T3 would also
     * have to sleep till 3.05 seconds.
     */
    return create(permitsPerSecond, SleepingStopwatch.createFromSystemTimer());
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초 당 허용할 Task의 개수를 인자로 넘겨준다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1614606475451&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static RateLimiter create(double permitsPerSecond, Duration warmupPeriod) {
    return create(permitsPerSecond, toNanosSaturated(warmupPeriod), TimeUnit.NANOSECONDS);
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원격 서버를 바로 호출하지 않고 웜업이 필요한 경우 이러한 생성자를 사용할 수도 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 획득&lt;/h3&gt;
&lt;p&gt;허용 &lt;i&gt;acquire() &lt;/i&gt;을 획득하여야만 Task를 처리할 수 있음&lt;/p&gt;
&lt;pre id=&quot;code_1614606543349&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public double acquire() {
    return acquire(1);
}

@CanIgnoreReturnValue
public double acquire(int permits) {
    long microsToWait = reserve(permits);
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return 1.0 * microsToWait / SECONDS.toMicros(1L);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예약 &lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;reserve()&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1614606576577&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;final long reserve(int permits) {
    checkPermits(permits);
    synchronized (mutex()) {
      return reserveAndGetWaitLength(permits, stopwatch.readMicros());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;예약은 허가 횟수(permits)를 예약하고, 대기 시간을 반환함&lt;/li&gt;
&lt;li&gt;획득 acquire() 은 있지만 반납은 없다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주의 사항&lt;/h2&gt;
&lt;p&gt;내부적으로는 &lt;span&gt;&lt;b&gt;synchronized&lt;/b&gt;로 동작하여 권한 획득 시 대기 시간을 반환받고 대기 시간 이후 작업을 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;즉, 작업 권한을 부여받은 작업(Task)이 완료하여 종료되지 않더라도 다른 작업이 대기 시간 이후 작업을 수행할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Threadpool size를 고정적으로 사용하는 경우에는 큰 문제가 발생하지 않지만 동적으로 poolsize를 조절하는 &lt;i&gt;newCachedThreadPool()&amp;nbsp;&lt;/i&gt;같은 경우 병목 현상이 발생한다면 최대 &lt;b&gt;&lt;span&gt;Integer&lt;/span&gt;.&lt;span&gt;MAX_VALUE&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;만큼 thread가 생성될 수 있기에 유의하여 사용해야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>자바 개발자되기</category>
      <category>Google</category>
      <category>guava</category>
      <category>java</category>
      <category>ratelimiter</category>
      <author>sowhat92</author>
      <guid isPermaLink="true">https://sowhat4.tistory.com/76</guid>
      <comments>https://sowhat4.tistory.com/76#entry76comment</comments>
      <pubDate>Mon, 1 Mar 2021 22:54:36 +0900</pubDate>
    </item>
    <item>
      <title>자바 원시(Primitive) 타입과 참조(Reference) 타입, 그리고 Reflection</title>
      <link>https://sowhat4.tistory.com/75</link>
      <description>&lt;h1&gt;발단&lt;/h1&gt;
&lt;h2&gt;Request 클래스의 Primitive type으로 선언된 변수에 대하여 Jackson Deserialize가 정상적으로 처리되지 않음&lt;/h2&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2&gt;How to Deserializing?&lt;/h2&gt;
&lt;p&gt;Spring에서 @ReuqestBody를 사용하는 경우&lt;br /&gt;body에 있는 data를 HttpMessageConverter 이용,&lt;br /&gt;특히 Json Data의 경우 MappingJackson2HttpMessageConverter 통하여 Jackson ObjectMapper &lt;b&gt;readValue&lt;/b&gt;() 호출하고, &lt;br /&gt;그 결과 개발자가 지정한 Request Object를 만들어 반환한다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;요약하자면 결국 JSON 요청 데이터를 &lt;b&gt;Java Reflection&lt;/b&gt;을 사용하여 개발자가 지정한 Request Object에 매핑해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;498&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnT00d/btqR3RdDtYR/RpJxHTDozHPKnqQkFPyPm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnT00d/btqR3RdDtYR/RpJxHTDozHPKnqQkFPyPm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnT00d/btqR3RdDtYR/RpJxHTDozHPKnqQkFPyPm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnT00d%2FbtqR3RdDtYR%2FRpJxHTDozHPKnqQkFPyPm1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;498&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;obj는 개발자가 지정한 Request Object&lt;/li&gt;
&lt;li&gt;value는 deserialize 된 request data 중 일부&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3&gt;예제 코드&lt;/h3&gt;
&lt;pre id=&quot;code_1609504335949&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
public class PrimitiveTestController {
    @PostMapping(value = &quot;test/primitive&quot;)
    public ResponseEntity&amp;lt;String&amp;gt; finalTest(@RequestBody final Request request){
        final long number = request.number;
        final Long number2 = request.number2;
        return ResponseEntity.ok().body(request.toString());
    }
}

class Request {
    public static final long DEFAULT = 100L;
    public final long number = DEFAULT;
    public final Long number2 = DEFAULT;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;호출 결과&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;328&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NCols/btqSdtW3uvM/DZ5NctGZBnrU9WzjewNb7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NCols/btqSdtW3uvM/DZ5NctGZBnrU9WzjewNb7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NCols/btqSdtW3uvM/DZ5NctGZBnrU9WzjewNb7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNCols%2FbtqSdtW3uvM%2FDZ5NctGZBnrU9WzjewNb7k%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;328&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Request{number=10, number2=10}&lt;/b&gt; 을 기대하였지만 결과는 위와 같다  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Why?&lt;/h1&gt;
&lt;p&gt;결과로 기대하였던 것은 number, number2 모두 Request Body로 Deserealize 된 Request{number=10, number2=10} 이다.&lt;/p&gt;
&lt;p&gt;하지만 왜 예상한 결과대로 처리되지 않았을까?  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;final로 초기화된 원시 타입&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4&quot;&gt;Chapter 4. Types, Values, and Variables&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A variable of primitive type or type String, that is final and initialized with a compile-time constant expression, is called a constant variable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html#jls-13.1&quot;&gt;Chapter 13. Binary Compatibility&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;References to fields that are constant variables are resolved at compile time to the constant value that is denoted. No reference to such a field should be present in the code in a binary file (except in the class or interface containing the field, which will have code to initialize it). Such a field must always appear to have been initialized the default initial value for the type of such a field must never be observed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;final로 초기화 된 원시 타입은 complie-time에 inline code화 된다?&lt;/p&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2&gt;무슨 말인지 모르겠으니 다시 예시 코드를 봐보자  &lt;/h2&gt;
&lt;h3&gt;PrimitiveTestController.java&lt;/h3&gt;
&lt;pre id=&quot;code_1609504511476&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
public class PrimitiveTestController {
    @PostMapping(value = &quot;test/primitive&quot;)
    public ResponseEntity&amp;lt;String&amp;gt; finalTest(@RequestBody final Request request){
        final long number = request.number;
        final Long number2 = request.number2;
        return ResponseEntity.ok().body(request.toString());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;제일 처음 보았던 Controller의 Java 코드이다.&lt;/p&gt;
&lt;p&gt;이 코드를 컴파일 한 .class 파일을 확인해보자!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;PrimitiveTestController.class  &lt;/h3&gt;
&lt;pre id=&quot;code_1609504531350&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
public class PrimitiveTestController {
    public PrimitiveTestController() {
    }

    @PostMapping({&quot;test/primitive&quot;})
    public ResponseEntity&amp;lt;String&amp;gt; finalTest(@RequestBody final Request request){
        request.getClass();
        long number = 100L;
        Long number2 = request.number2;
        return ResponseEntity.ok().body(request.toString());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;long number = 100L;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;원래 코드의 의도는 Json Deserialize 된 Request Object의 number를 반환하는 것이었다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;final long number = request.number;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 컴파일된 코드에서는 위와 같이 초기화에 사용된 constant value가 코드에 들어가 버렸다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;또한 Wrapper Class로 사용된 number2의 경우 primitive type과 별개로 의도하고자 했던 대로 컴파일되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;정리&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Primitive type, String의 경우 final &amp;amp; initialized 된다면 compile-time에 상수 표현식이 inline code화 된다.&lt;/li&gt;
&lt;li&gt;그러므로 primitive type &amp;amp; String에 대하여 final 키워드를 사용한다면 해당 변수가 초기화되는 시점을 잘 고려해야 할 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;참고&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/23273936/changing-static-variable-works-primitive-wrapper-but-not-with-primitive-type&quot;&gt;https://stackoverflow.com/questions/23273936/changing-static-variable-works-primitive-wrapper-but-not-with-primitive-type&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>자바 개발자되기</category>
      <category>deserialize</category>
      <category>Jackson</category>
      <category>Primitive</category>
      <category>Reflection</category>
      <category>Serialize</category>
      <category>Spring</category>
      <category>원시타입</category>
      <category>참조타입</category>
      <author>sowhat92</author>
      <guid isPermaLink="true">https://sowhat4.tistory.com/75</guid>
      <comments>https://sowhat4.tistory.com/75#entry75comment</comments>
      <pubDate>Fri, 1 Jan 2021 21:38:12 +0900</pubDate>
    </item>
    <item>
      <title>Spring @Transactional 어노테이션은 왜 Bean이 아닌 객체에서 적용되지 않는가?</title>
      <link>https://sowhat4.tistory.com/74</link>
      <description>&lt;p&gt;먼저 이 질문은 두 가지로 나눠야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Spring AOP는 왜 Bean으로 등록된 객체에 적용되는가?&lt;/li&gt;
&lt;li&gt;Spring AOP 동작 방식에는 어떤 것이 있는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 왜 Bean으로 동록되어야 하는가?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/questions/23394407/is-it-possible-to-use-spring-aop-without-creating-beans&quot;&gt;https://stackoverflow.com/questions/23394407/is-it-possible-to-use-spring-aop-without-creating-beans&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1609346424365&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Is it possible to use Spring AOP without creating beans?&quot; data-og-description=&quot;Spring AOP depends on proxy mechanism - J2SE dynamic proxies or using CGLIB(according to the spring documentation). Is it possible to use the AOP mechanism defined by Spring without creating/decla...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/23394407/is-it-possible-to-use-spring-aop-without-creating-beans&quot; data-og-url=&quot;https://stackoverflow.com/questions/23394407/is-it-possible-to-use-spring-aop-without-creating-beans&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bj3fYd/hyIK6ifVg5/FGLv2kJkbF2lSdO81sPRvK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/23394407/is-it-possible-to-use-spring-aop-without-creating-beans&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/23394407/is-it-possible-to-use-spring-aop-without-creating-beans&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bj3fYd/hyIK6ifVg5/FGLv2kJkbF2lSdO81sPRvK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Is it possible to use Spring AOP without creating beans?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Spring AOP depends on proxy mechanism - J2SE dynamic proxies or using CGLIB(according to the spring documentation). Is it possible to use the AOP mechanism defined by Spring without creating/decla...&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약하자면 Spring AOP는 기본적으로 Spring IoC와 협업한다.&lt;/li&gt;
&lt;li&gt;Spring AOP가 정상적으로 동작하기 위해서는 Spring Context 즉, Spring 관리 아래에 있어야 하지만 Bean으로 등록되지 않은 객체는 보통 &lt;b&gt;&lt;i&gt;new SomeClass()&amp;nbsp;&lt;/i&gt;&lt;/b&gt;와 같이 동적 할당하여 사용하기 때문에 Spring 관리 아래에 놓이지 못한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. AOP 동작 방식&lt;/h2&gt;
&lt;p&gt;Spring AOP는 기본적으로 RTW(Runtime weaving)으로 두 가지 방식이 존재한다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;ProxyFactoryBean은 interface의 유무에 따라 두 가지 방식 중 한 가지를 선택한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. JDK Proxy&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터페이스가 존재하는 경우 사용된다.&lt;/li&gt;
&lt;li&gt;자바 리플렉션을 사용하여 실제 실행되는 메서드를 invoke() 하기 때문에 CGLIB 방식보다 성능 상 불리하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. CGLIB&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;바이트 코드를 조작하기때문에 인터페이스가 없어도 실행 가능하다.&lt;/li&gt;
&lt;li&gt;다만 final 클래스나 메서드인 경우에는 사용할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsy3P7/btqRL2mx7M1/DmuNGFkhAx0KIqtE280pSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsy3P7/btqRL2mx7M1/DmuNGFkhAx0KIqtE280pSk/img.png&quot; data-alt=&quot;https://www.baeldung.com/spring-aop-vs-aspectj&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsy3P7/btqRL2mx7M1/DmuNGFkhAx0KIqtE280pSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbsy3P7%2FbtqRL2mx7M1%2FDmuNGFkhAx0KIqtE280pSk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.baeldung.com/spring-aop-vs-aspectj&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Bean이 아닌 POJO 객체, 그리고 final 클래스나 메서드에 사용하고 싶다면?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/aspectj&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AspectJ&lt;/a&gt;를 참고&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;추가) @Transactional은 기본적으로 CGLib Proxy 방식을 사용&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://do-study.tistory.com/83&quot;&gt;https://do-study.tistory.com/83&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1609347633817&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring AOP 동작 방식, 원리&quot; data-og-description=&quot;스프링은 기본적으로 프록시 기반 AOP 를 제공한다. 스프링에서는 Java Dynamic Proxy를 사용하거나 Cglib을 사용하여 프록시 기반 AOP를 구현했다. 이 글에서는 어떻게 Spring 에서 AOP를 사용할 수 있는 &quot; data-og-host=&quot;do-study.tistory.com&quot; data-og-source-url=&quot;https://do-study.tistory.com/83&quot; data-og-url=&quot;https://do-study.tistory.com/83&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dgYN2N/hyILbjx0RF/NNKoj6iBkKFzWuP2FKjgS1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/b89M8K/hyILd9vKFz/4I52D55YjZ82HpNrTljyc1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/fb8nB/hyILeHlFwz/QST3Y5F5rHCC6YxeKkBIuK/img.jpg?width=2160&amp;amp;height=1920&amp;amp;face=0_0_2160_1920&quot;&gt;&lt;a href=&quot;https://do-study.tistory.com/83&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://do-study.tistory.com/83&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dgYN2N/hyILbjx0RF/NNKoj6iBkKFzWuP2FKjgS1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/b89M8K/hyILd9vKFz/4I52D55YjZ82HpNrTljyc1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/fb8nB/hyILeHlFwz/QST3Y5F5rHCC6YxeKkBIuK/img.jpg?width=2160&amp;amp;height=1920&amp;amp;face=0_0_2160_1920');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Spring AOP 동작 방식, 원리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;스프링은 기본적으로 프록시 기반 AOP 를 제공한다. 스프링에서는 Java Dynamic Proxy를 사용하거나 Cglib을 사용하여 프록시 기반 AOP를 구현했다. 이 글에서는 어떻게 Spring 에서 AOP를 사용할 수 있는&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;do-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/questions/38043089/spring-aop-for-non-spring-component&quot;&gt;https://stackoverflow.com/questions/38043089/spring-aop-for-non-spring-component&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1609347739196&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Spring AOP for non spring component&quot; data-og-description=&quot;I am writing Spring 4 application with java config. I can use AOP in this project for all spring component. But i can't use it for a normal POJO class. what is the library I need to add and what ...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/38043089/spring-aop-for-non-spring-component&quot; data-og-url=&quot;https://stackoverflow.com/questions/38043089/spring-aop-for-non-spring-component&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FzxP5/hyILd9vLLH/iZkvQSfy8G8OfVvCpEwnJK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/38043089/spring-aop-for-non-spring-component&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/38043089/spring-aop-for-non-spring-component&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FzxP5/hyILd9vLLH/iZkvQSfy8G8OfVvCpEwnJK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Spring AOP for non spring component&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;I am writing Spring 4 application with java config. I can use AOP in this project for all spring component. But i can't use it for a normal POJO class. what is the library I need to add and what ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자바 개발자되기</category>
      <category>aspect</category>
      <category>BEAN</category>
      <category>proxy</category>
      <category>spring aop</category>
      <author>sowhat92</author>
      <guid isPermaLink="true">https://sowhat4.tistory.com/74</guid>
      <comments>https://sowhat4.tistory.com/74#entry74comment</comments>
      <pubDate>Thu, 31 Dec 2020 01:58:35 +0900</pubDate>
    </item>
    <item>
      <title>Junit5 @BeforeEach private method가 동작하는 이유</title>
      <link>https://sowhat4.tistory.com/73</link>
      <description>&lt;h1&gt;발단&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;어느 날과 다름없이 비즈니스 로직을 먼저 작성하고 테스트 코드를 작성하는 코드 몽키 ing 중  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;테스트 코드 실행 전 초기 설정이 필요하여 Junit5의 @BeforeEach 사용&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;호기심이 발동하여 접근 지정자 private 지정&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;292&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K35Y1/btqRGqBbb4r/jMZCf0sbJ5fkjGWQLv4nmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K35Y1/btqRGqBbb4r/jMZCf0sbJ5fkjGWQLv4nmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K35Y1/btqRGqBbb4r/jMZCf0sbJ5fkjGWQLv4nmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK35Y1%2FbtqRGqBbb4r%2FjMZCf0sbJ5fkjGWQLv4nmk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;292&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;실행&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;210&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BKfWX/btqRXH8OYOT/yKN3DvLjRRG99khVokWLcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BKfWX/btqRXH8OYOT/yKN3DvLjRRG99khVokWLcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BKfWX/btqRXH8OYOT/yKN3DvLjRRG99khVokWLcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBKfWX%2FbtqRXH8OYOT%2FyKN3DvLjRRG99khVokWLcK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;210&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;어? 성공하네?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;사용해도 되는건가? &lt;a href=&quot;https://junit.org/junit5/docs/5.2.0/api/org/junit/jupiter/api/BeforeEach.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 문서&lt;/a&gt;ㄱㄱ&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;@BeforeEach methods must have a void return type, must not be private, and must not be static.&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;private을 절대 사용하지 말라고 &lt;b&gt;must&lt;/b&gt;로 명시되어져 있는데 왜 동작하는 거지?  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;구글링을 하였지만 명확한 답변을 찾을 수 없음&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그래서 직접 삽질 시작  &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;동작하는 이유&lt;/h1&gt;
&lt;h2&gt;결국 역시나 답은 Reflection  &lt;/h2&gt;
&lt;p&gt;결론을 이미 부제에 달았지만 혹시나가 역시나 답은 리플렉션&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;디버깅을 걸어보자!&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;265&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beiAoy/btqR1E43e92/4cnPDoYhQn1alqpoR5SsJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beiAoy/btqR1E43e92/4cnPDoYhQn1alqpoR5SsJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beiAoy/btqR1E43e92/4cnPDoYhQn1alqpoR5SsJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeiAoy%2FbtqR1E43e92%2F4cnPDoYhQn1alqpoR5SsJ1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;265&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;@BeforeEach 메서드 내부에 breakpoint 지정&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Call Stack 중 눈에띄는 relfect!&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IhRG4/btqRTRD9zAt/EUjDeozF4ltMhbj5JrbtPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IhRG4/btqRTRD9zAt/EUjDeozF4ltMhbj5JrbtPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IhRG4/btqRTRD9zAt/EUjDeozF4ltMhbj5JrbtPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIhRG4%2FbtqRTRD9zAt%2FEUjDeozF4ltMhbj5JrbtPK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;하나 씩 거슬러 올라가다 의심 부근 발견!&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;RelectionUtils.invokeMethod()&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jy1zd/btqRQR5yWll/dZRWoBUK3Gu7KKen1NvZE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jy1zd/btqRQR5yWll/dZRWoBUK3Gu7KKen1NvZE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jy1zd/btqRQR5yWll/dZRWoBUK3Gu7KKen1NvZE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJy1zd%2FbtqRQR5yWll%2FdZRWoBUK3Gu7KKen1NvZE1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;RelectionUtils.makeAccessible()&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RDYgO/btqRTSXirNi/Ut3c87hnhrAInZ1t2e3xgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RDYgO/btqRTSXirNi/Ut3c87hnhrAInZ1t2e3xgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RDYgO/btqRTSXirNi/Ut3c87hnhrAInZ1t2e3xgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRDYgO%2FbtqRTSXirNi%2FUt3c87hnhrAInZ1t2e3xgk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;여기서 object는 Method 객체, 더 정확히 말하자면 @BeforeEach가 달린 &lt;b&gt;setUp()&lt;/b&gt; 메서드&lt;/p&gt;
&lt;p&gt;Java Reflection에서는 전지전능 한 기능을 제공하고, 그중 &lt;b&gt;private method&lt;/b&gt;를 접근할 수 있게 만들어버리는 매직(magic)을 제공 한다&lt;/p&gt;
&lt;p&gt;자세한 내용은 &lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt; 참고&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;심증은 찾았으니 물증 확인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4e36I/btqR1EYitMW/tnpBkrWUdKsyhb9geuWkaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4e36I/btqR1EYitMW/tnpBkrWUdKsyhb9geuWkaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4e36I/btqR1EYitMW/tnpBkrWUdKsyhb9geuWkaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4e36I%2FbtqR1EYitMW%2FtnpBkrWUdKsyhb9geuWkaK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;해당 부분에 Breakpoint를 걸고 다시 디버깅&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lFC8B/btqRQSJ78Mx/NvdkkSckZc4k7kjcCmcaQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lFC8B/btqRQSJ78Mx/NvdkkSckZc4k7kjcCmcaQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lFC8B/btqRQSJ78Mx/NvdkkSckZc4k7kjcCmcaQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlFC8B%2FbtqRQSJ78Mx%2FNvdkkSckZc4k7kjcCmcaQk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xr58C/btqRGq2bNIm/HTS7G9GyGVxxmV0yiEIvkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xr58C/btqRGq2bNIm/HTS7G9GyGVxxmV0yiEIvkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xr58C/btqRGq2bNIm/HTS7G9GyGVxxmV0yiEIvkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxr58C%2FbtqRGq2bNIm%2FHTS7G9GyGVxxmV0yiEIvkK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;AccessibleObject.setAccessible()&lt;/b&gt; 에 의하여 변경되는 값은 결국 &lt;b&gt;override&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;RelectionUtils.makeAccessible()&lt;/b&gt; 호출 전 override의 상태는 false!&lt;/p&gt;
&lt;p&gt;이 상태에서는 private 지정자의 역할이 발휘된다&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;403&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo49Gj/btqRXHuc401/Q9ekqHJWk9nUdMKc33XDVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo49Gj/btqRXHuc401/Q9ekqHJWk9nUdMKc33XDVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo49Gj/btqRXHuc401/Q9ekqHJWk9nUdMKc33XDVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo49Gj%2FbtqRXHuc401%2FQ9ekqHJWk9nUdMKc33XDVk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;403&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;호출 이후 값을 확인해보면 true로 변경될 것을 확인할 수 있다&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;결론&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Junit5에서 제공하는 @BeforeEach 어노테이션은 접근 지정자가 private이 되지 않기를 권고한다. 하지만 private을 지정해도 잘 돌아간다. 그 이유는 리플렉션을 사용하여 다 접근 가능하게끔 변경하기 때문이다. 고로 리플렉션은 깡패...  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자바 개발자되기</category>
      <category>beforeEach</category>
      <category>java</category>
      <category>JUnit</category>
      <category>Reflection</category>
      <category>Spring</category>
      <category>test</category>
      <author>sowhat92</author>
      <guid isPermaLink="true">https://sowhat4.tistory.com/73</guid>
      <comments>https://sowhat4.tistory.com/73#entry73comment</comments>
      <pubDate>Wed, 30 Dec 2020 13:59:09 +0900</pubDate>
    </item>
  </channel>
</rss>