WEB JAVA SPRING/PROJECT

스타트 스프링 부트 -1~5장

sshhhh 2023. 8. 28. 15:02

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";
	}
}

localhost:7777/myapp/hello

 

 

 

-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;

	}
}

-출력창

http://localhost:7777/sesac/sample

 

 

 


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 확인

insert 되었다.
title이 update 되었다.
561번 삭제되고 없다.

 

 

 


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 확인

testInsert30

 


#Querydsl

pom.xml에 2.2.1 추가

 

http://querydsl.com/static/querydsl/4.0.1/reference/ko-KR/html_single/

 

Querydsl - 레퍼런스 문서

Querydsl은 JPA, JDO, Mongodb 모듈에서 코드 생성을 위해 자바6의 APT 어노테이션 처리 기능을 사용한다. 이 절에서는 코드 생성을 위한 다양한 설정 옵션과 APT에 대한 대안을 설명한다. 기본적으로 Query

querydsl.com

 

 

 


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/

Help 클릭 Install New software




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>

 

http://localhost:7777/sample1

 

<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>

 

http://localhost:7777/sample2&amp;amp;amp;amp;amp;amp;nbsp;

 

 <!-- 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>

 

http://localhost:7777/sample3




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>

 

http://localhost:7777/sample3




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>

 

http://localhost:7777/sample4

 

<!--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>

 

http://localhost:7777/sample4




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>

 

http://localhost:7777/sample6

 



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://localhost:7777/sample7

 


경로
절대경로 : 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>

http://localhost:7777/sample8

 

클릭시 이동 http://localhost:7777/sample1


헤더,풋터

생성

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>

http://localhost:7777/sample8

 


레이아웃 ->템플릿상속
개발시 현재 본인이 작업하는 페이지의 내용만을 처리 할 수 있다는 편리함
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/

 

HTML5 ★ BOILERPLATE

The web’s most popular front-end template which helps you build fast, robust, and adaptable web apps or sites.

html5boilerplate.com

 

압축해제 후 프로젝트 내에 static 폴더에 넣기