Spring

Spring MVC 게시판 만들기 4편: MyBatis와 데이터베이스 연동 완벽 가이드


💡 이 글에서 배울 것
MyBatis가 Java 코드와 SQL을 어떻게 연결하는지, 커넥션 풀은 왜 필요한지 쉽게 알아봅니다.


🎯 한눈에 보기

주제핵심 내용
MyBatisSQL과 Java를 연결하는 프레임워크
HikariCP빠른 데이터베이스 커넥션 풀
동적 SQL조건에 따라 변하는 SQL

📌 이번 편에서 배울 것

이번 편에서는 MyBatis가 어떻게 Java와 SQL을 연결하는지 알아봅니다:

  • MyBatis의 기본 개념
  • Mapper 인터페이스와 XML 파일
  • HikariCP 데이터베이스 연결 풀
  • selectKey와 동적 SQL

🤔 MyBatis란?

MyBatis는 SQL을 직접 작성하면서도 Java 코드와 쉽게 연결해주는 프레임워크예요.

flowchart LR
    subgraph "Java 세계"
        Mapper["BoardMapper.java<br/>(인터페이스)"]
    end
    
    subgraph "MyBatis 다리"
        MB[MyBatis]
    end
    
    subgraph "SQL 세계"
        XML["boardMapper.xml<br/>(SQL 쿼리)"]
    end
    
    subgraph "데이터베이스"
        DB[(MySQL)]
    end
    
    Mapper <--> MB
    MB <--> XML
    XML <--> DB

MyBatis의 장점:

  • ✅ SQL을 직접 작성 → 복잡한 쿼리도 OK
  • ✅ Java 코드와 SQL 분리 → 유지보수 쉬움
  • ✅ 동적 SQL → 조건에 따라 쿼리 변경 가능

⚙️ MyBatis 설정 (root-context.xml)

1. HikariCP (데이터베이스 연결 풀):

커넥션 풀 개념

<!-- 연결 설정 -->
<bean name="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
    <property name="jdbcUrl" 
              value="jdbc:mysql://localhost:3306/springdb?serverTimezone=Asia/Seoul" />
    <property name="username" value="springdbuser" />
    <property name="password" value="1234" />
    <property name="connectionTimeout" value="30000" />
    <property name="minimumIdle" value="2" />
</bean>

<!-- 실제 DataSource -->
<bean name="dataSource" class="com.zaxxer.hikari.HikariDataSource" 
      destroy-method="close">
    <constructor-arg ref="hikariConfig" />
</bean>

💡 커넥션 풀이란? DB 연결을 미리 여러 개 만들어두고 재사용하는 것. 매번 새로 연결하면 느리니까요!

flowchart LR
    subgraph "HikariCP 풀"
        C1[Connection 1]
        C2[Connection 2]
        C3[Connection 3]
    end
    
    App[애플리케이션] --> |"빌려주세요!"| C1
    App --> |"다 썼어요~"| C1
    C1 --> |"풀로 반환"| C1

2. SqlSessionFactory:

<bean class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="mapperLocations" value="classpath:/mapper/*.xml" />
    <property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>

3. Mapper 스캔:

<mybatis-spring:scan base-package="org.zerock.mapper"/>

이 설정으로 org.zerock.mapper 패키지의 모든 Mapper 인터페이스가 자동 등록됩니다!


📋 BoardMapper 인터페이스

Mapper 인터페이스는 메서드만 선언합니다. 구현은 XML에서!

public interface BoardMapper {
    
    int insert(BoardDTO dto);           // 등록
    
    BoardDTO selectOne(Long bno);       // 상세 조회
    
    int remove(Long bno);               // 삭제
    
    int update(BoardDTO dto);           // 수정
    
    List<BoardDTO> list();              // 전체 목록
    
    // 검색 + 페이징 목록
    List<BoardDTO> listSearch(
        @Param("skip") int skip,
        @Param("count") int count,
        @Param("types") String[] types,
        @Param("keyword") String keyword
    );
    
    // 검색 결과 개수
    int listCountSearch(
        @Param("types") String[] types,
        @Param("keyword") String keyword
    );
}

💡 @Param(“이름”): SQL에서 #{이름}으로 값을 받을 수 있게 해줌


📄 boardMapper.xml 분석

1. INSERT (등록) + selectKey:

<insert id="insert">
    <!-- 삽입 후 자동 생성된 bno를 가져오기 -->
    <selectKey order="AFTER" keyProperty="bno" resultType="long">
        SELECT LAST_INSERT_ID()
    </selectKey>
    
    INSERT INTO tbl_board(title, content, writer)
    VALUES (#{title}, #{content}, #{writer})
</insert>
sequenceDiagram
    participant S as Service
    participant M as Mapper
    participant DB as MySQL

    S->>M: insert(dto)
    M->>DB: INSERT INTO tbl_board...
    DB-->>M: 완료 (새 bno = 42)
    M->>DB: SELECT LAST_INSERT_ID()
    DB-->>M: 42
    M->>M: dto.bno = 42 설정
    M-->>S: 1 (영향받은 행 수)

💡 selectKey가 실행되면 dto.getBno()로 새 글 번호를 알 수 있어요!


2. SELECT (조회):

<select id="selectOne" resultType="org.zerock.dto.BoardDTO">
    SELECT * FROM tbl_board WHERE bno = #{bno}
</select>

3. UPDATE (수정):

<update id="update">
    UPDATE tbl_board SET 
        title = #{title}, 
        content = #{content},
        updatedate = now(), 
        delflag = #{delFlag}
    WHERE bno = #{bno}
</update>

4. DELETE (소프트 삭제):

<update id="remove">
    UPDATE tbl_board 
    SET delflag = true 
    WHERE bno = #{bno}
</update>

💡 실제로 DELETE가 아니라 UPDATEdelflag = true만 변경합니다!


🔮 동적 SQL - 검색 기능의 핵심

검색 기능은 조건에 따라 SQL이 달라져야 해요. MyBatis의 동적 SQL 기능을 사용합니다!

<!-- 재사용 가능한 SQL 조각 -->
<sql id="search">
    <if test="types != null and types.length > 0 and keyword != null and keyword != ''">
        <foreach collection="types" item="type" 
                 open=" AND (" close=")" separator="OR">
            <if test='type.equals("T")'>
                title LIKE CONCAT('%', #{keyword}, '%')
            </if>
            <if test='type.equals("C")'>
                content LIKE CONCAT('%', #{keyword}, '%')
            </if>
            <if test='type.equals("W")'>
                writer LIKE CONCAT('%', #{keyword}, '%')
            </if>
        </foreach>
    </if>
</sql>

사용 예시:

<select id="listSearch" resultType="BoardDTO">
    SELECT bno, title, writer, regDate 
    FROM tbl_board
    WHERE delflag = false
    
    <include refid="search"></include>  <!-- 동적 검색 조건 삽입 -->
    
    ORDER BY bno DESC
    LIMIT #{count} OFFSET #{skip}
</select>

📊 동적 SQL 결과 예시

검색 조건생성되는 SQL
조건 없음WHERE delflag = false ORDER BY...
T + “스프링”WHERE delflag = false AND (title LIKE '%스프링%') ORDER BY...
TC + “자바”WHERE delflag = false AND (title LIKE '%자바%' OR content LIKE '%자바%') ORDER BY...
TCW + “홍길동”WHERE delflag = false AND (title LIKE '%홍길동%' OR content LIKE '%홍길동%' OR writer LIKE '%홍길동%') ORDER BY...
flowchart TD
    Input["types=TC, keyword=자바"] --> Check{types가 있나?}
    Check --> |Yes| ForEach["foreach 시작"]
    ForEach --> T{type=T?}
    T --> |Yes| TQ["title LIKE '%자바%'"]
    ForEach --> C{type=C?}
    C --> |Yes| CQ["content LIKE '%자바%'"]
    TQ --> |OR| Final["( title LIKE ... OR content LIKE ... )"]
    CQ --> Final

📝 정리

개념설명
MyBatisSQL과 Java를 연결하는 프레임워크
HikariCP빠른 DB 커넥션 풀
Mapper인터페이스 = 메서드 선언, XML = SQL 구현
selectKeyINSERT 후 자동 생성된 키 가져오기
동적 SQL<if>, <foreach>로 조건부 SQL 생성

🚀 다음 편 예고

**5편: 페이징과 검색 기능 구현하기**에서는:

  • JSP에서 페이징 UI 구현
  • JavaScript 이벤트 처리
  • 검색 조건 유지하기

을 알아볼 예정입니다!


📚 시리즈 목차