Java/Spring Boot

Spring Boot 기초(DB) - AWS 풀스택 과정 77일차

awspspgh 2024. 11. 14. 09:28
목차
1. list
2. layout
3. DB

 

1. list

TestController.java

package com.example.demo;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.*;
import java.util.stream.IntStream;

@Slf4j
@Controller
public class TestController {

    private static final Logger log = LoggerFactory.getLogger(TestController.class);

    @GetMapping("/")
    public String index(Model m){
        m.addAttribute("msg","Test Thymeleaf World!!");
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee","fff");
        m.addAttribute("list", list);
        return "index";
    }

    /* /ex01 => 리스트를 /ex/ex01.html 전송*/
    @GetMapping("/ex01")
    public String ex01(Model m){
        List<String> list = Arrays.asList("영이","철수","영철","길동","순신");
        m.addAttribute("list", list);
        return "/ex/ex01";
    }

    @GetMapping("/ex02")
    public String ex02(@RequestParam("name")String name, @RequestParam("age")int age, Model m){
        log.info("name : {}", name);
        log.info("age : {}", age);
        TestVO tvo = new TestVO("a","b","c");
        m.addAttribute("tvo",tvo);
        return "/ex/ex02";
    }

    @GetMapping("/ex03")
    public String ex03(Model m){
        List<String> strList = IntStream.range(1,10).mapToObj(i -> "data "+i).toList();
        m.addAttribute("strList", strList);
        log.info("strList {}", strList);

        Map<String, Integer> map = new HashMap<>();
        map.put("apple",500);
        map.put("orange",600);
        map.put("banana",300);
        map.put("kiwi",700);
        m.addAttribute("map",map);

        List<TestVO> testList = new ArrayList<>();
        testList.add(new TestVO("value-p1", "value-p2", "value-p3"));
        testList.add(new TestVO("value-p1-1", "value-p2-1", "value-p3-1"));
        testList.add(new TestVO("value-p1-2", "value-p2-2", "value-p3-2"));
        m.addAttribute("testList", testList);
        return "/ex/ex03";
    }

}

 

2. layout

 bulid.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.2.11'
	id 'io.spring.dependency-management' version '1.1.6'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(17)
	}
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
	useJUnitPlatform()
}

 

 header.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org">

<!-- th:fragment="이름" : 레이아웃에서 사용할 조각 -->
<div th:fragment="header">
  <h1>Header Area</h1>
</div>

 

 footer.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org">

<div th:fragment="footer">
    <h1>footer Area</h1>
</div>

 

 layout.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
    <meta charset="UTF-8">
    <title>My Thymeleaf Test</title>
</head>
<body>
<!-- <div th:replace="~{조각 파일의 경로 :: 이름}"></div> -->
  <div th:replace="~{fragments/header :: header}"></div>

  <div layout:fragment="content"></div>

  <div th:replace="~{fragments/footer :: footer}"></div>
</body>
</html>

 

 index.html

<!DOCTYPE html>
<html lang="en"
  xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/layout}">
<!--  layout:decorate="~{파일경로}" => layout 파일 경로의 변경 영역 지정  -->

<div layout:fragment="content">


    <h1>${msg}</h1>
    <h1 th:text="${msg}"></h1>
    <h1>[[${msg}]]</h1>
    <hr>
    <h1>[[${list}]]</h1>
<h1 th:text="${list}"></h1>

<!-- th:each를 이용하여 반복값을 출력 -->

    <ul>
        <li th:each="str:${list}" th:text="${str}"></li>
    </ul>

    <hr>

    <ul>
        <li th:each="str:${list}">[[${str}]]</li>
    </ul>

    <ul>
        <li th:each="str, status:${list}">
            [[${status.count}]] --- [[${str}]]
        </li>
    </ul>

    <hr>

    <ul>
        <li th:each="str, status:${list}">
            <span th:if="${status.odd}">ODD --- [[${str}]]</span>
            <span th:unless="${status.odd}">EVEN(unless) --- [[${str}]]</span>
        </li>
    </ul>

    <hr>

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

    <hr>

    <ul>
        <li th:each="str, status:${list}">
            <th:block th:switch="${status.index % 3}">
            <span th:case="0">0 -- [[${str}]]</span>
            <span th:case="1">1 -- [[${str}]]</span>
            <span th:case="2">2 -- [[${str}]]</span>
            </th:block>
        </li>
    </ul>

    <hr>

<th:block th:with="num1=${10}, num2=${20}">
    <span>[[${num1+num2}]]</span>
</th:block>
    <a th:href="@{/ex01}"><button type="button">Go to ex01</button></a>
</div>

 

 ex01.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/layout}">

<div layout:fragment="content">
  <h1>Controller에서 보낸 리스트를 인라인 방식으로 출력</h1>
[[${list}]]
<ul>
  <li th:each="str:${list}" th:text="${str}"></li>
</ul>

<a href="/ex02?name=hong&age=20">Go To /ex02</a> <br>
<a th:href="@{/ex02(name='kim', age='30')}">Go To /ex02</a>
</div>

 

 ex02.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/layout}">
<!--  layout:decorate="~{파일경로}" => layout 파일 경로의 변경 영역 지정  -->

<div layout:fragment="content">
<h1> EX02 HTML page</h1>
<h1>[[${param.name}]]</h1>
<h1>[[${param.age}]]</h1>
<h1>[[${tvo}]]</h1>

<a th:href="@{/ex03}">
    <button type="button">Go to ex03</button>
</a>
</div>

</html>

 

 ex03.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/layout}">
<!--  layout:decorate="~{파일경로}" => layout 파일 경로의 변경 영역 지정  -->

<div layout:fragment="content">

  <h1>Ex03 page</h1>
  <div>[[${strList}]]</div>
  <div th:text="${strList}"></div>
  <div>[[${map}]]</div>
  <div>[[${map.apple}]]</div>

  <th:block th:each="testvo:${testList}">
    <div>[[${testvo.p1}]]</div>
    <div>[[${testvo.p2}]]</div>
    <div>[[${testvo.p3}]]</div>
  </th:block>

  <script th:inline="javascript">
    const list= [[${strList}]];
    console.log(list);
  </script>
  <script th:inline="javascript">
    const strList = [[${strList}]];
    console.log(strList);
  </script>
</div>
</html>

 

출력

 

3. DB

- 새 프로젝트(이름 : spring) 생성 후

 

 bulid.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.2.11'
	id 'io.spring.dependency-management' version '1.1.6'
}

group = 'com.ezen'
version = '0.0.1-SNAPSHOT'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(17)
	}
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'com.mysql:mysql-connector-j'
	annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
	useJUnitPlatform()
}

 

 DatabaseConfig.java

package com.ezen.spring.config;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;
import java.nio.file.PathMatcher;

@Configuration
@PropertySource("classpath:/application.properties")
public class DatabaseConfig {

    @Autowired
    private ApplicationContext applicationContext;

    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public HikariConfig hikariConfig(){
        return new HikariConfig();
    }

    @Bean
    public DataSource dataSource() throws Exception{
        return new HikariDataSource(hikariConfig());
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception{
        SqlSessionFactoryBean sqlSessionFactoryBean =
                new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        /* mybatis-config.xml 존재할 경우*/
        sqlSessionFactoryBean.setConfigLocation(
                applicationContext.getResource("classpath:/mybatis-config.xml")
        );
        sqlSessionFactoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        return sqlSessionFactoryBean.getObject();
    }

    @Bean
    public org.apache.ibatis.session.Configuration mybatisConfig(){
        return new org.apache.ibatis.session.Configuration();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory){
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

 

◈ mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>

 

 BoardController.java

package com.ezen.spring.controller;

import com.ezen.spring.domain.BoardVO;
import com.ezen.spring.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@RequestMapping("/board/*")
@RequiredArgsConstructor
@Slf4j
@Controller
public class BoardController {
    private final BoardService bsv;

    @GetMapping("/register")
    public String register(){
        return"/board/register";
    }

    @PostMapping("/register")
    public String register(BoardVO boardVO){
        log.info(">>>> boardVO >>>> {}", boardVO);
        int isOk = bsv.register(boardVO);

        return "index";
    }

    @GetMapping("/list")
    public String list(Model m){
        m.addAttribute("list", bsv.getList());
        return"/board/list";
    }
}

 

 BoardVO.java

package com.ezen.spring.domain;

import lombok.*;

@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BoardVO {
    private long bno;
    private String title;
    private String writer;
    private String content;
    private String regDate;
}

 

 BoardService.java

package com.ezen.spring.service;

import com.ezen.spring.domain.BoardVO;

import java.util.List;

public interface BoardService {
    int register(BoardVO boardVO);

    List<BoardVO> getList();
}

 BoardServiceImpl.java

package com.ezen.spring.service;

import com.ezen.spring.domain.BoardVO;
import com.ezen.spring.repository.BoardMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements  BoardService{
    private final BoardMapper boardMapper;

    @Override
    public int register(BoardVO boardVO) {
        return boardMapper.register(boardVO);
    }

    @Override
    public List<BoardVO> getList() {
        return boardMapper.getList();
    }
}

 BoardMapper.java

package com.ezen.spring.repository;

import com.ezen.spring.domain.BoardVO;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface BoardMapper {
    int register(BoardVO boardVO);

    List<BoardVO> getList();
}

 

 index.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/layout}">

<div layout:fragment="content">

    <div class="container-md">
        <h1>My String Boot Project</h1>
    </div>
</div>

 

 layout.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <!-- 부트스태랩 css js 링크 연결 -->
  <link rel="sylesheet" th:href="@{/dist/css/bootstrap.min.css}">
  <script th:src="@{/dist/js/bootstrap.bundle.min.js}"></script>
</head>
<body>
<!-- <div th:replace="~{조각 파일의 경로 :: 이름}"></div> -->
<div th:replace="~{fragments/header :: header}"></div>

<div layout:fragment="content"></div>

<div th:replace="~{fragments/footer :: footer}"></div>
</body>
</html>

 

 header.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org">
<!-- th:fragment="이름" : 레이아웃에서 사용할 조각 -->
<div th:fragment="header" class="container">
  <nav class="navbar navbar-expand-lg bg-body-tertiary">
    <div class="container-fluid">
      <a class="navbar-brand" href="#">Boot</a>
      <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav me-auto mb-2 mb-lg-0">
          <li class="nav-item">
            <a class="nav-link active" aria-current="page" href="/">Home</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="/board/register">BoardRegister</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="/board/list">BoardList</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>
  <h5>Header Area</h5>
</div>

 

 footer.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org">
<!-- th:fragment="이름" : 레이아웃에서 사용할 조각 -->
<div th:fragment="footer">
  <h5>Footer Area</h5>
</div>

 

register.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/layout}">

<div layout:fragment="content" class="container-md">
    <h1>Boot Register page</h1>
  <hr>

  <form action="/board/register" method="post">
  <div class="mb-3">
    <label for="t" class="form-label">Title</label>
    <input type="text" class="form-control" name="title" id="t" placeholder="title...">
  </div>
  <div class="mb-3">
    <label for="w" class="form-label">Writer</label>
    <input type="text" class="form-control" name="writer" id="w" placeholder="writer...">
  </div>
  <div class="mb-3">
    <label for="c" class="form-label">Content</label>
    <textarea class="form-control" name="content" id="c" rows="3"></textarea>
  </div>
    <button type="submit" class="btn btn-primary">register</button>
  </form>
</div>

 

list.html

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/layout}">

<div layout:fragment="content" class="container-md">
  <h1>Boot List page</h1>
  <hr>
  <table class="table table-hover">
      <thead>
        <tr>
          <th>#</th>
          <th>title</th>
          <th>writer</th>
          <th>regDate</th>
        </tr>
      </thead>
      <tbody>
        <tr th:each="bvo:${list}">
          <td>[[${bvo.bno}]]</td>
          <td>[[${bvo.title}]]</td>
          <td>[[${bvo.writer}]]</td>
          <td>[[${bvo.regDate}]]</td>
        </tr>
      </tbody>
      <tfoot></tfoot>
  </table>
</div>

 

ApplicationTests.java

package com.ezen.spring;

import com.ezen.spring.domain.BoardVO;
import com.ezen.spring.repository.BoardMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ApplicationTests {

	@Autowired
	private BoardMapper boardMapper;

	@Test
	void contextLoads() {
		for(int i=0; i<300; i++){
			BoardVO boardVO = BoardVO.builder()
					.title("test title " + i)
					.writer("tester"+((int)(Math.random()*100)+1))
					.content("test content " + i)
					.build();
			boardMapper.register(boardVO);

		}
	}

}

 

출력