😊 본 게시글은 김인우님의 스프링 부트 시작하기를 참고해 공부한 내용입니다.
Java -> java11
IDE -> eclipse 3.18
DataBase -> MariaDB
쿼리문을 이용해 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;
임시로 데이터도 함께 추가해주었음
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(롬복) 이란?
자바 라이브러리로 반복되는 getter,setter,toString 등의 메서드 코드를 어노테이션을 통해 줄여주는 라이브러리.
여러가지 어노테이션을 제공하고, 이것을 기반으로 코드를 컴파일 과정에 생성해주는 방식임. 메서드는 눈에 보이지 않지만 코드를 실행할 때는 생성된다는 뜻!
Lombok Download
다운받은 jar 파일 실행해서 Specify location...에 eclipse 실행파일 선택 -> install/update -> 이클립스 재실행
만약 gradle에 추가되어 있지 않다면 build.gradle
에 추가
compileOnly 'org.projectlombok:lombok'
Spring-Boot-Devtools 이란?
spring boot가 제공하는 옵셔널한 툴
크게 다섯가지 기능을 제공하고, 주로 세가지 기능이 자주 쓰임
Property Defaults : 개발 시점과 배포 시점에 다른 설정을 기본적으로 개발 단계에 맞춰 설정해줌
Automatic Restart : 코드가 바뀔 때마다 바로 스프링 애플리케이션이 재실행된다.
Live Reload : JS 파일을 수정하기만 해도 자동으로 브라우저가 새로고침 됨
lombok과 마찬가지로 만약 gradle에 추가되어 있지 않다면 build.gradle
에 추가
developmentOnly 'org.springframework.boot:spring-boot-devtools'
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란?
관계형 데이터 베이스 프로그래밍을 쉽게 할 수 있도록 도와주는 개발 프레임 워크
SQL 쿼리들을 한 파일에 구성해, 프로그램 코드와 SQL을 분리할 수 있음
따라서 코드의 간결성과 유지보수성이 향상하고, 빠른 개발이 가능해 생산성이 향상된다.
application.properties
에 추가
mybatis.configuration.map-underscore-to-camel-case=true
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();
}
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?
데이터 접근 객체인 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;
}
마이바티스는 쿼리를 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에서 테스트해보기
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">