<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>wms's Programming&amp;amp;Study</title>
    <link>https://wms0603.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 10 Apr 2026 12:01:00 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>wms2275</managingEditor>
    <image>
      <title>wms's Programming&amp;amp;Study</title>
      <url>https://tistory1.daumcdn.net/tistory/3261097/attach/af9de7c0dde844daa87accf72f13672a</url>
      <link>https://wms0603.tistory.com</link>
    </image>
    <item>
      <title>MyBatis에서 동적 SQL 작성 팁</title>
      <link>https://wms0603.tistory.com/65</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서&amp;nbsp;자주&amp;nbsp;쓰는&amp;nbsp;&amp;lt;if&amp;gt;,&amp;nbsp;&amp;lt;choose&amp;gt;,&amp;nbsp;&amp;lt;foreach&amp;gt;&amp;nbsp;예제&amp;nbsp;중심 &lt;br /&gt;&lt;br /&gt;MyBatis를 쓰다 보면 동적 SQL을 작성할 일이 정말 많다. 특히 검색 조건이 많거나, 리스트를 반복해서 처리해야 할 때는 XML이 점점 복잡해진다. 내가 자주 쓰는 동적 SQL 태그들과, 유지보수하기 쉬운 작성 팁을 정리해봤다. &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. &amp;lt;if&amp;gt; 조건문&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예제&lt;/b&gt; &lt;/h4&gt;
&lt;pre id=&quot;code_1756442278078&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;select id=&quot;selectOrders&quot; resultType=&quot;Order&quot;&amp;gt;
  SELECT * FROM orders
  WHERE 1=1
  &amp;lt;if test=&quot;status != null&quot;&amp;gt;
    AND status = #{status}
  &amp;lt;/if&amp;gt;
  &amp;lt;if test=&quot;startDate != null&quot;&amp;gt;
    AND order_date &amp;amp;gt;= #{startDate}
  &amp;lt;/if&amp;gt;
&amp;lt;/select&amp;gt;&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;&lt;b&gt;  팁&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WHERE&amp;nbsp;1=1을&amp;nbsp;넣어두면&amp;nbsp;조건&amp;nbsp;추가가&amp;nbsp;편함 &lt;br /&gt;null&amp;nbsp;체크는&amp;nbsp;꼭&amp;nbsp;해줘야&amp;nbsp;함&amp;nbsp;(test=&quot;status&amp;nbsp;!=&amp;nbsp;null&quot;)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. &amp;lt;choose&amp;gt; 조건 분기&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1756442296215&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;select id=&quot;getUser&quot; resultType=&quot;User&quot;&amp;gt;
  SELECT * FROM users
  WHERE
  &amp;lt;choose&amp;gt;
    &amp;lt;when test=&quot;id != null&quot;&amp;gt;
      id = #{id}
    &amp;lt;/when&amp;gt;
    &amp;lt;when test=&quot;email != null&quot;&amp;gt;
      email = #{email}
    &amp;lt;/when&amp;gt;
    &amp;lt;otherwise&amp;gt;
      1 = 0
    &amp;lt;/otherwise&amp;gt;
  &amp;lt;/choose&amp;gt;
&amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;  팁&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러&amp;nbsp;조건&amp;nbsp;중&amp;nbsp;하나만&amp;nbsp;선택할&amp;nbsp;때&amp;nbsp;유용 &lt;br /&gt;otherwise는 예외 처리용으로 꼭 넣어두는 게 좋음&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. &amp;lt;foreach&amp;gt; 반복 처리&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1756442307907&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;select id=&quot;getOrdersByIds&quot; resultType=&quot;Order&quot;&amp;gt;
  SELECT * FROM orders
  WHERE order_id IN
  &amp;lt;foreach item=&quot;id&quot; collection=&quot;orderIds&quot; open=&quot;(&quot; separator=&quot;,&quot; close=&quot;)&quot;&amp;gt;
    #{id}
  &amp;lt;/foreach&amp;gt;
&amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  팁&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;open,&amp;nbsp;separator,&amp;nbsp;close&amp;nbsp;속성으로&amp;nbsp;SQL&amp;nbsp;포맷을&amp;nbsp;깔끔하게&amp;nbsp;유지 &lt;br /&gt;collection은 List, Set, 배열 모두 가능&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 유지보수하기 쉬운 XML 작성법&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  팁 정리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건문은&amp;nbsp;최대한&amp;nbsp;단순하게:&amp;nbsp;복잡한&amp;nbsp;로직은&amp;nbsp;Java에서&amp;nbsp;처리하고&amp;nbsp;XML은&amp;nbsp;깔끔하게 &lt;br /&gt;공통&amp;nbsp;조건은&amp;nbsp;SQL&amp;nbsp;Fragment로&amp;nbsp;분리:&amp;nbsp;&amp;lt;sql&amp;nbsp;id=&quot;commonWhere&quot;&amp;gt;로&amp;nbsp;재사용&amp;nbsp;가능 &lt;br /&gt;주석 적극 활용: XML은 가독성이 떨어지기 쉬우므로 주석으로 의도 명확히!!&lt;/p&gt;
&lt;pre id=&quot;code_1756442330504&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;sql id=&quot;commonWhere&quot;&amp;gt;
  WHERE is_deleted = 0
&amp;lt;/sql&amp;gt;

&amp;lt;select id=&quot;getActiveUsers&quot; resultType=&quot;User&quot;&amp;gt;
  SELECT * FROM users
  &amp;lt;include refid=&quot;commonWhere&quot;/&amp;gt;
&amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyBatis의 동적 SQL은 좋기도 하지만, 복잡해지면 유지보수가 어려워져서 &lt;br /&gt;실무에서는 조건 분기와 반복 처리를 잘 정리하고, 공통 SQL은 분리해서 관리하는 게 핵심이지 않을까 싶다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <author>wms2275</author>
      <guid isPermaLink="true">https://wms0603.tistory.com/65</guid>
      <comments>https://wms0603.tistory.com/65#entry65comment</comments>
      <pubDate>Fri, 29 Aug 2025 13:43:02 +0900</pubDate>
    </item>
    <item>
      <title>Oracle PL/SQL로 4천8백만 건 대용량 UPDATE 처리하기</title>
      <link>https://wms0603.tistory.com/64</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;color: #424242;&quot;&gt;운영 중인 시스템에서&amp;nbsp;&lt;/span&gt;고객&amp;nbsp;&lt;span style=&quot;color: #424242;&quot;&gt;테이블에 신규 컬럼을 추가한 뒤, 약&amp;nbsp;&lt;/span&gt;&lt;b&gt;48,000,000건의 데이터를 업데이트&lt;/b&gt;&lt;span style=&quot;color: #424242;&quot;&gt;해야 하는 상황이 생겼다. 단순한&amp;nbsp;&lt;/span&gt;UPDATE&lt;span style=&quot;color: #424242;&quot;&gt;&amp;nbsp;문으로 처리하기엔 너무 많은 데이터라&amp;nbsp;&lt;/span&gt;&lt;b&gt;성능 저하와 롤백 리스크&lt;/b&gt;&lt;span style=&quot;color: #424242;&quot;&gt;가 우려되어, PL/SQL을 활용한&amp;nbsp;&lt;/span&gt;&lt;b&gt;배치 처리 방식&lt;/b&gt;&lt;span style=&quot;color: #424242;&quot;&gt;으로 해결해보았다.&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa;&quot;&gt;&lt;span style=&quot;color: #424242;&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: justify;&quot; 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;테이블: CUSTOMER&lt;/li&gt; 
 &lt;li&gt;데이터 건수: 약 4천8백만 건&lt;/li&gt; 
 &lt;li&gt;작업 내용: 신규 컬럼에 대해 조건 기반으로 값 업데이트&lt;/li&gt; 
 &lt;li&gt;고려사항: 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;전체 트랜잭션 처리 시 롤백 리스크&lt;/li&gt; 
   &lt;li&gt;처리 시간 및 DB 부하&lt;/li&gt; 
   &lt;li&gt;중간&amp;nbsp;진행&amp;nbsp;상황&amp;nbsp;모니터링&amp;nbsp;필요&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;background-color: #fafafa;&quot;&gt;&lt;span style=&quot;color: #424242;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #424242;&quot;&gt;&lt;b&gt;해결전략&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #424242;&quot;&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #424242;&quot;&gt;&lt;b&gt;BULK&amp;nbsp;COLLECT&amp;nbsp;+&amp;nbsp;FORALL&amp;nbsp;+&amp;nbsp;배치&amp;nbsp;커밋 &lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;color: #424242;&quot;&gt;Oracle&amp;nbsp;PL/SQL에서는&amp;nbsp;BULK&amp;nbsp;COLLECT와&amp;nbsp;FORALL을&amp;nbsp;활용해&amp;nbsp;대량&amp;nbsp;데이터를&amp;nbsp;효율적으로&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;여기에&amp;nbsp;LIMIT을&amp;nbsp;설정해&amp;nbsp;배치&amp;nbsp;단위로&amp;nbsp;나누고,&amp;nbsp;각&amp;nbsp;배치마다&amp;nbsp;COMMIT을&amp;nbsp;수행하여&amp;nbsp;안정성을&amp;nbsp;확보해보고자 했다.&lt;/span&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;SQL&quot; data-ke-language=&quot;SQL&quot;&gt;&lt;code&gt;DECLARE
&amp;nbsp;&amp;nbsp;CURSOR c_customer IS
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SELECT customer_id
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FROM customer
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;WHERE 신규컬럼 IS NULL;

&amp;nbsp;&amp;nbsp;TYPE t_customer_ids IS TABLE OF customer.customer_id%TYPE;
&amp;nbsp;&amp;nbsp;v_customer_ids t_customer_ids;

&amp;nbsp;&amp;nbsp;v_limit CONSTANT PLS_INTEGER := 10000;
&amp;nbsp;&amp;nbsp;v_total_processed NUMBER := 0;
BEGIN
&amp;nbsp;&amp;nbsp;OPEN c_customer;
&amp;nbsp;&amp;nbsp;LOOP
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FETCH c_customer BULK COLLECT INTO v_customer_ids LIMIT v_limit;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXIT WHEN v_customer_ids.COUNT = 0;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BEGIN
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FORALL i IN 1 .. v_customer_ids.COUNT
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UPDATE customer
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SET 신규컬럼 = '값'
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;WHERE customer_id = v_customer_ids(i);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;COMMIT; -- 배치마다 커밋
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;v_total_processed := v_total_processed + v_customer_ids.COUNT;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DBMS_OUTPUT.PUT_LINE('현재까지 처리한 건수: ' || v_total_processed);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXCEPTION
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;WHEN OTHERS THEN
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ROLLBACK;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DBMS_OUTPUT.PUT_LINE('오류 발생: ' || SQLERRM);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;END;
&amp;nbsp;&amp;nbsp;END LOOP;

&amp;nbsp;&amp;nbsp;CLOSE c_customer;
&amp;nbsp;&amp;nbsp;DBMS_OUTPUT.PUT_LINE('전체 업데이트 완료. 총 처리 건수: ' || v_total_processed);
END;&lt;/code&gt;&lt;/pre&gt;&lt;h3 style=&quot;text-align: justify;&quot; 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;성능&amp;nbsp;향상:&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;많은&amp;nbsp;데이터를&amp;nbsp;처리하지&amp;nbsp;않고&amp;nbsp;나눠서&amp;nbsp;처리함으로써&amp;nbsp;DB&amp;nbsp;부하&amp;nbsp;감소 &lt;/li&gt;&lt;/ul&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;안정성&amp;nbsp;확보:&amp;nbsp;배치&amp;nbsp;단위&amp;nbsp;커밋으로&amp;nbsp;롤백&amp;nbsp;리스크&amp;nbsp;최소화 &lt;/li&gt;&lt;/ul&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;모니터링&amp;nbsp;가능:&amp;nbsp;DBMS_OUTPUT을&amp;nbsp;통해&amp;nbsp;진행&amp;nbsp;상황&amp;nbsp;확인&amp;nbsp;가능&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실무&amp;nbsp;팁 &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;LIMIT&amp;nbsp;값은&amp;nbsp;시스템&amp;nbsp;메모리&amp;nbsp;상황에&amp;nbsp;따라&amp;nbsp;조절해야하고&amp;nbsp;일반적으로&amp;nbsp;5,000~20,000&amp;nbsp;사이가&amp;nbsp;적당할 듯하다. &lt;/li&gt;&lt;/ul&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;WHERE&amp;nbsp;조건에&amp;nbsp;인덱스를&amp;nbsp;활용하면&amp;nbsp;성능이&amp;nbsp;크게&amp;nbsp;향상되니 추가할 수 있다면 추가!!&lt;/li&gt;&lt;/ul&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;운영&amp;nbsp;환경에서는&amp;nbsp;DBMS_OUTPUT&amp;nbsp;대신&amp;nbsp;로그&amp;nbsp;테이블에&amp;nbsp;INSERT하거나,&amp;nbsp;APEX/모니터링&amp;nbsp;도구와&amp;nbsp;연동하는&amp;nbsp;것도&amp;nbsp;고려해볼&amp;nbsp;수&amp;nbsp;있을 듯하다.&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;br&gt;이&amp;nbsp;방식은&amp;nbsp;Oracle&amp;nbsp;환경에서&amp;nbsp;대용량&amp;nbsp;데이터를&amp;nbsp;안정적으로&amp;nbsp;처리해야&amp;nbsp;할&amp;nbsp;때&amp;nbsp;매우&amp;nbsp;유용할 듯한데 요즘음 걍 스프링 배치 쓰는게 낫지 않을까&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <author>wms2275</author>
      <guid isPermaLink="true">https://wms0603.tistory.com/64</guid>
      <comments>https://wms0603.tistory.com/64#entry64comment</comments>
      <pubDate>Mon, 18 Aug 2025 18:58:35 +0900</pubDate>
    </item>
    <item>
      <title>MyBatis와 Spring 연동 시 자주 발생하는 이슈와 해결법</title>
      <link>https://wms0603.tistory.com/63</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring + MyBatis 조합은 실무에서 여전히 많이 쓰인다. 나 역시 이 조합으로 수많은 기능을 개발해왔고, 그만큼 다양한 이슈도 겪었다. 그래서 이번 글에서는 내가 직접 겪었던 문제들과 해결 방법을 정리해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.&amp;nbsp;Mapped&amp;nbsp;Statements&amp;nbsp;collection&amp;nbsp;does&amp;nbsp;not&amp;nbsp;contain&amp;nbsp;value&amp;nbsp;오류&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ 오류 내용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 실행 중 다음과 같은 오류 발생&lt;/p&gt;
&lt;pre id=&quot;code_1755162928316&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): 
com.example.mapper.OrderMapper.selectOrder&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;  원인&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mapper.xml에 해당 SQL이 정의되어 있지 않거나&lt;/li&gt;
&lt;li&gt;Mapper.xml은 있는데 namespace가 잘못되었거나&lt;/li&gt;
&lt;li&gt;@MapperScan&amp;nbsp;설정이&amp;nbsp;누락되었거나 &lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  해결법&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mapper.xml의 namespace가 인터페이스의 FQCN과 일치하는지 확인&lt;/li&gt;
&lt;li&gt;@MapperScan(&quot;co&lt;a href=&quot;http://m.example.mapper&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://m.example.mapper&lt;/a&gt;&quot;) 설정이 빠지지 않았는지 확인&lt;/li&gt;
&lt;li&gt;Mapper.xml이&amp;nbsp;resources에&amp;nbsp;포함되어&amp;nbsp;빌드되고&amp;nbsp;있는지&amp;nbsp;확인&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.&amp;nbsp;NullPointerException&amp;nbsp;in&amp;nbsp;Mapper&lt;/b&gt; &lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ 오류 내용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mapper를 주입받았는데 NPE 발생&lt;/p&gt;
&lt;pre id=&quot;code_1755163133662&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Autowired
private OrderMapper orderMapper;

orderMapper.selectOrder(); // NPE 발생&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  원인&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mapper 인터페이스에 @Mapper 어노테이션이 없거나&lt;/li&gt;
&lt;li&gt;@MapperScan이 설정되지 않음&lt;/li&gt;
&lt;li&gt;Spring이 해당 Mapper를 Bean으로 인식하지 못함&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  해결법&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mapper 인터페이스에 @Mapper 추가하거나&lt;/li&gt;
&lt;li&gt;@MapperScan으로 패키지 스캔 설정&lt;/li&gt;
&lt;li&gt;@ComponentScan 범위 안에 있는지 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.&amp;nbsp;ResultMap&amp;nbsp;매핑&amp;nbsp;오류&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ 오류 내용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리는&amp;nbsp;정상&amp;nbsp;실행되지만,&amp;nbsp;결과가&amp;nbsp;객체에&amp;nbsp;제대로&amp;nbsp;매핑되지&amp;nbsp;않음 &lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  원인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB&amp;nbsp;컬럼명과&amp;nbsp;VO&amp;nbsp;필드명이&amp;nbsp;다름 &lt;br /&gt;resultMap 설정이 누락되었거나 잘못됨&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  해결법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;resultMap을&amp;nbsp;명시적으로&amp;nbsp;작성하거나 &lt;br /&gt;alias를&amp;nbsp;사용해&amp;nbsp;컬럼명을&amp;nbsp;VO&amp;nbsp;필드명과&amp;nbsp;맞춤 &lt;/p&gt;
&lt;pre id=&quot;code_1755163284249&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;select id=&quot;selectOrder&quot; resultMap=&quot;orderResultMap&quot;&amp;gt;
  SELECT order_id AS orderId, order_date AS orderDate FROM orders
&amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 트랜잭션이 적용되지 않음&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ 오류 내용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Transactional을 붙였는데 롤백이 되지 않음&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  원인&lt;/b&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;/li&gt;
&lt;li&gt;예외가 catch로 먹혀서 롤백되지 않음&lt;/li&gt;
&lt;li&gt;예외가&amp;nbsp;Checked&amp;nbsp;Exception이라&amp;nbsp;롤백되지&amp;nbsp;않음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  해결법&lt;/b&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;/li&gt;
&lt;li&gt;@Transactional(rollbackFor = Exception.class) 명시&lt;/li&gt;
&lt;li&gt;예외를&amp;nbsp;throw로&amp;nbsp;던지도록&amp;nbsp;처리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. XML 쿼리 변경이 반영되지 않음&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ 오류 내용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리를 수정했는데도 실행 결과가 바뀌지 않음&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  원인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyBatis 캐시 또는 WAS 캐시로 인해 변경 사항이 반영되지 않음&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  해결법&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발 환경에서는 mybatis.configuration.cache-enabled=false 설정&lt;/li&gt;
&lt;li&gt;JEUS 등에서는 redeploy 시 캐시 클리어 확인&lt;/li&gt;
&lt;li&gt;Mapper.xml이&amp;nbsp;빌드에&amp;nbsp;포함되는지&amp;nbsp;확인&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyBatis는&amp;nbsp;단순하고&amp;nbsp;직관적인&amp;nbsp;만큼,&amp;nbsp;설정&amp;nbsp;하나만&amp;nbsp;빠져도&amp;nbsp;큰&amp;nbsp;문제가&amp;nbsp;생긴다.&amp;nbsp;특히&amp;nbsp;Spring과&amp;nbsp;연동할&amp;nbsp;때는&amp;nbsp;Bean&amp;nbsp;등록,&amp;nbsp;Mapper&amp;nbsp;스캔,&amp;nbsp;트랜잭션&amp;nbsp;처리&amp;nbsp;등&amp;nbsp;Spring의&amp;nbsp;동작&amp;nbsp;방식까지&amp;nbsp;이해하고&amp;nbsp;있어야&amp;nbsp;한다. &lt;br /&gt;&lt;br /&gt;이 글이 나처럼 MyBatis를 쓰는 실무자들에게 작은 도움이 되길 바라며 혹시 자주 겪는 다른 이슈가 있다면 댓글로 공유해주셔도 좋습니다 :)&lt;/p&gt;</description>
      <category>Programming</category>
      <author>wms2275</author>
      <guid isPermaLink="true">https://wms0603.tistory.com/63</guid>
      <comments>https://wms0603.tistory.com/63#entry63comment</comments>
      <pubDate>Thu, 14 Aug 2025 18:28:15 +0900</pubDate>
    </item>
    <item>
      <title>Spring 기반 모놀리식 시스템을 마이크로서비스로 전환하는 시나리오</title>
      <link>https://wms0603.tistory.com/62</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;하나의 시스템, 하나의 소스&amp;hellip; 그리고 장애&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가&amp;nbsp;담당하고&amp;nbsp;있는&amp;nbsp;시스템은&amp;nbsp;주문,&amp;nbsp;물류,&amp;nbsp;고객센터(C/S),&amp;nbsp;결제,&amp;nbsp;상품,&amp;nbsp;프로모션,&amp;nbsp;쿠폰,&amp;nbsp;적립금까지&amp;nbsp;모든&amp;nbsp;기능이&amp;nbsp;하나의&amp;nbsp;소스에&amp;nbsp;묶여&amp;nbsp;있다.&amp;nbsp;기술&amp;nbsp;스택은&amp;nbsp;Java&amp;nbsp;+&amp;nbsp;Spring&amp;nbsp;+&amp;nbsp;MyBatis&amp;nbsp;+&amp;nbsp;Oracle,&amp;nbsp;WAS는&amp;nbsp;JEUS,&amp;nbsp;웹서버는&amp;nbsp;WebtoB,&amp;nbsp;배포는&amp;nbsp;Jenkins. &lt;br /&gt;&lt;br /&gt;처음엔&amp;nbsp;이&amp;nbsp;구조가&amp;nbsp;편했다.&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;배포하고,&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;관리하고.&amp;nbsp;그런데&amp;nbsp;어느&amp;nbsp;순간부터&amp;nbsp;불편함이&amp;nbsp;일상이&amp;nbsp;됐다. &lt;br /&gt;&lt;br /&gt;주문&amp;nbsp;쪽&amp;nbsp;수정하려고&amp;nbsp;했는데,&amp;nbsp;물류&amp;nbsp;코드까지&amp;nbsp;같이&amp;nbsp;열어야&amp;nbsp;했다. &lt;br /&gt;프로모션&amp;nbsp;기능&amp;nbsp;하나&amp;nbsp;고치다가&amp;nbsp;전체&amp;nbsp;배포를&amp;nbsp;해야&amp;nbsp;했다. &lt;br /&gt;장애가&amp;nbsp;나면&amp;nbsp;모든&amp;nbsp;기능이&amp;nbsp;같이&amp;nbsp;죽는다. &lt;br /&gt;이제는 리팩토링도 겁난다. 그래서 가상의 시나리오로, 이 시스템을 마이크로서비스로 전환한다고 가정해봤다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;&lt;b&gt;마이크로서비스로 전환하면?&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 도메인 분리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능별로 서비스를 나눈다.&lt;br /&gt;예를&amp;nbsp;들면: &lt;br /&gt;&lt;br /&gt;주문&amp;nbsp;서비스 &lt;br /&gt;물류&amp;nbsp;서비스 &lt;br /&gt;C/S&amp;nbsp;서비스 &lt;br /&gt;결제&amp;nbsp;서비스 &lt;br /&gt;상품&amp;nbsp;서비스 &lt;br /&gt;프로모션&amp;nbsp;서비스 &lt;br /&gt;쿠폰&amp;nbsp;서비스 &lt;br /&gt;적립금&amp;nbsp;서비스 &lt;br /&gt;각&amp;nbsp;서비스는&amp;nbsp;독립적인&amp;nbsp;코드베이스,&amp;nbsp;DB,&amp;nbsp;배포&amp;nbsp;파이프라인을&amp;nbsp;갖는다.&amp;nbsp;서로&amp;nbsp;REST&amp;nbsp;API나&amp;nbsp;메시지&amp;nbsp;큐(Kafka&amp;nbsp;등)로&amp;nbsp;통신한다. &lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 기술 스택 변화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring&amp;nbsp;Boot&amp;nbsp;+&amp;nbsp;Spring&amp;nbsp;Cloud &lt;br /&gt;Docker&amp;nbsp;+&amp;nbsp;Kubernetes &lt;br /&gt;Kafka&amp;nbsp;(이벤트&amp;nbsp;기반&amp;nbsp;통신) &lt;br /&gt;GitHub&amp;nbsp;Actions&amp;nbsp;(CI/CD) &lt;br /&gt;API&amp;nbsp;Gateway&amp;nbsp;+&amp;nbsp;Service&amp;nbsp;Discovery &lt;br /&gt;기존&amp;nbsp;시스템에서&amp;nbsp;바로&amp;nbsp;전환하기는&amp;nbsp;어렵지만,&amp;nbsp;점진적으로&amp;nbsp;하나씩&amp;nbsp;분리해나가는&amp;nbsp;방식으로&amp;nbsp;접근할&amp;nbsp;수&amp;nbsp;있다. &lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장애&amp;nbsp;격리&lt;/b&gt;:&amp;nbsp;주문&amp;nbsp;서비스에&amp;nbsp;장애가&amp;nbsp;나도,&amp;nbsp;결제는&amp;nbsp;정상&amp;nbsp;동작 &lt;br /&gt;&lt;b&gt;독립&amp;nbsp;배포&lt;/b&gt;:&amp;nbsp;기능별로&amp;nbsp;배포&amp;nbsp;가능.&amp;nbsp;전체&amp;nbsp;배포&amp;nbsp;부담&amp;nbsp;없음 &lt;br /&gt;&lt;b&gt;유지보수&amp;nbsp;용이&lt;/b&gt;:&amp;nbsp;도메인별로&amp;nbsp;코드가&amp;nbsp;분리되어&amp;nbsp;리팩토링&amp;nbsp;쉬움 &lt;br /&gt;&lt;b&gt;확장성&lt;/b&gt;: 트래픽 많은 서비스만 따로 스케일링 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;단점과 어려움&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영&amp;nbsp;복잡도&amp;nbsp;증가:&amp;nbsp;서비스&amp;nbsp;수가&amp;nbsp;많아지면&amp;nbsp;모니터링,&amp;nbsp;배포,&amp;nbsp;장애&amp;nbsp;대응이&amp;nbsp;어려워짐 &lt;br /&gt;데이터&amp;nbsp;일관성&amp;nbsp;문제:&amp;nbsp;분산된&amp;nbsp;DB&amp;nbsp;간&amp;nbsp;트랜잭션&amp;nbsp;처리&amp;nbsp;어려움&amp;nbsp;(예:&amp;nbsp;주문-결제&amp;nbsp;간&amp;nbsp;연동) &lt;br /&gt;서비스&amp;nbsp;간&amp;nbsp;통신&amp;nbsp;비용:&amp;nbsp;네트워크&amp;nbsp;지연,&amp;nbsp;장애&amp;nbsp;가능성&amp;nbsp;증가 &lt;br /&gt;조직 문화 변화 필요: 팀 간 협업 방식, 테스트 전략 등도 함께 바뀌어야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전환 시 고려할 점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인&amp;nbsp;경계&amp;nbsp;명확히&amp;nbsp;하기&amp;nbsp;(DDD&amp;nbsp;기반&amp;nbsp;설계) &lt;br /&gt;공유&amp;nbsp;DB&amp;nbsp;지양,&amp;nbsp;서비스별&amp;nbsp;DB&amp;nbsp;분리 &lt;br /&gt;트랜잭션&amp;nbsp;처리&amp;nbsp;방식&amp;nbsp;고민&amp;nbsp;(Saga,&amp;nbsp;Eventual&amp;nbsp;Consistency&amp;nbsp;등) &lt;br /&gt;모니터링/로깅&amp;nbsp;체계&amp;nbsp;구축&amp;nbsp;(Prometheus,&amp;nbsp;Grafana,&amp;nbsp;ELK&amp;nbsp;등) &lt;br /&gt;점진적 전환 전략 수립 (Big Bang은 위험!!!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직&amp;nbsp;실무에서&amp;nbsp;마이크로서비스&amp;nbsp;전환을&amp;nbsp;직접&amp;nbsp;해본&amp;nbsp;건&amp;nbsp;아니지만,&amp;nbsp;지금의&amp;nbsp;시스템&amp;nbsp;구조를&amp;nbsp;보면&amp;nbsp;전환의&amp;nbsp;필요성은&amp;nbsp;분명하다.&amp;nbsp;이&amp;nbsp;글은&amp;nbsp;그런&amp;nbsp;고민의&amp;nbsp;시작점이다.&amp;nbsp;실제로&amp;nbsp;전환하게&amp;nbsp;된다면,&amp;nbsp;그&amp;nbsp;과정도&amp;nbsp;블로그에&amp;nbsp;기록해보고&amp;nbsp;싶다.&lt;/p&gt;</description>
      <category>기타</category>
      <author>wms2275</author>
      <guid isPermaLink="true">https://wms0603.tistory.com/62</guid>
      <comments>https://wms0603.tistory.com/62#entry62comment</comments>
      <pubDate>Wed, 13 Aug 2025 18:20:00 +0900</pubDate>
    </item>
    <item>
      <title>인텔리제이 IntelliJ VM 설정 추천</title>
      <link>https://wms0603.tistory.com/61</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; &amp;nbsp;기본 추천 VM 옵션&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1754885963751&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-Xms1024m
-Xmx4096m
-XX:ReservedCodeCacheSize=512m
-XX:+UseCompressedOops
-XX:+UseG1GC
-Dfile.encoding=UTF-8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fafafa; color: #424242; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;-Xms1024m: 초기 힙 메모리 크기 (1GB)&lt;/li&gt;
&lt;li&gt;-Xmx4096m: 최대 힙 메모리 크기 (4GB) &amp;mdash; 시스템 메모리에 따라 조절 가능&lt;/li&gt;
&lt;li&gt;-XX:ReservedCodeCacheSize=512m: 코드 캐시 크기 증가 (JVM 성능 향상)&lt;/li&gt;
&lt;li&gt;-XX:+UseCompressedOops: 64비트 JVM에서 객체 포인터 압축 (메모리 절약)&lt;/li&gt;
&lt;li&gt;-XX:+UseG1GC: G1 가비지 컬렉터 사용 (대규모 앱에 적합)&lt;/li&gt;
&lt;li&gt;-Dfile.encoding=UTF-8: 파일 인코딩 설정 (한글 깨짐 방지)&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;ol style=&quot;list-style-type: decimal; background-color: #fafafa; color: #424242; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;IntelliJ IDEA 실행&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Help &amp;gt; Edit Custom VM Options&amp;nbsp;클릭&lt;/li&gt;
&lt;li&gt;위 옵션을 복사해서 붙여넣기&lt;/li&gt;
&lt;li&gt;저장 후 IntelliJ 재시작&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>기타</category>
      <author>wms2275</author>
      <guid isPermaLink="true">https://wms0603.tistory.com/61</guid>
      <comments>https://wms0603.tistory.com/61#entry61comment</comments>
      <pubDate>Mon, 11 Aug 2025 13:25:49 +0900</pubDate>
    </item>
    <item>
      <title>인텔리제이 SVN 브랜치 생성 방법</title>
      <link>https://wms0603.tistory.com/60</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅ SVN &lt;/span&gt;&lt;span&gt;브랜치 생성 방법&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;프로젝트의 루트 디렉토리를 선택한 후&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;마우스 우클릭&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브랜치 생성 메뉴 접근&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Subversion &amp;gt; Branch or Tag &lt;/span&gt;&lt;span&gt;선택&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;상단 메뉴바에서&lt;/span&gt;&lt;span&gt; VCS &amp;gt; Subversion &amp;gt; Branch or Tag &lt;/span&gt;&lt;span&gt;선택&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciZEuk/btsPLRkLVga/NykSJTrunj2Kd7C0xj7kk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciZEuk/btsPLRkLVga/NykSJTrunj2Kd7C0xj7kk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciZEuk/btsPLRkLVga/NykSJTrunj2Kd7C0xj7kk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciZEuk%2FbtsPLRkLVga%2FNykSJTrunj2Kd7C0xj7kk1%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;1920&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브랜치 정보 입력&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;701&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bli93y/btsPK1VVKuU/3AStsMOBTLpDtrVThpwKc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bli93y/btsPK1VVKuU/3AStsMOBTLpDtrVThpwKc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bli93y/btsPK1VVKuU/3AStsMOBTLpDtrVThpwKc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbli93y%2FbtsPK1VVKuU%2F3AStsMOBTLpDtrVThpwKc0%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;574&quot; height=&quot;701&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;701&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Copy From: &lt;/span&gt;&lt;span&gt;현재 작업 중인 로컬 복사본 또는 저장소의 특정 경로를 선택&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Copy To: &lt;/span&gt;&lt;span&gt;브랜치를 생성할 경로와 브랜치 이름 지정&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;필요 시&lt;/span&gt;&lt;span&gt; Revision &lt;/span&gt;&lt;span&gt;번호 지정&lt;/span&gt;&lt;span&gt; (HEAD &lt;/span&gt;&lt;span&gt;또는 특정 리비전&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브랜치 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;OK &lt;/span&gt;&lt;span&gt;클릭 시 브랜치가 생성됨&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브랜치로 전환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;생성된 브랜치로 작업하려면&lt;/span&gt;&lt;span&gt; Update/Switch to specific URL&lt;/span&gt;&lt;span&gt;을 체크하고 해당 브랜치를 선택하여 소스를 업데이트 받아야 함&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;현재 브랜치 확인 방법&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;IntelliJ &lt;/span&gt;&lt;span&gt;하단 탭에서&lt;/span&gt;&lt;span&gt; Subversion &amp;gt; Subversion Working Copies Information&lt;/span&gt;&lt;span&gt;을 통해 현재 작업 중인 브랜치의&lt;/span&gt;&lt;span&gt; URL &lt;/span&gt;&lt;span&gt;확인 가능&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0C5eg/btsPLYxnfYo/kNaX3HF6tAkqBTsOR3jIFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0C5eg/btsPLYxnfYo/kNaX3HF6tAkqBTsOR3jIFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0C5eg/btsPLYxnfYo/kNaX3HF6tAkqBTsOR3jIFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0C5eg%2FbtsPLYxnfYo%2FkNaX3HF6tAkqBTsOR3jIFk%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;1008&quot; height=&quot;292&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>기타</category>
      <author>wms2275</author>
      <guid isPermaLink="true">https://wms0603.tistory.com/60</guid>
      <comments>https://wms0603.tistory.com/60#entry60comment</comments>
      <pubDate>Fri, 8 Aug 2025 10:53:43 +0900</pubDate>
    </item>
    <item>
      <title>JPA Querydsl from절에 subquery 사용하기(@Subselect 이용)</title>
      <link>https://wms0603.tistory.com/58</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이슈사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이번 내용도 배치로 통계로 구축하다가 생긴 이슈이다.&lt;br /&gt;통계 쿼리를 mybatis로 작성하는 것은 쉬운데 검색해보니&lt;br /&gt;querydsl은 subquery가 select 절이나 where 절에만 사용가능하고 &lt;br /&gt;from절에는 불가하다는 내용이 가득해서 통계 쿼리를 작성하는데 골머리 썩었다.&lt;br /&gt;그래도 방법을 찾아서 적어보려고 한다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결방안&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;@Subselect를 이용해서 서브 쿼리로 사용할 엔티티에 일종의 가상 테이블을 만드는 view 처럼 사용하는 것이다.&lt;br /&gt;그렇게 되면 native query로 작성하는 것처럼 사용이 가능하다.&lt;br /&gt;아니면 어플리케이션 레벨에서 처리해야한다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;from 절 &lt;span&gt;조회해오는&lt;span&gt;&amp;nbsp;데이터&lt;/span&gt;&lt;/span&gt;의 크기를 줄이는 것이 목적이어서 @Subselect를 사용해서 view처럼 사용하기로 했다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;subquery 엔티티 부분&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;엔티티 부분을 작성할 때 주의할 점은 select절의 컬럼 이름과 엔티티의 @Column(name = &quot;&quot;) &lt;br /&gt;이 부분이 같아야 컬럼을 찾을 수 있으니 주의해야한다.&lt;br /&gt;그리고 이부분의 단점은 where 절에 넣을 수 있는 조건이 동적으로 불가해서 고정적으로&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Entity
@Subselect(
    &quot;SELECT &quot; +
        &quot;id AS member_id, &quot; +
        &quot;TIMESTAMPDIFF(YEAR, birth, CURDATE()) AS age,&quot; +
        &quot;gender,&quot; +
        &quot;STR_TO_DATE(created_at, '%Y-%m-%d') AS created_at, &quot; +
        &quot;member_status &quot;
    &quot;FROM member &quot; +
    &quot;WHERE birth IS NOT NULL &quot; +
    &quot;AND gender IS NOT NULL&quot;
)
@Immutable
@Synchronize(&quot;member&quot;)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class AgeGenderSub {

  @Column(name = &quot;member_id&quot;)
  private String memberId;

  @Column(name = &quot;age&quot;)
  private Long age;

  @Column(name = &quot;gender&quot;)
  private String gender;

  @Column(name = &quot;created_at&quot;)
  private LocalDateTime createdAt;

  @Column
  @Enumerated(EnumType.STRING)
  private MemberStatus memberStatus;

  public AgeGenderSub(String memberId, Long age, String gender, LocalDateTime createdAt, MemberStatus memberStatus) {
    this.memberId = memberId;
    this.age = age;
    this.gender = gender;
    this.createdAt = createdAt;
    this.memberStatus = memberStatus;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;repository querydsl 부분&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;아까 생성한 엔티티를 Q테이블로 선언해주고 from 절에 엔티티 부분을 넣어주면 @Subselect 부분에서 조회된 데이터를 사용할 수 있다.&lt;br /&gt;그리고 엔티티에 선언되어 있는 필드를 사용해서 querydsl을 작성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Override
public List&amp;lt;AgeGenderDto&amp;gt; findAgeGenderStatistics(
    AnalysisKey analysisKey) {
  LocalDateTime startDate = analysisKey.getStartDate().atStartOfDay();
  LocalDateTime endDate = analysisKey.getEndDate().atStartOfDay();

  QAgeGenderSub ageGenderSub = QAgeGenderSub.ageGenderSub;

  StringTemplate dateFormat = Expressions.stringTemplate(&quot;DATE_FORMAT( {0}, {1} )&quot;,
      ageGenderSub.createdAt, ConstantImpl.create(&quot;%Y-%m-%d&quot;));
  StringPath ageType = Expressions.stringPath(&quot;age_type&quot;);
  return queryFactory
      .select(
          Projections.constructor(AgeGenderDto.class,
              dateFormat,
              new CaseBuilder()
                  .when(ageGenderSub.age.lt(10)).then(AgeType.LESS_THAN_10.toString())
                  .when(ageGenderSub.age.between(11, 19))
                  .then(AgeType.BETWEEN_11_AND_19.toString())
                  .when(ageGenderSub.age.between(20, 29))
                  .then(AgeType.BETWEEN_20_AND_29.toString())
                  .when(ageGenderSub.age.between(30, 39))
                  .then(AgeType.BETWEEN_30_AND_39.toString())
                  .when(ageGenderSub.age.between(40, 49))
                  .then(AgeType.BETWEEN_40_AND_49.toString())
                  .when(ageGenderSub.age.between(50, 59))
                  .then(AgeType.BETWEEN_50_AND_59.toString())
                  .when(ageGenderSub.age.goe(60)).then(AgeType.GREATER_THAN_60.toString())
                  .otherwise(AgeType.ETC.toString()).as(&quot;age_type&quot;),
              ageGenderSub.gender,
              ageGenderSub.count()
          )
      )
      .from(ageGenderSub)
      .where(
          ageGenderSub.isNotNull()
              .and(ageGenderSub.createdAt.goe(startDate))
              .and(ageGenderSub.createdAt.lt(endDate))
              .and(ageGenderSub.memberStatus.eq(MemberStatus.NORMAL))
      )
      .groupBy(dateFormat, ageType, ageGenderSub.gender)
      .fetch();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/JAVA</category>
      <author>wms2275</author>
      <guid isPermaLink="true">https://wms0603.tistory.com/58</guid>
      <comments>https://wms0603.tistory.com/58#entry58comment</comments>
      <pubDate>Sat, 14 Jan 2023 13:48:08 +0900</pubDate>
    </item>
    <item>
      <title>JPA 엔티티 복합키 설정하기</title>
      <link>https://wms0603.tistory.com/57</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이슈사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;배치로 통계를 구축해야하는데 mybatis 같은 경우에는 DB에서 pk를 여러개 만들어주면 되지만&lt;br /&gt;JPA에서는 엔티티에서도 id를 지정해줘야하기 때문에&lt;br /&gt;엔티티에 복합키를 설정하는 방법을 찾아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&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;size14&quot;&gt;엔티티에 복합키를 설정하는 방법으로 2가지를 찾았다.&lt;br /&gt;1. @Embeddable&lt;br /&gt;2. @IdClass&lt;br /&gt;&lt;br /&gt;그리고 각각 조건이 있는데 아래 사항을 만족해야한다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;@Embeddable&lt;/b&gt;&lt;br /&gt;- 식별자 클래스에 @Embeddable어노테이션 추가&lt;br /&gt;- 디폴트 생성자가 존재 (위의 코드는 Lombok의 @NoArgsConstructor어노테이션 추가로 자동 생성)&lt;br /&gt;- 식별자 클래스의 접근 지정자는 public&lt;br /&gt;- Serializable을 상속&lt;br /&gt;- 컬럼명과 변수명이 다를 경우 @Column어노테이션 사용&lt;br /&gt;&lt;br /&gt;&lt;b&gt;@IdClass&lt;/b&gt;&lt;br /&gt;- 식별자 클래스의 변수명과 엔티티에서 사용되는 변수명이 동일&lt;br /&gt;- 디폴트 생성자가 존재 (위의 코드는 Lombok의 @NoArgsConstructor어노테이션 추가로 자동 생성)&lt;br /&gt;- 식별자 클래스의 접근 지정자는 public&lt;br /&gt;- Serializable을 상속&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&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;size14&quot;&gt;@Embeddable 같은 경우에는 나중에 엔티티에서 통계를 꺼내 올 때 &lt;br /&gt;ID를 지정해둔 클래스에 한번 더 접근해서 가져와야하기 때문에 코드가 좀 더 길어지게 된다.&lt;br /&gt;그래서 좀 더 보기 쉬운 @IdClass 로 선택하였다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;엔티티 부분 코드&lt;/h4&gt;
&lt;pre id=&quot;code_1673186391262&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Table(name = &quot;statistics&quot;)
@IdClass(StatisticsId.class)
public class Statistics {
	@Id
    @Column(name = &quot;statistics_date&quot;)
    private LocalDate statisticsDate;
    
    @Id
    @Column(name = &quot;statistics_id&quot;)
    private Long statisticsId;
    
    @Column(name = &quot;statistics_type&quot;, nullable = false)
    @Enumerated(EnumType.STRING)
    private String statisticsType;
}&lt;/code&gt;&lt;/pre&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;pre id=&quot;code_1673186434608&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class StatisticsId implements Serializable {

  @Column(name = &quot;statistics_date&quot;)
  private LocalDate statisticsDate;

  @Column(name = &quot;statistics_id&quot;)
  private Long statisticsId;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Repository 부분 코드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JpaRepository의 제네릭 타입에는 엔티티 클래스와 엔티티 클래스의 기본키 클래스를 넣어주면 된다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1673186564324&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface StatisticsRepository extends JpaRepository&amp;lt;Statistics, StatisticsId&amp;gt; {
}&lt;/code&gt;&lt;/pre&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;size14&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;size14&quot;&gt;&lt;a href=&quot;https://jforj.tistory.com/84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jforj.tistory.com/84&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming/JAVA</category>
      <author>wms2275</author>
      <guid isPermaLink="true">https://wms0603.tistory.com/57</guid>
      <comments>https://wms0603.tistory.com/57#entry57comment</comments>
      <pubDate>Sun, 8 Jan 2023 23:06:52 +0900</pubDate>
    </item>
    <item>
      <title>나라별 시간대가 필요할 때 LocalDateTime to ZonedDateTime</title>
      <link>https://wms0603.tistory.com/56</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이슈사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;백엔트 서버에서 사용하는 시간은 LocalDateTime을 사용했다.&lt;br /&gt;프론트 쪽에 전달해줄때도 LocalDateTime 형식으로 전달해주었다.&lt;br /&gt;하지만 LocalDateTime 형식으로 전달해주게 되면 타임존을 모르기 때문에&lt;br /&gt;나라별 자동으로 시간대를 바꾸어주지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&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;size14&quot;&gt;LocalDateTime 형식은 예시로 2023-01-01T20:49:09.698591 이렇게 나오는데&lt;br /&gt;ZonedDateTime은 LocalDateTime 형식에 뒤에 Z가 붙어서 2023-01-01T20:49:09.698591Z 이런 형태로 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;LocalDateTime은 &lt;b&gt;ISO 8601&lt;/b&gt;의 기본 형식이다. 해당 시간이 로컬 시간 임을 의미한고&lt;br /&gt;ZonedDateTime의 마지막 뒤에 붙어 있는 Z가 시간대를 나타내는 &lt;span style=&quot;background-color: #ffffff; color: #4a4a4a;&quot;&gt;time-zone이다.&lt;br /&gt;해당 시간이&amp;nbsp;&lt;b&gt;UTC(GMT)&lt;/b&gt;&amp;nbsp;시간 임을 의미한다.&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a;&quot;&gt;따라서 LocalDateTime에 시간대(time-zone)를 추가하면, ZonedDateTime이 된다.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a;&quot;&gt;시간대(time-zone)까지 다뤄야 하고 &lt;span&gt;접속 나라에 따라 지역별로 시간대를 알아야할 때&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;ZonedDateTime&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;클래스를 사용하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;아래와 같이 LocalDateTime에 ZoneId를 UTC로 지정해주면 로컬 시간에 타임존을 추가한 형식으로 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneOffset.UTC);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고 사이트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jongmin92.github.io/2018/06/06/Java/java-date-time/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jongmin92.github.io/2018/06/06/Java/java-date-time/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming/JAVA</category>
      <author>wms2275</author>
      <guid isPermaLink="true">https://wms0603.tistory.com/56</guid>
      <comments>https://wms0603.tistory.com/56#entry56comment</comments>
      <pubDate>Sun, 1 Jan 2023 21:51:47 +0900</pubDate>
    </item>
    <item>
      <title>프로젝트 어플리케이션 전체 서버 시간 UTC-&amp;gt; KOR로 설정하기</title>
      <link>https://wms0603.tistory.com/55</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이슈사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이슈가 되었던 문제를 정리하자면 우리 어플리케이션 서버와 DB는 insert, update, delete 할 경우 time이 UTC로 되어 있다.&lt;br /&gt;근데 우리가 고객사에서 받는 데이터는 한국시간으로 되어 있어서 시간대를 하나의 시간대로 통일 시켜 줘야했다.&lt;br /&gt;고객사에서 받는 데이터는 어플리케이션에서 제어할 수 없는 엑셀이나 CSV 파일에 날짜가 들어가 있어서 통제가 불가능한 상황이었다.&lt;br /&gt;&lt;br /&gt;1. 어플리케이션과 DB 서버 시간은 UTC&lt;br /&gt;2. 고객사에서 받는 데이터는 KOR 한국시간&lt;br /&gt;3. 하나의 시간대로 통일 시켜서 관리 필요성 대두&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결방안&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;1. 고객사에서 받는 데이터를 고객사에 UTC 데이터로 넣어서 전달 요청하여 UTC 시간대로 맞춘다.&lt;br /&gt;2. 어플리케이션에서 데이터 저장시 KOR 시간대로 강제하여 통일시킨다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;처음에는 시간대 설정을 하기에 표준시간대인 UTC로 통일시키는게 맞다고 생각하였다.&lt;br /&gt;그래서 고객사에 UTC 시간대로 요청으로 하려고 했으나&lt;br /&gt;SDK에서 수집하는 클릭이벤트 데이터들도 한국시간대로 들어오고 있는 상황이었다.&lt;br /&gt;&lt;br /&gt;엑셀에서 수집하는 데이터, 어플리케이션에서 들어오는 데이터, SDK 수집 데이터를 통합할 때 모두 UTC 시간대로 통일 하려 했으나&lt;br /&gt;그렇게 했을 때 문제는 통계를 생성할 때 이슈가 발생하였다.&lt;br /&gt;통계 생성시 UTC 시간대로 하면 한국시간대랑 9시간이 차이나게 통계가 생성되고&lt;br /&gt;화면에 표출시 원하는 통계가 나오지 않는 이슈가 발생하였다.&lt;br /&gt;&lt;br /&gt;실장님께 여쭤보고 같이 이슈를 논의하여 결론은 KOR 한국시간대로 모든 시간을 통일하기로 하였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;어플리케이션과 DB 서버의 시간대가 UTC로 설정해줘야 해서 dev ops팀에 요청하여 시스템 설정에 박아둘까하다가&lt;br /&gt;그렇게 되면 관리 포인트가 우리팀 뿐만 아니라 다른 팀에도 영향이 있을 것 같아&lt;br /&gt;우리팀 내에서 해결할 수 있는 방법을 찾아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;우선 timezone 설정을 한국시간대로 바꾸려했다&lt;br /&gt;&lt;br /&gt;나 말고 다른 개발자가 유지운영을 해야할 때를 고려하여&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;LocalDateTime.now()를 뜯어보니 해당 메소드가 가져오는 값이 &lt;b&gt;TimeZone&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;getDefault()&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메소드를 호출하는 것을 확인하였다.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1671874384226&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static LocalDateTime now() {
    return now(Clock.systemDefaultZone());
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1671874406824&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Clock systemDefaultZone() {
    return new SystemClock(ZoneId.systemDefault());
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1671874428373&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static ZoneId systemDefault() {
    return TimeZone.getDefault().toZoneId();
}&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;size14&quot;&gt;그래서 TimeZone.setDefault 메소드를 살펴보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1671874554062&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    /**
     * Sets the {@code TimeZone} that is returned by the {@code getDefault}
     * method. {@code zone} is cached. If {@code zone} is null, the cached
     * default {@code TimeZone} is cleared. This method doesn't change the value
     * of the {@code user.timezone} property.
     *
     * @param zone the new default {@code TimeZone}, or null
     * @throws SecurityException if the security manager's {@code checkPermission}
     *                           denies {@code PropertyPermission(&quot;user.timezone&quot;,
     *                           &quot;write&quot;)}
     * @see #getDefault
     * @see PropertyPermission
     */
    public static void setDefault(TimeZone zone)
    {
        @SuppressWarnings(&quot;removal&quot;)
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new PropertyPermission
                               (&quot;user.timezone&quot;, &quot;write&quot;));
        }
        // by saving a defensive clone and returning a clone in getDefault() too,
        // the defaultTimeZone instance is isolated from user code which makes it
        // effectively immutable. This is important to avoid races when the
        // following is evaluated in ZoneId.systemDefault():
        // TimeZone.getDefault().toZoneId().
        defaultTimeZone = (zone == null) ? null : (TimeZone) zone.clone();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;해당 메서드가 user.timezone 속성의 값을 변경하지 않고 getDefault 메서드에서 반환되는 TimeZone을 설정한다고 하여&lt;br /&gt;application에 아래 코드를 추가해보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1671874197188&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TimeZone.setDefault(TimeZone.getTimeZone(&quot;Asia/Seoul&quot;));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;br /&gt;이렇게하니 DB 서버 시간대가 UTC 여도&lt;br /&gt;어플리케이션에서 데이터를 insert, update, delete 할 때 날짜가 한국시간대로 들어가는 것을 확인하였다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고 사이트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://sas-study.tistory.com/391&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sas-study.tistory.com/391&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/JAVA</category>
      <author>wms2275</author>
      <guid isPermaLink="true">https://wms0603.tistory.com/55</guid>
      <comments>https://wms0603.tistory.com/55#entry55comment</comments>
      <pubDate>Sat, 24 Dec 2022 18:42:05 +0900</pubDate>
    </item>
  </channel>
</rss>