<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Solmi's Development Blog</title>
    <link>https://intothemaze.tistory.com/</link>
    <description>Solmi's Development Blog</description>
    <language>ko</language>
    <pubDate>Wed, 8 Apr 2026 20:20:49 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>솔묘</managingEditor>
    <image>
      <title>Solmi's Development Blog</title>
      <url>https://tistory1.daumcdn.net/tistory/4900519/attach/7fe61e7faa3249339e592eaa92b6772f</url>
      <link>https://intothemaze.tistory.com</link>
    </image>
    <item>
      <title>[Spring Security] Filter에서 JWT 예외 처리</title>
      <link>https://intothemaze.tistory.com/21</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;현 프로젝트에서는 Spring에서 제공하는 @ControllerAdvice 어노테이션을 사용하여 전역 예외 처리를 하고 있으며, @ExceptionHandler를 통해 각 Custom Exception에 상응하는 status code를 내려주고 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 Filter에서 발생하는 JWT 예외는 위와 같은 방법으로 처리할 수 없다!&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Why&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Filter와 @ControllerAdvice는 서로 다른 컨텍스트에서 동작한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2019-02-18-20-20-08.png&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;689&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QiZdw/btsD4FJFbPk/PEQL7MoHpE2c4ZK2pVXN3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QiZdw/btsD4FJFbPk/PEQL7MoHpE2c4ZK2pVXN3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QiZdw/btsD4FJFbPk/PEQL7MoHpE2c4ZK2pVXN3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQiZdw%2FbtsD4FJFbPk%2FPEQL7MoHpE2c4ZK2pVXN3k%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;778&quot; height=&quot;689&quot; data-filename=&quot;2019-02-18-20-20-08.png&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;689&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위의 그림을 통해 볼 수 있듯이 Filter는 Spring Context의 앞단인 Servlet Context에서 관리되는 영역으로, Spring에서 제공하는 @ControllerAdvice와는 예외 처리 메커니즘이 분리되어 있다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;따라서 Filter에서 발생하는 예외는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;전역 예외 처리와는 별개의&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;How&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;JwtFilter 앞단에 별도의 예외 처리 Filter를 두는 방법을 떠올렸으나, 프로젝트 팀장님께서 JWT 예외만을 처리하기 위해 별도의 Filter를 하나 더 두는 것이 의미가 있는지 질문하였다. 나는&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;1. 예외 처리 로직을 JWT 필터와 분리시키기 위해&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;2. 대부분의 사람들이 해당 방식으로 구현해서 (즉, 리소스가 많아서)&amp;nbsp; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위 두 가지 이유로 예외 필터를 고민했었는데, 팀장님 말을 듣고 보니 실제로 예외 처리 코드가 몇 줄 되지 않아 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;JwtAuthenticationFilter에서 예외 처리까지 해주었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1706439099702&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                  FilterChain filterChain) throws ServletException, IOException {
    try {
      trySettingAuthentication(request);
    } catch (JwtException e) {
      setResponse(response, e);
    }

    filterChain.doFilter(request, response);
  }
  
  private void setResponse(HttpServletResponse response, JwtException e) throws IOException {
    var responseEntity = ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    response.getWriter().print(responseEntity);
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;현재 전역 예외 처리 시 ResponseEntity에 HttpStatus를 담아 내려주고 있어서 Filter에서도 같은 방식으로 response를 넘겨주었다.&amp;nbsp; 논의 후 에러 메시지 등이 추가될 것으로 보여진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Coding</category>
      <author>솔묘</author>
      <guid isPermaLink="true">https://intothemaze.tistory.com/21</guid>
      <comments>https://intothemaze.tistory.com/21#entry21comment</comments>
      <pubDate>Sun, 28 Jan 2024 19:57:35 +0900</pubDate>
    </item>
    <item>
      <title>[GIT] Trunk-based development</title>
      <link>https://intothemaze.tistory.com/20</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt; Git Branch 전략이라고 하면 Git-flow, Github-flow, Gitlab-flow 전략들을 떠올리나, 최근 영어로 검색했을 때 trunk-based development에 대한 글들과 feautrue-based development를 나눠서 비교하는 아티클들이 꽤나 많이 보였다. 새롭게 떠오르는 trunk-based 전략에 대해 간략히 살펴보고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;Trunk-Based Development란&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #666666;&quot;&gt; 개발자가 핵심 trunk 또는 main branch와 같은 단일 브랜치에서 모든 작업을 수행하고 병합하는 버전 관리 방법 &lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;trunk-based-development.jpg&quot; data-origin-width=&quot;1999&quot; data-origin-height=&quot;1122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QZInu/btsAzBjNWrC/fdkrsa83Xn6k6Ck7WB3JVk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QZInu/btsAzBjNWrC/fdkrsa83Xn6k6Ck7WB3JVk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QZInu/btsAzBjNWrC/fdkrsa83Xn6k6Ck7WB3JVk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQZInu%2FbtsAzBjNWrC%2Ffdkrsa83Xn6k6Ck7WB3JVk%2Fimg.webp&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;1999&quot; height=&quot;1122&quot; data-filename=&quot;trunk-based-development.jpg&quot; data-origin-width=&quot;1999&quot; data-origin-height=&quot;1122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;특징&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;짧은 개발 주기&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;개발자는 코드 변경 사항을 main branch에 바로 push 할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;그렇기에 더 자주, 그리고 더 작은 단위로 코드를 변경할 수 있다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;빠른 테스트와 배포 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;변경 사항 추적 용이&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;오랜 시간이 필요한 개발 건의 경우, main branch에서 새 branch를 checkout 받아 작업한 후 코드 리뷰를 거쳐 main branch로 merge 한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;checkout한 브랜치는 짧은 생명 주기를 가지며, 대개 2~3일 이내에 완료된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;production-ready&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;Trunk-based 전략을 사용 시 주 브랜치는 항상 production-ready 상태여야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;오류가 있는 코드는 전체 빌드를 깨뜨릴 수 있기에 main branch로 push하기 전 철저히 테스트해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;CI&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;진정한 의미의 CI를 수행할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모든 코드 변경이 주 브랜치로 직접 통합되므로 코드 변경 사항을 컴파일하고 자동화된 테스트를 실행하여 변경 사항이 주 브랜치를 깨뜨리지 않는지 확인할 수 있다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;장점&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;지속적인 코드 통합&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; &lt;span style=&quot;color: #0f0f0f; text-align: left;&quot;&gt;작은 단위의 변경을 main branch에 주기적으로 통합함으로써 &lt;/span&gt;실시간으로 빠르게 코드 변경이 통합될 수 있다. &lt;span style=&quot;color: #0f0f0f; text-align: left;&quot;&gt;개발 주기를 단축하고 새로운 기능을 신속하게 배포할 수 있습니다.&lt;/span&gt; &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;효율적인 코드 리뷰&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;생명 주기가 길고 코드 변경이 큰 feature branch에 비해 코드 리뷰가 더 효율적이고 용이하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;연속적인 프로덕션 코드 배포&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;자동화된 테스트, 코드 커버리지 및 코드 리뷰를 통해 main branch를 언제든 배포할 준비가 되었음을 보장해준다. 따라서 빈번한 프로덕션 배포 및 릴리스에 도움이 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;CI/CD&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지속적 통합과 함께 개발자의 commit 후 자동 테스트가 실행되는 것을 보장하여 프로젝트가 언제나 작동함을 보장한다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;적용 시 체크 사항&lt;/span&gt;&lt;/h2&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;✅ &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;CI/CD 구축&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;지속적 통합 및 지속적 배포 파이프라인을 구축하여 변경 사항이 자동으로 빌드되고 테스트되며, 프로덕션 환경에 안전하게 배포될 수 있도록 한다. 강&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;력한 CI 문화를 구축하는 것은 Trunk-based development 전략에 있어 중요한 요소이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;✅ &lt;/span&gt;자동화된 테스트 프로세스 구축&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; text-align: start;&quot;&gt; &lt;span style=&quot;color: #0f0f0f; text-align: left;&quot;&gt;자동화된 테스트 스위트를 통해 코드 변경이 안정적이며 예상대로 작동하는지 확인하고, 빠른 피드백을 얻어 빠른 수정이 가능하도록 한다. &lt;/span&gt;CI 실천을 위해 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;자동화된 테스트는 필수이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;✅&amp;nbsp;&lt;/span&gt;Code Review 강화&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0f0f0f; text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드 리뷰를 통해 코드 품질을 유지하고, 팀원 간 의사소통을 강화하여 통합 전 오류를 사전에 감지한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;✅&amp;nbsp;&lt;/span&gt;Feature Flags 사용&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0f0f0f; text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새로운 기능을 코드에 통합하고, 그 기능을 활성화 또는 비활성화할 수 있는 feature flags를 도입하여 실제 배포 전에 기능을 숨기거나 활성화할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Reference&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1700439270484&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;Trunk-based Development | Atlassian&quot; data-og-description=&quot;Learn about trunk-based development, a version control management practice where developers merge small, frequent updates to a core &amp;ldquo;trunk&amp;rdquo; or main branch&quot; data-og-host=&quot;www.atlassian.com&quot; data-og-source-url=&quot;https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development&quot; data-og-url=&quot;https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buJ5Vp/hyUCbkbTFj/c22gTyZcEkDkePL7p3U7jk/img.png?width=732&amp;amp;height=488&amp;amp;face=0_0_732_488,https://scrap.kakaocdn.net/dn/c1gYGx/hyUCc4tnzF/WFBybquLHJKLZ9GeAUGHR0/img.png?width=732&amp;amp;height=488&amp;amp;face=0_0_732_488,https://scrap.kakaocdn.net/dn/ctUI93/hyUyorYP3x/AaY0pCXwuqbc96RZWMnKPK/img.png?width=732&amp;amp;height=488&amp;amp;face=0_0_732_488&quot;&gt;&lt;a href=&quot;https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buJ5Vp/hyUCbkbTFj/c22gTyZcEkDkePL7p3U7jk/img.png?width=732&amp;amp;height=488&amp;amp;face=0_0_732_488,https://scrap.kakaocdn.net/dn/c1gYGx/hyUCc4tnzF/WFBybquLHJKLZ9GeAUGHR0/img.png?width=732&amp;amp;height=488&amp;amp;face=0_0_732_488,https://scrap.kakaocdn.net/dn/ctUI93/hyUyorYP3x/AaY0pCXwuqbc96RZWMnKPK/img.png?width=732&amp;amp;height=488&amp;amp;face=0_0_732_488');&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;Trunk-based Development | Atlassian&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn about trunk-based development, a version control management practice where developers merge small, frequent updates to a core &amp;ldquo;trunk&amp;rdquo; or main branch&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.atlassian.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://www.split.io/blog/a-complete-guide-to-trunk-based-development/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.split.io/blog/a-complete-guide-to-trunk-based-development/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1700439263795&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;A Complete Guide to Trunk-Based Development&quot; data-og-description=&quot;As the sector has expanded, so has the focus on trunk-based development, elevating it from a niche strategy to a favored industry approach.&quot; data-og-host=&quot;www.split.io&quot; data-og-source-url=&quot;https://www.split.io/blog/a-complete-guide-to-trunk-based-development/&quot; data-og-url=&quot;https://www.split.io/blog/a-complete-guide-to-trunk-based-development/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.split.io/blog/a-complete-guide-to-trunk-based-development/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.split.io/blog/a-complete-guide-to-trunk-based-development/&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;A Complete Guide to Trunk-Based Development&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;As the sector has expanded, so has the focus on trunk-based development, elevating it from a niche strategy to a favored industry approach.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.split.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Etc.</category>
      <author>솔묘</author>
      <guid isPermaLink="true">https://intothemaze.tistory.com/20</guid>
      <comments>https://intothemaze.tistory.com/20#entry20comment</comments>
      <pubDate>Mon, 20 Nov 2023 09:15:06 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] 생성자 대신 정적 팩터리 메서드를 고려하라</title>
      <link>https://intothemaze.tistory.com/19</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;무의식적으로 사용하는 패턴의 장&amp;middot;단점을 이해하고 동료 개발자에게 설득력 있는 설명을 하기 위해 Effective Java 첫 장을 다시 들여다보았다. 나는 주로 Builder 패턴과 함께 Dto를 매개변수로 받아 Entity로 반환할 때 사용하였던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정적 팩터리 메서드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 생성하는 메서드를 정적(static)으로 만들어서 객체 생성을 캡슐화 하는 메서드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장점&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 이름을 가질 수 있다.&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자에 넘기는 매개변수와 생성자 자체만으로는 반환될 객체의 특성을 제대로 설명하지 못한다.&lt;/li&gt;
&lt;li&gt;반면, 정적 팩터리 메서드는 이름을 통해 반환될 객체의 특성을 묘사할 수 있다.&lt;/li&gt;
&lt;li&gt;생성자는 하나의 시그니처로는 생성자를 하나만 만들 수 있다.&lt;/li&gt;
&lt;li&gt;하지만 정적 팩터리 메서드는 시그니처가 같은 생성자가 여러개 필요할 경우 각 &lt;span style=&quot;letter-spacing: 0px;&quot; data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQSVCMCU4MSVFQyU5RCU5OCUyMCVFQyVCMCVBOCVFQyU5RCVCNCVFQiVBNSVCQyUyMCVFQyU5RSU5OCUyMCVFQiU5MyU5QyVFQiU5RiVBQyVFQiU4MiVCNCVFQiU4QSU5NCUyMCVFQyU5RCVCNCVFQiVBNiU4NCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMmQxYzJiOGZmZGVhYjRkM2E4YWJkOTQ2OGEwN2RlNmMyJTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyYzdlNWM1ZjBkMTQ1NDdmOWIxYTMwOTk2ODkyYzk5ODYlMjIlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjI1OGFjYTg4ZDI3ZGY0ZWJkODIzOTk4NzEyOWMwODRmMiUyMiU3RA==&quot;&gt;각의 차이를 잘 드러내는 이름을 지어줌으로써&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;유용하게 사용이 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;불변 클래스는 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체생성을 피할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;따라서 같은 객체가 자주 요청되는 상황이라면 성능을 상당히 끌어 올려 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.&lt;/h3&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;구현 클래스를 공개하기 않고도 그 객체를 반환할 수 있다. -&amp;gt; 객체 생성을 캡슐화 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관 없다.&lt;/li&gt;
&lt;li&gt;ex) EnumSet&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;단점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적으로 정적 팩토리 메서드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;private&lt;span&gt;&amp;nbsp;&lt;/span&gt;접근 제어자를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. &lt;span data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJoZWFkaW5nLTIlMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQyVBMCU5NSVFQyVBMCU4MSUyMCVFRCU4QyVBOSVFRCU4NCVCMCVFQiVBNiVBQyUyMCVFQiVBOSU5NCVFQyU4NCU5QyVFQiU5MyU5QyVFQiU4QSU5NCUyMCVFRCU5NCU4NCVFQiVBMSU5QyVFQSVCNyVCOCVFQiU5RSU5OCVFQiVBOCVCOCVFQSVCMCU4MCUyMCVFQyVCMCVCRSVFQSVCOCVCMCUyMCVFQyU5NiVCNCVFQiVBMCVCNSVFQiU4QiVBNC4lMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCUyQyUyMnNlbGVjdGlvbnMlMjIlM0ElNUIlNUQlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjI4MTE3Y2YzMmE3Nzc0OWNjODI4NmQzYWM2ZmQ4MGRmMCUyMiU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMmVhNGIwOWI5YzA1MTRjOWQ4ZTYwN2Q3ZjY2ZWIzY2IxJTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyMTE0ZDg0YTM2NzMwNDJjZTkxNDBlOTU4NWY1MDQ4NjYlMjIlN0Q=&quot;&gt;정적 팩터리 메서드는 프로그래머가 찾기 어렵다.&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생&lt;span data-offset-key=&quot;028736042d5440f084df7eca3fdb14ac:0&quot;&gt;성자처럼 API 설명에 명확히 드러나지 않으니 사용자는 정적 팩터리 메서드 방식 클래스를 &lt;/span&gt;인스턴스화할 방법&lt;span data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQyU4NCVCMSVFQyU5RSU5MCVFQyVCMiU5OCVFQiU5RiVCQyUyMEFQSSUyMCVFQyU4NCVBNCVFQiVBQSU4NSVFQyU5NyU5MCUyMCVFQiVBQSU4NSVFRCU5OSU5NSVFRCU5RSU4OCUyMCVFQiU5MyU5QyVFQiU5RiVBQyVFQiU4MiU5OCVFQyVBNyU4MCUyMCVFQyU5NSU4QSVFQyU5QyVCQyVFQiU4QiU4OCUyMCVFQyU4MiVBQyVFQyU5QSVBOSVFQyU5RSU5MCVFQiU4QSU5NCUyMCVFQyVBMCU5NSVFQyVBMCU4MSUyMCVFRCU4QyVBOSVFRCU4NCVCMCVFQiVBNiVBQyUyMCVFQiVBOSU5NCVFQyU4NCU5QyVFQiU5MyU5QyUyMCVFQiVCMCVBOSVFQyU4QiU5RCUyMCVFRCU4MSVCNCVFQiU5RSU5OCVFQyU4QSVBNCVFQiVBNSVCQyUyMCUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJUVDJTlEJUI4JUVDJThBJUE0JUVEJTg0JUI0JUVDJThBJUE0JUVEJTk5JTk0JUVEJTk1JUEwJTIwJUVCJUIwJUE5JUVCJUIyJTk1JTIyJTJDJTIybWFya3MlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJtYXJrJTIyJTJDJTIydHlwZSUyMiUzQSUyMmNvZGUlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTdEJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCUyQyU3QiUyMm9iamVjdCUyMiUzQSUyMmxlYWYlMjIlMkMlMjJ0ZXh0JTIyJTNBJTIyJUVDJTlEJTg0JTIwJUVDJTk1JThDJUVDJTk1JTg0JUVCJTgyJUI0JUVDJTk1JUJDJTIwJUVEJTk1JTlDJUVCJThCJUE0LiUyMiUyQyUyMm1hcmtzJTIyJTNBJTVCJTVEJTJDJTIyc2VsZWN0aW9ucyUyMiUzQSU1QiU1RCU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMjAyM2IwZjc0ODM5NzRmMjQ4MDI4YTM3ZmEzNDUzYjYyJTIyJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyYjdjNzExODZkODZlNDgyNDhjNDkxZWQwMTA0ZTNhNjElMjIlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjJjYmNjYmFiZjE2ZmQ0YmUwOTA1MjIwNDJhM2Q3MWU2YyUyMiU3RA==&quot; data-offset-key=&quot;028736042d5440f084df7eca3fdb14ac:2&quot;&gt;을 알아내야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Study</category>
      <author>솔묘</author>
      <guid isPermaLink="true">https://intothemaze.tistory.com/19</guid>
      <comments>https://intothemaze.tistory.com/19#entry19comment</comments>
      <pubDate>Wed, 18 Oct 2023 18:25:57 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] executor failed running. exit code: 127</title>
      <link>https://intothemaze.tistory.com/18</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Docker를 사용해서 Jenkins를 띄우려고 하는데, 다른 개발자 분의 개발 환경에서는 Dockerfile이 정상적으로 build 되는 반면, 내 로컬에서는 에러가 났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러 내용&lt;/h2&gt;
&lt;pre id=&quot;code_1693370657849&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;=&amp;gt; ERROR [2/5] RUN &amp;lt;&amp;lt;EOF (apt-get update...)                                                                                                                                                                 9.4s
------
 &amp;gt; [2/5] RUN &amp;lt;&amp;lt;EOF (apt-get update...):
#9 8.358 E: Invalid operation update
#9 8.391 Reading package lists...
#9 8.436 Building dependency tree...
#9 8.437 Reading state information...
#9 8.462 E: Unable to locate package sudo
#9 8.464 /bin/sh: 3: sudo: not found
.ZIP.or awscliv2.zipnot find or open awscliv2.zip
#9 8.480 /bin/sh: 5: sudo: not found
------
executor failed running [/bin/sh -c apt-get update
apt-get -y install sudo
sudo curl &quot;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&quot; -o &quot;awscliv2.zip&quot;
unzip awscliv2.zip
sudo aws/install
]: exit code: 127&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apt-get update 줄부터 command를 찾지 못하고 있음을 발견&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;pre id=&quot;code_1693371671889&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;RUN &amp;lt;&amp;lt;EOF 
apt-get update
apt-get -y install sudo
sudo curl &quot;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&quot; -o &quot;awscliv2.zip&quot;
unzip awscliv2.zip
sudo aws/install
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile을 작성한 개발자 분은 macOS에서 작업하는 반면, 나는 Windows 운영체제를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 파일에서는 Heredoc의 EOF를 구분 식별자로 사용하였는데, 운영체제의 차이로 인해 실제 실행되는 명령어가 다른 것으로 보인다. (ex.apt-get update\r\n)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 방법&lt;/h2&gt;
&lt;pre id=&quot;code_1693371717532&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;RUN apt-get update  \
    &amp;amp;&amp;amp; apt-get -y install sudo \
    &amp;amp;&amp;amp; sudo curl &quot;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&quot; -o &quot;awscliv2.zip&quot; \
    &amp;amp;&amp;amp; unzip awscliv2.zip \
    &amp;amp;&amp;amp; sudo aws/install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가독성은 다소 떨어지지만, 위와 같이 개행 처리를 함으로써 Dockerfile을 통해 성공적으로 이미지를 생성 할 수 있었다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Troubleshooting</category>
      <author>솔묘</author>
      <guid isPermaLink="true">https://intothemaze.tistory.com/18</guid>
      <comments>https://intothemaze.tistory.com/18#entry18comment</comments>
      <pubDate>Wed, 30 Aug 2023 14:04:25 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] @OneToOne, 일대일(1:1) 연관관계 매핑 시 주의할 점 (지연 로딩 이슈)</title>
      <link>https://intothemaze.tistory.com/17</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA를 사용하며 테스트를 통해 실제 수행되는 쿼리를 수시로 체크하는데, 일대일(1:1) 양방향 연관관계를 지니는 테이블에서 FetchType.LAZY가 적용되지 않아 불필요한 쿼리가 호출되는 것을 발견하였다.&amp;nbsp;&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;아래는 문제가 되는 SHELTER와 SHELTER_USER의 연관관계 다이어그램이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vV2LY/btsqILpSn3g/QJmRKIkIaD5OsKZY8tnhw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vV2LY/btsqILpSn3g/QJmRKIkIaD5OsKZY8tnhw0/img.png&quot; data-alt=&quot;테이블 연관관계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vV2LY/btsqILpSn3g/QJmRKIkIaD5OsKZY8tnhw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvV2LY%2FbtsqILpSn3g%2FQJmRKIkIaD5OsKZY8tnhw0%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;993&quot; height=&quot;377&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테이블 연관관계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eLsodg/btsqESpEuc0/AP0pMqqkYUkvJCCf9nbtg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eLsodg/btsqESpEuc0/AP0pMqqkYUkvJCCf9nbtg1/img.png&quot; data-alt=&quot;객체 연관관계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eLsodg/btsqESpEuc0/AP0pMqqkYUkvJCCf9nbtg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeLsodg%2FbtsqESpEuc0%2FAP0pMqqkYUkvJCCf9nbtg1%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;971&quot; height=&quot;412&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;객체 연관관계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SHELTER와 SHETLER는 1:1 양방향 연관관계로 매핑되어 있으며, 대상 테이블인 SHELTER_USER에 외래키를 두고 있다. 문제는 SHELTER를 조회 시 SHELTER_USER 테이블도 즉시 로딩되어 아래와 같이 쿼리가 두 번 호출된다.&lt;/p&gt;
&lt;pre id=&quot;code_1691744952214&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2023-08-11T17:58:58.461+09:00 DEBUG 2272 --- [nio-8080-exec-3] org.hibernate.SQL                        : 
    /* &amp;lt;criteria&amp;gt; */ select
        s1_0.shelter_no,
        s1_0.created_date,
        s1_0.detail_address,
        s1_0.email,
        s1_0.last_modified_date,
        s1_0.shelter_name,
        s1_0.street_address,
        s1_0.zip_code 
    from
        shelter s1_0 
    where
        s1_0.shelter_name=?
2023-08-11T17:58:58.472+09:00 DEBUG 2272 --- [nio-8080-exec-3] org.hibernate.SQL                        : 
    select
        s1_0.shelter_user_no,
        s1_0.created_date,
        s1_0.last_modified_date,
        s1_0.shelter_no,
        s1_0.user_no 
    from
        shelter_user s1_0 
    where
        s1_0.shelter_no=?&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@OneToOne 양방향 연관 관계에서는&amp;nbsp;&lt;b&gt;연관관계의 주인이 아닌 쪽 엔티티를 조회 시 지연 로딩이 동작하지 않는다.&amp;nbsp;&lt;/b&gt;일대일 양방향 관계는 &lt;b&gt;외래 키가 있는 곳이 연관관계의 주인으로,&lt;/b&gt;&amp;nbsp; SHELTER_USER가 연관관계의 주인이며, 결과적으로 연관관계의 주인이 아닌 Shelter 엔티티를 조회할 때 연관된 ShelterUser 또한 즉시 로딩으로 조회 된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지연 로딩으로 설정해도 항상 즉시 로딩되는 이 문제는 JPA의 구현체인 Hibernate에서 프록시 기능의 한계로 인해 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프록시 객체를 만들기 위해서는 연관 객체에 값이 있는지 없는지 알아야 한다. ShelterUser는 Shelter의 FK를 지니고 있지만, Shelter는 ShelterUser의 값을 확인하기 위해서는 무조건 ShelterUser를 조회해야 한다. 결과적으로 ShelterUser 조회 쿼리가 발생하기 때문에 Hibernate는 프록시 객체를 만들 필요가 없어진다. 따라서 지연 로딩으로 설정해도 동작하지 않는 것이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;해결 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우 Shelter를 통해 ShelterUser를 조회할 필요가 없기에, 1:1 단방향 연관관계로 수정하여 쿼리가 두 번 나가는 문제를 해결할 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wave1994.tistory.com/156&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://wave1994.tistory.com/156&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1691821215129&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring boot :: JPA에서 OneToOne 관계 N+1 문제 정리&quot; data-og-description=&quot;이번에 간단하게 Entity를 조회하는 API인데 성능이 생각보다 안좋은 이슈를 발견하게 되었다. 그래서 Entity를 확인해보니 OneToOne 관계를 사용하고 있었고 로그를 확인 했을 때 쿼리가 한 번이 발생&quot; data-og-host=&quot;wave1994.tistory.com&quot; data-og-source-url=&quot;https://wave1994.tistory.com/156&quot; data-og-url=&quot;https://wave1994.tistory.com/156&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/i4E03/hyTCJJKXI5/G7PdffkkK7UCkWzkqfuru1/img.png?width=800&amp;amp;height=451&amp;amp;face=0_0_800_451,https://scrap.kakaocdn.net/dn/cUlbhw/hyTCDbHBOQ/SJ83CG2GvD59KIKjG8oeu1/img.png?width=800&amp;amp;height=451&amp;amp;face=0_0_800_451,https://scrap.kakaocdn.net/dn/bJZw1D/hyTCA67ZbP/1yo39V0W1LBZFZrQKvSEFK/img.png?width=1378&amp;amp;height=794&amp;amp;face=0_0_1378_794&quot;&gt;&lt;a href=&quot;https://wave1994.tistory.com/156&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://wave1994.tistory.com/156&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/i4E03/hyTCJJKXI5/G7PdffkkK7UCkWzkqfuru1/img.png?width=800&amp;amp;height=451&amp;amp;face=0_0_800_451,https://scrap.kakaocdn.net/dn/cUlbhw/hyTCDbHBOQ/SJ83CG2GvD59KIKjG8oeu1/img.png?width=800&amp;amp;height=451&amp;amp;face=0_0_800_451,https://scrap.kakaocdn.net/dn/bJZw1D/hyTCA67ZbP/1yo39V0W1LBZFZrQKvSEFK/img.png?width=1378&amp;amp;height=794&amp;amp;face=0_0_1378_794');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring boot :: JPA에서 OneToOne 관계 N+1 문제 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번에 간단하게 Entity를 조회하는 API인데 성능이 생각보다 안좋은 이슈를 발견하게 되었다. 그래서 Entity를 확인해보니 OneToOne 관계를 사용하고 있었고 로그를 확인 했을 때 쿼리가 한 번이 발생&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wave1994.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Coding</category>
      <author>솔묘</author>
      <guid isPermaLink="true">https://intothemaze.tistory.com/17</guid>
      <comments>https://intothemaze.tistory.com/17#entry17comment</comments>
      <pubDate>Fri, 11 Aug 2023 18:42:42 +0900</pubDate>
    </item>
    <item>
      <title>[Sentry] Exception Handler를 거치기 전 Sentry로 에러 전달 및 Exception 필터링 (Spring Boot)</title>
      <link>https://intothemaze.tistory.com/16</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://intothemaze.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://intothemaze.tistory.com/14&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1691560486224&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;[Sentry] Spring Boot에 Sentry 적용하기&quot; data-og-description=&quot;Sentry Sentry는 오픈소스 기반의 에러 트래킹 및 로깅 도구로, 애플리케이션에서 발생하는 버그, 예외, 성능 문제 등을 감지하고 이를 기록하여 개발자들에게 제공한다. 구독형인 클라우드 버전과 &quot; data-og-host=&quot;intothemaze.tistory.com&quot; data-og-source-url=&quot;https://intothemaze.tistory.com/14&quot; data-og-url=&quot;https://intothemaze.tistory.com/14&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b3WsCn/hyTBwcl4ya/xDFLRKJ0C0x3HhT8pU1C3K/img.png?width=800&amp;amp;height=186&amp;amp;face=0_0_800_186,https://scrap.kakaocdn.net/dn/co6DLt/hyTBvLhEBD/XxfyTmEsKMdkSrlPTKWSqK/img.png?width=800&amp;amp;height=186&amp;amp;face=0_0_800_186,https://scrap.kakaocdn.net/dn/pj5hq/hyTAx4ZDKu/M5oaTSzJh7bSKLzme2aAa1/img.png?width=1835&amp;amp;height=792&amp;amp;face=0_0_1835_792&quot;&gt;&lt;a href=&quot;https://intothemaze.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://intothemaze.tistory.com/14&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b3WsCn/hyTBwcl4ya/xDFLRKJ0C0x3HhT8pU1C3K/img.png?width=800&amp;amp;height=186&amp;amp;face=0_0_800_186,https://scrap.kakaocdn.net/dn/co6DLt/hyTBvLhEBD/XxfyTmEsKMdkSrlPTKWSqK/img.png?width=800&amp;amp;height=186&amp;amp;face=0_0_800_186,https://scrap.kakaocdn.net/dn/pj5hq/hyTAx4ZDKu/M5oaTSzJh7bSKLzme2aAa1/img.png?width=1835&amp;amp;height=792&amp;amp;face=0_0_1835_792');&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;[Sentry] Spring Boot에 Sentry 적용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Sentry Sentry는 오픈소스 기반의 에러 트래킹 및 로깅 도구로, 애플리케이션에서 발생하는 버그, 예외, 성능 문제 등을 감지하고 이를 기록하여 개발자들에게 제공한다. 구독형인 클라우드 버전과&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;intothemaze.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://intothemaze.tistory.com/15&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://intothemaze.tistory.com/15&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1691560498565&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;[Sentry] Sentry Slack 연동 및 Logging 레벨 설정 (Spring Boot)&quot; data-og-description=&quot;지난 포스팅과 연결 됩니다. https://intothemaze.tistory.com/14 [Sentry] Spring Boot에 Sentry 적용하기 Sentry Sentry는 오픈소스 기반의 에러 트래킹 및 로깅 도구로, 애플리케이션에서 발생하는 버그, 예외, 성능&quot; data-og-host=&quot;intothemaze.tistory.com&quot; data-og-source-url=&quot;https://intothemaze.tistory.com/15&quot; data-og-url=&quot;https://intothemaze.tistory.com/15&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cu9b3U/hyTBIqjZHu/z7wHWNzgN7bBVuOckFdbUK/img.png?width=800&amp;amp;height=254&amp;amp;face=0_0_800_254,https://scrap.kakaocdn.net/dn/c24IhQ/hyTBAshS30/0NVnxFZOft4JHAkReWp8G1/img.png?width=800&amp;amp;height=254&amp;amp;face=0_0_800_254,https://scrap.kakaocdn.net/dn/nGqJH/hyTAtauT1o/ISKOXzR3EfIGZPK1cor8g0/img.png?width=1572&amp;amp;height=851&amp;amp;face=0_0_1572_851&quot;&gt;&lt;a href=&quot;https://intothemaze.tistory.com/15&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://intothemaze.tistory.com/15&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cu9b3U/hyTBIqjZHu/z7wHWNzgN7bBVuOckFdbUK/img.png?width=800&amp;amp;height=254&amp;amp;face=0_0_800_254,https://scrap.kakaocdn.net/dn/c24IhQ/hyTBAshS30/0NVnxFZOft4JHAkReWp8G1/img.png?width=800&amp;amp;height=254&amp;amp;face=0_0_800_254,https://scrap.kakaocdn.net/dn/nGqJH/hyTAtauT1o/ISKOXzR3EfIGZPK1cor8g0/img.png?width=1572&amp;amp;height=851&amp;amp;face=0_0_1572_851');&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;[Sentry] Sentry Slack 연동 및 Logging 레벨 설정 (Spring Boot)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;지난 포스팅과 연결 됩니다. https://intothemaze.tistory.com/14 [Sentry] Spring Boot에 Sentry 적용하기 Sentry Sentry는 오픈소스 기반의 에러 트래킹 및 로깅 도구로, 애플리케이션에서 발생하는 버그, 예외, 성능&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;intothemaze.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sentry 설정의 (아마도) 마지막 포스팅은&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. ControllerAdvice에 의해 전역 예외 처리되기 전에 Exception을 Sentry로 전송&lt;br /&gt;2. 해당 예외를 Custom 필터링 한 후 Sentry에 전달&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 가지에 대해 알아보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SentryExceptionResolver&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sentry는 ExceptionHandler에 의해 예외가 처리되는 경우 Sentry로 예외를 보내지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Exception이 처리 되어도 로깅 및 모니터링이 필요한 예외가 있을 수 있기 때문에 ExceptionHandler 앞에 SentryExceptionResolver를 두어 모든 예외를 Sentry로 전달할 수 있다.&lt;/p&gt;
&lt;blockquote id=&quot;recording-exceptions&quot; data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Recording Exceptions&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;In order to record all exceptions thrown by your controllers, you can register io.sentry.spring.SentryExceptionResolver as a Bean in your application. Once registered, all exceptions will be sent to Sentry and then passed on to the default exception handlers.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1691558796236&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class SentryConfig {
  @Bean
  public HandlerExceptionResolver resolveSentryException() {
    return new SentryExceptionResolver();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1691558213056&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SentryExceptionResolver implements HandlerExceptionResolver, Ordered {
  
  @Override
  public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
                                       Object handler, Exception ex) {
    Sentry.captureException(ex);

    // null = run other HandlerExceptionResolvers to actually handle the exception
    return null;
  }

  @Override
  public int getOrder() {
    return Integer.MIN_VALUE;
  }
}&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;Sentry.captureException(): Exception 발생 시 Sentry로 전송한다.&lt;/li&gt;
&lt;li&gt;getOrder(): 값이 작을수록 우선순위가 높게 설정된다. Integer.MIN_VALUE를 반환함으로써 기존 Exception Handler 보다 먼저 실행되도록 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CustomBeforeSendCallback&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;SentryExceptionResolver를 구현한 건 좋지만, 모든 예외를 로깅 및 모니터링 하고 싶은 건 아닌데 &lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럴 때는 Sentry의 BeforeSendCallback를 구현하여 Custome 필터링 기능을 추가하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;beforeSend는 이벤트가 서버로 전송되기 바로 전에 호출되며, 데이터를 편집할 수 있는 마지막 단계이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 커스텀 필터링 로직을 추가하여 SentryExceptionResolver에 의해 전달된 예외를 필터링 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Using&amp;nbsp;beforeSend&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;All Sentry SDKs support the&amp;nbsp;beforeSend&amp;nbsp;callback method.&amp;nbsp;beforeSend&amp;nbsp;is called immediately before the event is sent to the server, so it&amp;rsquo;s the final place where you can edit its data. It receives the event object as a parameter, so you can use that to modify the event&amp;rsquo;s data or drop it completely (by returning&amp;nbsp;null) based on custom logic and the data available on the event.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1691559331993&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import io.sentry.SentryEvent;
import io.sentry.SentryOptions;
import io.sentry.Hint
import org.springframework.stereotype.Component;

@Component
public class CustomBeforeSendCallback implements SentryOptions.BeforeSendCallback {
  @Override
  public SentryEvent execute(SentryEvent event, Hint hint) {
    if (event.getThrowable() instanceof SQLException) {
      event.setFingerprints(Arrays.asList(&quot;database-connection-error&quot;));
    }
    return event;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sentry 공식 문서에 나와 있는 위의 코드를 참고하여 아래와 같이 작성하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1691559478329&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class CustomBeforeSendCallback implements SentryOptions.BeforeSendCallback {
  @Override
  public SentryEvent execute(SentryEvent event, Hint hint) {
    if (event.getThrowable() instanceof NotFoundException) {
      return null;
    }

    return event;
  }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버로 전송하지 않을 예외들을 필터링하는 Blacklist 방식의 코드이다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.sentry.io/platforms/java/guides/spring/configuration/filtering/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.sentry.io/platforms/java/guides/spring/configuration/filtering/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1691560154258&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;Filtering for Spring&quot; data-og-description=&quot;Learn more about how to configure your SDK to filter events reported to Sentry.&quot; data-og-host=&quot;docs.sentry.io&quot; data-og-source-url=&quot;https://docs.sentry.io/platforms/java/guides/spring/configuration/filtering/&quot; data-og-url=&quot;https://docs.sentry.io/platforms/java/guides/spring/configuration/filtering/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cFyq8T/hyTBIX86PT/9rMW50nRLMiWJEywfZfF30/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/tOD8h/hyTABGmrz7/75fcVReuCS2CgDdDEmxHXk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://docs.sentry.io/platforms/java/guides/spring/configuration/filtering/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.sentry.io/platforms/java/guides/spring/configuration/filtering/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cFyq8T/hyTBIX86PT/9rMW50nRLMiWJEywfZfF30/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/tOD8h/hyTABGmrz7/75fcVReuCS2CgDdDEmxHXk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&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;Filtering for Spring&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn more about how to configure your SDK to filter events reported to Sentry.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.sentry.io&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://www.tabnine.com/code/java/methods/io.sentry.Sentry/capture&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.tabnine.com/code/java/methods/io.sentry.Sentry/capture&lt;/a&gt;&lt;/p&gt;</description>
      <category>Etc.</category>
      <author>솔묘</author>
      <guid isPermaLink="true">https://intothemaze.tistory.com/16</guid>
      <comments>https://intothemaze.tistory.com/16#entry16comment</comments>
      <pubDate>Wed, 9 Aug 2023 14:49:53 +0900</pubDate>
    </item>
    <item>
      <title>[Sentry] Sentry Slack 연동 및 Logging 레벨 설정 (Spring Boot)</title>
      <link>https://intothemaze.tistory.com/15</link>
      <description>&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://intothemaze.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://intothemaze.tistory.com/14&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1691391574582&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;[Sentry] Spring Boot에 Sentry 적용하기&quot; data-og-description=&quot;Sentry Sentry는 오픈소스 기반의 에러 트래킹 및 로깅 도구로, 애플리케이션에서 발생하는 버그, 예외, 성능 문제 등을 감지하고 이를 기록하여 개발자들에게 제공한다. 구독형인 클라우드 버전과 &quot; data-og-host=&quot;intothemaze.tistory.com&quot; data-og-source-url=&quot;https://intothemaze.tistory.com/14&quot; data-og-url=&quot;https://intothemaze.tistory.com/14&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bJjgIR/hyTzie08yH/rVKAUkouZre3d62KDsrYd0/img.png?width=800&amp;amp;height=186&amp;amp;face=0_0_800_186,https://scrap.kakaocdn.net/dn/bFKy1n/hyTzkjBRg5/khKbJ8VzKQvGpDs6RcsUvK/img.png?width=800&amp;amp;height=186&amp;amp;face=0_0_800_186,https://scrap.kakaocdn.net/dn/bqzXnm/hyTy8QY6bc/on0HYp4OvRffRZwRGWiFX0/img.png?width=1835&amp;amp;height=792&amp;amp;face=0_0_1835_792&quot;&gt;&lt;a href=&quot;https://intothemaze.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://intothemaze.tistory.com/14&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bJjgIR/hyTzie08yH/rVKAUkouZre3d62KDsrYd0/img.png?width=800&amp;amp;height=186&amp;amp;face=0_0_800_186,https://scrap.kakaocdn.net/dn/bFKy1n/hyTzkjBRg5/khKbJ8VzKQvGpDs6RcsUvK/img.png?width=800&amp;amp;height=186&amp;amp;face=0_0_800_186,https://scrap.kakaocdn.net/dn/bqzXnm/hyTy8QY6bc/on0HYp4OvRffRZwRGWiFX0/img.png?width=1835&amp;amp;height=792&amp;amp;face=0_0_1835_792');&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;[Sentry] Spring Boot에 Sentry 적용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Sentry Sentry는 오픈소스 기반의 에러 트래킹 및 로깅 도구로, 애플리케이션에서 발생하는 버그, 예외, 성능 문제 등을 감지하고 이를 기록하여 개발자들에게 제공한다. 구독형인 클라우드 버전과&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;intothemaze.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Sentry Logging Level 설정&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;application.yml에서 Sentry의 Logging Level을 WARN으로 설정해줄 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;sentry:
  dsn: {dsn}
  logging:
    enabled: true
    minimum-event-level: warn&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Slack 연동&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1861&quot; data-origin-height=&quot;592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pI1lC/btsqrNVwhpy/kGgvS5kIVjGPhOcdsSZLr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pI1lC/btsqrNVwhpy/kGgvS5kIVjGPhOcdsSZLr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pI1lC/btsqrNVwhpy/kGgvS5kIVjGPhOcdsSZLr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpI1lC%2FbtsqrNVwhpy%2FkGgvS5kIVjGPhOcdsSZLr0%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;1861&quot; height=&quot;592&quot; data-origin-width=&quot;1861&quot; data-origin-height=&quot;592&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1867&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FlxyO/btsqodAehUi/KNLaKBg0uYvqDPMsf5kei0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FlxyO/btsqodAehUi/KNLaKBg0uYvqDPMsf5kei0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FlxyO/btsqodAehUi/KNLaKBg0uYvqDPMsf5kei0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFlxyO%2FbtsqodAehUi%2FKNLaKBg0uYvqDPMsf5kei0%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;1867&quot; height=&quot;620&quot; data-origin-width=&quot;1867&quot; data-origin-height=&quot;620&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;Settings &amp;gt; Integrations &amp;gt; Slack &amp;gt; Add Workspace&lt;/li&gt;
&lt;li&gt;Slack 워크스페이스 URL 입력 후 메일로 전송되는 Slack Code를 입력하면 연동된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dj2XKr/btsqttWxooc/rVKj6VqOHu8gQaECtgqLTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dj2XKr/btsqttWxooc/rVKj6VqOHu8gQaECtgqLTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dj2XKr/btsqttWxooc/rVKj6VqOHu8gQaECtgqLTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdj2XKr%2FbtsqttWxooc%2FrVKj6VqOHu8gQaECtgqLTk%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;1896&quot; height=&quot;568&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;851&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b95mdY/btsqfzRoXvq/RA7eMdEtnDRn9niWZfdhA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b95mdY/btsqfzRoXvq/RA7eMdEtnDRn9niWZfdhA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b95mdY/btsqfzRoXvq/RA7eMdEtnDRn9niWZfdhA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb95mdY%2FbtsqfzRoXvq%2FRA7eMdEtnDRn9niWZfdhA0%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;1572&quot; height=&quot;851&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;851&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1196&quot; data-origin-height=&quot;886&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmRGfd/btsqrDkVbfQ/WW6NknAqEKOD46fWWK5ILk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmRGfd/btsqrDkVbfQ/WW6NknAqEKOD46fWWK5ILk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmRGfd/btsqrDkVbfQ/WW6NknAqEKOD46fWWK5ILk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmRGfd%2FbtsqrDkVbfQ%2FWW6NknAqEKOD46fWWK5ILk%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;1196&quot; height=&quot;886&quot; data-origin-width=&quot;1196&quot; data-origin-height=&quot;886&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;Alerts &amp;gt; Create Alert &amp;gt; Set Conditions&lt;/li&gt;
&lt;li&gt;Slack Workspace 이름과 채널 이름을 순서대로 기입해주면 된다. (예. bemate, #개발-backend)&lt;/li&gt;
&lt;li&gt;IF는 알림 조건을 설정할 수 있는데, The event's level is greater than or equal to warning을 설정하여 이벤트 레벨이 warning 이상이면 Slack으로 알림을 보내도록 하였다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;+&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 Sentry 연동을 위해서는 유료 버전의 플랜을 요구하며, 무료 체험 시 30일 뒤 invalid 된다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Etc.</category>
      <author>솔묘</author>
      <guid isPermaLink="true">https://intothemaze.tistory.com/15</guid>
      <comments>https://intothemaze.tistory.com/15#entry15comment</comments>
      <pubDate>Mon, 7 Aug 2023 16:27:35 +0900</pubDate>
    </item>
    <item>
      <title>[Sentry] Spring Boot에 Sentry 적용하기</title>
      <link>https://intothemaze.tistory.com/14</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Sentry&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sentry는 오픈소스 기반의 에러 트래킹 및 로깅 도구로, 애플리케이션에서 발생하는 버그, 예외, 성능 문제 등을 감지하고 이를 기록하여 개발자들에게 제공한다. 구독형인 클라우드 버전과 설치형인 온 프레미스 버전으로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 기능&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;에러 모니터링: 애플리케이션에서 발생하는 에러와 예외를 실시간으로 모니터링하여 개발자들에게 알린다. 이를 통해 빠르게 에러에 대처할 수 있고 사용자들의 문제를 사전에 감지할 수 있다.&lt;/li&gt;
&lt;li&gt;스택 추적(Stack Trace): 에러가 발생한 위치를 포함한 스택 추적 정보를 제공하여, 어떤 코드에서 에러가 발생했는지를 파악할 수 있다. 이를 통해 디버깅과 문제 해결을 용이하게 한다.&lt;/li&gt;
&lt;li&gt;사용자 정보: 에러가 발생한 사용자의 정보(환경, 브라우저, 디바이스 등)를 수집하여, 특정 환경에서 에러가 발생하는 경우를 파악할 수 있다.&lt;/li&gt;
&lt;li&gt;통합 알림: 에러가 발생할 경우, 이를 이메일, 메시지, 슬랙 등 다양한 방법으로 개발자들에게 알림을 보내준다.&lt;/li&gt;
&lt;li&gt;이벤트 기록: 애플리케이션에서 발생하는 다양한 이벤트와 로그를 기록하고 추적하여 애플리케이션 동작을 파악하고 문제점을 파악할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;선택 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 굵직한 테크 기업들이 많이 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;339&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HUNsP/btso8yS2YTt/jnpYDRDK8K3ICJOVBnccrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HUNsP/btso8yS2YTt/jnpYDRDK8K3ICJOVBnccrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HUNsP/btso8yS2YTt/jnpYDRDK8K3ICJOVBnccrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHUNsP%2Fbtso8yS2YTt%2FjnpYDRDK8K3ICJOVBnccrk%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;1452&quot; height=&quot;339&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 다양한 프로그래밍 언어와 프레임워크를 지원한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1575&quot; data-origin-height=&quot;623&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/phx3y/btspsEZ2jRp/f39Zc7wdE4kebf4K4jRzxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/phx3y/btspsEZ2jRp/f39Zc7wdE4kebf4K4jRzxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/phx3y/btspsEZ2jRp/f39Zc7wdE4kebf4K4jRzxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fphx3y%2FbtspsEZ2jRp%2Ff39Zc7wdE4kebf4K4jRzxK%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;1575&quot; height=&quot;623&quot; data-origin-width=&quot;1575&quot; data-origin-height=&quot;623&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 프로젝트 리드 분이 현 회사에서 사용 중인 툴이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 세 번째가 이유가 가장 컸는데, 에러 로그 모니터링 도구 사용은 처음이라 다양한 도구들을 비교 후 선택하기에는 무리가 있어서 선배 개발자가 현 실무에서 사용 중인 툴을 선택하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Sentry를 Spring Boot에 연동하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sentry 연동 시 요구사항은 두 가지였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 5XX 에러 및 로그 레벨 WARN을 Sentry로 전송&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Sentry로 전송된 issue들은 Slack 알림으로 발송&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 &lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;우선&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;기본적인&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;틀을&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;만들고&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;세부적으로&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;어떤&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;예외들을&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;추가할지는&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1c1d; text-align: left;&quot;&gt;&lt;span&gt; 추후 의논하기로 했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #1d1c1d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;1. Sentry 프로젝트 생성&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1d1c1d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;a href=&quot;https://sentry.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sentry.io/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1690868523031&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;Application Performance Monitoring &amp;amp; Error Tracking Software&quot; data-og-description=&quot;Self-hosted and cloud-based application performance monitoring &amp;amp; error tracking that helps software teams see clearer, solve quicker, &amp;amp; learn continuously.&quot; data-og-host=&quot;sentry.io&quot; data-og-source-url=&quot;https://sentry.io/&quot; data-og-url=&quot;https://sentry.io/welcome/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/GPMrU/hyTu9brUOq/k091Ciz6MrKDuJ2UOKj130/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://sentry.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://sentry.io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/GPMrU/hyTu9brUOq/k091Ciz6MrKDuJ2UOKj130/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;Application Performance Monitoring &amp;amp; Error Tracking Software&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Self-hosted and cloud-based application performance monitoring &amp;amp; error tracking that helps software teams see clearer, solve quicker, &amp;amp; learn continuously.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;sentry.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccXjKL/btspxpIlnp5/NkkWD3tafLCmzkXJVWQ0yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccXjKL/btspxpIlnp5/NkkWD3tafLCmzkXJVWQ0yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccXjKL/btspxpIlnp5/NkkWD3tafLCmzkXJVWQ0yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccXjKL%2FbtspxpIlnp5%2FNkkWD3tafLCmzkXJVWQ0yk%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;1774&quot; height=&quot;680&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;680&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;Sentry 회원가입 후 왼쪽 사이드바에 Projects-Create project&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1601&quot; data-origin-height=&quot;793&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wTYme/btspBAXeVKx/3Xh3GW5Q4oKdsM5yXDkek1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wTYme/btspBAXeVKx/3Xh3GW5Q4oKdsM5yXDkek1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wTYme/btspBAXeVKx/3Xh3GW5Q4oKdsM5yXDkek1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwTYme%2FbtspBAXeVKx%2F3Xh3GW5Q4oKdsM5yXDkek1%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;1601&quot; height=&quot;793&quot; data-origin-width=&quot;1601&quot; data-origin-height=&quot;793&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;Popular &amp;gt; Spring Boot   Alert me on every new issue   sentry-project   Create Project&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Gradle&lt;/h4&gt;
&lt;pre class=&quot;clean&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;&lt;code&gt;implementation 'io.sentry:sentry-spring-boot-starter:6.27.0'
implementation 'io.sentry:sentry-logback:6.27.0'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. application.yml&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1835&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AJBnf/btspBzRFJGJ/GRbfg4IyIB3IU9ZUoKoKy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AJBnf/btspBzRFJGJ/GRbfg4IyIB3IU9ZUoKoKy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AJBnf/btspBzRFJGJ/GRbfg4IyIB3IU9ZUoKoKy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAJBnf%2FbtspBzRFJGJ%2FGRbfg4IyIB3IU9ZUoKoKy0%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;1835&quot; height=&quot;792&quot; data-origin-width=&quot;1835&quot; data-origin-height=&quot;792&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;Settings &amp;gt; Projects &amp;gt; sentry-project &amp;gt; Client Keys (DSN)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;sentry:
  dsn: {dsn}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. logback-spring.xml&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;resources 아래에 logback 설정 파일 추가&lt;/li&gt;
&lt;li&gt;Console에 로그를 어떻게 출력할 것인지, Sentry 로그 레벨, &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;local과 prod 환경에서의 로그 레벨 등을 설정&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;dust&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;
&amp;lt;configuration&amp;gt;
    &amp;lt;!-- Configure the Console appender --&amp;gt;
    &amp;lt;appender name=&quot;Console&quot; class=&quot;ch.qos.logback.core.ConsoleAppender&quot;&amp;gt;
        &amp;lt;encoder&amp;gt;
            &amp;lt;pattern&amp;gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&amp;lt;/pattern&amp;gt;
        &amp;lt;/encoder&amp;gt;
    &amp;lt;/appender&amp;gt;

    &amp;lt;!-- Configure the Sentry appender, overriding the logging threshold to the WARN level --&amp;gt;
    &amp;lt;appender name=&quot;Sentry&quot; class=&quot;io.sentry.logback.SentryAppender&quot;&amp;gt;
        &amp;lt;filter class=&quot;ch.qos.logback.classic.filter.ThresholdFilter&quot;&amp;gt;
            &amp;lt;level&amp;gt;WARN&amp;lt;/level&amp;gt;
        &amp;lt;/filter&amp;gt;
        &amp;lt;!-- Optionally add an encoder --&amp;gt;
        &amp;lt;encoder&amp;gt;
            &amp;lt;pattern&amp;gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&amp;lt;/pattern&amp;gt;
        &amp;lt;/encoder&amp;gt;
    &amp;lt;/appender&amp;gt;

    &amp;lt;!-- Enable the Console and Sentry appenders, Console is provided as an example
    of a non-Sentry logger that is set to a different logging threshold --&amp;gt;
    &amp;lt;springProfile name=&quot;local&quot;&amp;gt;
        &amp;lt;root level=&quot;INFO&quot;&amp;gt;
            &amp;lt;appender-ref ref=&quot;Console&quot;/&amp;gt;
        &amp;lt;/root&amp;gt;
    &amp;lt;/springProfile&amp;gt;

    &amp;lt;springProfile name=&quot;prod&quot;&amp;gt;
        &amp;lt;root level=&quot;INFO&quot;&amp;gt;
            &amp;lt;appender-ref ref=&quot;Console&quot;/&amp;gt;
            &amp;lt;appender-ref ref=&quot;Sentry&quot;/&amp;gt;
        &amp;lt;/root&amp;gt;
    &amp;lt;/springProfile&amp;gt;
&amp;lt;/configuration&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;The Sentry SDK는 추가적인 설정이나 처리 없이 애플리케이션 실행 중 발생하는 모든 Unhandled Exceptions를 자동으로 감지하고 보고한다.&lt;/i&gt; &lt;i&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 설정이 default인 것으로 알고 있는데, 다시 말하자면 ControllerAdvice를 통해 전역 예외를 처리하는 경우에는 에러가 감지되지 않고 패스 된다. 나는 500번대 서버 에러만 모니터링 할 예정이기에 이것으로 모든 설정이 끝이 났다.&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 widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1609&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfwRCi/btspE5bDWDK/hIO9CznyMHznU5Mi1OFakK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfwRCi/btspE5bDWDK/hIO9CznyMHznU5Mi1OFakK/img.png&quot; data-alt=&quot;테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfwRCi/btspE5bDWDK/hIO9CznyMHznU5Mi1OFakK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfwRCi%2FbtspE5bDWDK%2FhIO9CznyMHznU5Mi1OFakK%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;1609&quot; height=&quot;341&quot; data-origin-width=&quot;1609&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;Sentry와 Slack 연동 방법 및 WARN 로그 레벨 알림 전송은 다음 포스팅에서 다루겠다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Etc.</category>
      <author>솔묘</author>
      <guid isPermaLink="true">https://intothemaze.tistory.com/14</guid>
      <comments>https://intothemaze.tistory.com/14#entry14comment</comments>
      <pubDate>Tue, 1 Aug 2023 15:45:01 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] Failed to create query for method public abstract. No property found for type.</title>
      <link>https://intothemaze.tistory.com/13</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;에러 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #000000; color: #ffffff;&quot;&gt;Caused&amp;nbsp;by:&amp;nbsp;java.lang.IllegalArgumentException:&amp;nbsp;Failed&amp;nbsp;to&amp;nbsp;create&amp;nbsp;query&amp;nbsp;for&amp;nbsp;method&amp;nbsp;public&amp;nbsp;abstract&amp;nbsp;org.springframework.data.domain.Page&amp;nbsp;co&amp;nbsp;m.bemate.domain.shelter.repository.PetRepository.findByShelterNo(com.bemate.domain.shelter.entity.Shelter,org.springframework.data.domain.Pageable);&amp;nbsp;No&amp;nbsp;property&amp;nbsp;'no'&amp;nbsp;found&amp;nbsp;for&amp;nbsp;type&amp;nbsp;'Shelter';&amp;nbsp;Did&amp;nbsp;you&amp;nbsp;mean&amp;nbsp;'id';&amp;nbsp;Traversed&amp;nbsp;path:&amp;nbsp;Pet.shelter&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1690439021966&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface PetRepository extends JpaRepository&amp;lt;Pet, String&amp;gt;, PetRepositoryCustom {
    Page&amp;lt;Pet&amp;gt; findByShelterNo(Shelter shelter, Pageable pageable);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우 Shelter 엔티티를 쿼리 파라미터로 전달했는데, JPQL에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기본 키 값(ex.shelter_no)을 사용한다. 즉, 아래와 같이 SQL이 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1690439809283&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select
    (생략)
from
    pet p1_0 
where
    p1_0.shelter_no=? offset ? rows fetch first ? rows only&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 메서드 이름 또한 findByShelterNo로 명명했는데, 이게 문제가 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Data JPA에서는 인터페이스에 findBy 접두어를 사용한 뒤, 검색 대상 필드의 이름을 CamelCase로 이어붙이면 메서드의 이름으로 적절한 JPQL 쿼리를 생성해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shelter 엔티티를 사용해서 조회하는데, 메서드 명은 findByShelterNo이니, &lt;span style=&quot;background-color: #000000; color: #ffffff;&quot;&gt;No property 'no' found for type 'Shelter'&lt;/span&gt; 와 같은 오류가 발생한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 방법&lt;/h3&gt;
&lt;pre id=&quot;code_1690440223710&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface PetRepository extends JpaRepository&amp;lt;Pet, String&amp;gt;, PetRepositoryCustom {
    Page&amp;lt;Pet&amp;gt; findByShelter(Shelter shelter, Pageable pageable);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 명을 findByShelterNo -&amp;gt; findByShelter로 바꾸어주니 바로 해결되었다.&lt;/p&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;  앞으로 JpaRepository를 상속 받는 인터페이스를 사용할 때는 자동으로 쿼리가 만들어짐을 유의하여 메서드 이름을 정해야겠다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Troubleshooting</category>
      <author>솔묘</author>
      <guid isPermaLink="true">https://intothemaze.tistory.com/13</guid>
      <comments>https://intothemaze.tistory.com/13#entry13comment</comments>
      <pubDate>Thu, 27 Jul 2023 15:47:51 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] String Pool과 불변성</title>
      <link>https://intothemaze.tistory.com/12</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;String과 불변성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서 String은 immutable, 즉 불변 객체이다. 불변(immutable)하다는 것은 문자열이 일단 생성되면 그 값이 고정되어 있으며, 변경되더라도 원래의 문자열 내용이 변경되는 것이 아닌 새로운 문자열 객체가 생성됨을 의미한다.&lt;/p&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_1689313887231&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String str1 = &quot;Hello&quot;;
String str2 = str1;
str1 = str1 + &quot; World&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &quot;Hello&quot;라는 문자열이 먼저 생성되고, 변수 str1과 str2가 이 문자열을 참조한다. 그리고 str1에 &quot; World&quot;를 추가한 새로운 문자열을 할당한다.&lt;br /&gt;&lt;br /&gt;하지만 문자열은 변경할 수 없으므로, 실제로는 새로운 문자열이 생성되고 str1은 이 새로운 문자열을 참조하게 된다. str2는 여전히 &quot;Hello&quot;라는 원래의 문자열을 참조하고 있다. 원래의 문자열은 변경되지 않고 그대로 유지된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;❗&lt;/span&gt;이러한 String의 불변성은 문자열 풀(String Pool)로 인해 이뤄질 수 있다.❗&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;String Pool&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1871&quot; data-origin-height=&quot;1116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dffmOs/btsnDx14Jzy/KBJrG1uxIrDmUbL8JK7qG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dffmOs/btsnDx14Jzy/KBJrG1uxIrDmUbL8JK7qG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dffmOs/btsnDx14Jzy/KBJrG1uxIrDmUbL8JK7qG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdffmOs%2FbtsnDx14Jzy%2FKBJrG1uxIrDmUbL8JK7qG1%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;694&quot; height=&quot;414&quot; data-origin-width=&quot;1871&quot; data-origin-height=&quot;1116&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;문자열 리터럴(String literal)이 저장되는 공간으로, 자바의 Heap 메모리 내에 위치한다.&lt;/li&gt;
&lt;li&gt;문자열 상수 풀(String Constant Pool) 또는 문자열 인턴 풀(String Intern Pool)로도 불린다.&lt;/li&gt;
&lt;li&gt;문자열 리터럴이 생성될 때마다 JVM은 새로운 문자열 객체를 생성하기 전에 먼저 String Pool을 확인한다.&lt;/li&gt;
&lt;li&gt;이미 String Pool에 새로운 문자열이 존재할 경우 새로운 String 객체를 생성하지 않고, 이미 존재하는 문자열 참조를 변수에 할당한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;new String()&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;new 키워드를 사용하여 새로운 문자열을 생성할 경우 String Pool 외부에 있는 자바 힙 메모리에 메모리가 할당된다.&lt;/li&gt;
&lt;li&gt;이러한 방식으로 생성된 문자열 객체들은 String Pool과는 독립적으로 메모리를 사용하며, String Pool 내에 동일한 값을 가진 문자열 리터럴과는 다른 객체이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;String Pool을 통해 문자열의 캐싱과 재사용이 가능해지므로, 문자열을 자주 생성하고 변경하는 경우에도 효율적으로 메모리를 관리할 수 있다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Study</category>
      <author>솔묘</author>
      <guid isPermaLink="true">https://intothemaze.tistory.com/12</guid>
      <comments>https://intothemaze.tistory.com/12#entry12comment</comments>
      <pubDate>Fri, 14 Jul 2023 15:47:43 +0900</pubDate>
    </item>
  </channel>
</rss>