STS 4
설치 후 ini 파일에서 Xms만 1024로 변경
해당 버전으로는 legacy 프로젝트 생성이 불가능하다.
Back - boot, mybatis
properties 대신 yaml로 작성 시
spring :
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.99.100:3306/mydb
username: scott
password: tiger
hikari:
connection-timeout: 3000
validation-timeout: 1000
maximum-pool-size: 10
logging:
level:
web: info
'[com.bit.boot04]': debug
STS 4 설치 후 lombok 설치 (STS가 꺼져있어야 제대로 적용된다)
java -jar C:\Users\BIT\.m2\repository\org\projectlombok\lombok\1.18.26\lombok-1.18.26.jar
또는 공식홈페이지에서 lombok 받아서 실행하면 끝
ResponseEntity
@GetMapping
public ResponseEntity<?> index() {
log.debug("index...");
// 문자열을 보내면 text로 List와같은 자료구조를 보내면 jackson이 알아서 json으로 리턴해줌
// ResponseEntity resp = new ResponseEntity("성공",HttpStatus.OK);
// ResponseEntity resp = new ResponseEntity(new ArrayList(),HttpStatus.OK);
// return resp
// if (true) {
// return new ResponseEntity<DeptVo>(DeptVo.builder().deptno(1111).dname("tester").loc("test").build(),
// HttpStatus.OK);
// return ResponseEntity.status(HttpStatus.OK).body(DeptVo.builder().deptno(1111).dname("tester").loc("test").build());
return ResponseEntity.ok(DeptVo.builder().deptno(1111).dname("tester").loc("test").build());
// } else {
// return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
// }
}
Config
@Configuration
@MapperScan(basePackages = "com.bit.boot04.model")
@AllArgsConstructor
public class ServletConfig implements WebMvcConfigurer {
// sqlSession 주입을 못 받을 경우...
// @Bean
// SqlSession getSqlSession() {
// return new SqlSessionTemplate(sqlSessionFactory);
// }
}
Controller
import ...;
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/api/")
public class DeptController {
private final DeptService deptService;
@GetMapping
@CrossOrigin
public List<?> list() {
return deptService.selectAll();
}
@CrossOrigin
@PostMapping
public ResponseEntity<?> add(@ModelAttribute("bean") DeptVo bean) {
try {
deptService.insertOne(bean);
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok("success");
}
@CrossOrigin
@GetMapping("/{deptno}")
// public DeptVo detail(@PathVariable("deptno") int deptno) {
// return deptService.selectOne(deptno);
public ResponseEntity<?> detail(@PathVariable("deptno") int deptno) {
DeptVo bean = deptService.selectOne(deptno);
if(bean!=null) {
return ResponseEntity.ok(bean);
} else {
return ResponseEntity.notFound().build();
}
}
@CrossOrigin
@PutMapping("/{deptno}")
public ResponseEntity<?> edit(@RequestBody DeptVo bean, HttpServletRequest req) throws URISyntaxException {
// API 호출할 때 RestTemplate을 이용해서 받아올 수 있다.
if(deptService.updateOne(bean) > 0 ) {
RestTemplate template = new RestTemplate();
URI url = new URI(req.getRequestURL().toString());
RequestEntity param = new RequestEntity(HttpMethod.GET, url);
return template.exchange(url, HttpMethod.GET, param, DeptVo.class);
// return ResponseEntity.ok(bean);
}
return ResponseEntity.internalServerError().build();
}
@CrossOrigin
@DeleteMapping("/{deptno}")
public ResponseEntity<?> del(@PathVariable("deptno") int deptno) {
if(deptService.deleteOne(deptno) > 0) {
return ResponseEntity.ok("success");
}
return ResponseEntity.badRequest().build();
}
}
Service
package com.bit.boot04.service;
import ...;
@Service
@AllArgsConstructor
public class DeptService {
private final SqlSession sqlSession;
public List<DeptVo> selectAll() {
return sqlSession.getMapper(DeptMapper.class).findAll();
}
public DeptVo selectOne(int deptno) {
return sqlSession.getMapper(DeptMapper.class).findOne(deptno);
}
@Transactional
public void insertOne(DeptVo bean) {
sqlSession.getMapper(DeptMapper.class).insertOne(bean);
}
@Transactional
public int updateOne(DeptVo bean) {
return sqlSession.getMapper(DeptMapper.class).updateOne(bean);
}
@Transactional
public int deleteOne(int deptno) {
return sqlSession.getMapper(DeptMapper.class).deleteOne(deptno);
}
}
Mapper
import ...;
@Mapper
public interface DeptMapper {
@Select("select * from dept")
List<DeptVo> findAll();
@Select("select * from dept where deptno=#{pk}")
DeptVo findOne(int pk);
@Insert("insert into dept value(#{deptno}, #{dname}, #{loc})")
void insertOne(DeptVo bean);
@Update("update dept set dname=#{dname}, loc=#{loc} where deptno=#{deptno}")
int updateOne(DeptVo bean);
@Delete("delete from dept where deptno=#{pk}")
int deleteOne(int pk);
}
Front - React
npx create-react-app .
npm i -D react-router-dom
npm i axios
npm start
index.html - CDN 추가
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap-theme.min.css" integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
<title>React App</title>
</head>
App.js
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import './App.css';
import Layout from './components/Layout';
import DeptAdd from './pages/DeptAdd';
import DeptDetail from './pages/DeptDetail';
import DeptList from './pages/DeptList';
import Home from './pages/Home';
import Intro from './pages/Intro';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="intro" element={<Intro />} />
<Route path="dept/" element={<DeptList />} />
<Route path="dept/add" element={<DeptAdd />} />
<Route path="dept/:deptno" element={<DeptDetail />} />
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
Layout.js
import React from 'react'
import { Link, NavLink, Outlet } from 'react-router-dom'
export default function Layout() {
return (
<>
<nav className="navbar navbar-inverse">
<div className="container-fluid">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
</button>
<Link className="navbar-brand" to="/">비트교육센터</Link>
</div>
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul className="nav navbar-nav">
<li><NavLink className={({ isActive, isPending }) =>isPending ? "pending" : isActive ? "active" : ""} to="/" end>Home</NavLink></li>
<li><Link to="/intro">Intro</Link></li>
<li><Link to="/dept/">Dept</Link></li>
</ul>
</div>
</div>
</nav>
<div className='container'>
<Outlet/>
</div>
</>
)
}
DeptList.js
import axios from 'axios';
import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
export default function DeptList() {
const [depts, setDepts] = useState([]);
// const getList = new Promise((resolve, reject)=>{
// const xhr = new XMLHttpRequest();
// xhr.onload=(e)=>{
// if(xhr.readyState===4 && xhr.status===200){
// const result = JSON.parse(xhr.response);
// resolve(result);
// } else {
// reject(xhr.status);
// }
// };
// xhr.open('get', 'http://localhost:8080/api/');
// xhr.send();
// })
useEffect(()=>{
// xhr
// getList.then(function(result){
// setDepts(result);
// })
// fetch
// fetch('http://localhost:8080/api/')
// .then((e)=>e.json())
// .then((e)=>{
// setDepts(e);
// })
//axios
axios.get('http://localhost:8080/api/')
.then(e=>setDepts(e.data));
}, []);
return (
<>
<div className="page-header">
<h1>Dept List</h1>
</div>
<Link to='add' className='btn btn-primary btn-block' role='button'>add</Link>
{depts.map((ele)=>(
<Link to={"./"+ele.deptno} key={ele.deptno}>
<div className="panel panel-primary">
<div className="panel-heading">{ele.dname}</div>
<div className="panel-body">{ele.loc}</div>
</div>
</Link>
))}
</>
)
}
DeptDetail.js
import axios from 'axios';
import React, { useEffect, useRef, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
export default function DeptDetail() {
const {deptno} = useParams();
// 디테일 출력
const [bean, setBean] = useState({});
useEffect(()=>{
axios.get('http://localhost:8080/api/'+deptno)
.then(e=>{setBean(e.data); setDeptInput(e.data)});
},[]);
// 디테일 <-> 수정 스왑
const [edit, setEdit] = useState(false);
const editForm = e =>{
setEdit(true);
};
// submit
const sbt = e =>{
e.preventDefault();
// axios.put('http://localhost:8080/api/'+deptno, {
// deptno:e.target.deptno.value ,
// dname:e.target.dname.value,
// loc:e.target.loc.value
// }).then(e=>navigate(-1));
let params = {
deptno:e.target.deptno.value ,
dname:e.target.dname.value,
loc:e.target.loc.value
}
axios({
url: 'http://localhost:8080/api/'+deptno,
method: 'put',
data: params
}).then(e=>navigate(-1));
};
// back
const navigate = useNavigate();
const back = e => {
if(edit){
setEdit(!edit);
} else {
navigate(-1);
}
};
// 내용 입력할 수 있도록
const [deptInput, setDeptInput] = useState({dname:bean.dname, loc:bean.loc});
const dnameInput = e=>{
setDeptInput({...deptInput, dname: e.target.value});
}
const locInput = e=>{
setDeptInput({...deptInput, loc: e.target.value});
}
// 삭제
const del = () => {
axios.delete('http://localhost:8080/api/'+deptno)
.then(e=>navigate(-1));
}
return (
<>
<div className="page-header">
<h1>{!edit ?'Detail ':'Update '}Page</h1>
</div>
<form onSubmit={sbt}>
<div className='form-group'>
{!edit?bean.deptno:<input className='form-control' name='deptno' value={bean.deptno}/>}
</div>
<div className='form-group'>
{!edit?bean.dname:<input className='form-control' name='dname' value={deptInput.dname} onChange={dnameInput}/>}
</div>
<div className='form-group'>
{!edit?bean.loc:<input className='form-control' name='loc' value={deptInput.loc} onChange={locInput}/>}
</div>
<div className='form-group'>
{!edit
?<>
<button onClick={editForm} className='btn btn-primary btn-block' type='button'>수정</button>
<button onClick={del} className='btn btn-danger btn-block' type='button'>삭제</button>
</>
:<button className='btn btn-primary btn-block' type='submit'>입력</button>
}
<button className='btn btn-default btn-block' type='reset'>취소</button>
<button onClick={back} className='btn btn-default btn-block' type='button'>뒤로</button>
</div>
</form>
</>
)
}
DeptAdd.js
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
export default function DeptAdd() {
const navigate = useNavigate();
const back = () => navigate(-1);
const sub = e => {
e.preventDefault();
console.log(e);
// @ResponseBody
// let params = {
// deptno:Number(e.target.deptno.value),
// dname: e.target.dname.value,
// loc: e.target.loc.value
// };
// @ModelAttribute
let params = 'deptno='+Number(e.target.deptno.value)
+ '&dname='+e.target.dname.value
+ '&loc='+e.target.loc.value
// axios.post('http://localhost:8080/api/',params);
axios({
method:'post',
url:'http://localhost:8080/api/',
data: params
}).then(()=>{
return navigate('/dept');
})
};
return (
<>
<div className="page-header">
<h1>Dept List</h1>
</div>
<form onSubmit={sub}>
<div className='form-group'>
<input className='form-control' name='deptno' placeholder='deptno'/>
</div>
<div className='form-group'>
<input className='form-control' name='dname' placeholder='dname'/>
</div>
<div className='form-group'>
<input className='form-control' name='loc' placeholder='loc'/>
</div>
<div className='form-group'>
<button className='btn btn-primary btn-block' type='submit'>입력</button>
<button className='btn btn-default btn-block' type='reset'>취소</button>
<button onClick={back} className='btn btn-default btn-block' type='button'>뒤로</button>
</div>
</form>
</>
)
}
JPA - 프로젝트 생성시 JPA 라이브러리 추가 필요
properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.99.100/mydb
spring.datasource.username=scott
spring.datasource.password=tiger
logging.level.web=info
logging.level.db=debug
logging.level.com.bit.boot05=debug
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
Entity
import ...;
@Entity
@ToString
@Getter
public class Dept2 {
@Id
private int deptno;
private String dname;
private String loc;
}
DeptRepo
package com.bit.boot05.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import com.bit.boot05.domain.entity.Dept2;
public interface DeptRepo extends JpaRepository<Dept2, Integer> {
}
Controller
import ...;
@RestController
@RequestMapping("/api/")
@AllArgsConstructor
public class DeptController {
private final DeptService deptService;
@GetMapping
public List<DeptVo> getList() {
return (List<DeptVo>) deptService.selectAll();
}
}
Service
package com.bit.boot05.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.bit.boot05.domain.DeptRepo;
import com.bit.boot05.domain.entity.DeptVo;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class DeptService {
private final DeptRepo deptRepo;
public List<?> selectAll() {
List<DeptVo> list = new ArrayList<>();
deptRepo.findAll().forEach((ele)->{
list.add(
DeptVo.builder()
.deptno(ele.getDeptno())
.dname(ele.getDname())
.loc(ele.getLoc())
.build());
});
return list;
}
}
'회고록(TIL&WIL)' 카테고리의 다른 글
TIL 2023.03.21 암호화, JWT, Spring Security (0) | 2023.03.21 |
---|---|
TIL 2023.03.20 JPA, WebSocket (0) | 2023.03.20 |
TIL 2023.03.16 React (기본문법 jsx, useState, react-router) (0) | 2023.03.16 |
TIL 2023.03.15 Node.js 3 (Docker-network, Express-Generator, Express-session, JS-ES6, React) (0) | 2023.03.15 |
TIL 2023.03.14 Node.js 2 (express Project, nodemon, mongoDB) (0) | 2023.03.14 |