데이터융합 JAVA응용 SW개발자 기업 채용연계 연수과정 61일차 강의 정리

misung·2022년 6월 23일
0

Spring

MyBatis

학생 점수 프로젝트 MyBatis로 전환하기 (이어서)

[ScoreMapper.xml] 점수 목록 조회, 삭제, 개별 조회 기능 추가

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    

<mapper namespace="com.spring.db.repository.IScoreMapper">

	<!-- DB컬럼명과 VO의 필드(멤버변수)명을 맞추는 ResultMap 선언
		컬럼명과 VO의 변수명이 서로 일치하지 않는다면,
		SQL을 아무리 잘 작성해도, MyBatis에서 값을 끌고 오지 못한다...
	 -->
	 <resultMap type="com.spring.db.model.ScoreVO" id="ScoreMap">
	 	<id property="stuId" column="stu_id" /> <!-- id는 primary key 맵핑 -->
	 	<result property="stuName" column="stu_name" /> <!-- 나머지 컬럼은 다 result -->
	 </resultMap>

	<!-- 점수 등록 기능 -->
	<insert id="insertScore">
		INSERT INTO scores
		VALUES(id_seq.NEXTVAL,#{stuName},#{kor},#{eng},#{math},#{total},#{average})
	</insert>
	
	<!-- 점수 목록 조회 기능 -->
	<select id="selectAllScores" resultMap="ScoreMap">
		SELECT * FROM scores
		ORDER BY stu_id ASC
	</select>
	
	<!-- 점수 삭제 기능 -->
	<delete id="deleteScore">
		DELETE FROM scores
		WHERE stu_id=#{num}
	</delete>
	
	<!-- 점수 개별 조회 기능 -->
	<select id="selectOne" resultMap="ScoreMap">
		SELECT * FROM scores
		WHERE stu_id=#{num}
	</select>

</mapper>

어제에 이어서 목록 조회, 점수 삭제, 개별 조회 기능이 추가되었다.

각 쿼리문은 충분히 이해가 되는 수준이므로 따로 설명하지는 않겠다.

그리고 해당 쿼리가 어떤 작업을 수행하는지에 따라 태그의 시작이 <insert><select> 와 같이 지정되어 있고, EL문과 비슷하게 #{num} 등과 같이 변수를 지정하고 있다는 점을 잊지 말자.

간과하고 넘어갈 뻔 했는데, 상단의 <resultMap> 태그는 다중 행을 반환받는 쿼리문의 경우에 그것에 관해 지정해 두는 부분인데, VO 의 멤버변수와, DB 내의 변수 이름이 일치할 경우 별 문제 없이 resultSet 을 받아올 수 있지만, 자바는 카멜케이스, DB는 스네이크케이스로 작성하므로 변수명이 일치하지 않아 VO에 값이 대입되지 않는 문제가 생길 수 있다.

따라서, <resultMap> 태그에 type 에는 어떤 type인지 알려주기 위해서 VO 를, id 에는 resultMap 에 대한 id 를 지정해 두고 (나중에 호출하기 위함),

그 안의 <id> 태그의 경우에는 DB상의 primary key 에 매칭되는 VO의 멤버변수와 컬럼명을 적는다.
그 외의 컬럼이나 멤버변수의 경우에는 <result> 태그로 맵핑하면 된다.

이렇게 VO 의 멤버변수와 DB 상의 컬럼명이 서로 다르더라도, <resultMap> 태그로 매핑시켜주면 값이 잘 대입되게 된다.

게시판 프로젝트 MyBatis로 전환하기

[IBoardMapper.java][BoardMapper.xml] 생성 및 [BoardService.java] 수정

IBoardMapper.java

package com.spring.db.repository;

import java.util.List;

import com.spring.db.model.BoardVO;

public interface IBoardMapper {
	
	//게시글 등록
	void insertArticle(BoardVO vo);

	//전체 게시글 목록
	List<BoardVO> getArticles();

	//게시글 상세 보기
	BoardVO getArticle(int bId);

	//게시글 삭제
	void deleteArticle(int bId);

	//게시글 수정
	void updateArticle(BoardVO vo);
	
	//게시글 검색
	List<BoardVO> searchList(String keyword);


}

게시판에 대해서 일련의 기능을 수행할 메서드를 IBoardMapper 인터페이스에 정의해 둔다.

BoardMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.spring.db.repository.IBoardMapper">

	<resultMap type="com.spring.db.model.BoardVO" id="BoardMap">
		<id property="boardNo" column="board_no" />
	</resultMap>

	<insert id="insertArticle">
		INSERT INTO jdbc_board
		VALUES(bid_seq.NEXTVAL,#{writer},#{title},#{content})
	</insert>
	
	<select id="getArticles" resultMap="BoardMap">
		SELECT * FROM jdbc_board
		ORDER BY board_no DESC
	</select>
	
	<select id="getArticle" resultMap="BoardMap">
		SELECT * FROM jdbc_board
		WHERE board_no=#{bId}
	</select>
	
	<delete id="deleteArticle">
		DELETE FROM jdbc_board
		WHERE board_no=#{bId}
	</delete>
	
	<update id="updateArticle">
		UPDATE jdbc_board SET
		writer=#{writer}, title=#{title}, content=#{content}
		WHERE board_no=#{boardNo}
	</update>
	
	<select id="searchList" resultMap="BoardMap">
		SELECT * FROM jdbc_board
		WHERE writer LIKE #{keyword}
	</select>

</mapper>

이제 BoardMapper.xml 파일에 이것이 MyBatis를 통한 Mapper 파일임을 알리기 위해 어제 만든 ScoreMapper 파일의 상단 부분을 복사해 오고, 내용을 채워나간다.

앞서 설명한 <resultMap> 태그 내의 <id> 등이 어떤 역할을 하고 있는지는 설명했으므로 추가 설명 없이 넘어가겠다.

BoardService.java

package com.spring.db.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.spring.db.model.BoardVO;
import com.spring.db.repository.IBoardDAO;
import com.spring.db.repository.IBoardMapper;

@Service
public class BoardService implements IBoardService {
	
//	@Autowired
//	@Qualifier("boardDAO")
//	private IBoardDAO dao;
	
	@Autowired
	private IBoardMapper mapper;

	@Override
	public void insertArticle(BoardVO vo) {
		mapper.insertArticle(vo);
	}

	@Override
	public List<BoardVO> getArticles() {
		return mapper.getArticles();
	}

	@Override
	public BoardVO getArticle(int bId) {
		return mapper.getArticle(bId);
	}

	@Override
	public void deleteArticle(int bId) {
		mapper.deleteArticle(bId);
	}

	@Override
	public void updateArticle(BoardVO vo) {
		mapper.updateArticle(vo);
	}
	
	@Override
	public List<BoardVO> searchList(String keyword) {
		return mapper.searchList("%" + keyword + "%");
	}

}

서비스 클래스가 원래는 DAO 와 연결되어 있었겠지만, mapper 를 사용하도록 변경해 준다.

이렇게 해 두면, Board 프로젝트가 문제없이 이전처럼 잘 작동하는 것을 확인할 수 있다.

SpringWebMvcProject

사전 설정

전에 Spring 프로젝트들 pom.xml 수정했던거랑 똑같이 라이브러리 버전들 수정해줌. 그리고 추가로 아래의 작업 진행.

MvnRepository로 접속 - Spring TestContext Framework 검색 - 5.3.18 코드 복사

https://mvnrepository.com/artifact/org.springframework/spring-test/5.3.18

<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.18</version>
    <scope>test</scope>
</dependency>

게시판 테이블, 시퀀스 생성

CREATE TABLE mvc_board (
    board_no NUMBER PRIMARY KEY,
    title VARCHAR2(100) NOT NULL,
    content VARCHAR2(2000) NOT NULL,
    writer VARCHAR2(50) NOT NULL,
    reg_date DATE DEFAULT sysdate,
    view_cnt NUMBER DEFAULT 0
);

CREATE SEQUENCE board_seq
     START WITH 1
     INCREMENT BY 1
     MAXVALUE 1000
     NOCYCLE
     NOCACHE;

[BoardVO] 클래스 생성

일단은 클래스 생성만 해둔다.

lombok 다운로드 및 설치

https://projectlombok.org/download
해당 링크에서 최신 버전 다운로드.

이후 STS.exe 나 eclipse.exe 파일이 존재하는 동일 디렉토리에 파일을 놓으면 된다.
단, 경로상에 한글이 포함되는경우 lombok이 동작하지 않을 여지가 있다.

이후 lombok.jar 파일을 더블 클릭하여 실행하고,
ide를 잘 찾아낸 경우 Install / Update 를 눌러 설치 및 실행한다.

project lombok 라이브러리 로드

https://mvnrepository.com/artifact/org.projectlombok/lombok/1.18.24

MvnRepository 로 이동해서 1.18.24 xml 코드를 복사해와 pom.xml에 붙여넣는다.

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    <scope>provided</scope>
</dependency>

[pom.xml] 확인 및 Maven Update

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.spring</groupId>
	<artifactId>mvc</artifactId>
	<name>SpringWebMvcProject</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>5.3.18</org.springframework-version>
		<org.aspectj-version>1.6.10</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<!-- spring-jdbc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<!-- HikariCP: 커넥션 풀 -->
		<dependency>
			<groupId>com.zaxxer</groupId>
			<artifactId>HikariCP</artifactId>
			<version>3.3.1</version>
		</dependency>

		<!-- ojdbc6 DB 커넥터 드라이버 -->
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.4</version>
		</dependency>

		<!-- mybatis 라이브러리 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.6</version>
		</dependency>

		<!-- mybatis와 spring을 연동해 주는 api -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.3.2</version>
		</dependency>

		<!-- spring-test 모듈 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>5.3.18</version>
			<scope>test</scope>
		</dependency>

		<!-- lombok 라이브러리 추가 -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.24</version>
			<scope>provided</scope>
		</dependency>



		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>

		<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
			<exclusions>
				<exclusion>
					<groupId>javax.mail</groupId>
					<artifactId>mail</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jms</groupId>
					<artifactId>jms</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jdmk</groupId>
					<artifactId>jmxtools</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jmx</groupId>
					<artifactId>jmxri</artifactId>
				</exclusion>
			</exclusions>
			<scope>runtime</scope>
		</dependency>

		<!-- @Inject -->
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>

		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- Test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-eclipse-plugin</artifactId>
				<version>2.9</version>
				<configuration>
					<additionalProjectnatures>
						<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
					</additionalProjectnatures>
					<additionalBuildcommands>
						<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
					</additionalBuildcommands>
					<downloadSources>true</downloadSources>
					<downloadJavadocs>true</downloadJavadocs>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.5.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<compilerArgument>-Xlint:all</compilerArgument>
					<showWarnings>true</showWarnings>
					<showDeprecation>true</showDeprecation>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>exec-maven-plugin</artifactId>
				<version>1.2.1</version>
				<configuration>
					<mainClass>org.test.int1.Main</mainClass>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

이런 느낌으로 pom.xml 이 구성된다.

[BoardVO] 수정

이제 lombok을 통해 BoardVO를 이전과는 다르게 구성할 것이다.

package com.spring.mvc.board.model;

import java.sql.Timestamp;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

/*
 -- 게시판 테이블 생성
CREATE TABLE mvc_board (
    board_no NUMBER PRIMARY KEY,
    title VARCHAR2(100) NOT NULL,
    content VARCHAR2(2000) NOT NULL,
    writer VARCHAR2(50) NOT NULL,
    reg_date DATE DEFAULT sysdate,
    view_cnt NUMBER DEFAULT 0
);

-- board_no에 대한 시퀀스 설정
CREATE SEQUENCE board_seq
    START WITH 1
    INCREMENT BY 1
    MAXVALUE 1000
    NOCYCLE
    NOCACHE; 
 */

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class BoardVO {
	
	private int boardNo;
	private String title;
	private String content;
	private String writer;
	private Timestamp regDate;
	private int viewCnt;

}

클래스명에 붙어있는 어노테이션들은 lombok 라이브러리를 로드해야만 사용할 수 있는 어노테이션들이다.

@Getter, @Setter - getter, setter
@NoArgsConstructor - 매개값 없는 생성자
@AllArgsConstructor - 모든 멤버변수를 매개변수로 받는 생성자
@ToString - ToString() 메서드

[mvc-config.xml] 수정 (원래 이름 : root-context.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- JDBC, DB 관련 빈을 등록하고 관리하는 설정 파일 -->
	
	<!-- 히카리 커넥션 풀 빈 등록 -->
	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
		<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:xe" />
		<property name="username" value="spring" />
		<property name="password" value="spring" />
	</bean>
	
	<!-- 히카리 데이터소스 빈 등록 -->
	<bean id="ds" class="com.zaxxer.hikari.HikariDataSource">
		<constructor-arg ref="hikariConfig" />
	</bean>
		
	<!-- 마이바티스 SQL 동작을 위한 핵심 객체 SqlSessionFactory 클래스 빈 등록 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="ds" />
		<property name="mapperLocations" value="classpath:/mappers/**/*Mapper.xml" />
 	</bean>
	
	<!-- 지정한 패키지를 스캔하여 존재하는 mapper 인터페이스를 빈 타입으로 등록. 
		나중에 sqlSessionFactory가 xml파일을 클래스로 변환하여 빈으로 등록하려는 시도를 할 때
		타입을 지정해 줘야 하기 때문. 
	-->
	<mybatis-spring:scan base-package="com.spring.mvc.board.repository"/>		
</beans>

그리고 나서 jdbc, mvc, mybatis 3개를 namespace 탭에서 추가한다. 위 코드상에선 이미 추가되어있긴 하지만 이미 설정을 하고 난 뒤에 복사했기 때문이다.

[IBoardMapper] 인터페이스 생성

package com.spring.mvc.board.repository;

import java.util.List;

import com.spring.mvc.board.model.BoardVO;

public interface IBoardMapper {
	
	//게시글 등록 기능
	void insert(BoardVO article);
	
	//게시글 전체 조회 기능(페이징 전)
	List<BoardVO> getArticleList();
	
	//게시글 상세 조회 기능
	BoardVO getArticle(int boardNo);
	
	//게시글 수정 기능
	void update(BoardVO article);
	
	//게시글 삭제 기능
	void delete(int boardNo);
	
	//게시글 수 조회 기능

}

패키지는 com.spring.mvc.board.repository 하위에 만든다.

[BoardMapper.xml] 생성

classpath: (src/main/resources) 하위에 board 디렉토리를 만들고 그 안에 생성한다.

내용은 이렇게.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
<mapper namespace="com.spring.mvc.board.repository.IBoardMapper">

	

</mapper>

일단 매퍼 만들어만 놓고, 화면이 없는 상태에서 테스트를 진행해본다. 화면이 없는데 테스트를 어떻게 진행하나 생각했는데 junit 라이브러리를 사용하면 된다고 한다.

[BoardMapperTest] 클래스 생성

src/test/java 디렉토리 하위에 com.spring.mvc.board 패키지 내에 BoardMapperTest.java 파일을 만든다.

package com.spring.mvc.board;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.spring.mvc.board.repository.IBoardMapper;

@RunWith(SpringJUnit4ClassRunner.class)
//테스트 환경에서 Mapper 객체 활용을 위해 빈 등록 설정이 있는 xml 파일을 로딩.
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/mvc-config.xml"})
public class BoardMapperTest {
	
	@Autowired
	private IBoardMapper mapper;

}

@RunWith(SpringJUnit4ClassRunner.class) 이렇게 어노테이션을 붙여두면, 테스트를 SpringJUnit4ClassRunner 이 객체 타입으로 돌리겠다는 뜻이 된다. (정확히 뭐 하는 객체인지는 모르겠고)

@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/mvc-config.xml"}) 이 부분은, mapper 객체를 자동주입하기 위해서는 mapper.xml의 위치를 알아야 하는데, 설정 파일을 끌어와서 그것을 해결하는 부분이다.

아까 mvc-config.xml 파일 안에 이 부분을 참고하면 된다.

mvc-config.xml

<!-- 지정한 패키지를 스캔하여 존재하는 mapper 인터페이스를 빈 타입으로 등록. 
		나중에 sqlSessionFactory가 xml파일을 클래스로 변환하여 빈으로 등록하려는 시도를 할 때
		타입을 지정해 줘야 하기 때문. 
	-->
	<mybatis-spring:scan base-package="com.spring.mvc.board.repository"/>

이 부분에 닿아서 *.repository 패키지에 닿아서 그 패키지 안의 IBoardMapper 인터페이스를 찾게 되어 자동 주입을 해 줄 것이다.

이것으로 오늘 강의는 마치게 되었다!

0개의 댓글