티스토리 뷰
728x90
화면 그리기
리액트에서 cookie 사용
- yarn i react-cookie
리액트에서 부트스트랩 사용
- yarn install react-bootstrap bootstrap
화면 그리기
- 3 일차 첫 번째 시간
- App.js
import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import Board from "./Board";
import { instanceOf } from "prop-types";
import { withCookies, Cookies } from "react-cookie";
class App extends Component {
static propTypes = {
cookies: instanceOf(Cookies).isRequired,
};
static defaultProps = {
menu1: true,
menu2: false,
boardType: "board",
};
constructor(props) {
super(props);
const { cookies } = this.props;
console.log(
"App constructor:" + cookies.get("menu1") + ", " + cookies.get("menu2")
);
this.state = {
menu1:
cookies.get("menu1") !== "" ? cookies.get("menu1") : this.props.menu1,
menu2:
cookies.get("menu2") !== "" ? cookies.get("menu2") : this.props.menu2,
boardType:
cookies.get("boardType") !== ""
? cookies.get("boardType")
: this.props.boardType,
};
this.handleCreate = this.handleCreate.bind(this);
}
handleCreate(data) {
console.log("App handleCreate:" + data.boardType);
const { cookies } = this.props;
cookies.set("menu1", data.menu1, { path: "/" });
cookies.set("menu2", data.menu2, { path: "/" });
cookies.set("boardType", data.boardType, { path: "/" });
/* this.setState({
menu1: data.menu1,
menu2: data.menu2,
boardType: data.boardType
}); */
}
render() {
return (
<div>
<Board
key={this.state.boardType}
boardType={this.state.boardType}
menu1={this.state.menu1}
menu2={this.state.menu2}
onCreate={this.handleCreate}
/>
</div>
);
}
}
export default withCookies(App);
- index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import "bootstrap/dist/css/bootstrap.min.css";
import { CookiesProvider } from "react-cookie";
ReactDOM.render(
<React.StrictMode>
<CookiesProvider>
<App />
</CookiesProvider>
</React.StrictMode>,
document.getElementById("root")
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
- board.js
import React, { useState, Component, Fragment } from "react";
class Board extends Component {
constructor(props) {
super(props);
this.state = {
menu1: this.props.menu1,
menu2: this.props.menu2,
boardType: this.props.boardType,
boardData: {
id: "",
title: "",
contents: "",
fname: "",
},
};
}
componentDidMount() {
console.log("-------------------------- : " + this.props.boardType);
}
componentWillUnmount() {
console.log("----------------Board Unmount");
}
render() {
//console.log('===Board render:'+this.props.boardType);
//console.log("menu1:"+this.state.menu1);
//console.log("menu2:"+this.state.menu2);
return (
<div className="card">
<h1>react board</h1>
<div className="card-header">
<ul className="nav nav-tabs">
<li className="nav-item">
<a
className={"nav-link " + (this.state.menu1 ? "active" : "")}
onClick={this.menuClick}
>
싱글이미지 게시판
</a>
</li>
<li className="nav-item">
<a
className={"nav-link " + (this.state.menu2 ? "active" : "")}
onClick={this.menuClick2}
>
멀티이미지 게시판
</a>
</li>
</ul>
</div>
<div className="card-body">
<div className="row">
<div className="col-lg-4">
<List
key={this.state.boardType}
boardType={this.state.boardType}
onCreate={this.handleCreate}
/>
</div>
<div className="col-lg-5">
<Detail
key={this.state.boardType + this.state.boardData.id}
id={this.state.boardData.id}
title={this.state.boardData.title}
contents={this.state.boardData.contents}
date={this.state.boardData.date}
fname={this.state.boardData.fname}
boardType={this.state.boardType}
onCreate={this.handleCreate2}
/>
</div>
<div className="col-lg-3">
<Image
key={
this.state.boardType +
this.state.boardData.id +
this.state.boardData.fname
}
boardType={this.state.boardType}
fname={this.state.boardData.fname}
/>
</div>
</div>
</div>
<div className="card-footer">
SpringBoot + MySQL + <strong>React</strong> + bootstrap4 게시판 만들기
</div>
</div>
);
}
}
class List extends Component {
render() {
const divStyle = {
minHeight: "500px",
maxHeight: "500px",
overflowY: "auto",
};
return (
<div className="card" style={divStyle}>
<table className="table">
<thead>
<tr>
<th>게시물 리스트</th>
</tr>
</thead>
<tbody>
{/* <tr>
<td>게시물 1</td>
</tr>
<tr>
<td>게시물 2</td>
</tr> */}
{/* {this.state.boardList} */}
</tbody>
</table>
</div>
);
}
}
class Detail extends Component {
render() {
const divStyle = {
minHeight: "500px",
maxHeight: "1000px",
};
const divCenter = {
textAlign: "center",
};
return (
<div className="card bg-light text-dark" style={divStyle}>
<form name="form1" action="">
<div className="form-group">
<label className="control-label">제목:</label>
<div>
<input
type="text"
className="form-control"
placeholder="제목을 입력하세요"
/>
</div>
</div>
<div className="form-group">
<label className="control-label">내용:</label>
<div>
<textarea className="form-control" rows="10"></textarea>
</div>
</div>
<div className="form-group">
<label className="control-label">이미지첨부: jpg,gif,png</label>
<div>
<input type="file" className="form-control" name="file" />
</div>
</div>
<input type="hidden" name="id" />
</form>
<div style={divCenter}>
<div className="btn-group">
<button type="button" className="btn btn-primary">
저장
</button>
<button type="button" className="btn btn-secondary">
취소
</button>
<button type="button" className="btn btn-danger">
삭제
</button>
<button type="button" className="btn btn-info">
그림삭제
</button>
</div>
</div>
</div>
);
}
}
class Image extends Component {
render() {
const divStyle1 = {
minHeight: "500px",
maxHeight: "500px",
overflowY: "auto",
display: "block",
};
const divStyle1_1 = {
minHeight: "500px",
maxHeight: "1000px",
display: "none",
};
const divStyle2 = {
minHeight: "500px",
maxHeight: "500px",
overflowY: "auto",
display: "block",
};
const divStyle2_1 = {
minHeight: "500px",
maxHeight: "1000px",
display: "none",
};
const imgStyle = {
width: "100%",
};
return (
<Fragment>
<div className="card bg-light text-dark">
<img alt="image"></img>
</div>
<div className="card bg-light text-dark">
<span></span>
</div>
</Fragment>
);
}
}
export default Board;
리액트로 하다 올해 못할 것 같아서 thymeleaf 환경으로 변경..
- https://start.spring.io/
- 프로젝트 생성
- 의존성 추가
- Spring Boot DevTools
- Lombok
- Spring Web
- Thymeleaf
- Spring Data JPA
- MySQL Driver
- mysql 연결을 위해 application.properties 입력
#디비 연결
spring.datasource.url=jdbc:mysql://localhost:3307/testproject?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# mysql 사용
spring.jpa.database=mysql
# 콘솔에 sql 출력 여부
spring.jpa.show-sql=true
편익성을 높이는 몇 가지 설정
- 자동 리로딩 설정 ( 자동 재시작 )
- 웹 개발 시 코드를 수정하고 다시 deploy를 하는 과정 자동으로 설정
- EditConfigurations -> Build and run -> Modify options 선택
- On update action, On freme deactivation의 옵션 값을 Update classeds and resources로 지정
- 설정이 추가된 후 수정하고 다른 작업을 하면 자동으로 빌드와 배포가 이루어짐
로그 레벨의 설정
- 스프링 부트는 기본적으로 Log4j2가 추가
# 로그 레벨ㅇ 설정
logging.level.com.board.springframwork=info
logging.level.com.board=debug
인텔리제이의 DataSource 설정
- 인텔리제이 얼티메이트는 JPA 관련 플러그인이 이미 설치 되어 있기 때문에 Data-Source를 설정해두면 나중에 엔티티 클래스의 생성이나 기타 클래스의 생성과 설정 시에 도움
테스트 환경과 의존성 주입 테스트
- 스프링에는 test 라이브러리를 추가해야 하고 JUnit 등도 직접 추가 해야 하지만 부트는 프로젝트 생성할 때 이미 테스트 관련 설정이 완료되고 테스트 코드도 하나 생성되어 있음
- 테스트 코드의 실행을 점검하기 위해 Data-SourceTests를 작성해서 HikaraCP의 테스트와 Lombok을 확인
// 테스트 폴더에서 lombok 하고 Log4j2 사용하게 하기 위한
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
- 해당 코드를 build.gradle 에 추가해줘야 한다.
Spring Data JPA를 위한 설정
- Spring Data JPA를 이용할 때 필요한 설정
- application.properties에 작성
spring.jpa.hibernate.ddl-auto=create
# none - DDL을 하지 않음
# create-drop - 실행할 때 DDL을 실행하고 종료 시에 만들어진 테이블 등을 모두 삭제
# create - 실행할 때 마다 새롭게 테이블을 생성
# update - 기존과 다르게 변경된 부분이 있을 때는 새로 생성
# validate - 변경된 부분만 알려주고 종료
# JPA가 실행하는 SQL 을 같이 출력
spring.jpa.show-sql=true
#실제로 실행되는 SQL 을 포맷팅해서 알아보기 쉽게 출력
spring.jpa.properties.hibernate.format_sql=true
컨트롤러와 Thymeleaf 만들기
- controller 패키지 생성 -> SampleController 클래스 생성
@Controller
@Log4j2
public class SampleController {
@GetMapping("/hello")
public void hello(Model model){
log.info("hello---------------------");
model.addAttribute("msg","Hello World");
}
}
- 화면은 Thymeleaf를 이용하는데 위치를 주의해서 작성해야 한다,
- 프로젝트 생성 시 만들어져 있는 templates 폴더에 hello.html을 작성
- hello.html에서 가장 중요한 부분은 Thymeleaf의 네임스페이스를 추가하는 것
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${msg}"></h1>
</body>
</html>
JSON 데이터 만들기
- 스프링 부트를 이용해 웹프로젝트를 구성할 때는 API 서버라는 것을 구성하는 경우도 많다
- API 서버는 순수한 데이터만 전송하는 방식
@org.springframework.web.bind.annotation.RestController
@Log4j2
public class RestController {
@GetMapping("/helloArr")
public String[] helloArr(){
log.info("helloArr-----------");
return new String[]{"AAA", "BBB", "CDCDD"};
}
}
- 가장 많이 사용하는 방식 위 두가지
Thymeleaf
- 반복문
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li th:each="str: ${list}" th:text="${str}"></li>
</ul>
<ul>
<th:block th:each="str: ${list}">
<li>[[${str}]]</li>
</th:block>
</ul>
</body>
</html>
반복문의 status 변수
- Thymeleaf는 th:each를 처리할 때 현재 반복문의 내부 상태에 변수를 추가해서 사용할 수 있다
- 일명 status 변수라 하는데 index/count/size/first/last/odd/even 등을 이용해 자주 사용하는 값 출력 가능
<ul>
<li th:each="str, status: ${list}">
[[${status.index}]] -- [[${str}]]
</li>
</ul>
- status 변수명은 사용자가 지정가능하고 inex는 0번부터 시작 하는 번호를 의미
- count 는 1부터 시작
th:if / th:unless / th:switch
- Thymeleaf는 제어문의 형태로 th:if / th:unless / th:switch를 이용할 수 있다
- th:if와 th:unless는 사실상 별도의 속성으로 사용 할 수 있으므로 if ~ else와는 조금 다르게 사용 된다
- 반복문의 홀짝을 구분해서 처리하고 싶다면?
// 홀짝 출력
<ul>
<li th:each="str, status: ${list}">
<span th:if="${status.odd}"> ODD -- [[${str}]]</span>
<span th:unless="${status.odd}"> EVEN -- [[${str}]]</span>
</li>
</ul>
<!-- 삼항 처리로 홀수만 출력하게 -->
<ul>
<li th:each="str, status: ${list}">
<span th:text="${status.odd} ? 'ODD ---' + ${str}"></span>
</li>
</ul>
인라인 처리
728x90
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- App
- JPA
- CheckBox
- 다중체크박스 처리
- 셀프로젝트
- 제약조건
- optional
- findFirstBy
- commit 에러
- SCP
- 받아오기
- reactStart
- 체크박스
- @reqeustBody
- selectbox
- @RequestParam
- ID
- reactApp
- React
- mircrosoft visual studio
- th:selected
- 서버전송
- react 시작 오류
- C
- @Builder
- 다른데서 react
- 씹어먹는 C 언어
- C언어
- 셀렉트박스
- 아이디
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
글 보관함