1장
스프링 부트
기존 스프링프레임워크(불편한설정+버전충돌) 없앰 + 빠르고 쉬운 서버환경/테스트 환경
=>자동화된 라이브러리, xml 없음 ,내장 톰캣 ,자동설정
-기본출력
package com.sesac.boot1.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/*
* 스프링빈으로 등록
* jsp,html파일 생성안해도 바로 브라우저로 전달가능
*
*/
@RestController
public class SampleController {
//주소 이렇게
@GetMapping("/hello")
public String sayHello() {
return "Hello World";
}
}
-SampleVO
package com.sesac.boot1.domain;
import lombok.Data;
import lombok.ToString;
@Data //get,set자동생성 equals,hashCode,toString,파라미터x생성자 자동생성
@ToString(exclude= {"val3"})
//원하는 속성만 출력되도록 조정함 ->val3 속성은 출력x
/*@Getter
@Setter
*/
public class SampleVO {
private String val1;
private String val2;
private String val3;
}
-SampleController
package com.sesac.boot1.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.sesac.boot1.domain.SampleVO;
/*
* 스프링빈으로 등록
* jsp,html파일 생성안해도 바로 브라우저로 전달가능
*
*/
@RestController
public class SampleController {
//주소 이렇게
@GetMapping("/hello")
public String sayHello() {
return "Hello World";
}
@GetMapping("/sample")
public SampleVO makeSample() {
SampleVO vo = new SampleVO();
vo.setVal1("v1");
vo.setVal2("v2");
vo.setVal3("v3");
System.out.println(vo);
return vo;
}
}
-출력창
2장
Spring Data JPA -- java persistence API
자바를 이용해서 데이터를 관리하는 기법
- 기존 JDBC를 이용해 직접 구현하던걸 대신 처리해주는 추상화된 계층의 구현스펙
- DB처리를 JPA계층에서 처리
- 엔티티 : DB상에서 데이터로 관리하는 대상 (하나의 클래스or인스턴스)
- 과거 DAO==Repository
- PagingAndSortingRepository<T,ID> > CrudRepository<T,ID> > Repository<T,ID>
페이징+검색처리 crud 기능이랄게 없음
JPA 어노테이션
@id | 기본키 , @GeneratedValue와 같이 이용해 어떤 전략으로 생성하는지 명시 |
@GeneratedValue | 기본키 생성 전략 -AUTO : 특정 DB에 맞게 자동으로 생성 -IDEMTITY : 기본키 생성 방식 자체를 DB에 위임(mysql) -SEQUENCE : DB시퀀스를 이용해서 식별키 생성(오라클) -TABLE : 별도의 키를 생성해주는 채번(번호를 취할 목적으로 만든 테이블) 이용하는 방식 |
@Column | DB테이블을 구성할때 인스턴스 변수가 칼럼이 된다. 필요시 칼럼명 별도지정, 기타 등등 조건 추가 하기 위해 사용 |
@Table | 테이블명 지정 |
@Entity | 엔티티임을 명시. 반드시 설정 |
-application.properties
#최초에는 create 설정을 사용하지만 이후에는 update로 조정하고 테스트 진행
spring.jpa.hibernate.ddl-auto=update
#Dialect(방언) :
#Hibernate는 DB에 맞게 SQL문을 생성하기 위해 방언을 사용한다.
#sequence(숫자자동증가), autoincrement
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost/xe
spring.datasource.username=
spring.datasource.password=
spring.jpa.database=oracle
logging.level.org.hibernate=info
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
#DB마다 방언이 다르다 MySQL :
#spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
-Board
package com.sesac.boot2.domain;
import java.sql.Timestamp;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
//컨트롤+시프트+O
@Getter
@Setter
@ToString
@Entity
@Table(name="tbl_boards")
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long bno;
private String title;
private String writer;
private String content;
//이 둘은 import org로 시작
@CreationTimestamp
private Timestamp regdate;
@UpdateTimestamp
private Timestamp updatedate;
}
-BoardRepository
package com.sesac.boot2.persistence;
import org.springframework.data.repository.CrudRepository;
import com.sesac.boot2.domain.Board;
public interface BoardRepository extends CrudRepository<Board, Long> {
}
-BoardRepositoryTests
package com.sesac.boot2;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.sesac.boot2.domain.Board;
import com.sesac.boot2.persistence.BoardRepository;
@RunWith(SpringRunner.class)
@SpringBootTest // 이것만 쓰면 application context 전부 로딩해서 무거워진다. 위에거 쓰는 이유
public class BoardRepositoryTests {
@Autowired
private BoardRepository boardRepo; // CrudRepository 상속받으려고
/*
* 만들어진 클래스 정보 조사 <출력> com.sun.proxy.$Proxy107
* com.sesac.boot2.persistence.BoardRepository
* org.springframework.data.repository.Repository
* org.springframework.transaction.interceptor.TransactionalProxy
* org.springframework.aop.framework.Advised
* org.springframework.core.DecoratingProxy
*/
// @Test
public void inspect() {
Class<?> clz = boardRepo.getClass();
System.out.println(clz.getName());
Class<?>[] interfaces = clz.getInterfaces();
Stream.of(interfaces).forEach(inter -> System.out.println(inter.getName()));
Class<?> superClasses = clz.getSuperclass();
System.out.println(superClasses.getName());
}
/*
* 등록 CrudRepository에서 save()라는 메소드 이용
*/
// @Test
public void testInsert() {
Board board = new Board();
board.setTitle("2월23일");
board.setContent("11시..졸려");
board.setWriter("졸린사람");
boardRepo.save(board);
}
/*
* 조회 findByID 이용 Hibernate: select board0_.bno as bno1_0_0_, board0_.content as
* content2_0_0_, board0_.regdate as regdate3_0_0_, board0_.title as
* title4_0_0_, board0_.updatedate as updatedate5_0_0_, board0_.writer as
* writer6_0_0_ from tbl_boards board0_ where board0_.bno=? Hibernate: select
* hibernate_sequence.nextval from dual
*/
// @Test
public void testRead() {
// Board board = boardRepo.findOne(1L);
// System.out.println(board);
boardRepo.findById(1L).ifPresent((board) -> {
System.out.println(board);
});
}
// @Test
public void testUpdate() {
System.out.println("수정 전 제목~_~");
Board board = boardRepo.findById(561L).get(); // 기본키 번호 ==bno
System.out.println("수정 후 제목~_~");
board.setTitle("안졸린데?");
System.out.println("저장해줄게~");
boardRepo.save(board);
}
@Test
public void testDelete() {
System.out.println("삭제");
boardRepo.deleteById(561L);
}
}
-sql 확인
3장
#board.java
package com.sesac.boot3.domain;
import java.sql.Timestamp;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
//컨트롤+시프트+O
@Getter
@Setter
@ToString
@Entity
@Table(name="tbl_boards")
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long bno;
private String title;
private String writer;
private String content;
//이 둘은 import org로 시작
@CreationTimestamp
private Timestamp regdate;
@UpdateTimestamp
private Timestamp updatedate;
}
#BoardRepository
package com.sesac.boot3.persistence;
import java.util.Collection;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import com.sesac.boot3.domain.Board;
public interface BoardRepository extends CrudRepository<Board, Long> {
// JPA는 메소드의 이름만으로 원하는 쿼리를 실행한다.(쿼리=select만 해당)
// find클래스명By칼럼명
// 리턴값 : Collection<T>형태로 주로 list, page
public List<Board> findBoardByTitle(String title);
// Collection: 예쁘게 출력된다..
public Collection<Board> findByWriter(String writer);
// 작성자에 대한 like % 키워드 %
public Collection<Board> findByWriterContaining(String writer);
// OR조건의 처리
// findBy + 'Title'Containing + Or + 'Content'Containing --title과 content로 찾겠다
public Collection<Board> findByTitleContainingOrContentContaining(String title, String content);
// title LIKE % ? % AND BNO > ?
// GreaterThan or LessThan 사용
// findBy + 'Title'Containing+ And+ 'Bno'GreaterThan
// title에 특정문자 포함 bno가 특정 숫자 초과인 데이터 조회
public Collection<Board> findByTitleContainingAndBnoGreaterThan(String keywoard, Long num);
// bno > ? ORDER BY bno DESC
// findBy + 'Bno'GreaterThan + OrderBy'Bno' + Desc or Asc
public Collection<Board> findByBnoGreaterThanOrderByBnoDesc(Long bno);
// bno > ? ORDER BY bno DESC limit ?, ?
/*
* <Pageable> 페이징 처리에 필요한 정보 제공 import org.springframework.data.domain.Pageable;
* 리턴값 : List<>,or Page<>
*/
public List<Board> findByBnoGreaterThanOrderByBnoDesc(Long bno, Pageable paging);
public Page<Board> findByBnoGreaterThan(Long bno, Pageable paging);
// JPQL이용 -> JPA에서 사용하는 쿼리랭귀지..
/*
* %?1% ? : JDBC의 PreparedStatement ?1 : 첫번째로 전달되는 파라미터
*/
@Query("SELECT b FROM Board b WHERE b.title like %?1% and b.bno > 0 ORDER BY b.bno desc")
public List<Board> findByTitle(String title);
@Query("select board.bno, board.title, board.writer, board.regdate "
+ " from Board board where board.title like %?1% and board.bno > 0 order by board.bno desc")
public List<Object[]> findByTitle2(String title);
@Query("select board from Board board where board.bno > 0 order by board.bno desc")
public List<Board> findBypage(Pageable pageable);
//@Param("컬럼명")
@Query("SELECT b from Board b WHERE b.content like %:content% and b.bno > 0 order by b.bno desc")
public List<Board> findByContent(@Param("content") String content);
}
#Boot3ApplicationTests
package com.sesac.boot3;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;
import com.sesac.boot3.domain.Board;
import com.sesac.boot3.persistence.BoardRepository;
@RunWith(SpringRunner.class)
@SpringBootTest
class Boot3ApplicationTests {
@Autowired
private BoardRepository repo;
//@Test
public void testInsert30() {
for (int i = 1; i <= 30; i++) {
Board board = new Board();
board.setTitle("오늘뭐먹지" + i);
board.setContent("잘 모르겠네" + i + "흠 ");
board.setWriter("소크라테스" + (i % 10));
repo.save(board);
}
}
/*
* <출력>
* Board(bno=201, title=오늘뭐먹지1, writer=소크라테스1, content=잘 모르겠네1흠 , regdate=2022-02-24 09:35:10.996, updatedate=2022-02-24 09:35:10.996)
*/
//@Test
public void testByTitle() {
repo.findBoardByTitle("오늘뭐먹지1").forEach(board -> System.out.println(board));
}
/*
* Board(bno=201, title=오늘뭐먹지1, writer=소크라테스1, content=잘 모르겠네1흠 ,~)
Board(bno=211, title=오늘뭐먹지11, writer=소크라테스1 ,~)
*/
//@Test
public void testByWriter() {
//특정한 칼럼의값을 조회
Collection<Board> results = repo.findByWriter("소크라테스1");
results.forEach(board -> System.out.println(board));
}
/*
* Writer에 1들어간거 다 출력
*/
//@Test
public void testByWriterContaining() {
Collection<Board> results = repo.findByWriterContaining("1");
results.forEach(board -> System.out.println(board));
}
//@Test
public void testByTitleAndBno() {
//제목에 5 포함, 게시물 번호 220보다 큰거 조회
Collection<Board> results = repo.findByTitleContainingAndBnoGreaterThan("5", 220L);
results.forEach(board -> System.out.println(board));
}
//@Test
public void testBnoOrderBy() {
//220보다 큰 데이터 정렬 : 230,229 ~~
Collection<Board> results = repo.findByBnoGreaterThanOrderByBnoDesc(220L);
results.forEach(board -> System.out.println(board));
}
//@Test
public void testBnoOrderByPaging() {
//첫번째 페이지부터 10건의 데이터 가져옴 (인덱스 번호는 0부터 시작)
//230~221 출력
Pageable paging = PageRequest.of(0, 10);
Collection<Board> results = repo.findByBnoGreaterThanOrderByBnoDesc(0L, paging);
results.forEach(board -> System.out.println(board));
}
/*
*
PAGE SIZE: 10
TOTAL PAGES: 3
TOTAL COUNT: 30
NEXT: Page request [number: 1, size 10, sort: bno: ASC]
201~210번 출력
*/
@Test
public void testBnoPagingSort() {
Pageable paging = PageRequest.of(0, 10, Sort.Direction.ASC, "bno");
Page<Board> result = repo.findByBnoGreaterThan(0L, paging);
System.out.println("PAGE SIZE: " + result.getSize());
System.out.println("TOTAL PAGES: " + result.getTotalPages());
System.out.println("TOTAL COUNT: " + result.getTotalElements());
System.out.println("NEXT: " + result.nextPageable());
List<Board> list = result.getContent();
list.forEach(board -> System.out.println(board));
}
//제목에 17들어간거 그냥 출력
@Test
public void testByTitle2() {
repo.findByTitle("17").forEach(board -> System.out.println(board));
}
//배열로 출력
//@Test
public void testByTitle17() {
repo.findByTitle2("17").forEach(arr -> System.out.println(Arrays.toString(arr)));
}
}
#참고
형태 | 쿼리메소드 |
like | Like |
키워드 +'%' | StartWith |
'%' + 키워드 | EndingWith |
'%' + 키워드 +'%' | Containing |
of( ) | 설명 |
PageRequest.of(int page, int size) | 페이지번호(0부터시작),페이지당 데이터수 |
PageRequest.of(int page, int size, Sort.Direction.ASC, String~) |
페이지번호(0부터시작),페이지당 데이터수,정렬방향,칼럼들 |
PageRequest.of(int page, int size,Sort | 페이지번호(0부터시작),페이지당 데이터수,정렬방향 |
#sql 확인
#Querydsl
pom.xml에 2.2.1 추가
http://querydsl.com/static/querydsl/4.0.1/reference/ko-KR/html_single/
4장
#JPA 연관관계
- ex) 게시물<->댓글
- 더미테이블(가짜데이터) 이용해 테이블 구조 설계 -> 데이터 세보며 관계파악
<구분>
- 일대일 @OneToOne
- 일대다 @OneToMany
ex) 회원입장 : 한명의 회원, 여러번 구매 가능
- 다대일 @ManyToOne
ex) 구매입장 : 구매는 회원에 의해 이루어짐
- 다대다 @ManyToMany
<방향>
- 단방향 참조 : 한쪽 클래스만이 다른 클래스의 인스턴스를 참조하도록 설정
- 양방향 참조 : 양쪽 클래스 모두 다른 클래스 참조
<예>
[일대다, 다대일(가장 많이 사용) ,단방향]
- 회원<-프로필 사진
- 자료실->첨부파일
[일대다, 다대일, 양방향]
- 게시물<->댓글
-Member
package com.sesac.domain;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
@EqualsAndHashCode(of = {"mid"})
@Entity
@Table(name = "members_boot")
@Builder
public class Member {
@Id
String mid;
String mpassword;
String mname;
@Enumerated(EnumType.STRING)
MemberRole mrole;
}
-MemberRole
package com.sesac.domain;
//enum 은 상수들의 묶음
public enum MemberRole {
Manager, User;
}
-Profile
package com.sesac.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
@EqualsAndHashCode(of = {"fno"}) //동등성 비교- of : 포함, exclude : 제외
@Entity
@Table(name = "profile_boot")
@Builder
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Long fno;
String fname;
boolean currentYn; //현재 프로필 사진으로 활용되는지 나타낸다.
//Member <- Profile : 일대다,단방향
//Profile -> Member : 다대일,단방향
@ManyToOne
Member member;
//프로필테이블에 회원정보가 입력되어야 한다. 그래서 여기에 설계 : 회원 <-프로필 으로 설계되었다.
//profile이 member를 참조하고
//컬럼 이름은 member_mid 이렇게 들어온다.
}
- SQL (오라클)
5장
Thymeleaf
- 템플릿이 기본적으로 출력할 데이터가 없는 상황에서 HTML로 작성된 내용이 그대로 반영된다는 장점
- 유지보수가 쉽다.
- JSP와 달리 Servlet Code로 변환되지 않는다. 비즈니스 로직과 분리된다. -> View에 집중가능
- Model로 전달된 객체는 jsp에서 el을 이용해 출력하는것과 거의 동일하게 사용된다.
편리한 사용을 위해 Thymeleaf 플러그인 설치
http://www.thymeleaf.org/eclipse-plugin-update-site/
application.properties
#작성한 화면을 서버 내부에 보관(캐싱)하지 않음 #html수정하고도 서버 새시작 없이 확인 가능 spring.thymeleaf.cache=false
SampleController - sample1
//bean으로 등록, Controller로 사용됨을 Spring Framework에 알린다. //view 화면 return @Controller public class SampleController { @GetMapping("/sample1") public void sample1(Model model) { model.addAttribute("greeting", "안녕하세요"); } }
sample1.html
<html xmlns:th="http://www.thymeleaf.org"> <head> <title>Thymeleaf3</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h1 >Thymeleaf Test Page</h1> </body> </html>
<h1 th:text="${greeting}">Thymeleaf Test Page</h1> <!-- -th: 이후에 작성되는 속성은 html에서 사용하는 속성과 동일하게 사용될 수 있다. -th:text 속성을 사용해서 서버를 통한 값을 주입, 태그의 내용물로 문자열 출력 -->
SampleController - sample2
@GetMapping("/sample2") public void sample2(Model model) { MemberVO vo = new MemberVO(227, "휴..", "비번..", "하..", new Timestamp(System.currentTimeMillis())); model.addAttribute("vo", vo); }
sample2.html
<h1 th:text="${vo}">Thymeleaf Test Page</h1>
<!-- th:utext : 문자열이 아닌 html 자체를 출력하는데 사용 --> <div th:utext='${"<h3>"+vo.mid+"</h3>"}'></div> <div th:text='${"<h3>"+vo.mid+"</h3>"}'></div>
SampleController - sample3
//MemberVO객체를 리스트에 담아서 화면출력 @GetMapping("/sample3") public void sample3(Model model) { List<MemberVO> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(new MemberVO(228, "아이디" + i, "비밀번호" + i, "하길동씨" + i, new Timestamp(System.currentTimeMillis()))); } model.addAttribute("list", list); }
sample3.html
<table border="1"> <tr> <td>MID</td> <td>MPW</td> <td>MNAME</td> <td>REGDATE</td> </tr>
<!--변수명 : ${컨트롤러에서 model로 등록한거} -->
<tr th:each="member : ${list}">
<td th:text="${member.mid}"></td>
<td th:text="${member.mpw}"></td>
<td th:text="${member.mname}">Doe</td>
<td th:text="${#dates.format(member.regdate, 'yyyy-MM-dd')}">
</td> <!--thymeleaf의 보조객체 --> </tr> </table>
th:each
반복의 상태에 대한 변수를 지정하여 필요한 추가 정보를 추출할 수 있다.
반복 상태를 추적할 수 있는 status 변수 : iterState
-index : 0부터 시작하는 인덱스 번호
-count: 1부터 시작하는 번호
-size : 현재 대상의 length 혹은 size
-odd/even : 현재 번호의 홀/짝 여부
-first/last : 처음 요소인지 마지막 요소인지
<table border="1"> <tr> <td>INDEX</td> <td>SIZE</td> <td>ODD/EVEN</td> <td>MID</td> <td>MNAME</td> <td>REGDATE</td> </tr> <tr th:each="member, iterState : ${list}"> <td th:text="${iterState.index} "></td> <td th:text="${iterState.size} "></td> <td th:text="${iterState.odd +' ' + iterState.even} "></td> <td th:text="${member.mid} "></td> <td th:text="${member.mname}">Doe</td> <td th:text="${#dates.format(member.regdate, 'yyyy-MM-dd')}"></td> </tr> </table>
SampleController - sample4
@GetMapping("/sample4") public void sample4(Model model) { List<MemberVO> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(new MemberVO(i, "u000" + i % 3, "p0000" + i % 3, "홍길동" + i, new Timestamp(System.currentTimeMillis()))); } model.addAttribute("list", list); }
sample4.html
<!-- th:with를 이용하여 targeer이란 이름의 변수작성 table태그 내에서만 유효한 지역변수 값이'u0001'인 사용자만 다른 내용으로 출력하려고 3항연산자 사용해 SECRET으로 출력 --> <table border="1" th:with="target='u0001'"> <tr> <td>MID</td> <td>MNAME</td> <td>REGDATE</td> </tr> <tr th:each="member : ${list}"> <td th:text="${member.mid == target ?'SECRET':member.mid }"></td> <td th:text="${member.mname}"></td> <td th:text="${#dates.format(member.regdate, 'yyyy-MM-dd')}"></td> </tr> </table>
<!--th:if ~ th:unless 이용한 if else --> <table border="1" th:with="target='u0001'"> <tr> <td>MID</td> <td>MNAME</td> <td>REGDATE</td> </tr> <tr th:each="member : ${list}"> <td th:if="${member.mid}"> <a href="/modify" th:if="${member.mid == target}">MODIFY</a> <p th:unless="${member.mid == target}">VIEW</p></td> <td th:text="${member.mname}"></td> <td th:text="${#dates.format(member.regdate, 'yyyy-MM-dd')}"></td> </tr> </table>
SampleController - sample5
@GetMapping("/sample5") public void sample5(Model model) { String result = "SUCCESS"; model.addAttribute("result", result); }
sample5.html
-> th:inline을 이용해 자바스크립트 사용
<script th:inline="javascript"> var result = [[${result}]]; <--! 문자열로 생성 --> </script> <script> var result = [[${result}]]; <--! 변수로 선언 --> </script>
SampleController - sample6
@GetMapping("/sample6") public void sample6(Model model) { List<MemberVO> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(new MemberVO(i, "u0" + i, "p0" + i, "부길동" + i, new Timestamp(System.currentTimeMillis()))); } model.addAttribute("list", list); String result = "SUCCESS"; model.addAttribute("result", result); }
sample6.html
<table border="1" th:inline="text"> <tr> <td>MID</td> <td>MNAME</td> <td>REGDATE</td> </tr> <tr th:each="member : ${list}"> <td>[[${member.mid}]]</td> <td>[[${member.mname}]]</td> <td>[[${member.regdate}]]</td> </tr> </table> <div>[[${result}]]</div> <div>[[${#vars.result}]]</div> <script th:inline="javascript"> var result = [[${result}]]; </script>
SampleController - sample7
@GetMapping("/sample7") public void sample7(Model model) { model.addAttribute("now", new Date()); model.addAttribute("price", 22745678); model.addAttribute("title", "배고픔 아이고"); model.addAttribute("options", Arrays.asList("가","나","다","라")); }
sample7.html
<h5 th:text="${now}"></h5> <h5 th:text="${price}"></h5> <h5 th:text="${title}"></h5> <h5 th:text="${options}"></h5> <br> <!-- 날짜관련 #dates , #date.createToday() --> <h2 th:text="${#dates.format(now, 'yyyy-MM-dd')}"></h2> <!--timeValue(변수명)= --> <div th:with="timeValue=${#dates.createToday()}"> <p>[[${timeValue}]]</p> </div> <br> <!--숫자관련 --> <h2 th:text="${#numbers.formatInteger(price,3,'COMMA')}"></h2> <div th:with="priceValue=99.87654"> <p th:text="${#numbers.formatInteger(priceValue,3,'COMMA')}"></p> <p th:text="${#numbers.formatDecimal(priceValue,5,10,'POINT')}"></p> </div> <br> <br> <!-- 문자관련 --> <span th:utext="${#strings.replace(title,'s','<b>s</b>')}"></span> <ul> <li th:each="str:${#strings.listSplit(title,' ')}">[[${str}]]</li> </ul>
경로
절대경로 : http:// ~
상대경로 : 현재 URL을 기준으로 이동
스프링부트는 / 를 기준으로 동작하지만 was에서는 특정 경로에서 프로젝트가 실행되는 경우가 있다.
이러한 문제해결을 위해 thymeleaf에서는 @{}를 이용해 경로에 대한 처리를 자동으로 처리
SampleController - sample8
// /sample1과 같은 경로를 화면에서 처리 @GetMapping("/sample8") public void sample8(Model model) { }
sample8.html
<ul> <li><a th:href="@{http://localhost:7777/sample1}">sample1</a></li><!--절대경로--> <li><a th:href="@{/sample1}">sample1</a></li> <li><a th:href="@{~/sample1}">sample1</a></li> <!--http://localhost:7777/sample1 로 이동 --> <br> <li><a th:href="@{/sample1(p1='aaa', p2='bbb')}">sample1</a></li> <!--http://localhost:7777/sample1?p1=aaa&p2=bbb 로 이동 --> </ul>
헤더,풋터
header.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <div th:fragment="header">헤더 파일입니다.</div> </html>
sample8.html - 추가하기
<div th:insert="~{fragments/header::header}"></div>
레이아웃 ->템플릿상속
개발시 현재 본인이 작업하는 페이지의 내용만을 처리 할 수 있다는 편리함
https://mvnrepository.com/artifact/nz.net.ultraq.thymeleaf/thymeleaf-layout-dialect
pom.xml 추가
<!-- https://mvnrepository.com/artifact/nz.net.ultraq.thymeleaf/thymeleaf-layout-dialect --> <dependency> <groupId>nz.net.ultraq.thymeleaf</groupId> <artifactId>thymeleaf-layout-dialect</artifactId> <version>2.4.1</version> </dependency>
html5boilerplate
웹페이지에서 가장 기본적으로 필요한 구조를 템플릿으로 만들어 둔것
-> js ,image 폴더들의 구조 같은것 만들어둠
https://html5boilerplate.com/
'WEB JAVA SPRING > PROJECT' 카테고리의 다른 글
2.연관관계 매핑 (0) | 2023.09.12 |
---|---|
1.로그인/로그아웃 (0) | 2023.09.12 |
[Day-16] 간단한 주문 조회 V2: 엔티티를 DTO로 변환 (0) | 2023.08.28 |
[Day-15] rest api 엔티티 직접 노출 (0) | 2023.08.28 |
[Day-14] 회원등록/수정/조회 API (0) | 2023.08.25 |