목차 | |
1. | comment |
2. | security |
1. comment
◈ detail.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<div layout:fragment="content" class="container-md">
<h1>Boot Detail page</h1>
<hr>
<th:block th:with="bvo=${bdto.bvo}">
<form action="/board/modify" method="post" id="modForm" enctype="multipart/form-data">
<div class="mb-3">
<label for="r" class="form-label">Created At</label>
<input type="text" class="form-control" name="regDate" id="r" th:value="${bvo.regDate}" readonly>
</div><div class="mb-3">
<input type="hidden" class="form-control" name="bno" id="n" th:value="${bvo.bno}">
</div>
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" name="title" id="title" th:value="${bvo.title}" readonly>
</div>
<div class="mb-3">
<label for="writer" class="form-label">Writer</label>
<input type="text" class="form-control" name="writer" id="writer" th:value="${bvo.writer}" readonly>
</div>
<div class="mb-3">
<label for="content" class="form-label">Content</label>
<textarea class="form-control" name="content" id="content" rows="3" readonly>[[${bvo.content}]]</textarea>
</div>
<!-- file print line -->
<div class="mb-3">
<ul class="list-group">
<li th:each="fvo:${bdto.flist}" class="list-group-item">
<div th:if="${fvo.fileType > 0}" class="ms-2 me-auto">
<img th:src="@{|/upload/${fvo.saveDir}/${fvo.uuid}_${fvo.fileName}|}" alt="img" />
</div>
<div th:unless="${fvo.fileType > 0}" class="ms-2 me-auto">
<!-- icon -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-paperclip" viewBox="0 0 16 16">
<path d="M4.5 3a2.5 2.5 0 0 1 5 0v9a1.5 1.5 0 0 1-3 0V5a.5.5 0 0 1 1 0v7a.5.5 0 0 0 1 0V3a1.5 1.5 0 1 0-3 0v9a2.5 2.5 0 0 0 5 0V5a.5.5 0 0 1 1 0v7a3.5 3.5 0 1 1-7 0z"/>
</svg>
</div>
<div class="ms-2 me-auto">
<div class="fw-bold">[[${fvo.fileName}]]</div>
[[${fvo.regDate}]]
</div>
<span class="badge text-bg-success rounded-pill">[[${fvo.fileSize}]]Bytes</span>
<button type="button" th:data-uuid="${fvo.uuid}" class="btn btn-outline-danger bnt-nm file-x" disabled>x</button>
</li>
</ul>
</div>
<!-- file 추가 라인 -->
<div class="mb-3">
<input type="file" class="form-control" name="files" id="file" multiple style="display:none;">
</div>
<button type="button" id="trigger" class="btn btn-primary" disabled>File Upload</button> <br>
<button type="button" id="listBtn" class="btn btn-primary">List</button>
<!-- de-tail page에서 modify 상태로 변경하기 위한 버튼 -->
<button type="button" id="modBtn" class="btn btn-warning">Modify</button>
<a th:href="@{/board/delete(bno=${bvo.bno})}">
<button type="button" id="delBtn" class="btn btn-danger">Delete</button>
</a>
</form>
<!-- comment line -->
<!-- post -->
<div class="input-group mb-3">
<span class="input-group-text" id="cmtWriter">nick</span>
<input type="text" id="cmtText" class="form-control" placeholder="Add Comment..." aria-label="Username" aria-describedby="basic-addon1">
<button type="button" id="cmtAddBtn" class="btn btn-secondary">post</button>
</div>
<!-- spread -->
<ul class="list-group list-group-flush" id="cmtListArea">
<li class="list-group-item">
<div class="ms-2 me-auto">
<div class="fw-bold">writer</div>
Content
</div>
<span class="badge text-bg-primary rounded-pill">regDate</span>
</li>
</ul>
<!-- moreBtn-->
<div>
<button type="button" id="moreBtn" data-page="1" class="btn btn-dark" style="visibility:hidden">MORE + </button>
</div>
<!-- modal -->
<div class="modal fade" id="myModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="cmtWriterMod">${authNick }</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="text" class="form-control" id="cmtTextMod">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" id="cmtModBtn" class="btn btn-primary">changes</button>
</div>
</div>
</div>
</div>
<script th:inline="javascript">
const bnoVal = [[${bvo.bno}]]
console.log(bnoVal);
</script>
</th:block>
<script th:src="@{/js/boardDetail.js}"></script>
<script th:src="@{/js/boardRegister.js}"></script>
<script th:src="@{/js/boardComment.js}"></script>
<script>
spreadCommentList(bnoVal);
</script>
</div>
◈ boardComment.js
console.log("boardComment.js in");
console.log(bnoVal);
document.getElementById('cmtAddBtn').addEventListener('click',()=>{
const cmtText = document.getElementById('cmtText');
if(cmtText == null || cmtText == ''){
alert('댓글 내용을 입력해주세요.')
cmtText.focus();
return false;
}
const cmtData = {
bno : bnoVal,
writer : document.getElementById('cmtWriter').innerText,
content : cmtText.value
};
postCommentToServer(cmtData).then(result => {
if(result > 0){
alert("댓글 등록 성공");
cmtText.value="";
}
// 댓글 뿌리기 호출
spreadCommentList(bnoVal);
})
});
function spreadCommentList(bnoVal, page=1){
getCommentListFromServer(bnoVal, page).then(result =>{
console.log(result);
const ul = document.getElementById('cmtListArea');
if(result.cmtList.length > 0){
if(page == 1){
ul.innerHTML=""; // 기존 샘플값 비우기.
}
for(let cvo of result.cmtList){
let li = `<li data-cno = "${cvo.cno}" class="list-group-item">`;
li += `<div class="ms-2 me-auto">`;
li += `<div class="fw-bold">${cvo.writer}</div>`;
li += `${cvo.content}`;
li += `</div>`;
li += `<span class="badge text-bg-primary rounded-pill">${cvo.regDate}</span>`;
li += `<button type="button" class="btn btn-outline-warning btn-sm mod" data-bs-toggle="modal" data-bs-target="#myModal">%</button>`;
li += `<button type="button" class="btn btn-outline-danger btn-sm del">X</button>`;
li += `</li>`;
ul.innerHTML += li;
}
// page 처리
let moreBtn= document.getElementById('moreBtn');
// 전체 페이지가 현재 페이지보다 크다면... (나와야 할 페이지가 존재한다면...)
if(result.pgvo.pageNo < result.realEndPage){
moreBtn.style.visibility = "visible"; // 표시
moreBtn.dataset.page = page + 1;
}else{
moreBtn.style.visibility = "hidden"; // 숨김
}
}else{
let li = `<li class="list-group-item">Comment List Empty</li>`;
ul.innerHTML = li;
}
});
};
document.addEventListener('click',(e)=> {
if(e.target.id == 'moreBtn'){
let page = parseInt(e.target.dataset.page);
spreadCommentList(bnoVal, page);
}
if(e.target.classList.contains('del')){
let cno = e.target.closest('li').dataset.cno;
removeCommentToServer(cno).then(result => {
if(result > 0){
alert("삭제 성공!!");
spreadCommentList(bnoVal);
}
})
}
if(e.target.classList.contains('mod')){
let li = e.target.closest('li');
let modWriter = li.querySelector(".fw-bold").innerText;
document.getElementById('cmtWriterMod').innerText = modWriter;
let cmtText =li.querySelector('.fw-bold').nextSibling; // 쿼리 값의 같은 부모의 다른 형제 값
document.getElementById('cmtTextMod').value = cmtText.nodeValue; // 그냥 넣으면 Object로 입력
// 수정 버튼 id = cmtModBtn dataset
document.getElementById('cmtModBtn').setAttribute("data-cno", li.dataset.cno);
}
if(e.target.id == 'cmtModBtn'){
let cmtModData={
cno : e.target.dataset.cno,
content : document.getElementById('cmtTextMod').value
}
console.log(cmtModData);
// 비동기 처리
updateCommentToServer(cmtModData).then(result =>{
if(result > 0){
alert("수정 성공!!");
}
// 모달창 닫기
document.querySelector('.btn-close').click();
spreadCommentList(bnoVal);
});
}
});
async function updateCommentToServer(cmtModData) {
try{
const url = "/comment/update"
const config ={
method : "put",
headers : {
'content-type':'application/json; charset=utf-8'
},
body : JSON.stringify(cmtModData)
}
const resp = await fetch(url, config);
const result = await resp.text();
return result;
} catch(error){
console.log(error);
}
}
async function removeCommentToServer(cno) {
try{
const url = "/comment/remove/"+cno;
const config={
method:'delete'
}
const resp = await fetch(url, config);
const result = await resp.text();
return result;
} catch(error){
console.log(error);
}
}
async function postCommentToServer(cmtData) {
try {
const url = "/comment/post";
const config = {
method: "post",
headers:{
'Content-Type':'application/json; charset=utf-8'
},
body: JSON.stringify(cmtData)
};
const resp = await fetch(url, config);
const result = await resp.text();
return result;
} catch (error) {
console.log(error);
}
}
async function getCommentListFromServer(bnoVal, page) {
try{
const resp = await fetch("/comment/list/"+bnoVal+"/"+page);
const result = await resp.json();
return result;
} catch(error){
console.log(error);
}
}
◈ PagingHandler.java
package com.ezen.spring.handler;
import com.ezen.spring.domain.CommentVO;
import com.ezen.spring.domain.PagingVO;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.xml.stream.events.Comment;
import java.util.List;
@Getter
@Setter
@ToString
public class PagingHandler {
private int startPage;
private int endPage;
private int realEndPage;
private boolean prev, next;
private int totalCount;
private PagingVO pgvo;
private List<CommentVO> cmtList;
public PagingHandler(PagingVO pgvo, int totalCount){
this.pgvo = pgvo;
this.totalCount = totalCount;
// 1~10 => 10 / 11~20 => 20 / 21~30 => 30
// (현재 페이지번호 / 10) 올림 => * 10
this.endPage = (int)Math.ceil(pgvo.getPageNo() / 10.0)*10;
this.startPage = endPage - 9;
this.realEndPage = (int)Math.ceil(totalCount / (double)pgvo.getQty());
if(realEndPage < endPage){
this.endPage = realEndPage;
}
this.prev = this.startPage > 1; // 1 11 21
this.next = this.endPage < realEndPage;
}
public PagingHandler(PagingVO pgvo, int totalCount, List<CommentVO> cmtList){
this(pgvo, totalCount);
this.cmtList = cmtList;
}
}
◈ CommentVO.java
package com.ezen.spring.domain;
import lombok.*;
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class CommentVO {
private long cno;
private long bno;
private String writer;
private String content;
private String regDate;
}
◈ CommentController.java
package com.ezen.spring.controller;
import com.ezen.spring.domain.CommentVO;
import com.ezen.spring.domain.PagingVO;
import com.ezen.spring.handler.PagingHandler;
import com.ezen.spring.service.CommentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/comment/*")
@RestController
public class CommentController {
private final CommentService csv;
@PostMapping("/post")
@ResponseBody
public String post(@RequestBody CommentVO commentVO){
log.info(">>> commentVO >> {}", commentVO);
int isOk = csv.post(commentVO);
return isOk > 0 ? "1" : "0";
}
@GetMapping("/list/{bno}/{page}")
@ResponseBody
public PagingHandler list(@PathVariable("bno")long bno, @PathVariable("page") int page){
PagingVO pgvo = new PagingVO(page, 5);
PagingHandler ph = csv.getList(bno, pgvo);
log.info(">>> ph >>> {}", ph);
return ph;
}
@DeleteMapping("/remove/{cno}")
@ResponseBody
public String remove(@PathVariable("cno") long cno){
int isOk = csv.remove(cno);
return isOk > 0 ? "1" : "0";
}
@PutMapping("/update")
@ResponseBody
public String update(@RequestBody CommentVO commentVO){
int isOk = csv.update(commentVO);
return isOk > 0 ? "1" : "0";
}
}
◈ CommentService.java
package com.ezen.spring.service;
import com.ezen.spring.domain.CommentVO;
import com.ezen.spring.domain.PagingVO;
import com.ezen.spring.handler.PagingHandler;
public interface CommentService {
int post(CommentVO commentVO);
PagingHandler getList(long bno, PagingVO pgvo);
int remove(long cno);
int update(CommentVO commentVO);
}
◈ CommentServiceImpl.java
package com.ezen.spring.service;
import com.ezen.spring.domain.CommentVO;
import com.ezen.spring.domain.PagingVO;
import com.ezen.spring.handler.PagingHandler;
import com.ezen.spring.repository.CommentMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@RequiredArgsConstructor
@Slf4j
@Service
public class CommentServiceImpl implements CommentService{
private final CommentMapper commentMapper;
@Override
public int post(CommentVO commentVO) {
return commentMapper.post(commentVO);
}
@Transactional
@Override
public PagingHandler getList(long bno, PagingVO pgvo) {
int totalCount = commentMapper.getTotalCount(bno);
List<CommentVO> cmtList = commentMapper.getList(bno,pgvo);
PagingHandler ph = new PagingHandler(pgvo, totalCount, cmtList);
return ph;
}
@Override
public int remove(long cno) {
return commentMapper.remove(cno);
}
@Override
public int update(CommentVO commentVO) {
return commentMapper.update(commentVO);
}
}
◈ CommentMapper.java
package com.ezen.spring.repository;
import com.ezen.spring.domain.CommentVO;
import com.ezen.spring.domain.PagingVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface CommentMapper {
int post(CommentVO commentVO);
int getTotalCount(long bno);
List<CommentVO> getList(@Param("bno") long bno,@Param("pgvo") PagingVO pgvo);
int remove(long cno);
int update(CommentVO commentVO);
}
◈ commentMapper.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="com.ezen.spring.repository.CommentMapper">
<insert id = "post">
insert into comment(bno, writer, content) values(#{bno}, #{writer}, #{content})
</insert>
<select id="getTotalCount" resultType="int">
select count(cno) from comment where bno = #{bno}
</select>
<select id="getList" resultType="com.ezen.spring.domain.CommentVO">
select * from comment where bno = #{bno} order by cno desc limit #{pgvo.startIndex}, #{pgvo.qty}
</select>
<delete id="remove">
delete from comment where cno = #{cno}
</delete>
<update id="update">
update comment set content=#{content}, reg_date = now() where cno = #{cno}
</update>
</mapper>
▷ 출력
2. security
◈ build.gradle (spring)
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.11'
id 'io.spring.dependency-management' version '1.1.6'
}
group = 'com.ezen'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
implementation 'org.apache.tika:tika-core:2.4.1'
implementation 'org.apache.tika:tika-parsers:2.4.1'
implementation 'net.coobird:thumbnailator:0.4.17'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
◈ sqlFile.txt
create table board(
bno bigint auto_increment,
title varchar(200) not null,
writer varchar(200) not null,
content text,
reg_date datetime default now(),
primary key(bno));
create table file(
uuid varchar(256) not null,
save_dir varchar(256) not null,
file_name varchar(200) not null,
file_type tinyint(1) default 0,
bno bigint not null,
file_size bigint,
reg_date datetime default now(),
primary key(uuid));
create table comment(
cno bigint auto_increment,
bno bigint not null,
writer varchar(200),
content text,
reg_date datetime default now(),
primary key(cno));
create table user(
email varchar(200),
pwd varchar(256),
nick_name varchar(200),
reg_date datetime default now(),
last_login datetime default now(),
primary key(email));
create table auth(
id int auto_increment,
email varchar(200),
auth varchar(50),
primary key(id)
foreign key(email) references user(email));
◈ SecurityConfig.java
package com.ezen.spring.config;
import com.ezen.spring.security.CustomUserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
/* springSecurity6 => bcEncoder => createDelegationPasswordEncoder */
@Bean
PasswordEncoder passwordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
// SecurityFilterChain 객체로 설정
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
return http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorize ->
authorize.requestMatchers("/user/list").hasAnyRole("ADMIN")
.requestMatchers("/","/index","/js/**","/upload/**","/user/login", "/dist/**",
"/user/signup","/board/list", "/board/detail", "/comment/list/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(login -> login
.usernameParameter("email")
.passwordParameter("pwd")
.loginPage("/user/login")
.defaultSuccessUrl("/board/list").permitAll()
)
.logout(logout -> logout
.logoutUrl("/user/logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.logoutSuccessUrl("/")
)
.build();
}
// userDetailService : spring에서 만든 클래스와 같은 객체
@Bean
UserDetailsService userDetailsService(){
return new CustomUserService(); // security 패키지에 클래스로 생성
}
// authenticationManager 객체
@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
◈ AuthVO.java
package com.ezen.spring.domain;
import lombok.*;
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class AuthVO {
private int id;
private String email;
private String auth;
}
◈ UserVO.java
package com.ezen.spring.domain;
import lombok.*;
import java.util.List;
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class UserVO {
private String email;
private String pwd;
private String nickName;
private String regDate;
private String lastLogin;
private List<AuthVO> authList;
}
◈ CustomUserService.java
package com.ezen.spring.security;
import com.ezen.spring.domain.UserVO;
import com.ezen.spring.repository.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
@Slf4j
public class CustomUserService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// userName 주고 UserVO 객체 리턴 (authList 같이)
log.info(">>> username {}",username);
UserVO userVO = userMapper.selectEmail(username);
log.info(">>> userVO {}",userVO);
userVO.setAuthList(userMapper.selectAuths(username));
// UserDetails return
return new AuthUser(userVO);
}
}
◈ UserController.java
package com.ezen.spring.controller;
import com.ezen.spring.domain.UserVO;
import com.ezen.spring.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Slf4j
@RequestMapping("/user/*")
@RequiredArgsConstructor
@Controller
public class UserController {
private final UserService usv;
private final PasswordEncoder passwordEncoder;
@GetMapping("/signup")
public String signup(){
return "/user/register";
}
@PostMapping("/signup")
public String singup(UserVO userVO){
userVO.setPwd(passwordEncoder.encode(userVO.getPwd()));
int isOk = usv.insert(userVO);
return "/index";
}
@GetMapping("/login")
public String login(){
return "/user/login";
}
}
◈ UserService.java
package com.ezen.spring.service;
import com.ezen.spring.domain.UserVO;
public interface UserService {
int insert(UserVO userVO);
}
◈ UserServiceImpl.java
package com.ezen.spring.service;
import com.ezen.spring.domain.UserVO;
import com.ezen.spring.repository.UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@RequiredArgsConstructor
@Service
public class UserServiceImpl implements UserService{
private final UserMapper userMapper;
@Transactional
@Override
public int insert(UserVO userVO) {
int isOk = userMapper.insert(userVO);
if(isOk > 0){
userMapper.authInsert(userVO.getEmail());
}
return isOk;
}
}
◈ UserMapper.java
package com.ezen.spring.repository;
import com.ezen.spring.domain.AuthVO;
import com.ezen.spring.domain.UserVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper {
int insert(UserVO userVO);
void authInsert(String email);
UserVO selectEmail(String username);
List<AuthVO> selectAuths(String username);
}
◈ userMapper.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="com.ezen.spring.repository.UserMapper">
<insert id="insert">
insert into user(email, pwd, nick_name)
value(#{email}, #{pwd}, #{nickName})
</insert>
<insert id="authInsert">
insert into auth(email, auth)
values(#{email}, 'ROLE_USER')
</insert>
<select id="selectEmail" resultType="com.ezen.spring.domain.UserVO">
select * from user where email = #{email}
</select>
<select id="selectAuths" resultType="com.ezen.spring.domain.AuthVO">
select * from auth where email = #{email}
</select>
</mapper>
◈ register.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<div layout:fragment="content" class="container-md">
<h1>User Sign Up Page</h1>
<hr>
<form action="/user/signup" method="post">
<div class="mb-3">
<label for="e" class="form-label">Email</label>
<input type="text" class="form-control" name="email" id="e" placeholder="email...">
</div>
<div class="mb-3">
<label for="p" class="form-label">Password</label>
<input type="text" class="form-control" name="pwd" id="p" placeholder="password...">
</div>
<div class="mb-3">
<label for="n" class="form-label">Nickname</label>
<input type="text" class="form-control" name="nickName" id="n" placeholder="nickname...">
</div>
<button type="submit" class="btn btn-primary" id="regBtn">register</button>
</form>
</div>
◈ login.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<div layout:fragment="content" class="container-md">
<h1>User Login Page</h1>
<form action="/user/login" method="post">
<div class="mb-3">
<label for="e" class="form-label">Email</label>
<input type="text" class="form-control" name="email" id="e" placeholder="email...">
</div>
<div class="mb-3">
<label for="p" class="form-label">Password</label>
<input type="text" class="form-control" name="pwd" id="p" placeholder="password...">
</div>
<button type="submit" class="btn btn-primary" id="regBtn">register</button>
</form>
</div>
◈ header.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org">
<!-- th:fragment="이름" : 레이아웃에서 사용할 조각 -->
<div th:fragment="header" class="container">
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="#">Boot</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/board/register">BoardRegister</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/board/list">BoardList</a>
</li><li class="nav-item">
<a class="nav-link" href="/user/signup">SignUp</a>
</li><li class="nav-item">
<a class="nav-link" href="/user/login">Login</a>
</li>
</ul>
</div>
</div>
</nav>
<h5>Header Area</h5>
</div>
▷ 출력
'Java > Spring Boot' 카테고리의 다른 글
Spring Boot 기초(security) - AWS 풀스택 과정 81일차 (0) | 2024.11.20 |
---|---|
Spring Boot 기초(file) - AWS 풀스택 과정 79일차 (0) | 2024.11.18 |
Spring Boot 기초(paging) - AWS 풀스택 과정 78일차 (0) | 2024.11.15 |
Spring Boot 기초(DB) - AWS 풀스택 과정 77일차 (0) | 2024.11.14 |
Spring Boot 설정 및 기초 - AWS 풀스택 과정 76일차 (0) | 2024.11.13 |