자동 formating 단축키
command + shift + f
자동 import 단축키
command + shift + o
§ database object (oracle) 생성
§ 개발도구(STS) 환경 설정
§ spring boot project 생성 및 설정
§ project 패키지 구조 정의, DTO 생성
§ Layered Archtecture
§ Mybatis 설정
§ Request mapping & action
§ 입력 폼 출력
§ 입력 폼 데이터를 table에 insert
§ 리스트 출력
§ 상세 보기
§ 삭제 폼 출력 (비밀번호 입력)
§ 삭제 폼 데이터로 해당 row delete
§ 수정 폼 출력
§ 수정 폼 데이터로 해당 row update
§ Assignment
sys.sql
create user xciusername
identified by xcipassword
default tablespace users
temporary tablespace temp;
alter user xciusername
quota unlimited on users;
grant connect, resource
to xciusername;
conn xciusername/xcipassword@localhost:1521/xepdb1
SQL> create user xciusername
identified by xcipassword
default tablespace users
temporary tablespace temp;
사용자가 생성되었습니다.
SQL> alter user xciusername
quota unlimited on users;
사용자가 변경되었습니다.
SQL> grant connect, resource
to xciusername;
SQL> conn xciusername/xcipassword@localhost:1521/xepdb1
test.sql
create table article (
no number primary key,
name varchar2(20) not null,
title varchar2(100) not null,
content clob not null,
regdate date default sysdate,
readcount number(5) default 0,
password varchar2(128) not null
);
create sequence s_article nocache;
create sequence s_article nocache;
application.properties
# datasource (oracle)
server.port=8080
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
spring.datasource.username=xciusername
spring.datasource.password=xcipassword
# log4jdbc2
logging.level.root=info
pom.xml 일부
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
ArticleDTO.java
package net.developia.boot_article.dto;
import java.io.Serializable;
import java.util.Date;
import org.apache.commons.codec.digest.DigestUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class ArticleDTO implements Serializable {
private long no;
private String name;
private String title;
private String content;
private Date regdate;
private int readcount;
private String password;
public void setPassword(String password) {
this.password = DigestUtils.sha512Hex(password);
}
}
ArticleController.java
package net.developia.boot_article.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import net.developia.boot_article.service.ArticleService;
@Controller
public class ArticleController {
@Autowired
private ArticleService articleService;
}
ArticleService.java
package net.developia.boot_article.service;
public interface ArticleService {
}
ArticleServiceImpl.java
package net.developia.boot_article.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import net.developia.boot_article.dao.ArticleDAO;
@Service
public class ArticleServiceImpl implements ArticleService {
@Autowired
private ArticleDAO articleDAO;
}
ArticleDAO.java
package net.developia.boot_article.dao;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ArticleDAO {
}
ArticleDAOImpl.java
package net.developia.boot_article.dao;
import org.springframework.stereotype.Repository;
@Repository
public class ArticleDAOImpl implements ArticleDAO{
}
application.properties
# datasource (oracle)
server.port=8080
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
spring.datasource.username=xciusername
spring.datasource.password=xcipassword
# log4jdbc2
logging.level.root=info
# DTO models
mybatis.type-aliases-package=net.developia.boot_article
# dao mapper xml location
mybatis.mapper-locations=classpath:mapper/**/*.xml
ArticleContrller.java
@GetMapping("insert")
public String insert() {
return "article/insert";
}
insert.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form method="post">
<table>
<caption>게시물 쓰기</caption>
<tr>
<th>제목</th>
<td><input type="text" name="title" autofocus="autofocus"
required="required" /></td>
</tr>
<tr>
<th>이름</th>
<td><input type="text" name="name" required="required" /></td>
</tr>
<tr>
<th>비밀번호</th>
<td><input type="password" name="password" required="required" /></td>
</tr>
<tr>
<th>내용</th>
<td><textarea name="content" rows="5" cols="40"
required="required"></textarea></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="완료" />
</td>
</tr>
</table>
</form>
</body>
</html>
http://localhost:8080/insert 실행화면
ArticleController.java
package net.developia.boot_article.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import lombok.extern.log4j.Log4j2;
import net.developia.boot_article.dto.ArticleDTO;
import net.developia.boot_article.service.ArticleService;
@Controller
@Log4j2
public class ArticleController {
@Autowired
private ArticleService articleService;
@GetMapping("insert")
public String insert(@ModelAttribute ArticleDTO articleDTO, Model model) {
// log.info(articleDTO.toString());
try {
articleService.insertArticle(articleDTO);
return "redirect:list";
} catch (Exception e) {
model.addAttribute("msg","게시물 등록 오류입니다.");
model.addAttribute("url","javascript:history.back();");
return "article/result";
}
}
}
ArticleService.java
package net.developia.boot_article.service;
import net.developia.boot_article.dto.ArticleDTO;
public interface ArticleService {
void insertArticle(ArticleDTO articleDTO) throws Exception; // insert
}
ArticleServiceImpl.java
package net.developia.boot_article.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import net.developia.boot_article.dao.ArticleDAO;
import net.developia.boot_article.dto.ArticleDTO;
@Service
public class ArticleServiceImpl implements ArticleService {
@Autowired
private ArticleDAO articleDAO;
@Override
public void insertArticle(ArticleDTO articleDTO) throws Exception {
try {
articleDAO.insertArticle(articleDTO);
// log.info("게시물 입력 성공");
} catch (Exception e) {
// log.info(e.getMessage());
throw e;
}
}
}
ArticleDAO.java
package net.developia.boot_article.dao;
import java.sql.SQLException;
import org.apache.ibatis.annotations.Mapper;
import net.developia.boot_article.dto.ArticleDTO;
@Mapper
public interface ArticleDAO {
void insertArticle(ArticleDTO articleDTO) throws SQLException; // insert
}
ArticleDAO.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="net.developia.boot_article.dao.ArticleDAO">
<insert id="insertArticle" parameterType="ArticleDTO">
insert into article(no, name, title, content, password)
values(s_article.nextval, #{name}, #{title}, #{content}, #{password} )
</insert>
</mapper>
result.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
alert('[[${msg}]]');
location.href='[[${url}]]';
</script>
</body>
</html>
ArticleController.java 일부
@GetMapping("list")
public String list(Model model) throws Exception {
try {
List<ArticleDTO> list = articleService.getArticleList();
model.addAttribute("list", list);
return "article/list";
} catch (Exception e) {
throw e;
}
}
ArticleService.java
package net.developia.boot_article.service;
import java.util.List;
import net.developia.boot_article.dto.ArticleDTO;
public interface ArticleService {
void insertArticle(ArticleDTO articleDTO) throws Exception; // insert
List<ArticleDTO> getArticleList() throws Exception;
}
ArticleServiceImpl.java
@Override
public List<ArticleDTO> getArticleList() throws Exception {
try {
return articleDAO.getArticleList();
} catch (Exception e) {
// log.info(e.getMessage());
throw e;
}
}
ArticleDAO.java
package net.developia.boot_article.dao;
import java.sql.SQLException;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import net.developia.boot_article.dto.ArticleDTO;
@Mapper
public interface ArticleDAO {
void insertArticle(ArticleDTO articleDTO) throws SQLException; // insert
List<ArticleDTO> getArticleList() throws SQLException;
}
ArticleDAO.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="net.developia.boot_article.dao.ArticleDAO">
<insert id="insertArticle" parameterType="ArticleDTO">
insert into article(no,
name, title, content, password)
values(s_article.nextval, #{name},
#{title}, #{content}, #{password} )
</insert>
<select id="getArticleList" resultType="ArticleDTO">
SELECT no
,title
,name
,regdate
,readcount
FROM article
ORDER BY no DESC
</select>
</mapper>
list.html
<!DOCTYPE html>
<html lang="ko" xmlns:th=http://www.thymeleaf.org
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8">
<title>게시물</title>
</head>
<body>
<table border="1">
<caption>게시물 리스트</caption>
<tr>
<th>번호</th>
<th>제목</th>
<th>이름</th>
<th>날짜</th>
<th>조회수</th>
</tr>
<tr th:if="${not #lists.isEmpty(list)}" th:each="dto: ${list}">
<td th:text="${dto.no}" />
<td><a th:href="@{detail(no=${dto.no})}" th:text="${dto.title}" /></td>
<td th:text="${dto.name}" />
<td th:text="${#calendars.format(dto.regdate, 'yyyy-MM-dd')}" />
<td th:text="${dto.readcount}" />
</tr>
</table>
<br />
<a href="insert">글쓰기</a>
</body>
</html>
ArticleController.java
@GetMapping("detail")
public String detail(@RequestParam(defaultValue = "0") long no, Model model) throws Exception {
try {
ArticleDTO articleDTO = articleService.getDetail(no);
model.addAttribute("articleDTO", articleDTO);
return "article/detail";
} catch (Exception e) {
model.addAttribute("msg", "접근할 수 없는 게시물이거나 시스템 오류입니다.");
model.addAttribute("url", "list");
return "article/result";
}
}
ArticleService.java
package net.developia.boot_article.service;
import java.util.List;
import net.developia.boot_article.dto.ArticleDTO;
public interface ArticleService {
void insertArticle(ArticleDTO articleDTO) throws Exception; // insert
List<ArticleDTO> getArticleList() throws Exception;
ArticleDTO getDetail(long no) throws Exception;
}
ArticleServiceImpl.java 일부
@Override
public ArticleDTO getDetail(long no) throws Exception {
try {
ArticleDTO articleDTO = articleDAO.getDetail(no);
if (articleDTO == null) {
throw new RuntimeException("없는 게시물이거나 접근 권한이 없습니다.");
}
return articleDTO;
} catch (Exception e) {
// log.info(e.getMessage());
throw e;
}
}
ArticleDAO.java 일부
package net.developia.boot_article.dao;
import java.sql.SQLException;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import net.developia.boot_article.dto.ArticleDTO;
@Mapper
public interface ArticleDAO {
void insertArticle(ArticleDTO articleDTO) throws SQLException; // insert
List<ArticleDTO> getArticleList() throws SQLException;
ArticleDTO getDetail(long no) throws SQLException;
}
ArticleDAO.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="net.developia.boot_article.dao.ArticleDAO">
<insert id="insertArticle" parameterType="ArticleDTO">
insert into article(no,
name, title, content, password)
values(s_article.nextval, #{name},
#{title}, #{content}, #{password} )
</insert>
<select id="getArticleList" resultType="ArticleDTO">
SELECT no
,title
,name
,regdate
,readcount
FROM article
ORDER BY no DESC
</select>
<select id="getDetail" parameterType="long"
resultType="ArticleDTO">
SELECT no
,title
,name
,regdate
,readcount
,content
FROM article
WHERE no=#{no}
</select>
</mapper>
detail.html
<!DOCTYPE html>
<html lang="ko" xmlns:th=http://www.thymeleaf.org
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<table>
<caption>게시물 상세보기</caption>
<tr>
<th>글번호</th>
<td th:text="${articleDTO.no}" />
</tr>
<tr>
<th>제목</th>
<td th:text="${articleDTO.title}" />
</tr>
<tr>
<th>이름</th>
<td th:text="${articleDTO.name}" />
</tr>
<tr>
<th>내용</th>
<td th:text="${articleDTO.content}" />
</tr>
<tr>
<th>작성일</th>
<td
th:text="${#calendars.format(articleDTO.regdate, 'yyyy-MM-dd HH:mm:ss')}" />
</tr>
<tr>
<th>조회수</th>
<td th:text="${articleDTO.readcount}" />
</tr>
</table>
<br />
<a th:href="@{update(no=${articleDTO.no})}" th:text="수정" /> |
<a th:href="@{delete(no=${articleDTO.no})}" th:text="삭제" /> |
<a th:href="@{list}" th:text="리스트" />
</body>
</html>
ArticleController.java 일부
@GetMapping("delete")
public String delete(@RequestParam long no, Model model) {
try {
model.addAttribute("no", no);
return "article/delete";
} catch (Exception e) {
throw e;
}
}
delete.html
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8">
<title>게시물</title>
</head>
<body>
<form method="post">
<table>
<caption>게시물 삭제</caption>
<tr>
<th>번호</th>
<td>[[${no}]]<input type="hidden" name="no" th:value="${no}" /></td>
</tr>
<tr>
<th>비밀번호</th>
<td><input type="password" name="password" required="required"
autofocus="autofocus" /><br /> * 처음 글 입력시 비밀번호를 재 입력하세요.</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="완료" /></td>
</tr>
</table>
</form>
</body>
</html>