2026. 4. 2. 20:53ㆍ대우개발원 수업 내용/spring boot, framework
1단계: SampleController와 데이터 준비(SampleController.java)
Thymeleaf 화면에 출력할 다양한 데이터를 생성하고 전달하는 기초 단계.
- 매핑 및 모델 전달: @GetMapping으로 요청을 받고, Model 객체에 리스트, 맵(Map), DTO 객체 등을 담아 화면으로 전송.
- 데이터 가공: IntStream을 활용한 리스트 생성 및 HashMap을 이용한 키-값 형태의 데이터 구성.
- 화면 이동: 리턴 타입이 void인 경우 요청 경로와 일치하는 HTML(ex1.html, ex2.html)을 자동 호출.
2단계: Thymeleaf 기초와 데이터 바인딩 (ex1.html)
HTML 구조 내에서 서버 데이터를 바인딩하는 서버 사이드 템플릿 엔진.
- 텍스트 출력: th:text로 태그 속성 대입, [[...]]로 HTML 텍스트 사이에 직접 삽입.
- 주석 처리: ``를 사용해 브라우저 소스에 노출되지 않는 파서 주석 활용.
- 변수 선언: th:with로 특정 범위 내에서만 사용하는 지역 변수 정의.
3단계: 반복문과 제어문 처리 (ex1.html)
리스트 데이터를 조건에 따라 동적으로 화면에 구성하는 로직.
- 반복문 (th:each): 리스트 순회 및 status 변수로 인덱스, 순번, 홀짝 여부 등 파악.
- 블록형 (th:block): 태그 노출 없이 반복 및 조건 로직만 수행할 때 사용.
- 제어문: th:if, th:unless로 조건부 렌더링, th:switch와 th:case로 다중 분기 처리.
4단계: 링크, 인라인 및 레이아웃 (ex1, ex2, ex3)
경로 관리, JS 연동, 페이지 구조 재사용 등 특별 기능 활용.
- 링크 (th:href): @{...} 문법으로 경로 및 쿼리 스트링, 한글 인코딩 자동 처리.
- JavaScript 인라인: th:inline="javascript" 설정으로 Java 객체를 JS Object로 자동 변환.
- 레이아웃: layout:decorate와 layout:fragment를 이용해 공통 틀(Header/Footer) 재사용.
5단계: JPA 및 Entity 설계 (Board, BaseEntity)
객체와 DB 테이블을 연결하는 ORM 기술 적용 및 자동화.
- JPA 핵심: @Entity, @Id, @GeneratedValue 등으로 Java 객체를 DB 테이블에 매핑.
- 필드 설정: @Column을 통해 컬럼 길이, Null 허용 여부 등 DB 제약조건 설정.
- Auditing: BaseEntity를 상속받아 등록/수정 시간을 공통으로 자동 관리.
- Repository: JpaRepository 상속으로 기본적인 CRUD 메서드 자동 생성.
오늘 변경한 코드

Thymeleaf 소개
• Thymeleaf의 특징
• JSP의 경우 서블릿으로 변환된 후에 실행되는 방식
• Thymeleaf는 서버사이드 템플릿 엔진
• HTML의 구조에 추가적인 태그없이 선언적으로 데이터 바인딩 처리
SampleController.java
GetMapping을 받는 메서드 추가

ex 폴더를 생성하고 ex1.html 파일 생성
- Java 서버에서 보내준 list라는 데이터를 웹 화면에 출력하는 Thymeleaf(타임리프) 문법
- 차이: [[...]]는 텍스트 사이에 변수를 직접 넣을 때 쓰고, th:text는 HTML 태그의 전체 내용을 변수 값으로 바꿀 때 사용
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h4>[[${list}]]</h4>
<hr/>
<h4 th:text="${list}"></h4>
</body>
</html>

실행해 보면

Thymeleaf 주석처리
• 주석 처리를 해야 할 때에는 ‘<!--/* ... */-->’(파서주석) 를 이용
예시
<body>
<h1 th:text="${msg}"></h1>
<!--/* <h3 th:each="${sos}">SOS</h3> */-->
<!--/* ${aaaa + bbb } */-->
<!--/*
<div>
<h1>AAAA</h1>
</div>
*/-->
</body>
ex1.html
th:with를 이용한 변수 선언

- th:with를 사용해 해당 div 범위 내에서만 사용할 변수(num1, num2)를 정의
- 두 변수를 더한 값인 30을 <h4> 태그의 텍스트로 화면에 출력
<div th:with="num1 = ${10}, num2 = ${20}">
<h4 th:text="${num1 + num2}"></h4>
</div>
을 추가
실행하면

ex1.html
반복문과 제어문 처리
th:each를 이용해서 배열/리스트/컬렉션 처리
반복문의 status 변수
- 반복문에서 자주 사용하는 인덱스 번호나 개수등을 사용
- index/count/size/first/odd/even
- 첫 번째 (기본형): list의 요소를 순회하며 각 값을 <li> 태그의 텍스트로 채워 반복 생성
- 두 번째 (블록형): th:block을 사용해 태그 노출 없이 로직만 반복하며, 리스트 내부에 자유로운 형태의 HTML을 구성
- 세 번째 (상태 변수형): 반복되는 요소뿐만 아니라 현재 몇 번째 순서인지 같은 인덱스(순번) 정보를 함께 활용
<ul>
<li th:each="str: ${list}" th:text="${str}"></li>
</ul>
<ul>
<th:block th:each="str: ${list}">
<li>[[${str}]]</li>
</th:block>
</ul>
<ul>
<li th:each="str, status: ${list}">
[[${status.index}]] -- [[${str}]]
</li>
</ul>

실행하면

ex1.html
th:if / th:unless / th:swith 를 이용한 제어 처리

리스트를 반복하면서 홀수 번째와 짝수 번째 요소에 따라 서로 다른 내용을 출력하는 조건부 렌더링 코드
- 반복문의 상태 변수(status.odd)를 체크하여, 홀수 번째 순서면 'ODD'를 출력하고
그렇지 않으면(짝수면) 'EVEN'을 출력 - th:if(참일 때 실행)와 th:unless(거짓일 때 실행)를 사용해 동일한 조건에 대한 상반된 결과를 제어
<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>
실행 결과 창

위의 if문을 삼항 연산자로 써봄

<ul>
<li th:each="str,status: ${list}">
<span th:text="${status.odd} ? 'ODD --- '+${str} : 'EVEN --- '+${str}"></span>
</li>
</ul>

ex1.html
swtich문을 씀

리스트를 반복하면서 인덱스 번호를 3으로 나눈 나머지 값에 따라 서로 다른 내용을 출력하는 분기 처리 코드입니다.
- status.index % 3 연산을 통해 인덱스를 3으로 나눈 나머지가 0, 1, 2인 경우를 각각 구분
- th:switch와 th:case를 사용하여 다중 조건문을 처리하며, 나머지에 해당하는 숫자를 화면에 보여줌
<ul>
<li th:each="str,status: ${list}">
<th:block th:switch="${status.index % 3}">
<span th:case="0">0</span>
<span th:case="1">1</span>
<span th:case="2">2</span>
</th:block>
</li>
</ul>
ex1.html
Thymeleaf를 이용한 링크 처리
- 절대 경로/컨텍스트 경로를 자동으로 처리
- ‘@’를 이용해서 처리
- 쿼리 스트링 처리

<a th:href="@{/hello}">Go to /hello</a>
<br>
<a th:href="@{/hello(name='AAA', age= 16)}">Go to /hello</a>
<br>
<a th:href="@{/hello(name='한글 처리', age= 16)}">Go to /hello</a>
<br>
<a th:href="@{/hello(types=${ {'AA','BB','CC'} }, age= 16)}">Go to/hello</a>
- 첫 번째: /hello 경로로 이동하는 단순한 하이퍼링크를 생성합니다.
- 두 번째: 경로 뒤에 ?name=AAA&age=16과 같이 고정된 값의 쿼리 파라미터를 자동으로 붙여줍니다.
- 세 번째: 파라미터에 한글이 포함된 경우, 웹 브라우저가 인식할 수 있도록 URL 인코딩을 자동으로 처리하여 전달합니다.
- 네 번째: 리스트 형태의 데이터(types)를 전달하며, 동일한 이름의 파라미터가 여러 개 붙는 형태(types=AA&types=BB...)로 URL을 구성합니다.

1.

2.

3.

4.

Thymeleaf의 특별한 기능
• 인라인 처리
• JavaScript의 경우 변수를 자동으로 JavaScript Object 형태로 출력
SampleController에 데이터를 받는 DTO 클래스를 추가,

class SampleDTO {
private String p1, p2, p3;
public String getP1() {
return p1;
}
public String getP2() {
return p2;
}
public String getP3() {
return p3;
}
}
SampleController에 메서드를 추가

@GetMapping("ex/ex2")
public void ex2(Model model) {
log.info("ex/ex2................");
List<String> strList = IntStream.range(1,10)
.mapToObj(i -> "Data" + i)
.collect(Collectors.toList());
model.addAttribute("list", strList);
Map<String, String> map = new HashMap<>();
map.put("A", "AAAA");
map.put("B", "BBBB");
model.addAttribute("map", map);
SampleDTO sampleDTO = new SampleDTO();
sampleDTO.p1 = "Value -- p1";
sampleDTO.p2 = "Value -- p2";
sampleDTO.p3 = "Value -- p3";
model.addAttribute("dto", sampleDTO);
}
ex2.html 추가

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:text="${list}"></div>
<div th:text="${map}"></div>
<div th:text="${dto}"></div>
<script th:inline="javascript">
const list = [[${list}]]
const map = [[${map}]]
const dto = [[${dto}]]
console.log(list)
console.log(map)
console.log(dto)
</script>
</body>
</html>
Thymeleaf의 레이아웃 기능
• <th:block>을 이용해서 필요한 부분만을 작성하는 방식
bulid.gradle에 구문 추가
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.1.0'

layout1.html
layout 폴더를 추가 하고 layout1.html 파일 생성
이제 네임스페이스를 한 줄 더 추가


<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Layout page</title>
</head>
<body>
<div>
<h3>Sample Layout Header</h3>
</div>
<div layout:fragment="content">
<p>Page content goes here</p>
</div>
<div>
<h3>Sample Layout Footer</h3>
</div>
<th:block layout:fragment="script" >
</th:block>
</body>
</html>
SampleController.java에 ex3메서드 추가

@GetMapping("/ex/ex3")
public void ex3(Model model){
model.addAttribute("arr", new String[]{"AAA", "BBB", "CCC"});
}
ex3.html 파일 생성

layout1.html 문서를 불러오는데 layout1.html 안에있는 content에 대해서는
ex3,html안에 있는 content에 맞게 바꾼다는 코드

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout1.html}">
<div layout:fragment="content">
<h1>ex3.html</h1>
</div>
원래 layout1.html을 실행 하면

아래 처럼 나오지만
content를 바꿔서

이렇게 보이게 된다
ex3.html에 script 추가

<script layout:fragment="script" th:inline="javascript">
const arr = [[${arr}]]
console.log(arr)
</script>
위의 코드를 추가하면 로그가 정상적으로 찍힌다

JPA 란?
• JPA는 자바 진영에서 ORM(Object-Relational Mapping) 기술 표준으로 사용되는 인터페이스의 모음이다.
그 말은 즉, 실제적으로 구현된것이 아니라 구현된 클래스와 매핑을 해주기 위해 사용되는 프레임워크이다.
JPA를 구현한 대표적인 오픈소스로는 Hibernate가 있다.
• ORM(Object-Relational Mapping)
• 우리가 일반 적으로 알고 있는 애플리케이션 Class와 RDB(Relational DataBase)의 테이블을 매핑(연결)한다는 뜻이며,
기술적으로는 어플리케이션의 객체를 RDB 테이블에 자동으로 영속화 해주는 것이라고 보면된다.
• JPA(Java Persistence API)
• Java 진영에서 ORM(Object-Relational Mapping) 기술 표준으로 사용하는 인터페이스 모음
• 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
• 인터페이스 이기 때문에 Hibernate, OpenJPA 등이 JPA를 구현함
JPA(Java Persistence API) 핵심 요약
JPA란? 자바 객체와 관계형 데이터베이스 사이의 차이를 메우고, 데이터를 관리하기 위한 표준 기술입니다.
왜 사용하는가?
- 생산성 및 유지보수 향상: 반복적인 CRUD SQL을 자동 처리(간단하게)해 주어,
SQL이 아닌 객체 중심의 개발에 집중할 수 있음 - 패러다임 불일치 해결: Java의 상속 관계와 같은 객체 지향 특성을 데이터베이스 구조에 맞게 자동으로 매핑해줌.
- 예측 가능한 SQL: 매핑된 관계를 통해 실행될 SQL을 쉽게 예측할 수 있으며,
필요시 네이티브 SQL을 직접 작성해 성능 이슈나 복잡한 쿼리도 해결 가능합니다.
JPA 필드와 컬럼 매핑 : @Column
• 객체 필드를 테이블 컬럼에 매핑하는 어노테이션
• @Column 속성
• name : 필드와 매핑할 테이블의 컬럼 이름. 기본값은 객체의 필드 이름
• nullable : null값의 허용 여부 설정. 기본값은 true. false로 하면 DDL 생성 시 nuo null 제약조건 추가. (DDL)
• unique : @Table의 uniqueConstraints와 같지만 컬럼에 간단히 유니크 제약조건 걸 때 사용. (DDL)
• columnDefinition : DB 컬럼 정보를 직접 줄 수 있음. 기본값은 필드의 자바 타입과
방언 정보를 사용해 적절한 컬럼 타입을 생성(DDL)
• length : 문자 길이 제약조건, String 타입에만 사용. 기본값 255. (DDL)
JPA Repository
• Spring Data JPA에서 제공하는 인터페이스 중 하나로, JPA를 사용하여 데이터베이스를
조작하기 위한 메서드들을 제공합니다.
• JPARepository 인터페이스를 상속받는 인터페이스를 정의하면, 해당 인터페이스를 구현하는
클래스는 JPA에서 제공하는 메서드들을 사용할 수 있습니다.
• JpaRepository를 사용하면, 복잡한 JDBC(Java DataBase Connectivity) 코드를 작성하지 않아도
간단하게 DB와의 데이터 접근 작업을 처리할 수 있습니다.
• JPARepository 인터페이스는 제네릭 타입을 사용하여 Entity클래스와 복합키를 사용하고
있다면 해당 Entity의 ID클래스를 명시합니다. 이를 통해 해당 인터페이스를 상속받는
구현체는 Entity클래스와 ID클래스에 대한 정보를 알고 있어서, 런타임 시점에 적절한 쿼리를
생성하고 실행할 수 있습니다.
domain 패키지를 만들고
Board.java 클래스를 만듬

코드 추가

package com.example.b01.domain;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // auto_increment를 쓰겠다는 것.
// Sequence -> DB를 oracle을 쓰면 Sequence를 쓴다(지금은 maria DB이기 때문에 X)
private Long bno;
private String title;
private String content;
private String writer;
}
BaseEntity.java
abstract클래스를 만듬


package com.example.b01.domain;
import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
@MappedSuperclass
@EntityListeners(value = {AuditingEntityListener.class})
@Getter
abstract class BaseEntity {
@CreatedDate
@Column(name = "regdate", updatable = false) // 한번 값이 들어가게 되면 수정이 불가능
private LocalDateTime regDate;
@LastModifiedDate
@Column(name = "moddate")
private LocalDateTime modDate;
}
Board.java 코드 수정

package com.example.b01.domain;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Board extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // auto_increment를 쓰겠다는 것.
// Sequence -> DB를 oracle을 쓰면 Sequence를 쓴다(지금은 maria DB이기 때문에 X)
private Long bno;
@Column(length = 500, nullable = false) // 컬럼의 길이와 null 허용 여부
private String title;
@Column(length = 2000, nullable = false)
private String content;
@Column(length = 50, nullable = false)
private String writer;
public void change(String title, String content) {
this.title =title;
this.content = content;
}
}
B01Application 코드 수정
@EnableJpaAuditing
를 추가

'대우개발원 수업 내용 > spring boot, framework' 카테고리의 다른 글
| 자바 스프링 부트 4일차 b01 (0) | 2026.04.06 |
|---|---|
| 자바 스프링 부트 3일차 b01 Spring Data JPA 기본 및 CRUD (0) | 2026.04.06 |
| 자바 스프링 부트 1일차 b01 (0) | 2026.04.01 |
| 자바 프레임 워크 11일차 springex_web 검색 및 데이터 보존 페이징처리 (1) | 2026.04.01 |
| 자바 프레임 워크 10일차 springex_web 페이징처리, 검색처리 (1) | 2026.04.01 |