개발을 시작해봐요! 스프링 부트(Spring Boot ) - 게시판 CRUD 처리하기

개발을 시작해봐요! 스프링 부트(Spring Boot ) - 게시판 CRUD 처리하기

이전 포스팅에서 우리는 스프링 부트와 MySQL을 연동하는 방법을 알아보았습니다. 이번 포스팅부터는 약속대로 게시판을 구현해 보도록 하겠습니다. 대학에서 동아리원들과 처음으로 진행해 본 프로젝트가 게시판이었는데요, 저도 그랬었지만 "뜬금없이 갑자기 무슨 게시판?"이라는 생각이 들 수 있습니다. 하지만 웹 개발에 있어서 게시판은 가장 기본적이고, 어떠한 시스템이던 게시판의 특성을 가지고 있습니다. 이번 포스팅의 제목은 "게시판 CRUD 처리하기"인데요, CRUD는 각각 Create(등록), Read(조회), Update(수정), Delete(삭제)를 의미합니다. 게시판 만들기 시작합니다!

1. 게시판 테이블 생성하기

먼저 게시글을 저장할 게시판 테이블을 생성해 보도록 하겠습니다. MySQL Workbench를 실행하고, Develop 커넥션에 접속해 보도록 하겠습니다. 우리는 이전 포스팅에서 Default Schema를 board로 설정했었습니다. board 스키마가 활성화되어있는지 확인하고, 게시판 테이블을 생성하는 다음의 스크립트를 실행해 주세요.

CREATE TABLE tb_board ( idx INT NOT NULL AUTO_INCREMENT COMMENT '번호 (PK)', title VARCHAR(100) NOT NULL COMMENT '제목', content VARCHAR(3000) NOT NULL COMMENT '내용', writer VARCHAR(20) NOT NULL COMMENT '작성자', view_cnt INT NOT NULL DEFAULT 0 COMMENT '조회 수', notice_yn ENUM('Y', 'N') NOT NULL DEFAULT 'N' COMMENT '공지글 여부', secret_yn ENUM('Y', 'N') NOT NULL DEFAULT 'N' COMMENT '비밀글 여부', delete_yn ENUM('Y', 'N') NOT NULL DEFAULT 'N' COMMENT '삭제 여부', insert_time DATETIME NOT NULL DEFAULT NOW() COMMENT '등록일', update_time DATETIME NULL COMMENT '수정일', delete_time DATETIME NULL COMMENT '삭제일', PRIMARY KEY (idx) ) COMMENT '게시판';

제대로 생성이 되었는지 확인하려면 DESC tb_board 또는 SHOW TABLES 명령어를 실행하거나, 스키마의 이름에서 마우스 오른쪽 버튼을 클릭하고, 가장 하단의 Refresh All을 클릭하면 테이블이 생성되었음을 확인할 수 있습니다. 다음은 DESC tb_board 명령어를 실행한 결과입니다. 여기서 enum 타입은 뒤에서 다룰 예정입니다. 지금은 enum이라는 타입이 있다는 것만 기억해 주세요.

2. 도메인 클래스 생성하기

앞에서 생성했던 TB_BOARD 테이블을 구조화하는 역할을 하는 클래스를 생성해 보도록 하겠습니다. 보통 테이블 구조화 클래스는 xxxVO 또는 xxxDTO로 네이밍을 하는데요, VO는 Read Only(읽기 전용)의 특성을 가진다고 합니다. 우리는 입력받은 데이터를 저장하고, 전송하는 역할을 하는 DTO라는 이름으로 네이밍 하도록 하겠습니다. 먼저 src/main/java 디렉터리의 com.board 패키지 밑에 controller, domain, mapper, service 네 개의 패키지를 추가해 주세요. 결과적으로 다음과 같은 구조가 되어야 합니다.

이제 domain 패키지에 BoardDTO 클래스를 추가하고, 더보기의 코드를 작성해 주세요. 인스턴스 변수의 순서는 테이블의 컬럼 순서와 동일합니다.

더보기 package com.board.domain; import java.time.LocalDateTime; import lombok.Getter; import lombok.Setter; @Getter @Setter public class BoardDTO { /** 번호 (PK) */ private Long idx; /** 제목 */ private String title; /** 내용 */ private String content; /** 작성자 */ private String writer; /** 조회 수 */ private int viewCnt; /** 공지 여부 */ private String noticeYn; /** 비밀 여부 */ private String secretYn; /** 삭제 여부 */ private String deleteYn; /** 등록일 */ private LocalDateTime insertTime; /** 수정일 */ private LocalDateTime updateTime; /** 삭제일 */ private LocalDateTime deleteTime; }

클래스 레벨에 지정한 @Getter, @Setter는 롬복(Lombok)이라는 라이브러리에서 제공해주는 기능입니다. 기존의 방식대로 getter/setter를 추가해도 무관하고, 롬복을 사용해보고 싶으시다면 롬복 설치하기를 진행해 주시면 되겠습니다.

이클립스나 STS에서 Alt + Shift + S 키를 누르고, Generate Getters and Setters를 클릭해서 getter/setter를 쉽게 생성할 수 있습니다.

3. Mapper 인터페이스 생성하기

이제 데이터베이스와의 통신 역할을 하는 Mapper 인터페이스와 XML을 추가해 보도록 하겠습니다. 먼저 앞에서 생성한 mapper 패키지에 BoardMapper 인터페이스를 생성하고, 다음의 코드를 작성해 주세요.

package com.board.mapper; import java.util.List; import org.apache.ibatis.annotations.Mapper; import com.board.domain.BoardDTO; @Mapper public interface BoardMapper { public int insertBoard(BoardDTO params); public BoardDTO selectBoardDetail(Long idx); public int updateBoard(BoardDTO params); public int deleteBoard(Long idx); public List selectBoardList(); public int selectBoardTotalCount(); }

@Mapper

기존의 스프링에서 Data Access Object(이하 DAO) 클래스에 @Repository를 지정해서 해당 클래스가 데이터베이스와 통신하는 클래스임을 나타내고는 했었습니다. 하지만 마이바티스(MyBatis)는 인터페이스에 @Mapper만 지정해주면 XML에서 메서드의 이름과 일치하는 SQL 문을 찾아서 실행합니다. Mapper 영역은 데이터베이스와의 통신, 즉 쿼리를 호출하는 것이 전부이기 때문에 다른 로직은 전혀 필요하지 않습니다.

insertBoard

게시글을 등록하는 INSERT 쿼리를 호출하는 메서드입니다. 리턴 타입이 int로 되어 있는데요, 보통 insert, update, delete와 같은 쿼리를 호출하는 메서드는 void를 리턴 타입으로 가지는 경우가 많습니다. 하지만 서비스에서 결괏값을 가지고 확실하게 처리하기 위해서 int를 사용합니다. 서비스에서 비즈니스 로직을 작성하는 과정에서 다시 한번 이야기하도록 하겠습니다. 인자로 BoardDTO 클래스를 params라는 이름으로 지정했는데요, 뒤에서 테스트를 진행하면서 자세히 알아보도록 하겠습니다. 지금은 게시글의 정보가 담겨있는 파라미터 클래스 정도로 생각해 주세요

selectBoardDetail

하나의 게시글을 조회하는 SELECT 쿼리를 호출하는 메서드입니다. 리턴 타입이 BoardDTO로 되어 있는데요, SELECT 쿼리를 실행하면 그에 해당하는 결괏값이 BoardDTO 클래스의 각 인스턴스 변수들에 매핑됩니다. 인자로 지정된 idx는 게시글의 PK인 게시글 번호를 의미합니다. IDX를 WHERE 조건으로 사용해서 특정 게시글을 조회합니다.

updateBoard

게시글을 수정하는 UPDATE 쿼리를 호출하는 메서드입니다. 인자로 지정된 params는 insertBoard 메서드와 마찬가지로 뒤에서 알아보도록 하겠습니다.

deleteBoard

게시글을 삭제하는 DELETE 쿼리를 호출하는 메서드입니다. 우리는 앞에서 테이블에 delete_yn 컬럼을 생성했었는데요, 이 컬럼은 실제로 데이터를 삭제하지 않고, 상태를 'Y' 또는 'N'으로 지정해서 삭제 여부가 'N'으로 되어 있는 컬럼들만 노출하게끔 하기 위한 역할을 합니다. 정말 중요한 데이터들이 테이블에서 DELETE가 되어버리면 손실이 크기 때문에 요즘에는 이러한 방법을 많이 사용합니다. 인자로 지정된 idx를 WHERE 조건으로 사용해서 특정 게시글을 삭제합니다.

selectBoardList

게시글 목록을 조회하는 SELECT 쿼리를 호출하는 메서드입니다. 리턴 타입이 List로 되어 있는데요, 쉽게 이야기해서 selectBoardDetails 메서드의 결과 여러 개를 리스트에 담는 것으로 생각하시면 되겠습니다.

selectBoardTotalCount

사용하는 게시글의 개수를 조회하는 SELECT 쿼리를 호출하는 메서드입니다. 추후에 페이징 처리를 하면서 사용되는 메서드입니다.

4. 마이바티스 XML Mapper 생성하기

다음으로 마이바티스의 XML Mapper에 SQL 문을 작성해 보도록 하겠습니다. 먼저 src/main/resources 디렉터리에 mappers 폴더를 생성해 주세요.

다음으로 mappers 폴더에 BoardMapper.xml 파일을 추가해 주세요. MyBatis XML Mapper 파일은 개발 환경 설정하기에서 MyBatipse 플러그인을 설치해야 보이는 파일입니다. 일반 XML 파일을 사용해도 무관하긴 하지만 자동 완성(Ctrl + Space) 기능을 사용할 수 없기 때문에 플러그인을 설치하시는 것을 권장합니다.

파일이 생성되었으면 STS 중앙의 Source 탭을 선택하고, 더보기의 코드를 입력해 주세요. Copy & Paste를 해도 되지만 정확한 이해를 위해서 꼭! 한 줄 한 줄 타이핑하시는 것을 권장합니다.

더보기 idx , title , content , writer , view_cnt , notice_yn , secret_yn , delete_yn , insert_time , update_time , delete_time INSERT INTO tb_board ( ) VALUES ( #{idx} , #{title} , #{content} , #{writer} , 0 , IFNULL(#{noticeYn}, 'N') , IFNULL(#{secretYn}, 'N') , 'N' , NOW() , NULL , NULL ) SELECT FROM tb_board WHERE delete_yn = 'N' AND idx = #{idx} UPDATE tb_board SET update_time = NOW() , title = #{title} , content = #{content} , writer = #{writer} , notice_yn = IFNULL(#{noticeYn}, 'N') , secret_yn = IFNULL(#{secretYn}, 'N') WHERE idx = #{idx} UPDATE tb_board SET delete_yn = 'Y' , delete_time = NOW() WHERE idx = #{idx} SELECT FROM tb_board WHERE delete_yn = 'N' ORDER BY notice_yn ASC, idx DESC, insert_time DESC SELECT COUNT(*) FROM tb_board WHERE delete_yn = 'N'

태그

마이바티스에서 쿼리문을 담아 두고 있는 파일을 XML Mapper라 이야기합니다. 와 사이에 쿼리와 관련된 태그 형태의 코드들이 존재하게 됩니다. 여기서 중요한 것은 태그를 여는 부분을 보면 namespace(이하 네임스페이스)라는 속성에 BoardMapper 인터페이스의 경로를 지정했는데요, 네임스페이스는 XML Mapper의 쿼리 문과 Mapper 인터페이스의 메서드를 매핑하기 위해 지정하는 속성입니다.

태그와 태그

마이바티스에서 과 사이에 원하는 쿼리 조각을 정의하고, 인클루드(include) 해서 사용할 수 있습니다. 예를 들어 SELECT를 할 때 asterisk(*)를 사용해서 전체 컬럼을 조회할 수 있지만 서브 쿼리나 조인을 사용하는 경우에는 일일이 컬럼을 직접 쿼리문에 추가해야 하는 경우가 생기게 됩니다. 만약 컬럼이 30개라면 30개의 컬럼을 일일이 SELECT 절에 지정해야 합니다. 이러한 반복적인 작업이나 공통으로 사용되는 부분을 효율적으로 처리하기 위해 사용합니다. 우리는 boardColumns라는 이름의 SQL 조각을 와 같은 형태로 게시판 테이블의 전체 컬럼을 인클루드 해서 사용합니다.

parameterType

각 쿼리를 실행하기 위해서 필요한 파라미터의 타입을 해당 속성을 사용해서 지정합니다. CRUD 테스트를 진행하는 과정에서 알아보도록 하겠습니다.

resultType

쿼리의 실행 결과를 매핑할 타입을 지정합니다. parameterType과 마찬가지로 테스트를 진행하는 과정에서 알아보도록 하겠습니다.

파라미터 표현식

마이바티스에서 쿼리에 파라미터를 사용할 때 #{ } 표현식을 사용합니다. 마찬가지로 테스트를 진행하는 과정에서 알아보도록 하겠습니다.

5. 마이바티스 SELECT 컬럼과 DTO의 인스턴스 변수 매핑하기

마이바티스에서 SELECT 문의 결과 컬럼은 DTO의 인스턴스 변수와 매핑됩니다. 하지만 XML Mapper의 boardColumns를 보면 notice_yn AS noticeYn과 같은 별칭(Alias) 처리를 하지 않고, 테이블의 컬럼명과 같이 언더바(_)가 들어가는 스네이크 케이스를 사용합니다. 하지만 자바에서 변수의 이름은 소문자로 시작하고, 구분되는 단어는 앞 글자만 대문자로 처리하는 카멜 케이스를 사용합니다. 이러한 경우에 마이바티스의 map-underscore-to-comel-case 설정을 사용할 수 있습니다. application.properties 파일을 열고, 해당 설정을 추가해 주세요.

#HikariCP 데이터 소스(DataSource) spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.hikari.jdbc-url=jdbc:mysql://localhost:3306/board?serverTimezone=UTC&useUnicode;=true&characterEncoding;=utf8&useSSL;=false spring.datasource.hikari.username=username spring.datasource.hikari.password=password spring.datasource.hikari.connection-test-query=SELECT NOW() FROM dual #MyBatis mybatis.configuration.map-underscore-to-camel-case=true

6. DBConfiguration 클래스 처리하기

application.properties에 마이바티스 설정이 추가되었으니 해당 설정을 처리할 빈(Bean) 메서드를 정의해야 합니다. DBConfiguration 클래스를 열고, 더보기의 코드와 같이 변경해 주세요.

더보기 package com.board.configuration; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @Configuration @PropertySource("classpath:/application.properties") public class DBConfiguration { @Autowired private ApplicationContext applicationContext; @Bean @ConfigurationProperties(prefix = "spring.datasource.hikari") public HikariConfig hikariConfig() { return new HikariConfig(); } @Bean public DataSource dataSource() { return new HikariDataSource(hikariConfig()); } @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource()); factoryBean.setMapperLocations(applicationContext.getResources("classpath:/mappers/**/*Mapper.xml")); factoryBean.setTypeAliasesPackage("com.board.domain"); factoryBean.setConfiguration(mybatisConfg()); return factoryBean.getObject(); } @Bean public SqlSessionTemplate sqlSession() throws Exception { return new SqlSessionTemplate(sqlSessionFactory()); } @Bean @ConfigurationProperties(prefix = "mybatis.configuration") public org.apache.ibatis.session.Configuration mybatisConfg() { return new org.apache.ibatis.session.Configuration(); } }

40번 라인 setMapperLocations 메서드

setMapperLocations 메서드의 주석이 해제된 이유는 다음과 같습니다. 해당 설정은 getResources 메서드의 인자로 지정된 패턴과 동일한 XML Mapper 파일들을 인식하도록 하는 역할을 하는데요, 기존에는 XML Mapper 파일이 없었지만 앞에서 BoardMapper.xml 파일이 추가되었으니 해당 파일을 인식할 수 있도록 주석을 해제합니다.

41번 라인 setTypeAliasesPackage 메서드

BoardMapper.xml 파일에서 parameterType과 resultType은 클래스의 풀 패키지 경로가 포함되어야 합니다. 우리는 BoardDTO와 같이 클래스의 이름만 지정하였는데요, 해당 메서드를 사용해서 패키지 경로를 제외할 수 있습니다. 만약 패키지의 구조가 복잡할 땐 com.board.*.domain과 같이 asterisk(*)를 사용할 수 있습니다.

42번 라인 setConfiguration 메서드

51~55번 라인에 추가된 마이바티스 설정 관련 빈(Bean)을 지정합니다.

51~55번 라인 mybatisConfig 메서드

해당 라인에 빈(Bean)이 추가되었습니다. application.properties에서 mybatis.configuration으로 시작하는 모든 설정을 읽어 들여서 빈(Bean)으로 등록합니다.

변경된 DBConfiguration 클래스

7. CRUD 테스트하기

이제 Mapper 영역의 테스트를 진행해 보도록 하겠습니다. src/test/java 디렉터리의 com.board 패키지 안에 MapperTests 클래스를 생성하고, 다음의 코드를 추가해 주세요.

더보기 package com.board; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import com.board.domain.BoardDTO; import com.board.mapper.BoardMapper; @SpringBootTest class MapperTests { @Autowired private BoardMapper boardMapper; @Test public void testOfInsert() { BoardDTO params = new BoardDTO(); params.setTitle("1번 게시글 제목"); params.setContent("1번 게시글 내용"); params.setWriter("테스터"); int result = boardMapper.insertBoard(params); System.out.println("결과는 " + result + "입니다."); } }

boardMapper

@Autowired를 사용해서 BoardMapper 빈(Bean)을 주입합니다.

testOfInsert 메서드

게시글 등록을 테스트하는 메서드입니다. 테이블을 구조화한 클래스인 BoardDTO 클래스의 인스턴스를 생성하고, setter를 사용해서 제목, 내용, 작성자를 지정합니다. 그리고 insertBoard 메서드의 인자로 params를 넘겨주었습니다. BoardMapper.xml 파일에서 20번 라인의 insert 쿼리를 보면 id가 insertBoard이고 parameterType은 BoardDTO로 지정되어 있습니다. id는 태그의 namepsace 속성에 지정된 BoardMapper 인터페이스의 insertBoard 메서드를 의미합니다. parameterType은 속성의 이름 그대로 쿼리에서 사용할 파라미터의 타입을 의미합니다. 즉, params의 타입인 BoardDTO 클래스가 파라미터 타입이 됩니다. 그리고 23번 라인의 VALUES 부분을 보면 params 인스턴스를 생성하고, setter로 값을 지정했었던 제목, 내용, 작성자만 파라미터 표현식인 #{ } 안에 들어가 있습니다. #{idx}의 경우에는 게시판 테이블을 생성하면서 AUTO_INCREMENT 속성을 지정했기 때문에 PK가 자동으로 1씩 증가하기 때문에 따로 처리하지 않아도 됩니다. 만약 오라클과 같이 AUTO_INCREMENT 기능이 없는 데이터베이스의 경우에는 시퀀스(Sequence) 또는 마이바티스의 구문을 사용할 수 있습니다.

Mybatipse 플러그인이 설치되어 있을 때 Mapper 인터페이스의 메서드 이름과 XML Mapper의 id 속성이 일치하면, 즉 정상적으로 매핑이 되어있으면 XML Mapper의 쿼리 id에서 Ctrl + 마우스 왼쪽 버튼을 클릭하거나 F3 버튼을 누르면 해당 id와 매핑된 메서드가 있는 Mapper 인터페이스로 이동하게 됩니다. 이미지와 같이 id 속성이 Active 됩니다.

Ctrl을 누르고 있는 상태에서 id에 마우스를 Over했을 때

이전 포스팅에서 JUnit Test를 진행했을 때와 같이 메서드의 이름을 더블 클릭하고, 마우스 오른쪽 버튼을 클릭한 다음 Run As 또는 Debug As - JUnit Test를 선택하면 됩니다.

JUnit 테스트 실행

JUnit 테스트 실행 결과

JUnit 탭의 테스트 실행 결과

게시판 테이블 조회 결과

다음은 하나의 게시글을 조회하는 기능을 하는 selectBoardDetails 메서드입니다. MapperTests 클래스에 다음의 코드를 추가하고, JUnit 테스트를 진행해 주세요.

@Test public void testOfSelectDetail() { BoardDTO board = boardMapper.selectBoardDetail((long) 1); try { String boardJson = new ObjectMapper().writeValueAsString(board); System.out.println("========================="); System.out.println(boardJson); System.out.println("========================="); } catch (JsonProcessingException e) { e.printStackTrace(); } }

board 변수에 selectBoardDetail 메서드의 결과를 담습니다. 인자로 지정된 (long) 1은 앞에서 추가된 1번 게시글의 PK인 idx를 의미합니다. 그리고 board 인스턴스에 담긴 게시글 정보를 Jackson 라이브러리를 이용해서 boardJson에 담은 뒤에 JSON으로 출력합니다. boardMapper.xml 파일의 38번 라인을 보면 parameterType은 long, resultType은 BoardDTO로 지정되어 있습니다. parameterType은 게시글의 PK인 idx의 타입이고, resultType은 게시판 테이블을 구조화한 클래스인 BoardDTO 클래스를 의미합니다.

JUnit 테스트 실행 결과

다음은 등록된 게시글의 정보를 수정하는 updateBoard 메서드입니다. 사실 게시글 수정은 게시글 생성과 마찬가지로 게시글을 등록하는 작업이라고 생각할 수 있습니다. 그저 INSERT냐 UPDATE냐의 차이입니다. 그렇기 때문에 우리는 서비스 영역에서 비즈니스 로직을 작성할 때 등록을 의미하는 registerBoard라는 이름의 메서드로 INSERT와 UPDATE를 구분해서 처리하게 됩니다. 실제로 코드를 작성하는 과정에서 다시 이야기하도록 하겠습니다. if 문 안의 로직은 testSelectDetails 메서드에서 설명했던 부분과 동일한데요, 게시글 수정에 성공하면 변경된 게시글 정보를 JSON으로 출력합니다.

@Test public void testOfUpdate() { BoardDTO params = new BoardDTO(); params.setTitle("1번 게시글 제목을 수정합니다."); params.setContent("1번 게시글 내용을 수정합니다."); params.setWriter("홍길동"); params.setIdx((long) 1); int result = boardMapper.updateBoard(params); if (result == 1) { BoardDTO board = boardMapper.selectBoardDetail((long) 1); try { String boardJson = new ObjectMapper().writeValueAsString(board); System.out.println("========================="); System.out.println(boardJson); System.out.println("========================="); } catch (JsonProcessingException e) { e.printStackTrace(); } } }

JUnit 테스트 실행 결과

다음은 게시글을 삭제하는 deleteBoard 메서드입니다. 해당 메서드는 실제로 데이터를 삭제하는 것이 아닌 컬럼의 상태만 변경하는 것이기 때문에 XML Mapper에서 deleteBoard는 태그를 사용합니다. 테스트를 실행하면 null을 반환하는데요, SELECT 쿼리의 WHERE 조건에 삭제 여부(delete_yn)가 'N'인 데이터만 조회하도록 처리되어 있기 때문입니다. 삭제된 데이터를 조회할 수 있다면 문제가 되겠죠?

@Test public void testOfDelete() { int result = boardMapper.deleteBoard((long) 1); if (result == 1) { BoardDTO board = boardMapper.selectBoardDetail((long) 1); try { String boardJson = new ObjectMapper().writeValueAsString(board); System.out.println("========================="); System.out.println(boardJson); System.out.println("========================="); } catch (JsonProcessingException e) { e.printStackTrace(); } } }

JUnit 테스트 실행 결과

이제 마지막으로 selectBoardList 메서드입니다. 현재 데이터베이스에는 삭제된 게시글 한 개가 전부이기 때문에 게시글을 여러 개 추가해 보도록 하겠습니다. testOfInsert 메서드를 다음과 같이 변경하고 JUnit 테스트를 진행해 주세요.

@Test public void testMultipleInsert() { for (int i = 2; i <= 50; i++) { BoardDTO params = new BoardDTO(); params.setTitle(i + "번 게시글 제목"); params.setContent(i + "번 게시글 내용"); params.setWriter(i + "번 게시글 작성자"); boardMapper.insertBoard(params); } }

앞의 작업이 완료되었으면 다음의 코드를 작성하고, JUnit 테스트를 진행해 주세요. 코드의 의미는 다음과 같습니다. 먼저 게시글의 전체 개수를 카운팅하고, 게시글이 한개 이상이면 boardList 변수에 selectBoardList 메서드의 실행 결과인 게시글 리스트를 담습니다. 그리고 스프링에서 지원해주는 CollectionUtils의 isEmpty 메서드로 boardList가 비어있지 않은지 체크하고, forEach를 실행합니다.

@Test public void testSelectList() { int boardTotalCount = boardMapper.selectBoardTotalCount(); if (boardTotalCount > 0) { List boardList = boardMapper.selectBoardList(); if (CollectionUtils.isEmpty(boardList) == false) { for (BoardDTO board : boardList) { System.out.println("========================="); System.out.println(board.getTitle()); System.out.println(board.getContent()); System.out.println(board.getWriter()); System.out.println("========================="); } } } }

JUnit 테스트 실행 결과

스프링은 다양한 유틸 클래스들을 제공해주는데요, StringUtils와 ObjectUtils 그리고 testSelectList 메서드에서 사용한 CollectionUtils 등 유용한 기능들이 많이 있습니다.

마무리

이번에는 게시판 테이블을 생성하고, XML Mapper의 쿼리가 정상적으로 실행되는지 Mapper 영역을 테스트하는 시간을 가져보았습니다. 다음 포스팅부터는 화면을 처리하는 컨트롤러(Controller)와 뷰(View) 그리고 비즈니스 로직을 담당하는 서비스(Service)를 처리하는 방법에 대해서 알아보도록 하겠습니다. 추가적으로 프로젝트를 압축해서 첨부하도록 하겠습니다. application.properties에 데이터베이스 정보만 올바르게 변경해서 사용해 주시면 되겠습니다. 다음 포스팅에서 뵙겠습니다. 감사합니다 ^^

Board.zip 0.07MB

from http://congsong.tistory.com/15 by ccl(A) rewrite - 2020-03-20 19:20:27

댓글

이 블로그의 인기 게시물

2020 LCK 롤챔스 Spring 경기 재개 및 일정

데이터 바인딩 추상화 - propertyEditor

Spring Web Form