mysql -u boot(user이름) -p;
use boot_db; // db 사용
desc customer;
select * from customer; // table 조회
create table customer(
id int(10) not null auto_increment primary key,
name varchar(100) not null,
email varchar(100) not null,
age int(10),
entry_date date,
UNIQUE KEY uk_name (email)
);
// alter table customer add unique(id);
insert into customer(name, email, age, entry_date) values ('gildong', 'gildong@naver.com', 20, '2023-10-01');
insert into customer(name, email, age, entry_date) values ('dooly', 'dooly@google.com', 25, '2023-10-05');
insert into customer(name, email, age, entry_date) values ('huidong', 'huidong@google.com', 18, '2023-09-05');
insert into customer(name, email, age, entry_date) values ('micole', 'micole@naver.com', 28, '2022-10-10');
insert into customer(name, email, age, entry_date) values ('ddochi', 'ddochi@google.com', 20, '2023-05-05');
commit;
xml 관련 파일은 코드의 이해도를 위해 분할하여 추가하는 방식으로 작성하였으나, 시험때는 하단의 코드 복사해도 좋습니당
- 이 전에 DBConn.java로 SQL + DB 연결 설정 정보를 저장
- SQL변경하거나 DB 연결 바꿀때마다
<?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:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Properties file 정보 설정 -->
<context:property-placeholder location="classpath:value.properties"/>
<!-- DataSource 구현체인 HikariDataSource를 SpringBean으로 등록 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
p:driverClassName="${db.driverClass}"
p:jdbcUrl="${db.url}"
p:username="${db.username}"
p:password="${db.password}"
/>
</beans>
db.driverClass=org.mariadb.jdbc.Driver
db.url=jdbc:mariadb://127.0.0.1:3306/boot_db?useUnicode=true&charaterEncoding=utf-8&useSSL=false&serverTimezone=UTC
db.username=boot
db.password=boot
myname=Spring
myprinter=printer
value1=JUnit
value2=AOP
value3=DI
printer1=stringPrinter
printer2=consolePrinter
CustomerDBTest.java
package myspring.customer;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans-customer.xml")
public class CustomerDBTest {
@Autowired
DataSource dataSource;
@Test
public void conn() {
try {
Connection connection = dataSource.getConnection();
DatabaseMetaData metaData = connection.getMetaData();
System.out.println("DB Product Name : " + metaData.getDatabaseProductName()); //MariaDB
System.out.println("DB Driver : " + metaData.getDriverName()); // MariaDB Connector/J
System.out.println("DB URL : " + metaData.getURL()); // jdbc:mariadb://127.0.0.1/boot_db?user=boot&password=***&...
System.out.println("DB Username : " + metaData.getUserName()); // boot
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
sqlsessionfactory
setDataSource()
메서드의 인자로 hikaridatasource 들어옴setConfigLocation()
으로 MyBatisConfig(sqlMapConfig) 파일 연결setMapperLocations()
mapping (*Mapper) 파일 연결sqlsession
package myspring.customer.vo;
public class CustomerVO {
private Long id;
private String name;
private String email;
private int age;
private String entryDate;
// 기본생성자
public CustomerVO() {}
public CustomerVO(String name, String email, int age, String entryDate) {
this.name = name;
this.email = email;
this.age = age;
this.entryDate = entryDate;
}
public CustomerVO(Long id, String name, String email, int age, String entryDate) {
this(name, email, age, entryDate);
this.id = id;
}
// getter와 setter 생성
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEntryDate() {
return entryDate;
}
public void setEntryDate(String entryDate) {
this.entryDate = entryDate;
}
// toString 생성
@Override
public String toString() {
return "CustomerVO [id=" + id + ", name=" + name + ", email=" + email + ", age=" + age + ", entryDate="
+ entryDate + "]";
}
}
// 하단에 추가
<!-- Mybatis-spring의 SqlSessionFactoryBean을 SpringBean으로 등록 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
<property name="mapperLocations">
<list>
<value>classpath:mybatis/*Mapper.xml</value>
</list>
</property>
</bean>
<!-- SqlSessionTemplate -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"/>
</bean>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- log4j2 설정 -->
<settings>
<setting name="defaultStatementTimeout" value="3"/>
<setting name="logImpl" value="LOG4J2"/>
</settings>
<typeAliases>
<!-- CustomerVO 설정 -->
<typeAlias alias="Customer" type="myspring.customer.vo.CustomerVO" />
</typeAliases>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<File name="File" fileName="./logs/logfile.log" append="true">
<PatternLayout
pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-5level %logger{36} - %msg%n" />
</File>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout
pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Logger name="org.springframework" level="INFO" additivity="false" />
<Logger name="myspring" level="DEBUG" />
<Root level="DEBUG">
<AppenderRef ref="console" level="DEBUG" />
<AppenderRef ref="File" level="DEBUG" />
</Root>
</Loggers>
</Configuration>
CustomerDBTest.java
package myspring.customer;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import myspring.customer.vo.CustomerVO;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans-customer.xml")
public class CustomerDBTest {
@Autowired
SqlSession sqlSession;
@Test
public void session() {
CustomerVO customer = sqlSession.selectOne("customerNS.selectCustomerById", "dooly");
System.out.println(customer);
}
}
package myspring.customer.dao.mapper;
import java.util.List;
import myspring.customer.vo.CustomerVO;
public interface CustomerMapper {
CustomerVO selectCustomerById(String id);
List<CustomerVO> selectCustomerList();
}
<?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="myspring.customer.dao.mapper.CustomerMapper">
<!-- 조회 쿼리는 id를 이용하여 실행 -->
<!-- parameter에 들어가는 값이 {value} 변수로 들어옴 -->
<select id="selectCustomerById" parameterType="string" resultType="Customer">
select * from customer where name=#{value}
</select>
<select id="selectCustomerList" resultType="Customer">
select * from customer order by id
</select>
</mapper>
// 하단에 추가
<!-- Mybatis-Spring의 MapperScannerConfigurer을 SpringBean으로 등록 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 참조하는 것 없어서 bean id 없어도 됨 -->
<property name="basePackage" value="myspring.customer.dao.mapper" />
<property name="sqlSessionTemplateBeanName" value="sqlSession"/>
</bean>
CustomerDBTest.java
package myspring.customer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import myspring.customer.dao.mapper.CustomerMapper;
import myspring.customer.vo.CustomerVO;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans-customer.xml")
public class CustomerDBTest {
@Autowired
CustomerMapper customerMapper;
@Test
public void mapper() {
// id가 메서드 이름이 되어 argument 전달
CustomerVO customer = customerMapper.selectCustomerById("dooly");
System.out.println(customer);
}
}
package myspring.customer.dao;
import java.util.List;
import myspring.customer.vo.CustomerVO;
public interface CustomerDao {
public List<CustomerVO> readAll();
public CustomerVO read(String id);
}
package myspring.customer.dao;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import myspring.customer.dao.mapper.CustomerMapper;
import myspring.customer.vo.CustomerVO;
@Repository("customerDao")
public class CustomerDaoImpl implements CustomerDao {
@Autowired
private CustomerMapper customerMapper;
@Override
public CustomerVO read(String id) {
CustomerVO customer = customerMapper.selectCustomerById(id);
return customer;
}
public List<CustomerVO> readAll() {
List<CustomerVO> customerList = customerMapper.selectCustomerList();
return customerList;
}
}
package myspring.customer.sevice;
import java.util.List;
import myspring.customer.vo.CustomerVO;
public interface CustomerService {
public List<CustomerVO> getCustomerList();
public CustomerVO getCustomer(String id);
}
package myspring.customer.sevice;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import myspring.customer.dao.CustomerDao;
import myspring.customer.vo.CustomerVO;
@Service("customerService")
public class CustomerServiceImpl implements CustomerService {
@Autowired
CustomerDao customerdao;
public List<CustomerVO> getCustomerList() {
return customerdao.readAll();
}
@Override
public CustomerVO getCustomer(String id) {
return customerdao.read(id);
}
}
// 하단에 추가
<!-- DAO, Service에 해당하는 Bean을 Scanning -->
<context:component-scan base-package="myspring.customer" >
<!-- 하단의 myspring.customer에서 controller만 제외하고 scan -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
CustomerDBTest.java
package myspring.customer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import myspring.customer.dao.mapper.CustomerMapper;
import myspring.customer.sevice.CustomerService;
import myspring.customer.vo.CustomerVO;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans-customer.xml")
public class CustomerDBTest {
@Autowired
CustomerService customerService;
@Test
public void service() {
CustomerVO customer = customerService.getUser("dooly");
System.out.println(customer);
}
}
contextLoadListener 추가
dispatcherservlet 추가
src/main/webapp/WEB-INF의 web.xml 수정
// 하단에 코드 추가
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-beans-customer.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-beans-customer.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>customer list</title>
</head>
<body>
<h1>고객 관리 메인</h1>
<ul>
<li><a href="customerList.do">고객 리스트</a></li>
</ul>
</body>
</html>
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<context:component-scan base-package="myspring.customer">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- Spring MVC에 필요한 Bean들을 자동으로 등록해주는 태그-->
<mvc:annotation-driven />
<!-- DispatcherServlet의 변경된 url-pattern 때문에 필요한 태그 설정 -->
<mvc:default-servlet-handler/>
<!-- 아래 주석은 Controller에서 포워딩 되는 .jsp 확장자를 생략할 수 있다. -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- annotation-driven 태그에서 내부적으로 처리해주는 설정 -->
<!-- <bean id="jsonHttpMessageConverter" -->
<!-- class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /> -->
<!-- <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> -->
<!-- <property name="messageConverters"> -->
<!-- <list> -->
<!-- <ref bean="jsonHttpMessageConverter"/> -->
<!-- </list> -->
<!-- </property> -->
<!-- </bean> -->
</beans>
// 하단에 추가
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans-web.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>고객 관리</title>
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<h2 class="text-center">고객 목록</h2>
<table class="table table-bordered table table-hover">
<thead>
<tr>
<th>번호</th>
<th>이름</th>
<th>이메일</th>
<th>나이</th>
<th>등록일</th>
</tr>
</thead>
<tbody>
<c:forEach var="customer" items="${customerList}">
<tr>
<td>${customer.id}</td>
<td>
<a href="getCustomer.do?id=${customer.name}">${customer.name}</a>
</td>
<td>${customer.email}</td>
<td>${customer.age}</td>
<td>${customer.entryDate}</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>고객 상세정보</title>
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<h2 class="text-center">고객 상세정보</h2>
<table class="table table-bordered table table-hover">
<tr><td>이름 :</td><td>${customer.name}</td></tr>
<tr><td>이메일 :</td><td>${customer.email}</td></tr>
<tr><td>나이 :</td><td>${customer.age}</td></tr>
<tr><td>등록일자 : </td><td>${customer.entryDate}</td></tr>
</table>
</div>
</body>
</html>
package myspring.customer.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import myspring.customer.sevice.CustomerService;
import myspring.customer.vo.CustomerVO;
@Controller
public class CustomerController {
@Autowired
private CustomerService customerService;
// db에서 가져오고 화면에 보이는 것도 함께 설정
// View와 Model을 한꺼번에 전달하는 방법
@RequestMapping("/customerList.do")
public ModelAndView customerList() {
// service 불러와서 리스트로 받기
// 뿌려줄 jsp 페이지를 ModelAndView 객체에 담음 (viewName=jsp파일 이름(jsp확장자 없이 이름만 기재), modelName=키값(forEach구문의 items), modelList=서비스에서 받아온 list 기재)
// key 값(customerList)과 일치하여 list 변수명 바꾸기
List<CustomerVO> customerVOList = customerService.getCustomerList();
// ModelAndView(viewName, keyName, valueObject)
return new ModelAndView("customerList", "customerList", customerVOList);
}
//getUser.do?id=dooly
// View와 Model을 분리해서 전달하는 방법
@RequestMapping("/getCustomer.do")
public String getCutomer(@RequestParam("id") String userId, Model model) {
// @requestparam을 이용하여 ?(쿼리 스트링) 다음의 id 값 가져올 수 있음
// 받아온 customerVO를 model에 담아줌
CustomerVO customerVO = customerService.getCustomer(userId);
model.addAttribute("customer", customerVO);
// 페이지 이름 return
return "customerInfo";
}
}
application.properties
+ pom.xml
파일 복사해 그대로 설정advice
+ Business/System Exception
패키지 및 클래스 복사package com.mission.mymission;
import org.modelmapper.ModelMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MyMissionApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyMissionApplication.class);
application.run(args);
}
}
package com.mission.mymission.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import java.time.LocalDateTime;
@Entity
@Table(name = "customer")
@Getter @Setter
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String email;
private int age;
@Column(updatable = false)
@CreationTimestamp
private LocalDateTime entryDate = LocalDateTime.now();
}
package com.mission.mymission.repository;
import com.mission.mymission.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface CustomerRepository extends JpaRepository<Customer, Long> {
// < Entity 클래스, PK값 >
// Insert, Delete, Select만 존재
Optional<Customer> findByEmail(String email);
List<Customer> findByName(String name);
}
package com.mission.mymission.controller;
import com.mission.mymission.entity.Customer;
import com.mission.mymission.exception.BusinessException;
import com.mission.mymission.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/customer")
public class CustomerController {
@Autowired
private CustomerRepository customerRepository;
}
@PostMapping
public Customer create(@RequestBody Customer customer) {
return customerRepository.save(customer);
}
locolhost:8080(포트번호)/customer(테이블명)
- POST : http://localhost:8080/customer
- Body (raw - JSON) 입력
@GetMapping
public List<Customer> getCustomers() {
return customerRepository.findAll();
}
locolhost:8080(포트번호)/customer(테이블명)
@GetMapping("/{id}")
public Customer getCustomer(@PathVariable Long id) {
Optional<Customer> optionalCustomer = customerRepository.findById(id);
Customer customer = optionalCustomer.orElseThrow(() -> new BusinessException("Customer Not Found", HttpStatus.NOT_FOUND));
return customer;
}
@GetMapping("/email/{email}")
public Customer getCustomerByEmail(@PathVariable String email) {
return customerRepository.findByEmail(email)
.orElseThrow(() -> new BusinessException("Email이 존재하지 않습니다", HttpStatus.NOT_FOUND));
}
@GetMapping("/name/{name}")
public List<Customer> getCustomerByName(@PathVariable String name) {
return customerRepository.findByName(name);
}
locolhost:8080(포트번호)/customer(테이블명)
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteUser(@PathVariable Long id) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new BusinessException("Customer Not Found", HttpStatus.NOT_FOUND));
customerRepository.delete(customer);
return ResponseEntity.ok(id + " User가 삭제 되었습니다");
}
}
locolhost:8080(포트번호)/customer(테이블명)
- DELETE : http://localhost:8080/customer/1
package com.mission.mymission.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@NoArgsConstructor
@AllArgsConstructor
@Getter @Setter
public class CustomerReqDTO {
private String name;
@Email(message = "Email 형식이 아닙니다")
private String email;
private int age;
}
package com.mission.mymission.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime;
@NoArgsConstructor
@AllArgsConstructor
@Getter @Setter
public class CustomerResDTO {
private Long id;
private String name;
private String email;
private int age;
private LocalDateTime entryDate = LocalDateTime.now();
}
package com.mission.mymission.service;
import com.mission.mymission.dto.CustomerReqDTO;
import com.mission.mymission.dto.CustomerReqForm;
import com.mission.mymission.dto.CustomerResDTO;
import com.mission.mymission.entity.Customer;
import com.mission.mymission.exception.BusinessException;
import com.mission.mymission.repository.CustomerRepository;
import org.modelmapper.ModelMapper;
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
@Service
@RequiredArgsConstructor
@Transactional
public class CustomerService {
private final CustomerRepository customerRepository;
private final ModelMapper modelMapper;
}
public CustomerResDTO saveCustomer(CustomerReqDTO customerReqDTO) {
Customer customer = modelMapper.map(customerReqDTO, Customer.class);
Customer savedCustomer = customerRepository.save(customer);
return modelMapper.map(savedCustomer, CustomerResDTO.class);
}
@Transactional(readOnly = true)
public CustomerResDTO getCustomerById(Long id) {
Customer customerEntity = customerRepository.findById(id) // return type : Optional<Customer>
.orElseThrow(() -> new BusinessException(id + "User Not Found", HttpStatus.NOT_FOUND));
// Entity -> ResDTO로 변환
CustomerResDTO customerResDTO = modelMapper.map(customerEntity, CustomerResDTO.class);
return customerResDTO;
}
@Transactional(readOnly = true)
public List<CustomerResDTO> getCustomers() {
List<Customer> customerList = customerRepository.findAll(); // List<Customer>
// List<Customer> -> List<CustomerResDTO>
List<CustomerResDTO> customerResDTOList = customerList.stream() // List<Customer> -> Stream<Customer>
.map(customer -> modelMapper.map(customer, CustomerResDTO.class)) // Stream<Customer> -> Stream<CustomerResDTO>
.collect(toList());// Stream<CustomerResDTO> -> List<CustomerResDTO>
return customerResDTOList;
}
public CustomerResDTO updateCustomer(String email, CustomerReqDTO customerReqDto) {
Customer existCustomer = customerRepository.findByEmail(email)
.orElseThrow(() ->
new BusinessException(email + " User Not Found", HttpStatus.NOT_FOUND));
if (customerReqDto.getName() != null)
existCustomer.setName(customerReqDto.getName());
if (customerReqDto.getAge() != 0)
existCustomer.setAge(customerReqDto.getAge());
return modelMapper.map(existCustomer, CustomerResDTO.class); // Customer -> CustomerResDTO
}
public void deleteCustomer(Long id) {
Customer customer = customerRepository.findById(id) //Optional<Customer>
.orElseThrow(() ->
new BusinessException(id + " User Not Found", HttpStatus.NOT_FOUND));
customerRepository.delete(customer);
}
}
package com.mission.mymission.controller;
import com.mission.mymission.dto.CustomerReqDTO;
import com.mission.mymission.dto.CustomerResDTO;
import com.mission.mymission.service.CustomerService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/customer")
@RequiredArgsConstructor
public class CustomerController2 {
private final CustomerService customerService;
}
@PostMapping
public CustomerResDTO saveCustomer(@RequestBody CustomerReqDTO customerReqDTO) {
return customerService.saveCustomer(customerReqDTO);
}
locolhost:8080(포트번호)/api/customer(테이블명)
@GetMapping("/{id}")
public CustomerResDTO getCustomerById(@PathVariable Long id) {
return customerService.getCustomerById(id);
}
locolhost:8080(포트번호)/api/customer(테이블명)
@GetMapping
public List<CustomerResDTO> getCustomers() {
return customerService.getCustomers();
}
locolhost:8080(포트번호)/api/customer(테이블명)
@PatchMapping("/{email}")
public CustomerResDTO updateCustomer(@PathVariable String email, @RequestBody CustomerReqDTO customerReqDTO) {
return customerService.updateCustomer(email, customerReqDTO);
}
locolhost:8080(포트번호)/api/customer(테이블명)
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteCustomer(@PathVariable Long id) {
customerService.deleteCustomer(id);
return ResponseEntity.ok(id + " Customer가 삭제처리 되었습니다.");
}
}
locolhost:8080(포트번호)/api/customer(테이블명)
package com.mission.mymission.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class IndexController {
@GetMapping("/")
public String index() {
return "redirect:/cpage/index";
}
}
package com.mission.mymission.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@NoArgsConstructor
@AllArgsConstructor
@Getter @Setter
public class CustomerReqDTO {
@NotEmpty(message = "Name은 필수 입력 항목입니다") // " " 허용
private String name;
@NotBlank(message = "Email은 필수 입력 항목입니다") // " " 허용하지 않음
@Email(message = "Email 형식이 아닙니다")
private String email;
private int age;
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<body>
<table border="1">
<tr>
<th>Id</th>
<th>Name</th>
<th>Email</th>
<th>Age</th>
<th>entryDate</th>
<th>Update</th>
<th>Delete</th>
</tr>
<tr th:each="customer, customerStat : ${customer}">
<td th:text="${customerStat.count}"></td> <!-- id 연속적인 수로 출력 -->
<td th:text="${customer.name}"></td>
<td th:text="${customer.email}"></td>
<td th:text="${customer.age}"></td>
<td th:text="${#temporals.format(customer.entryDate, 'yyyy-MM-dd hh:mm')}"></td>
<td><a th:href="@{/cpage/edit/{id}(id=${customer.id})}">Update</a></td>
<td><a th:href="@{/cpage/delete/{id}(id=${customer.id})}">Delete</a></td>
</tr>
</table>
<p><a href="/cpage/signup">Insert</a></p>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<body>
<form action="#" th:action="@{/cpage/addcustomer}" th:object="${customerReqDTO}" method="post">
<label for="name">Name</label>
<input type="text" th:field="*{name}" id="name">
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
<br/>
<label for="email">Email</label>
<input type="text" th:field="*{email}" id="email">
<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span>
<br/>
<label for="age">Age</label>
<input type="text" th:field="*{age}" id="age">
<span th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></span>
<br/>
<input type="submit" value="Add Customer">
</form>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<body>
<form action="#" th:action="@{/cpage/update/{id}(id=${customer.id})}" th:object="${customer}" method="post">
<label for="name">Name</label>
<input type="text" th:field="*{name}" id="name">
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span> <br />
<label for="email">Email</label>
<input type="text" th:field="*{email}" id="email" readonly>
<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span> <br />
<label for="age">Age</label>
<input type="text" th:field="*{age}" id="age">
<span th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></span> <br />
<input type="submit" value="Update Customer">
</form>
</body>
</html>
package com.mission.mymission.controller;
import com.mission.mymission.dto.CustomerReqDTO;
import com.mission.mymission.dto.CustomerReqForm;
import com.mission.mymission.dto.CustomerResDTO;
import com.mission.mymission.service.CustomerService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
@RequestMapping("/cpage")
@RequiredArgsConstructor
public class CustomerController3 {
private final CustomerService customerService;
}
@GetMapping("/index")
public ModelAndView index() {
List<CustomerResDTO> customerResDTOList = customerService.getCustomers();
return new ModelAndView("index", "customer", customerResDTOList);
}
// 등록 폼 호출
@GetMapping("/signup")
public String showSignUpForm(CustomerReqDTO customer) {
return "add-customer";
}
// 등록
@PostMapping("/addcustomer")
public String addCustomer(@Valid CustomerReqDTO customer, BindingResult result, Model model) {
if (result.hasErrors()) {
return "add-customer"; // 이전 페이지 그대로 유지
}
customerService.saveCustomer(customer);
return "redirect:/cpage/index"; // 등록 후 첫화면으로 돌아감
}
// 수정 폼 호출
@GetMapping("/edit/{id}")
public String showUpdateForm(@PathVariable Long id, Model model){
CustomerResDTO customerResDTO = customerService.getCustomerById(id);
model.addAttribute("customer", customerResDTO);
return "update-customer";
}
// 수정
@PostMapping("/update/{id}")
public String updateCustomer(@PathVariable("id") Long id, @Valid CustomerReqForm customer, BindingResult result, Model model) {
if (result.hasErrors()) {
model.addAttribute("customer", customer);
return "update-customer";
}
customerService.updateCustomerForm(customer);
return "redirect:/cpage/index";
}
@GetMapping("/delete/{id}")
public String deleteCustomer(@PathVariable("id") Long id) {
customerService.deleteCustomer(id);
return "redirect:/cpage/index";
}
}
public void updateCustomerForm(CustomerReqForm customerReqForm) {
Customer exist = customerRepository.findById(customerReqForm.getId())
.orElseThrow(() ->
new BusinessException(customerReqForm.getId() + " User Not Found", HttpStatus.NOT_FOUND));
exist.setName(customerReqForm.getName());
exist.setAge(customerReqForm.getAge());
}
package com.mission.mymission.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.*;
@NoArgsConstructor // 기본 생성자 생성
@AllArgsConstructor
@Getter @Setter
@ToString
public class CustomerReqForm {
private Long id;
@NotEmpty(message = "Name은 필수 입력 항목입니다") // " " 허용
private String name;
@NotBlank(message = "Email은 필수 입력 항목입니다") // " " 허용하지 않음
@Email(message = "Email 형식이 아닙니다")
private String email;
private int age;
}
locolhost:8080(포트번호)/cpage/index
등록
수정
삭제
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.maven.spring</groupId>
<artifactId>MySpringFW</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>CustomerSpringWeb Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.2.25.RELEASE</spring.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
<!-- scope : test라고 이름 지어진 폴더에만 적용 -->
</dependency>
<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Hikari Connection Pooling -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<finalName>MySpringFW</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven
defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
# 서버 포트 설정
#server.port=8087
# 한글 사용하기 위한 스프링 유니코드 작성
# (applcation-prod/test.properties에서 설정)
myboot.name=\uc2a4\ud504\ub9c1
myboot.fullName=${myboot.name} Boot
# mariaDB 연결 정보 설정
spring.datasource.url=jdbc:mariadb://127.0.0.1:3306/boot_db
spring.datasource.username=boot
spring.datasource.password=boot
spring.datasource.driverClassName=org.mariadb.jdbc.Driver
# JPA를 사용한 데이터베이스 초기화
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# DB Dialect 설정
spring.jpa.database-platform=org.hibernate.dialect.MariaDBDialect
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.11</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.basic</groupId>
<artifactId>MySpringBoot3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>MySpringBoot3</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<spring-framework.version>6.0.12</spring-framework.version> <!-- spring framework 버전 바꾸기 가능 (spring-boot-dependencies에서 추가)-->
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.1.1</version>
</dependency>
<!-- thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
exception
패키지 생성 -> 하단 코드 복사package com.mission.mymission.exception;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Getter
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String message;
private HttpStatus httpStatus;
public BusinessException(String message) {
//417
this(message, HttpStatus.EXPECTATION_FAILED);
}
public BusinessException(String message, HttpStatus httpStatus) {
this.message = message;
this.httpStatus = httpStatus;
}
}
package com.mission.mymission.exception;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Getter
public class SystemException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String message;
private HttpStatus httpStatus;
private Throwable throwable;
public SystemException(Exception e) {
this(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
public SystemException(String message) {
this(message, HttpStatus.INTERNAL_SERVER_ERROR);
}
public SystemException(String message, Throwable t) {
this.message = message;
this.throwable =t;
}
public SystemException(Throwable t) {
this.throwable = t;
}
public SystemException(String message, HttpStatus httpStatus) {
this.message = message;
this.httpStatus = httpStatus;
}
public Throwable getThrowable() {
return this.throwable;
}
}
advice
패키지 생성 -> 하단 코드 복사package com.mission.mymission.advice;
import com.mission.mymission.exception.BusinessException;
import com.mission.mymission.exception.SystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class DefaultExceptionAdvice {
private final Logger LOGGER = LoggerFactory.getLogger(DefaultExceptionAdvice.class);
@ExceptionHandler(BusinessException.class)
protected ResponseEntity<Object> handleException(BusinessException e) {
Map<String, Object> result = new HashMap<String, Object>();
result.put("message", "[안내] " + e.getMessage());
result.put("httpStatus", e.getHttpStatus().value());
return new ResponseEntity<>(result, e.getHttpStatus());
}
@ExceptionHandler(SystemException.class)
protected ResponseEntity<Object> handleException(SystemException e) {
Map<String, Object> result = new HashMap<String, Object>();
result.put("message", "[시스템 오류] " + e.getMessage());
result.put("httpStatus", e.getHttpStatus().value());
return new ResponseEntity<>(result, e.getHttpStatus());
}
//숫자타입의 값에 문자열타입의 값을 입력으로 받았을때 발생하는 오류
@ExceptionHandler(HttpMessageNotReadableException.class)
protected ResponseEntity<Object> handleException(HttpMessageNotReadableException e) {
Map<String, Object> result = new HashMap<String, Object>();
result.put("message", e.getMessage());
result.put("httpStatus", HttpStatus.BAD_REQUEST.value());
return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
protected ResponseEntity<Object> handleException(Exception e) {
Map<String, Object> result = new HashMap<String, Object>();
ResponseEntity<Object> ret = null;
if (e instanceof BusinessException) {
BusinessException b = (BusinessException) e;
result.put("message", "[안내]\n" + e.getMessage());
result.put("httpStatus", b.getHttpStatus().value());
} else if ( e instanceof SystemException) {
SystemException s = (SystemException)e;
result.put("message", "[시스템 오류]\n" + s.getMessage());
result.put("httpStatus", s.getHttpStatus().value());
ret = new ResponseEntity<>(result, s.getHttpStatus());
LOGGER.error(s.getMessage(), s);
} else {
String msg = "예상치 못한 문제가 발생했습니다.\n관리자에게 연락 하시기 바랍니다.";
result.put("message", msg);
result.put("httpStatus", HttpStatus.INTERNAL_SERVER_ERROR.value());
ret = new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
e.printStackTrace();
LOGGER.error(e.getMessage(), e);
}
return ret;
}
}
인증할 때 email을 이용해서 인증하도록 구현 했기 때문에 이제 username이 아닌 email/pwd 이용
SecurityConfig.java 수정
package com.basic.myspringboot.security.config;
import com.basic.myspringboot.security.service.UserInfoUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // 인가
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> {
auth.requestMatchers("/api/users/welcome",
"/userspage/**",
"/userinfos/new").permitAll()
.requestMatchers("/api/users/**").authenticated();
})
.formLogin(withDefaults())
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
return new UserInfoUserDetailsService();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider
= new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService
(userDetailsService());
authenticationProvider.setPasswordEncoder
(passwordEncoder());
return authenticationProvider;
}
// @Bean
// //authentication
// public UserDetailsService userDetailsService (PasswordEncoder encoder){
// UserDetails admin = User.withUsername("adminboot")
// .password(encoder.encode("pwd1"))
// .roles("ADMIN")
// .build();
// UserDetails user = User.withUsername("userboot")
// .password(encoder.encode("pwd2"))
// .roles("USER")
// .build();
// return new InMemoryUserDetailsManager(admin, user);
// }
}
<?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="myspring.customer.dao.mapper.CustomerMapper">
<!-- 조회 쿼리는 id를 이용하여 실행 -->
<!-- parameter에 들어가는 값이 {value} 변수로 들어옴 -->
<select id="selectCustomerById" parameterType="string" resultType="Customer">
select * from customer where name=#{value}
</select>
<select id="selectCustomerList" resultType="Customer">
select * from customer order by id
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- log4j2 설정 -->
<settings>
<setting name="defaultStatementTimeout" value="3"/>
<setting name="logImpl" value="LOG4J2"/>
</settings>
<typeAliases>
<!-- CustomerVO 설정 -->
<typeAlias alias="Customer" type="myspring.customer.vo.CustomerVO" />
</typeAliases>
</configuration>
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<context:component-scan base-package="myspring.customer">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- Spring MVC에 필요한 Bean들을 자동으로 등록해주는 태그-->
<mvc:annotation-driven />
<!-- DispatcherServlet의 변경된 url-pattern 때문에 필요한 태그 설정 -->
<mvc:default-servlet-handler/>
<!-- 아래 주석은 Controller에서 포워딩 되는 .jsp 확장자를 생략할 수 있다. -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- annotation-driven 태그에서 내부적으로 처리해주는 설정 -->
<!-- <bean id="jsonHttpMessageConverter" -->
<!-- class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /> -->
<!-- <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> -->
<!-- <property name="messageConverters"> -->
<!-- <list> -->
<!-- <ref bean="jsonHttpMessageConverter"/> -->
<!-- </list> -->
<!-- </property> -->
<!-- </bean> -->
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<File name="File" fileName="./logs/logfile.log" append="true">
<PatternLayout
pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-5level %logger{36} - %msg%n" />
</File>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout
pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Logger name="org.springframework" level="INFO" additivity="false" />
<Logger name="myspring" level="DEBUG" />
<Root level="DEBUG">
<AppenderRef ref="console" level="DEBUG" />
<AppenderRef ref="File" level="DEBUG" />
</Root>
</Loggers>
</Configuration>
<?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:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Properties file 정보 설정 -->
<context:property-placeholder location="classpath:value.properties"/>
<!-- DataSource 구현체인 HikariDataSource를 SpringBean으로 등록 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
p:driverClassName="${db.driverClass}"
p:jdbcUrl="${db.url}"
p:username="${db.username}"
p:password="${db.password}"
/>
<!-- Mybatis-spring의 SqlSessionFactoryBean을 SpringBean으로 등록 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
<property name="mapperLocations">
<list>
<value>classpath:mybatis/*Mapper.xml</value>
</list>
</property>
</bean>
<!-- SqlSession으로 sql 실행 -->
<!-- SqlSessionFactory를 통해 취득한 SqlSession을 실행중인 트랜잭션에 할당함 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"/>
</bean>
<!-- Mybatis-Spring의 MapperScannerConfigurer을 SpringBean으로 등록 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 참조하는 것 없어서 bean id 없어도 됨 -->
<property name="basePackage" value="myspring.customer.dao.mapper" />
<property name="sqlSessionTemplateBeanName" value="sqlSession"/>
</bean>
<!-- DAO, Service에 해당하는 Bean을 Scanning -->
<context:component-scan base-package="myspring.customer" >
<!-- 하단의 myspring.customer에서 controller만 제외하고 scan -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>CustomerSpringWeb</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-beans-customer.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans-web.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.maven.spring</groupId>
<artifactId>MySpringFW</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>CustomerSpringWeb Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.2.25.RELEASE</spring.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
<!-- scope : test라고 이름 지어진 폴더에만 적용 -->
</dependency>
<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Hikari Connection Pooling -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<finalName>MySpringFW</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven
defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>