240109과 다른 점


<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<div class="card-body">
<h5 class="card-title">title</h5>
<table class="table">
<thead>
<tr>
<th scope="col">Tno</th>
<th scope="col">Title</th>
<th scope="col">Writer</th>
<th scope="col">DueDate</th>
<th scope="col">Finished</th>
</tr>
</thead>
<tbody>
<c:forEach items="${dtoList}" var="dto">
<tr>
<th scope="row"><c:out value="${dto.tno}"/></th>
<td>
<a href="/todo/read?tno=${dto.tno}" class="text-decoration-none">
<c:out value="${dto.title}"/>
</a>
</td>
<td><c:out value="${dto.writer}"/> </td>
<td><c:out value="${dto.dueDate}"/> </td>
<td><c:out value="${dto.finished}"/> </td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<script>
document.querySelector('#register').addEventListener('click', function (){
location.href="/todo/register";
})
</script>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<title>Hello, world!</title>
<button type="button" id="list" class="btn btn-primary">List</button>
</head>
<body>
<div class="card-body">
<form action="/todo/modify" method="post">
<div class="input-group mb-3">
<span class="input-group-text">tno</span>
<input type="text" name="tno" class="form-control" value="<c:out value="${dto.tno}"></c:out>" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">title</span>
<input type="text" name="title" class="form-control" value="<c:out value="${dto.title}"></c:out>">
</div>
<div class="input-group mb-3">
<span class="input-group-text">DueDate</span>
<input type="date" name="dueDate" class="form-control" value="<c:out value="${dto.dueDate}"></c:out>">
</div>
<div class="input-group mb-3">
<span class="input-group-text">writer</span>
<input type="text" name="writer" class="form-control" value="<c:out value="${dto.writer}"></c:out>" readonly>
</div>
<div class="input-group mb-3">
<label class="form-check-label">
finished
<input type="checkbox" name="finished" ${dto.finished?"checked":""}>
</label>
</div>
<div class="my-4">
<div class="float-end">
<button type="button" id="remove" class="btn btn-danger">Remove</button>
<button type="button" id="modify" class="btn btn-primary">Modify</button>
<button type="button" id="list" class="btn btn-primary">List</button>
</div>
</div>
</form>
</div>
<script>
const formObj = document.querySelector("form");
document.querySelector("#remove").addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
formObj.action = "/todo/remove";
formObj.method = "post";
formObj.submit();
}, false);
document.querySelector("#modify").addEventListener("click", function () {
formObj.submit();
}, false);
document.querySelector("#list").addEventListener("click", function () {
window.location = "../todo/list"
}, false);
// 오류
const serverValidResult = {}
<c:forEach items="${errors}" var="error">
serverValidResult['${error.getField()}'] = '${error.defaultMessage}'
</c:forEach>
console.log(serverValidResult)
</script>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<title>Hello, world!</title>
<button type="button" id="list" class="btn btn-primary">List</button>
</head>
<body>
<div class="card-body">
<div class="input-group mb-3">
<span class="input-group-text">TNO</span>
<input type="text" name="tno" class="form-control"
value="<c:out value="${dto.tno}"/>" readonly>
</div>
<div class="input-group mb-3" >
<span class="input-group-text">Title</span>
<input type="text" name="title" class="form-control"
value="<c:out value="${dto.title}"/>" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">DueDate</span>
<input type="date" name="dueDate" class="form-control"
value="<c:out value="${dto.dueDate}"/>" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Writer</span>
<input type="text" name="writer" class="form-control"
value="<c:out value="${dto.writer}"/>" readonly>
</div>
<div class="form-check mb-3">
<label class="form-check-label">
Finished
<input type="checkbox" name="finished" class="form-check-control"
${dto.finished?"checked":""} disabled>
</label>
</div>
<div class="my-4">
<div class="float-end">
<button type="button" id="modify" class="btn btn-primary">Modify</button>
<button type="button" id="list" class="btn btn-primary">List</button>
</div>
</div>
</div>
<script>
document.querySelector('#modify').addEventListener('click', function (){
self.location = '../todo/modify?tno=${dto.tno}'
},false) ;
document.querySelector('#list').addEventListener('click', function (){
self.location = '../todo/list';
}, false) ;
</script>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- 기존의 <h1>Header</h1> -->
<div class="row">
<div class="col">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link active" aria-current="page" href="#">Home</a>
<a class="nav-link" href="#">Features</a>
<a class="nav-link" href="#">Pricing</a>
<a class="nav-link disabled">Disabled</a>
</div>
</div>
</div>
</nav>
</div>
</div>
<!-- header end -->
<!-- 기존의 <h1>Header</h1>끝 -->
<div class="row content">
<div class="col">
<div class="card">
<div class="card-header">
Featured
</div>
<div class="card-body">
<form action="/todo/register" method="post">
<div class="input-group mb-3">
<span class="input-group-text">Title</span>
<input type="text" name="title" class="form-control" placeholder="Title">
</div>
<div class="input-group mb-3">
<span class="input-group-text">DueDate</span>
<input type="date" name="dueDate" class="form-control" placeholder="Writer">
</div>
<div class="input-group mb-3">
<span class="input-group-text">Writer</span>
<input type="text" name="writer" class="form-control" placeholder="Writer">
</div>
<div class="my-4">
<div class="float-end">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="result" class="btn btn-secondary">Reset</button>
<button type="button" id="list" class="btn btn-primary">List</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="row content">
<h1>Content</h1>
</div>
<div class="row footer">
<!--<h1>Footer</h1>-->
<div class="row fixed-bottom" style="z-index: -100">
<footer class="py-1 my-1 ">
<p class="text-center text-muted">Footer</p>
</footer>
</div>
</div>
</div>
<script>
const serverValidResult = {}
<c:forEach items="${errors}" var="error">
serverValidResult['${error.getField()}'] = '${error.defaultMessage}'
</c:forEach>
console.log(serverValidResult)
document.querySelector("#list").addEventListener("click", function () {
window.location = "../todo/list"
}, false);
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</body>
</html>
객체를 특정한 포맷에 맞춰 문자로 출력하는 등의 기능이 필요할 수 있다. 이런 기능을 포캣터(Formatter)라고 한다.
package com.example.springex.controller.formatter;
import org.springframework.format.Formatter;
import java.text.ParseException;
import java.util.Locale;
public class CheckboxFormatter implements Formatter<Boolean> {
@Override
public Boolean parse(String text, Locale locale) throws ParseException {
if(text == null){
return false;
}else {
return text.equals("on");
}
}
@Override
public String print(Boolean object, Locale locale) {
return object.toString();
}
}
package com.example.springex.controller.formatter;
import org.springframework.cglib.core.Local;
import org.springframework.format.Formatter;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class LocalDateFormatter implements Formatter<LocalDate> {
@Override
public LocalDate parse(String text, Locale locale) throws ParseException {
return LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
@Override
public String print(LocalDate object, Locale locale) {
return DateTimeFormatter.ofPattern("yyyy-MM-dd").format(object);
}
}
LocalDateFormatter 클래스는 주로 스프링 MVC에서 폼 데이터를 LocalDate 객체로 변환하거나, LocalDate 객체를 폼 데이터로 변환할 때 사용됩니다. 예를 들어, 웹 어플리케이션에서 날짜를 입력하고 전송할 때 해당 날짜를 LocalDate 객체로 변환하거나, 반대로 LocalDate 객체를 화면에 표시하기 위해 문자열로 변환할 때 사용될 수 있습니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- mvc 설정을 어노테이션 기반으로 처리한다-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!-- 이미지나 정적인 html 파일의 경로를 지정-->
<mvc:resources mapping="/resources/**" location="/resources/"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.example.springex.controller.formatter.LocalDateFormatter"/>
<bean class="com.example.springex.controller.formatter.CheckboxFormatter"/>
</set>
</property>
</bean>
<context:component-scan base-package="com.example.springex.controller"/>
</beans>
빈을 등록해줘야한다!!
package com.example.springex.controller;
import com.example.springex.dto.TodoDTO;
import com.example.springex.service.TodoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.validation.Valid;
@Controller
@RequestMapping("/todo")
@Log4j2
@RequiredArgsConstructor
public class TodoController {
private final TodoService todoService;
// list
@RequestMapping ("/list")
public void list(Model model) {
log.info("todo list");
model.addAttribute("dtoList",todoService.getAll());
}
// register get
@GetMapping ("/register")
public void registerGet() {
log.info("todo registerGet");
}
// register post
@PostMapping("/register")
public String registerPost(@Valid TodoDTO todoDTO,
BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
log.info("todo registerPost");
if(bindingResult.hasErrors()) {
log.info("has errors..");
redirectAttributes.addFlashAttribute("errors",bindingResult.getAllErrors());
return "redirect:/todo/register";
}
todoService.register(todoDTO);
return "redirect:/todo/list";
}
@GetMapping ({"/read", "/modify"})
public void read(Long tno, Model model) {
TodoDTO todoDTO = todoService.getOne(tno);
log.info("todo read :: " + todoDTO);
model.addAttribute("dto", todoDTO);
}
@PostMapping("/remove")
public String remove(Long tno, RedirectAttributes redirectAttributes) {
log.info("=================remove : " + tno + " =================");
todoService.remove(tno);
return "redirect:/todo/list";
}
@PostMapping("/modify")
public String modify(@Valid TodoDTO todoDTO,
BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
log.info("todo modify");
if(bindingResult.hasErrors()) {
log.info("has errors..");
redirectAttributes.addFlashAttribute("errors",bindingResult.getAllErrors());
redirectAttributes.addAttribute("tno", todoDTO.getTno());
return "redirect:/todo/modify";
}
todoService.modify(todoDTO);
return "redirect:/todo/list";
}
}
스프링 프레임워크에서 URL을 핸들링하기 위한 어노테이션 입니다. 각각 HTTP GET 및 POST 요청에 대한 핸들러 메서드를 지정하는 데 사용됩니다.
@GetMapping 어노테이션은 HTTP GET 요청을 처리하는 핸들러 메서드를 지정합니다.
특정 URL에 대한 GET 요청이 들어오면 해당 메서드가 실행됩니다.
주로 데이터 조회와 관련된 작업에 사용됩니다.
@PostMapping 어노테이션은 HTTP POST 요청을 처리하는 핸들러 메서드를 지정합니다.
특정 URL에 대한 POST 요청이 들어오면 해당 메서드가 실행됩니다.
주로 데이터를 생성 또는 수정하는 작업에 사용됩니다.
차이점
1. HTTP Method:
@GetMapping은 HTTP GET 요청에 대한 핸들러를 지정합니다.
@PostMapping은 HTTP POST 요청에 대한 핸들러를 지정합니다.
2. 데이터 전송:
GET은 주로 데이터를 URL의 쿼리 매개변수를 통해 전송합니다.
POST는 주로 HTTP 요청 본문을 통해 데이터를 전송합니다.
3. 보안:
GET은 URL에 데이터가 노출되므로 보안에 취약할 수 있습니다.
POST는 본문에 데이터를 담아 전송하므로 GET에 비해 보안적으로 더 강력합니다.
4. 캐싱:
GET은 브라우저에 의해 캐싱될 수 있습니다.
POST는 기본적으로 브라우저에 의해 캐싱되지 않습니다.
주로 데이터를 조회할 때는 @GetMapping을 사용하고, 데이터를 생성 또는 수정할 때는 @PostMapping을 사용하는 것이 일반적인 관례입니다.
package com.example.springex.service;
import com.example.springex.dto.TodoDTO;
import java.util.List;
public interface TodoService {
void register(TodoDTO todoDTO);
List<TodoDTO> getAll();
TodoDTO getOne(Long tno);
void remove(Long tno);
void modify(TodoDTO todoDTO);
}
<?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.example.springex.mapper.TodoMapper">
<insert id="insert">
insert into tbl_todo (title, dueDate, writer)
values (#{title}, #{dueDate}, #{writer})
</insert>
<select id="selectAll" resultType="com.example.springex.domain.TodoVO">
select * from tbl_todo order by tno desc
</select>
<select id="selectOne" resultType="com.example.springex.domain.TodoVO">
select * from tbl_todo where tno = #{tno}
</select>
<delete id="delete">
delete from tbl_todo where tno=#{tno}
</delete>
<update id="update">
update tbl_todo
set title=#{title}
, dueDate = #{dueDate}
, finished = #{finished}
where tno = #{tno}
</update>
<select id="selectList" resultType="com.example.springex.domain.TodoVO">
select * from tbl_todo order by tno desc limit #{skip}, #{size}
</select>
</mapper>
MyBatis 프레임워크를 사용하여 데이터베이스와 상호 작용하기 위한 SQL 매퍼 파일
밑에 TodoServiceImpl 에서 사용한다.
이렇게 정의된 SQL 문들은 MyBatis를 통해 Todo 데이터와 관련된 데이터베이스 작업을 수행하기 위해 사용될 것입니다. 각각의 쿼리는 해당하는 기능에 따라 데이터를 삽입, 조회, 수정, 삭제하거나 특정 범위의 데이터를 조회하는 역할을 합니다.
package com.example.springex.service;
import com.example.springex.domain.TodoVO;
import com.example.springex.dto.TodoDTO;
import com.example.springex.mapper.TodoMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Log4j2
@RequiredArgsConstructor
public class TodoServiceImpl implements TodoService{
private final TodoMapper todoMapper;
private final ModelMapper modelMapper;
@Override
public void register(TodoDTO todoDTO) {
log.info(modelMapper);
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
log.info(todoVO);
todoMapper.insert(todoVO);
}
@Override
public List<TodoDTO> getAll() {
List<TodoDTO> dtoList = todoMapper.selectAll().stream()
.map(vo -> modelMapper.map(vo, TodoDTO.class))
.collect(Collectors.toList());
return dtoList;
}
@Override
public TodoDTO getOne(Long tno) {
TodoVO todoVO = todoMapper.selectOne(tno);
TodoDTO todoDTO = modelMapper.map(todoVO, TodoDTO.class);
return todoDTO;
// todoservicejava에서 TodoDTO getOne(Long tno);로 메소드 만들면 구현자 만들라고 뜬다. 구현자 알트엔터 누르면 구현자 만들수잇음
}
@Override
public void remove(Long tno) {
todoMapper.delete(tno);
}
@Override
public void modify(TodoDTO todoDTO) {
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
todoMapper.update(todoVO);
}
}
TodoServiceImpl 클래스는 TodoMapper와 ModelMapper를 주입받아서 비즈니스 로직을 수행하고, XML 매퍼 파일에서 정의한 SQL 쿼리들은 TodoMapper를 통해 실행되어 데이터베이스와 상호 작용합니다.
나누는 이유: 서비스 레이어와 데이터 액세스 레이어 간의 역할 분담.