[새싹] 현대IT&E 231208 기록 - Spring Boot

최정윤·2023년 12월 8일
0

새싹

목록 보기
39/67
post-custom-banner

자동 formating 단축키
command + shift + f
자동 import 단축키
command + shift + o

Spring Boot-Oracle, Mybatis, thymeleaf 이용 단순 게시판 작성

§ database object (oracle) 생성
§ 개발도구(STS) 환경 설정
§ spring boot project 생성 및 설정
§ project 패키지 구조 정의, DTO 생성
§ Layered Archtecture
§ Mybatis 설정
§ Request mapping & action
§ 입력 폼 출력
§ 입력 폼 데이터를 table에 insert
§ 리스트 출력
§ 상세 보기
§ 삭제 폼 출력 (비밀번호 입력)
§ 삭제 폼 데이터로 해당 row delete
§ 수정 폼 출력
§ 수정 폼 데이터로 해당 row update
§ Assignment

프로젝트 설정하기

User 생성 및 권한 부여

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;

STS4 실습

spring boot project 생성 및 설정

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

project 패키지 구조 정의, DTO 생성

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);
	}
}

Layered Architecture

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{

}

Mybatis 설정

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

http://localhost:8080/ 실행화면

입력 폼 출력

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 실행화면

입력 폼 데이터를 table에 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>
profile
개발 기록장
post-custom-banner

0개의 댓글