<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Jdbctemplate on kastori</title><link>http://blog.kastori.dev/tags/jdbctemplate/</link><description>Recent content in Jdbctemplate on kastori</description><generator>Hugo -- gohugo.io</generator><language>ko-kr</language><lastBuildDate>Tue, 19 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="http://blog.kastori.dev/tags/jdbctemplate/index.xml" rel="self" type="application/rss+xml"/><item><title>[Spring 완전 정복 #6] Java 데이터 접근 기술의 진화 — JDBC에서 JPA까지, 왜 바뀌었나</title><link>http://blog.kastori.dev/tech/2026-05-19-spring-06-data-access-evolution/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>http://blog.kastori.dev/tech/2026-05-19-spring-06-data-access-evolution/</guid><description>&lt;h2 id="jpa-vs-mybatis-뭐가-더-낫나요"&gt;&lt;a href="#jpa-vs-mybatis-%eb%ad%90%ea%b0%80-%eb%8d%94-%eb%82%ab%eb%82%98%ec%9a%94" class="header-anchor"&gt;&lt;/a&gt;&amp;ldquo;JPA vs MyBatis 뭐가 더 낫나요?&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;이 질문의 답을 제대로 하려면 두 기술이 각각 어떤 문제를 해결하기 위해 등장했는지 알아야 한다. 역사를 따라가면 답이 보인다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1단계-순수-jdbc--모든-것을-직접"&gt;&lt;a href="#1%eb%8b%a8%ea%b3%84-%ec%88%9c%ec%88%98-jdbc--%eb%aa%a8%eb%93%a0-%ea%b2%83%ec%9d%84-%ec%a7%81%ec%a0%91" class="header-anchor"&gt;&lt;/a&gt;1단계: 순수 JDBC — 모든 것을 직접
&lt;/h2&gt;&lt;p&gt;Java에서 DB에 접근하는 가장 원시적인 방법이다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; Order &lt;span style="color:#a6e22e"&gt;findById&lt;/span&gt;(Long id) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Connection conn &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; PreparedStatement pstmt &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ResultSet rs &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; conn &lt;span style="color:#f92672"&gt;=&lt;/span&gt; DriverManager.&lt;span style="color:#a6e22e"&gt;getConnection&lt;/span&gt;(URL, USER, PASSWORD);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pstmt &lt;span style="color:#f92672"&gt;=&lt;/span&gt; conn.&lt;span style="color:#a6e22e"&gt;prepareStatement&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;SELECT * FROM orders WHERE id = ?&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pstmt.&lt;span style="color:#a6e22e"&gt;setLong&lt;/span&gt;(1, id);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rs &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pstmt.&lt;span style="color:#a6e22e"&gt;executeQuery&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (rs.&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Order order &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Order();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; order.&lt;span style="color:#a6e22e"&gt;setId&lt;/span&gt;(rs.&lt;span style="color:#a6e22e"&gt;getLong&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; order.&lt;span style="color:#a6e22e"&gt;setStatus&lt;/span&gt;(rs.&lt;span style="color:#a6e22e"&gt;getString&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; order;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (SQLException e) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; RuntimeException(e);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;finally&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 안 닫으면 커넥션 누수 — 매 메서드마다 이 코드가 반복&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (rs &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; { rs.&lt;span style="color:#a6e22e"&gt;close&lt;/span&gt;(); } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (SQLException e) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (pstmt &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; { pstmt.&lt;span style="color:#a6e22e"&gt;close&lt;/span&gt;(); } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (SQLException e) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (conn &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; { conn.&lt;span style="color:#a6e22e"&gt;close&lt;/span&gt;(); } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (SQLException e) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이 코드의 문제점이 한눈에 보인다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;커넥션 획득 → 쿼리 실행 → 결과 매핑 → 리소스 반납&lt;/strong&gt; 패턴이 모든 메서드에 반복된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;finally&lt;/code&gt;에서 리소스를 수동으로 닫아야 한다. 빠트리면 커넥션 풀 고갈.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SQLException&lt;/code&gt;이 체크 예외라 모든 메서드가 예외를 처리하거나 throws해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="2단계-jdbctemplate--반복-코드-제거"&gt;&lt;a href="#2%eb%8b%a8%ea%b3%84-jdbctemplate--%eb%b0%98%eb%b3%b5-%ec%bd%94%eb%93%9c-%ec%a0%9c%ea%b1%b0" class="header-anchor"&gt;&lt;/a&gt;2단계: JdbcTemplate — 반복 코드 제거
&lt;/h2&gt;&lt;p&gt;Spring이 JDBC의 반복 코드를 추상화한 것이 &lt;code&gt;JdbcTemplate&lt;/code&gt;이다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Repository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OrderRepository&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; JdbcTemplate jdbcTemplate;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; Order &lt;span style="color:#a6e22e"&gt;findById&lt;/span&gt;(Long id) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; jdbcTemplate.&lt;span style="color:#a6e22e"&gt;queryForObject&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;SELECT * FROM orders WHERE id = ?&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (rs, rowNum) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Order order &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Order();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; order.&lt;span style="color:#a6e22e"&gt;setId&lt;/span&gt;(rs.&lt;span style="color:#a6e22e"&gt;getLong&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; order.&lt;span style="color:#a6e22e"&gt;setStatus&lt;/span&gt;(rs.&lt;span style="color:#a6e22e"&gt;getString&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; order;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;커넥션 획득/반납, &lt;code&gt;try-finally&lt;/code&gt;, &lt;code&gt;SQLException&lt;/code&gt; 처리가 사라졌다. Spring이 내부적으로 처리해준다. &lt;code&gt;SQLException&lt;/code&gt;은 Spring의 &lt;code&gt;DataAccessException&lt;/code&gt;(런타임 예외)으로 변환된다.&lt;/p&gt;
&lt;p&gt;하지만 SQL은 여전히 직접 작성해야 하고, 결과를 객체에 매핑하는 &lt;code&gt;RowMapper&lt;/code&gt; 코드도 직접 써야 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="3단계-mybatis--sql을-코드에서-분리"&gt;&lt;a href="#3%eb%8b%a8%ea%b3%84-mybatis--sql%ec%9d%84-%ec%bd%94%eb%93%9c%ec%97%90%ec%84%9c-%eb%b6%84%eb%a6%ac" class="header-anchor"&gt;&lt;/a&gt;3단계: MyBatis — SQL을 코드에서 분리
&lt;/h2&gt;&lt;p&gt;MyBatis는 SQL을 XML 파일로 분리하고, 결과 매핑을 자동화한다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!-- OrderMapper.xml --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;mapper&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;namespace=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;com.example.mapper.OrderMapper&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;resultMap&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;orderResultMap&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Order&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;id&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;property=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;column=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;result&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;property=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;column=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;result&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;property=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;memberId&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;column=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;member_id&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/resultMap&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;select&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;findById&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;resultMap=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;orderResultMap&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SELECT id, status, member_id FROM orders WHERE id = #{id}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/mapper&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Mapper&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OrderMapper&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Order &lt;span style="color:#a6e22e"&gt;findById&lt;/span&gt;(Long id); &lt;span style="color:#75715e"&gt;// 인터페이스만 선언하면 구현체 자동 생성&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;MyBatis의 진짜 강점은 &lt;strong&gt;동적 쿼리&lt;/strong&gt;다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;select&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;findByCondition&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;resultMap=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;orderResultMap&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SELECT * FROM orders
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;where&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;status != null&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;AND status = #{status}&lt;span style="color:#f92672"&gt;&amp;lt;/if&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;memberId != null&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;AND member_id = #{memberId}&lt;span style="color:#f92672"&gt;&amp;lt;/if&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/where&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;조건에 따라 WHERE절을 동적으로 구성하는 것이 직관적이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="4단계-jpa--sql을-아예-작성하지-않는다"&gt;&lt;a href="#4%eb%8b%a8%ea%b3%84-jpa--sql%ec%9d%84-%ec%95%84%ec%98%88-%ec%9e%91%ec%84%b1%ed%95%98%ec%a7%80-%ec%95%8a%eb%8a%94%eb%8b%a4" class="header-anchor"&gt;&lt;/a&gt;4단계: JPA — SQL을 아예 작성하지 않는다
&lt;/h2&gt;&lt;p&gt;JPA(ORM)는 SQL을 직접 작성하지 않고, &lt;strong&gt;객체 간 관계를 그대로 DB에 매핑&lt;/strong&gt;한다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Entity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Order&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Id&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;@GeneratedValue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; Long id;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@ManyToOne&lt;/span&gt;(fetch &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FetchType.&lt;span style="color:#a6e22e"&gt;LAZY&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@JoinColumn&lt;/span&gt;(name &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;member_id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; Member member;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// SQL 없이 CRUD&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;orderRepository.&lt;span style="color:#a6e22e"&gt;save&lt;/span&gt;(order); &lt;span style="color:#75715e"&gt;// INSERT 자동&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;orderRepository.&lt;span style="color:#a6e22e"&gt;findById&lt;/span&gt;(id); &lt;span style="color:#75715e"&gt;// SELECT 자동&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;order.&lt;span style="color:#a6e22e"&gt;setStatus&lt;/span&gt;(COMPLETED); &lt;span style="color:#75715e"&gt;// UPDATE 자동 (Dirty Checking)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;orderRepository.&lt;span style="color:#a6e22e"&gt;delete&lt;/span&gt;(order); &lt;span style="color:#75715e"&gt;// DELETE 자동&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;DB 벤더가 바뀌어도 코드 변경이 없다. 객체 관계를 그대로 코드로 표현할 수 있다.&lt;/p&gt;
&lt;p&gt;단점도 있다. 학습 곡선이 높고(영속성 컨텍스트, 지연 로딩, N+1 등), 복잡한 집계·통계 쿼리는 JPQL이나 QueryDSL이 필요하다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="4가지-기술-한눈에-비교"&gt;&lt;a href="#4%ea%b0%80%ec%a7%80-%ea%b8%b0%ec%88%a0-%ed%95%9c%eb%88%88%ec%97%90-%eb%b9%84%ea%b5%90" class="header-anchor"&gt;&lt;/a&gt;4가지 기술 한눈에 비교
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;기준&lt;/th&gt;
 &lt;th&gt;JDBC&lt;/th&gt;
 &lt;th&gt;JdbcTemplate&lt;/th&gt;
 &lt;th&gt;MyBatis&lt;/th&gt;
 &lt;th&gt;JPA&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;SQL 작성&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;직접&lt;/td&gt;
 &lt;td&gt;직접&lt;/td&gt;
 &lt;td&gt;직접 (XML)&lt;/td&gt;
 &lt;td&gt;자동 생성&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;결과 매핑&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;수동&lt;/td&gt;
 &lt;td&gt;RowMapper&lt;/td&gt;
 &lt;td&gt;자동 (resultMap)&lt;/td&gt;
 &lt;td&gt;자동&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;복잡한 쿼리&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;자유롭게&lt;/td&gt;
 &lt;td&gt;자유롭게&lt;/td&gt;
 &lt;td&gt;자유롭게 (동적 쿼리 강점)&lt;/td&gt;
 &lt;td&gt;JPQL/QueryDSL 필요&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;DB 종속성&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;높음&lt;/td&gt;
 &lt;td&gt;높음&lt;/td&gt;
 &lt;td&gt;높음&lt;/td&gt;
 &lt;td&gt;낮음&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;학습 난이도&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;낮음&lt;/td&gt;
 &lt;td&gt;낮음&lt;/td&gt;
 &lt;td&gt;중간&lt;/td&gt;
 &lt;td&gt;높음&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;생산성&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;낮음&lt;/td&gt;
 &lt;td&gt;중간&lt;/td&gt;
 &lt;td&gt;중간&lt;/td&gt;
 &lt;td&gt;높음 (단순 CRUD)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="jpa-vs-mybatis--이분법이-아니다"&gt;&lt;a href="#jpa-vs-mybatis--%ec%9d%b4%eb%b6%84%eb%b2%95%ec%9d%b4-%ec%95%84%eb%8b%88%eb%8b%a4" class="header-anchor"&gt;&lt;/a&gt;JPA vs MyBatis — 이분법이 아니다
&lt;/h2&gt;&lt;p&gt;&amp;ldquo;JPA vs MyBatis&amp;quot;를 선택의 문제로 보는 건 틀린 프레임이다. 실무에서는 함께 쓰는 경우도 많고, 상황에 따라 선택이 다르다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JPA가 유리한 경우&lt;/strong&gt;: 비즈니스 로직이 복잡하고 객체 중심 설계가 중요한 서비스, 단순 CRUD가 많은 경우.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MyBatis가 유리한 경우&lt;/strong&gt;: 복잡한 통계·집계 쿼리가 많은 경우, DBA와 협업하며 SQL을 직접 관리해야 하는 경우, 레거시 DB 스키마에 맞춰야 하는 경우.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;실무에서 가장 많이 쓰는 조합&lt;/strong&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;단순 CRUD → JPA Repository
복잡한 조회 → QueryDSL (타입 안전한 JPQL 빌더)
통계·집계 쿼리 → Native Query 또는 MyBatis (별도 모듈)
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id="jdbctemplate은-여전히-쓴다"&gt;&lt;a href="#jdbctemplate%ec%9d%80-%ec%97%ac%ec%a0%84%ed%9e%88-%ec%93%b4%eb%8b%a4" class="header-anchor"&gt;&lt;/a&gt;JdbcTemplate은 여전히 쓴다
&lt;/h2&gt;&lt;p&gt;JPA 프로젝트에서도 JdbcTemplate이 필요한 상황이 있다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Repository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OrderBatchRepository&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; JdbcTemplate jdbcTemplate;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 수백만 건 상태 일괄 변경 — JPA Dirty Checking으로 하면 메모리 부족&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;bulkUpdateStatus&lt;/span&gt;(String oldStatus, String newStatus) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; jdbcTemplate.&lt;span style="color:#a6e22e"&gt;update&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;UPDATE orders SET status = ? WHERE status = ?&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; newStatus, oldStatus
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;JPA &lt;code&gt;@Modifying&lt;/code&gt;으로 안 되는 벌크 업데이트, Spring Batch 처리, 간단한 유틸리티 쿼리 등에서 여전히 활용된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="마치며"&gt;&lt;a href="#%eb%a7%88%ec%b9%98%eb%a9%b0" class="header-anchor"&gt;&lt;/a&gt;마치며
&lt;/h2&gt;&lt;p&gt;각 기술의 진화 방향은 &lt;strong&gt;반복 코드 제거 → SQL 관리 편의 → 객체 중심 개발&lt;/strong&gt;이었다. 이 흐름을 이해하면 &amp;ldquo;왜 JPA를 쓰는가&amp;quot;와 &amp;ldquo;언제 MyBatis가 더 나은가&amp;quot;를 맥락 있게 설명할 수 있다.&lt;/p&gt;
&lt;p&gt;다음 편에서는 Spring Data JPA — 영속성 컨텍스트와 N+1 문제를 자세히 정리한다.&lt;/p&gt;</description></item></channel></rss>