Spring

부트캠프85일차 (스프링부트변경 , board추가)

동곤일상 2025. 6. 12. 17:40
반응형

1) 스프링 tool설치

2) 프로젝트생성

2-1) lombok적용

2-2)  기존패키지가져오기

2-3) application설정

2-4) siteMesh설정

 

3)board(스프링부트에 동작방식을 이해하려면 보길)

 


 

1) 스프링tool 설치

 

방법1) 

이클립스의 marketplace에 들어가

springTools를 설치!!!!!!

 

 

 

방법2)

https://spring.io/tools

 

Spring | Tools

 

spring.io

해당사이트에 접속 후 다음과같이 클릭해 다운로드 후

압축을 풀어

해당이름과같은 파일을 실행시켜주면 끝

 


2) 프로젝트생성

 

의존성을 미리 추가

 

2-1 ) lombok적용

lombok.jar파일을 실행시켜보자

아마처음에는 springtool이 뜨지않을거다

 

specify location을 클릭해서 아까 실행시켰던 exe파일을 추가해준후

install/Update를 클릭해주자

 

2-2) 기존패키지가져오기

shop1프로젝트의 패키지들을 다 가져오자

(경로를 일일이 바꿔줘야함)

 

pom.xml설정

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.5.0</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>kr.gdu</groupId>
	<artifactId>shop2</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>shop2</name>
	<description>Demo project for Spring Boot</description>
	<url />
	<licenses>
		<license />
	</licenses>
	<developers>
		<developer />
	</developers>
	<scm>
		<connection />
		<developerConnection />
		<tag />
		<url />
	</scm>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web-services</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.mariadb.jdbc</groupId>
			<artifactId>mariadb-java-client</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!--JSP사용을 위해 -->
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
		</dependency>
		<dependency>
			<groupId>jakarta.servlet</groupId>
			<artifactId>jakarta.servlet-api</artifactId>
		</dependency>

		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.14</version>
		</dependency>

		<dependency>
			<groupId>jakarta.servlet.jsp.jstl</groupId>
			<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
		</dependency>

		<dependency>
			<groupId>org.glassfish.web</groupId>
			<artifactId>jakarta.servlet.jsp.jstl</artifactId>
		</dependency>
		<dependency>
			<groupId>org.sitemesh</groupId>
			<artifactId>sitemesh</artifactId>
			<version>3.2.0-M2</version>
		</dependency>
		<dependency>
			<groupId>javax.validation</groupId>
			<artifactId>validation-api</artifactId>
			<version>2.0.1.Final</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>3.0.4</version>
		</dependency>
		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.5.5</version>
		</dependency>
		
		
		
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<annotationProcessorPaths>
						<path>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</path>
					</annotationProcessorPaths>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

 

 


2-3) application설정

 

스프링부트가 시작되는곳이라 생각하면됨

(기존방식처럼 componentScan을 사용해 패키지를 하나하나 써서 등록할필요X)

package kr.gdu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import kr.gdu.sitemesh.SiteMeshFilter;

@SpringBootApplication
@ServletComponentScan //하위패키지모두를 scan대상으로 삼음
@EnableAspectJAutoProxy
public class Shop2Application {

	public static void main(String[] args) {
		SpringApplication.run(Shop2Application.class, args);
	}
	
	@Bean
	public FilterRegistrationBean<SiteMeshFilter> siteMeshFilter(){
		FilterRegistrationBean<SiteMeshFilter> filter = 
		new FilterRegistrationBean<>();
		filter.setFilter(new SiteMeshFilter());
		return filter;
	}
}

2-4) sitemesh 설정

application쪽에 siteMeshFilter를 적용해놓은것을 확인할수있다

(모든페이지에 적용하겠다는 뜻이다)

 

다음과 같이 sitemesh로 지정할 페이지와,제외할경로를 입력해준다.

package kr.gdu.sitemesh;

import org.sitemesh.builder.SiteMeshFilterBuilder;
import org.sitemesh.config.ConfigurableSiteMeshFilter;
import jakarta.servlet.annotation.WebFilter;
@WebFilter("/*")
public class SiteMeshFilter extends ConfigurableSiteMeshFilter {

	@Override
	protected void applyCustomConfiguration(SiteMeshFilterBuilder builder) {
		builder.addDecoratorPath("/*", "layout/gdulayout.jsp")
		.addExcludedPath("/user/idSearch*")
		.addExcludedPath("/user/pwSearch")
		.addExcludedPath("/user/login")
		.addExcludedPath("/user/join");		
	}
}

/WEB-INF/decorators/layout/gdulayout.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<sitemesh:write property="title" />
   <sitemesh:write property="head" />
   <sitemesh:write property="body"  />

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title><sitemesh:title default="관리자 대시보드" /></title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- Bootstrap 5 -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">

  <style>
    body {
      overflow-x: hidden;
    }

    .sidebar {
      width: 250px;
      transition: all 0.3s ease;
      position: fixed;
      top: 56px;
      bottom: 0;
      left: 0;
      background-color: #f8f9fa;
      overflow-y: auto;
      z-index: 1000;
    }

    .sidebar.collapsed {
      width: 80px;
    }

    .main-content {
      margin-left: 250px;
      transition: all 0.3s ease;
      padding: 1.5rem;
      margin-top: 56px;
    }

    .main-content.collapsed {
      margin-left: 80px;
    }

    .sidebar .list-group-item {
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .footer {
      text-align: center;
      padding: 1rem;
      border-top: 1px solid #dee2e6;
      margin-top: 2rem;
    }
  </style>

  <sitemesh:head />
</head>
<body>

  <!-- 상단 네비게이션 -->
  <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
    <div class="container-fluid">
      <button class="btn btn-outline-light me-2" id="toggleSidebar">☰</button>
      <a class="navbar-brand" href="#">MyAdmin</a>
      <div class="collapse navbar-collapse">
        <ul class="navbar-nav ms-auto">
          <li class="nav-item"><a class="nav-link" href="#">홈</a></li>
          <li class="nav-item"><a class="nav-link" href="#">설정</a></li>
          <li class="nav-item"><a class="nav-link" href="/user/logout">로그아웃</a></li>
        </ul>
      </div>
    </div>
  </nav>

  <!-- 왼쪽 사이드바 -->
  <div id="sidebar" class="sidebar border-end">
    <div class="list-group list-group-flush mt-3">
      <a href="/admin/dashboard" class="list-group-item list-group-item-action">📊 대시보드</a>
<a href="/admin/users" class="list-group-item list-group-item-action">👥 사용자 관리</a>
<a href="/board/list?boardid=1" class="list-group-item list-group-item-action">📌 공지사항</a>
<a href="/board/list?boardid=2" class="list-group-item list-group-item-action">💬 자유게시판</a>
<a href="/board/list?boardid=3" class="list-group-item list-group-item-action">❓ Q&A</a>
<a href="#" class="list-group-item list-group-item-action">⚙️ 설정</a>
      
    </div>
  </div>

  <!-- 메인 콘텐츠 -->
  <div id="mainContent" class="main-content">
    <sitemesh:body />
  </div>

  <!-- 푸터 -->
  <footer class="footer text-muted">
    &copy; 2025 MyAdmin. All rights reserved.
  </footer>

  <!-- JS -->
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
  <script>
    const toggleBtn = document.getElementById("toggleSidebar");
    const sidebar = document.getElementById("sidebar");
    const mainContent = document.getElementById("mainContent");

    toggleBtn.addEventListener("click", () => {
      sidebar.classList.toggle("collapsed");
      mainContent.classList.toggle("collapsed");
    });
  </script>
</body>
</html>

3) Board

3-1)controller

package kr.gdu.controller;

import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("board")
public class BoardController {
	@Autowired
	private BoardService service;
	
	@GetMapping("*")
	public ModelAndView write() {
		ModelAndView mav = new ModelAndView();
		mav.addObject(new Board());
		return mav;
	}
	/* Spring에서 파라미터 전달 방식
	 *   1. 파라미터이름과 매개변수의 이름이 같은 경우 매핑
	 *   2. Bean 클래스의 프로퍼티명과 파라미터이름이 같은 경우 매핑
	 *   3. Map 객체에 RequestParam 어노테이션을 이용한 매핑
	 * 
	 * @RequestParam : 파라미터값을 Map 객체에 매핑하여 전달
	 */
	@GetMapping("list")
	public String list(@RequestParam Map<String,String> param,
			 HttpSession session,Model model) {

		Integer pageNum = null;
		for(String key : param.keySet()) {
			if(param.get(key) == null || param.get(key).trim().equals("")) {
			   param.put(key, null);	
			}
		}
		if (param.get("pageNum") != null) {
			   pageNum = Integer.parseInt(param.get("pageNum"));
		} else { 
			pageNum = 1;
		}
		String boardid = param.get("boardid");
		String searchtype = param.get("searchtype");
		String searchcontent = param.get("searchcontent");
		String boardName = null;
		switch(boardid) {
		   case "1" : boardName = "공지사항"; break;
		   case "2" : boardName = "자유게시판"; break;
		   case "3" : boardName = "QNA"; break;
		   default : boardName = "공지사항";
		             boardid = "1";
		   break;
		}
		//게시판 조회 처리
		int limit = 10;
		int listcount = service.boardcount(boardid,searchtype,searchcontent); 
		List<Board> boardlist = service.boardlist
				          (pageNum,limit,boardid,searchtype,searchcontent);
		int maxpage = (int)((double)listcount/limit + 0.95); 
		int startpage = (int)((pageNum/10.0 + 0.9) - 1) * 10 + 1;
		int endpage = startpage + 9;
		if(endpage > maxpage) endpage = maxpage;
		int boardno = listcount - (pageNum - 1) * limit;		
		model.addAttribute("boardid",boardid);
		model.addAttribute("boardName",boardName);
		model.addAttribute("pageNum",pageNum);
		model.addAttribute("maxpage",maxpage);
		model.addAttribute("startpage",startpage);
		model.addAttribute("endpage",endpage);
		model.addAttribute("listcount",listcount);
		model.addAttribute("boardlist",boardlist);
		model.addAttribute("boardno",boardno);
		return "/board/list";
	}
}

기존 스프링레거시 방식과 다르게 model을 이용해 속성등록을 진행했으며(request영역)

String(뷰페이지가 존재하는경로) 를 반환해 해당페이지로 이동!!!!

 


3-2) service

package kr.gdu.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import kr.gdu.dao.BoardDao;
import kr.gdu.logic.Board;

@Service
public class BoardService {
	@Autowired
	BoardDao boardDao;
	
	public int boardcount(String boardid, String searchtype, String searchcontent) {
		return boardDao.count(boardid,searchtype,searchcontent);
	}
	public List<Board> boardlist
	(Integer pageNum, int limit, String boardid, String searchtype, String searchcontent) {
		return boardDao.list(pageNum,limit,boardid,searchtype,searchcontent);
	}

}

여기서 서비스계층이 뭔가 dao를 호출하는 용도로 사용했다

(솔직히말하면 컨트롤러는 주소만 반환해주는곳이고

서비스계층에서 무거운로직을 다루는게맞음 이건 이상한방식임)

 

3-3) Dao

package kr.gdu.dao;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import kr.gdu.dao.mapper.BoardMapper;
import kr.gdu.logic.Board;

@Repository
public class BoardDao {
	
	@Autowired
	private SqlSessionTemplate template;
	private Class<BoardMapper> cls = BoardMapper.class;
	private Map<String,Object> param = new HashMap<>();
	
	public int count(String boardid, String searchtype, String searchcontent) {
		param.clear();
		param.put("boardid", boardid);
		param.put("searchtype",searchtype);
		param.put("searchcontent",searchcontent);
		return template.getMapper(cls).count(param);
	}	
	public List<Board> list	(Integer pageNum, int limit, 
		String boardid, String searchtype, String searchcontent) {
		param.clear();
		param.put("startrow", (pageNum - 1) * limit); 
		param.put("limit",  limit);		
		param.put("boardid",  boardid);		
		param.put("searchtype",searchtype);
		param.put("searchcontent",searchcontent);
		return template.getMapper(cls).select(param);
	}
	
}

dao도 솔직히 다른방법으로도 mapper를 호출가능함 

다음과같이 map에 담아서 전달하는 방식이 아닌

그냥 설정 조금만 해준다면 객체를 넘기고 받는 방식이가능해짐

 

3-4) mapper

package kr.gdu.dao.mapper;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import kr.gdu.logic.Board;
@Mapper
public interface BoardMapper {
    String select = "select num,writer,pass,title,content,file1 fileurl,"
		+ " regdate, readcnt, grp, grplevel, grpstep, boardid from board";
    
    
    @Select({"<script>",
   	"select count(*) from board where boardid=#{boardid} ",
    "<if test='searchtype != null'> "
    + " and ${searchtype} like '%${searchcontent}%'</if>",
   	"</script>"})
	int count(Map<String, Object> param);
    
    @Select({"<script>",
        select,
        "<where>", 
        "<if test='num != null'> num = #{num} </if>",
        "<if test='num == null and boardid != null'> boardid = #{boardid} </if>",
        "<if test='searchtype != null'> and ${searchtype} like '%${searchcontent}%'</if>",
        "</where>",
        "<if test='limit != null and startrow != null'> "
        + " order by grp desc, grpstep asc limit #{startrow},#{limit}</if>",
        "<if test='limit != null and startrow == null'> "
        + " order by grp desc, grpstep asc limit 0,#{limit}</if>",
        "</script>"})    
	List<Board> select(Map<String, Object> param);
	
}

해당코드를 이용해 넘어온값을 사용해

DB를 조회해 값을 반환해줌!!!