#5-1 spring boot 게시판 만들기 - 게시글 목록과 MyBatis 설정

😊 본 게시글은 김인우님의 스프링 부트 시작하기를 참고해 공부한 내용입니다.

Java -> java11
IDE -> eclipse 3.18
DataBase -> MariaDB


DB 테이블 생성

쿼리문을 이용해 t_board 테이블을 생성함 (SQLyog 사용)

CREATE TABLE t_board (
    board_idx INT AUTO_INCREMENT COMMENT '글 번호',
    title VARCHAR(300) NOT NULL COMMENT '제목',
    contents TEXT NOT NULL COMMENT '내용',
    hit_cnt SMALLINT(10) DEFAULT 0 NOT NULL COMMENT '조회수',
    created_datetime DATETIME NOT NULL COMMENT '작성시간',
    creator_id VARCHAR(50) NOT NULL COMMENT '작성자',
    updated_datetime DATETIME DEFAULT NULL COMMENT '수정시간',
    updater_id VARCHAR(50) DEFAULT NULL COMMENT '수정자',
    deleted_yn CHAR(1) DEFAULT 'N' NOT NULL COMMENT '삭제여부',
    PRIMARY KEY(board_idx)
);

INSERT INTO t_board SET title="게시글1", contents="내용1", created_datetime=NOW(), creator_id="hong";

SELECT * FROM t_board;

임시로 데이터도 함께 추가해주었음

css 추가

src/main/resources/static/css style.css

style.css

@CHARSET "UTF-8";

@import url(http://fonts.googleapis.com/earlyaccess/nanumgothic.css);
@import url(http://cdn.jsdelivr.net/font-nanum/1.0/nanumbarungothic/nanumbarungothic.css);

html{overflow:scorll;}
html, body, div, h1, h2, a, form, table, caption, thead, tbody, tr, th, td, submit {
    margin:0; outline:0; border:0; padding:0; font-size:100%; vertical-align:baseline; background:transparent;
}
body {
    font-size:0.875em; line-height:1.5; color:#666; -webkit-text-size-adjust:none; min-width:320px;
    font-family:'NanumGothic','나눔고딕',dotum, "Helvetica Neue", Helvetica, Verdana, Arial, Sans-Serief;
}
h1, h2, h3 {font-size: 1.5em;}
p{margin:0; padding:0;}
ul{margin:0;}
a:link, a:visited {text-decoration:none; color: #656565;}
input{vertical-align:middle;}
input:focus {outline:0;}
caption {display:none; width:0; height:0; margin-top:-1px; overflow:hidden; visibility:hidden; font-size:0; line-height:0;}

.container {max-width:1024px; margin:30px auto;}
.board_list {width:100%; border-top:2px solid #252525; border-bottom:1px solid #ccc; margin:15px 0; border-collapse: collapse;}
.board_list thead th:first-child {background-image:none;}
.board_list thead th {border-bottom:1px solid #ccc; padding:13px 0; color:#3b3a3a; text-align: center; vertical-align:middle;}
.board_list tbody td {border-top:1px solid #ccc; padding:13px 0; text-align:center; vertical-align:middle;}
.board_list tbody tr:first-child td {border:none;}
.board_list tbody tr:hover{background:#ffff99;}
.board_list tbody td.title {text-align:left; padding-left:20px;}
.board_list tbody td a {display:inline-block}

.board_detail {width:100%; border-top:2px solid #252525; border-bottom:1px solid #ccc; border-collapse:collapse;}
.board_detail tbody input {width:100%;}
.board_detail tbody th {text-align:left; background:#f7f7f7; color:#3b3a3a; vertical-align:middle; text-align: center;}
.board_detail tbody th, .board_detail tbody td {padding:10px 15px; border-bottom:1px solid #ccc;}
.board_detail tbody textarea {width:100%; min-height:170px}

.btn {margin:5px; padding:5px 11px; color:#fff !important; display:inline-block; background-color:#7D7F82; vertical-align:middle; border-radius:0 !important; cursor:pointer; border:none;}
.btn:hover {background: #6b9ab8;}

.file_list a {display:inherit !important;}

Lombok(롬복) 추가

Lombok(롬복) 이란?
자바 라이브러리로 반복되는 getter,setter,toString 등의 메서드 코드를 어노테이션을 통해 줄여주는 라이브러리.
여러가지 어노테이션을 제공하고, 이것을 기반으로 코드를 컴파일 과정에 생성해주는 방식임. 메서드는 눈에 보이지 않지만 코드를 실행할 때는 생성된다는 뜻!

Lombok Download
다운받은 jar 파일 실행해서 Specify location...에 eclipse 실행파일 선택 -> install/update -> 이클립스 재실행

만약 gradle에 추가되어 있지 않다면 build.gradle 에 추가

compileOnly 'org.projectlombok:lombok'

Spring-Boot-Devtools 추가

Spring-Boot-Devtools 이란?
spring boot가 제공하는 옵셔널한 툴
크게 다섯가지 기능을 제공하고, 주로 세가지 기능이 자주 쓰임
Property Defaults : 개발 시점과 배포 시점에 다른 설정을 기본적으로 개발 단계에 맞춰 설정해줌
Automatic Restart : 코드가 바뀔 때마다 바로 스프링 애플리케이션이 재실행된다.
Live Reload : JS 파일을 수정하기만 해도 자동으로 브라우저가 새로고침 됨

lombok과 마찬가지로 만약 gradle에 추가되어 있지 않다면 build.gradle 에 추가

developmentOnly 'org.springframework.boot:spring-boot-devtools'

스프링의 데이터 접근 흐름


DTO 생성

dto?
계층 간 데이터를 주고 받는데 사용되는 객체
계층이란? 뷰, 컨트롤러, 서비스, DAO, DB

board 패키지 아래 dto 패키지 생성 > BoardDto 클래스 생성

BoardDto.java

package board.dto;

import lombok.Data;

@Data //lombok을 사용하기 때문에 getter,setter를 적어주지 않아도 됨
public class BoardDto {
    private int boardIdx;
    private String title;
    private String contents;
    private int hitCnt;
    private String creatorId;
    private String createdDatetime;
    private String updaterId;
    private String updatedDatetime;
}

Java → 카멜 표기법 (userId)
DB → 스네이크 표기법 (user_id)
표기법이 달라 맞춰 주어야 함 따라서 마이바티스 사용!

MyBatis 연동

MyBatis란?
관계형 데이터 베이스 프로그래밍을 쉽게 할 수 있도록 도와주는 개발 프레임 워크
SQL 쿼리들을 한 파일에 구성해, 프로그램 코드와 SQL을 분리할 수 있음
따라서 코드의 간결성과 유지보수성이 향상하고, 빠른 개발이 가능해 생산성이 향상된다.

application.properties

application.properties 에 추가

mybatis.configuration.map-underscore-to-camel-case=true

bean 추가

MyBatis를 사용하기 위해 DatabaseCofiguration.java에 bean 추가

@Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
@Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath:/mapper/**/*.xml")); 
        // 해당 코드 한줄만 추가 
        sqlSessionFactoryBean.setConfiguration(mybatisConfig());
        return sqlSessionFactoryBean.getObject();
    }

mapper 생성

src/main/resources 밑에 mapper 폴더 생성

컨트롤러

컨트롤러?
사용자의 요청을 받아 해당 요청을 수행하는 데 필요한 로직을 호출하고, 그 결과를 포함해 응답을 해 주는 디스패처(Dispatcher) 역할

board 패키지 아래 controller 패키지 생성 > BoardController 클래스 생성

BoardController.java

package board.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import board.dto.BoardDto;
import board.service.BoardService;

@Controller // 컨트롤러라고 선언함
public class BoardController {

    @Autowired
    private BoardService boardService; //서비스와 연결

    @RequestMapping("/board/openBoardList.do") //노테이션의 값으로 주소 지정
    public ModelAndView openBoardList() throws Exception{
    	//templates 폴더 아래있는 /boardList.html을 의미함. Thymeleaf와 같은 템플릿엔진을 사용할 경우 스프링 부트의 자동 설정 기능으로 '.html'과 같은 접미사 생략 가능
    	ModelAndView mv = new ModelAndView("/boardList"); 
        //게시글 목록을 조회하기 위해 ServiceImpl 클래스의 selectBoardList 메서드 호출
        List<BoardDto> list = boardService.selectBoardList();  
        mv.addObject("list", list);

        return mv;
    }
}

여기서 오류가 뜰텐데 BoardService를 만들지 않았기 때문에 오류가 뜨는 것은 당연한 것임!

서비스

서비스?
비지니스 로직을 수행하기 위한 메서드를 정의함

board 패키지 아래 service 패키지 생성 > BoardService 인터페이스와 BoardServiceImpl 클래스를 생성

BoardService vs BoardServiceImpl
서비스는 일반적으로 두개로 구성되는데 Service 인터페이스와 ServiceImpl 클래스로 구성된다. 이렇게 분리하는 이유는 각 기능 간의 의존관계를 최소화하고, 유연함을 가질 수 있으며 모듈화를 통해 재사용성을 높이기 위함임.

BoardService.java

package board.service;

import java.util.List;

import board.dto.BoardDto;

public interface BoardService {

    List<BoardDto> selectBoardList() throws Exception;

}

BoardServiceImpl.java

package board.service;

import java.util.List;

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

import board.dto.BoardDto;
import board.mapper.BoardMapper;

@Service //서비스임을 선언
public class BoardServiceImpl implements BoardService{

	@Autowired //Mapper와 연결
	private BoardMapper boardMapper;

	@Override
	public List<BoardDto> selectBoardList() throws Exception {
		// TODO Auto-generated method stub
		return boardMapper.selectBoardList();
	}
}

Mapper

mapper?
데이터 접근 객체인 DAO와 같은 역할. 마이바티스에서는 DAO보다 SqlSessionDaoSupport나 SqlSessionTemplate를 사용하기를 권장함. 매퍼를 사용하면 일일이 DAO를 만들지 않고, 인터페이스만을 이용해 편하게 개발이 가능하다.

board 패키지 아래 mapper 패키지 생성 > BoardMapper 인터페이스 생성

BoardMapper.java

package board.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import board.dto.BoardDto;

@Mapper		// Mapper라고 선언함
public interface BoardMapper {
	// 여기서 지정한 메서드의 이름은 쿼리의 이름과 동일해야 함 (selectBoardList)
	List<BoardDto> selectBoardList() throws Exception; 
}

SQL 작성

마이바티스는 쿼리를 XML에 작성하고 아이디를 이용해 매핑함.

src/main/resources/mapper에 sql-board.xml 생성

sql-board.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="board.mapper.BoardMapper">	<!-- xml파일을 사용할 mapper가 있는 경로 -->
  
	<!-- <select> 태그를 이용하여 select 쿼리임을 나타냄. 앞서 mapper에서 말했던 메소드의 이름은 select 쿼리의 id값과 같아야함. -->
	<select id="selectBoardList" resultType="board.dto.BoardDto">	
		<![CDATA[
			SELECT
				board_idx, title, hit_cnt, DATE_FORMAT(created_datetime, '%Y.%m.%d %H:%i:%s') AS created_datetime
			FROM
				t_board
			WHERE
				deleted_yn = 'N'
			ORDER BY board_idx DESC
		]]>
	</select>
	
</mapper>

쿼리문을 맞게 작성했는지 DB에서 테스트해보기

View 작성

src/main/resources/templates 폴더에 boardList.html 생성

boardList.html

<!DOCTYPE html>
<html lang="ko" xmlns:th="<http://www.thymeleaf.org>">
<head>
	<title>board</title>
	<link rel="stylesheet" th:href="@{/css/style.css}"></link>
</head>
<body>
	<div class="container">
		<h2>게시글 목록</h2>
		<table class="board_list">
			<colgroup>
				<col width="15%"/>
				<col width="*"/>
				<col width="15%"/>
				<col width="25%"/>
			</colgroup>
			<thead>
				<tr>
					<th scope="col">글번호</th>
					<th scope="col">제목</th>
					<th scope="col">조회수</th>
					<th scope="col">작성일</th>
				</tr>
			</thead>
			<tbody>
				<tr th:if="${#lists.size(list)} > 0" th:each="list : ${list}">
					<td th:text="${list.boardIdx}"></td>
					<td th:text="${list.title}"></td>
					<td th:text="${list.hitCnt}"></td>
					<td th:text="${list.createdDatetime}"></td>
				</tr>
				<tr th:unless="${#lists.size(list)} > 0">
					<td colspan="4">조회된 결과가 없습니다.</td>
				</tr>
			</tbody>
		</table>
	</div>
</body>
</html>

실행

http://localhost:8080/board/openBoardList.do

만약 한글이 깨져서 나온다면?

windows > Preferences > Workspace > Text file encoding을 other: UTF-8 로 설정

그래도 한글이 깨져 나오면 html 파일 head에 코드 추가

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

좋은 웹페이지 즐겨찾기