스프링의 DI(Dependency Injection)는 객체지향 개발에서 인스턴스 생성 및 관리에 대한 일부 책임을 객체간의 관계를 정의하는 외부 시스템으로 옮기는 개념입니다. 이를 통해 결합도(coupling)를 낮추고 유연한 코드를 작성할 수 있습니다.
스프링에서 DI를 구현하는 방식 중 하나는 제어역전(Inversion of Control, IoC)입니다. 제어역전은 프로그램의 제어 흐름을 뒤집는 것으로, 일반적으로 객체 생성과 관리에 대한 책임을 프레임워크 또는 컨테이너에게 위임합니다.
스프링에서는 DI를 구현하기 위해 ApplicationContext라는 컨테이너를 제공합니다. ApplicationContext는 애플리케이션 컴포넌트들의 생성과 관리를 담당하며, 빈(bean)이라는 개념을 이용해 객체들을 관리합니다. 빈은 스프링 컨테이너에 등록되어 관리되는 객체를 말하며, 빈의 생성, 의존성 주입, 소멸 등의 라이프사이클을 스프링이 제어합니다.
즉, 제어역전을 이용한 DI는 개발자가 객체를 생성하고 관리하는 것이 아니라 스프링 컨테이너가 객체를 생성하고 관리하며, 필요한 곳에 필요한 객체를 주입해줌으로써 개발자가 더욱 집중해야 할 핵심 로직에 더 집중할 수 있도록 도와줍니다.
- Dao - SqlMapper.xml : 함수 반환형을 정하고 서로 매핑만 함. Dao는 철저하게 DB와 연결되는 역할만 담당함
- Service : 인터페이스인 Dao를 @Autowired로서 객체를 주입해 불러와(스프링의 DI-제어 역전), 자료형이 반환되기까지의 구체적인 로직을 작성함
- Controller : Service를 @Autowired로 불러와서 Model 객체에 담아 View 단의 URL로 보냄
# 서버 포트 설정
server:
port: 9090
# 스프링 로그 예쁘게 나오기 설정
spring:
output:
ansi:
enabled: always
# 데이터 베이스 연동
datasource:
driver-class-name: oracle.jdbc.driver.OracleDriver
url: jdbc:oracle:thin:@localhost:1521:xe
username: "---------"
password: "---------"
#로그 찍기
logging:
level:
'[com.jjang051.ch04]': DEBUG
#mybatis 설정
mybatis:
mapper-locations: classpath:sqlmapper/**/*.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jjang051.ch04.dao.BoardDao">
<select id="getAllBoard" resultType="com.jjang051.ch04.dto.BoardDto">
SELECT * FROM BOARD03
</select>
<select id="getOneBoard" resultType="com.jjang051.ch04.dto.BoardDto">
SELECT * FROM BOARD03 WHERE no = #{no}
</select>
<insert id="insertBoard" parameterType="com.jjang051.ch04.dto.BoardDto">
INSERT INTO BOARD03 VALUES (BOARD03_SEQ.NEXTVAL,
#{userName},
#{subject},
#{contents},
SYSDATE,0)
</insert>
<update id="updateHit" parameterType="Integer">
UPDATE BOARD03 SET HIT = HIT + 1 WHERE NO = #{no}
</update>
<update id="updateBoard" parameterType="com.jjang051.ch04.dto.BoardDto">
UPDATE BOARD03 SET
USERNAME = #{userName}, SUBJECT = #{subject}, CONTENTS = #{contents}
WHERE NO = #{no}
</update>
<delete id="deleteBoard" parameterType="Integer">
DELETE FROM BOARD03 WHERE no = #{no}
</delete>
</mapper>
package com.jjang051.ch04.dao;
import com.jjang051.ch04.dto.BoardDto;
import java.util.ArrayList;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface BoardDao {
ArrayList<BoardDto> getAllBoard();
public void insertBoard(BoardDto boardDto);
public BoardDto getOneBoard(int no);
public void updateBoard(BoardDto boardDto);
public void deleteBoard(int no);
public void updateHit(int no);
}
package com.jjang051.ch04.service;
import com.jjang051.ch04.dao.BoardDao;
import com.jjang051.ch04.dto.BoardDto;
import java.util.ArrayList;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class BoardService {
@Autowired
BoardDao boardDao;
public BoardService() {
log.info("=========boardService===========");
}
public ArrayList<BoardDto> getAllBoard() {
ArrayList<BoardDto> boardList = boardDao.getAllBoard();
//log.info("boardList=={}", boardList);
return boardList;
}
public void insertBoard(BoardDto boardDto) {
boardDao.insertBoard(boardDto);
}
public BoardDto getOneBoard(int no) {
BoardDto boardDto = boardDao.getOneBoard(no);
return boardDto;
}
public void updateBoard(BoardDto boardDto) {
boardDao.updateBoard(boardDto);
}
public void deleteBoard(int no) {
boardDao.deleteBoard(no);
}
public void updateHit(int no) {
boardDao.updateHit(no);
}
}
<input type="text" name="userName">
의 경우에 name이 Dto의 변수와 이름이 동일하고, 이미 이 관련해서 dao와 mapper가 잘 연결되어 있으면 Controller가 알아서 얘가 Dto의 변수라고 생각한다.바인딩
: 그래서 Controller의 public String updateProcess(BoardDto boardDto, int no)
함수에는 개별적으로 userName, subject, contents가 넘어갔지만 setter 함수 사용 없이도 저 argument boardDto에 저절로 userName, subject, contents가 빨려 들어간다.String userName = request.getParameter("userName")
, dto.setUserName(userName)
과 같은 작업이 스프링에서는 사라지는 것이다.package com.jjang051.ch04.controller;
import com.jjang051.ch04.dto.BoardDto;
import com.jjang051.ch04.service.BoardService;
import java.io.IOException;
import java.util.ArrayList;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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;
@Controller
@RequestMapping("/board")
@Slf4j
public class BoardController {
@Autowired
BoardService boardService;
@GetMapping("/list")
public String list(Model model) {
//BoardService boardService = new BoardService();
ArrayList<BoardDto> boardList = boardService.getAllBoard();
model.addAttribute("boardList", boardList);
return "/board/list";
}
@GetMapping("/write")
public String write() {
return "/board/write";
}
@PostMapping("/writeProcess")
public String writeProcess(BoardDto boardDto) {
//log.info("boardDto=={}", boardDto);
boardService.insertBoard(boardDto);
return "redirect:/";
}
@GetMapping("/view")
public String view(Model model, int no) {
log.info("no : {}", no);
BoardDto boardDto = boardService.getOneBoard(no);
boardService.updateHit(no);
model.addAttribute("boardDto", boardDto);
return "board/view";
}
@GetMapping("/update")
public String update(Model model, int no) {
log.info("no : {}", no);
BoardDto boardDto = boardService.getOneBoard(no);
model.addAttribute("boardDto", boardDto);
return "board/update";
}
@PostMapping("/updateProcess")
public String updateProcess(BoardDto boardDto, int no) {
log.info("updateProcess no : {}", no);
boardService.updateBoard(boardDto);
return "redirect:/board/view?no=" + no;
}
@GetMapping("/delete")
public String delete(Model model, int no) {
log.info("no : {}", no);
boardService.deleteBoard(no);
return "redirect:/board/list";
}
}
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Board List</title>
<style>
.table {
text-align: center;
}
</style>
</head>
<body>
<h1> Board List </h1>
<hr>
<table class="table">
<colgroup>
<col width="100px" height="30px" style="background: #ffffff" />
<col width="300px" height="30px" style="background: #eeeeee" />
<col width="400px" height="30px" style="background: #999999" />
<col width="200px" height="30px" style="background: #eeeeee" />
<col width="50px" height="30px" style="background: #999999" />
</colgroup>
<tr>
<th>번호</th>
<th>이름</th>
<th>제목</th>
<th>날짜</th>
<th>조회수</th>
</tr>
<tr th:each="board : ${boardList}">
<td><a th:href="@{/board/view(no = ${board.no})}">[[${board.no}]]</a></td>
<td th:text="${board.userName}"></td>
<td th:text="${board.subject}"></td>
<td th:text="${board.regDate}"></td>
<td th:text="${board.hit}"></td>
</tr>
</table>
</body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VIEW List</title>
<style>
.table {
text-align: center;
}
</style>
</head>
<body>
<h1> Board List </h1>
<hr>
<table class="table">
<tr>
<td style="width:20%; background-color:#ffffff">번호</td>
<td>[[${boardDto.no}]]</td>
</tr>
<tr>
<td style="width:20%; background-color:#eeeeee">이름</td>
<td>[[${boardDto.userName}]]</td>
</tr>
<tr>
<td style="width:20%; background-color:#eeeeee">제목</td>
<td>[[${boardDto.subject}]]</td>
</tr>
<tr>
<td style="width:20%; background-color:#eeeeee">게시일</td>
<td>[[${boardDto.regDate}]]</td>
</tr>
<tr>
<td style="width:20%; background-color:#eeeeee">조회수</td>
<td>[[${boardDto.hit}]]</td>
</tr>
<tr>
<td style="width:20%; background-color:#eeeeee">내용</td>
<td>[[${boardDto.contents}]]</td>
</tr>
</table>
<a th:href="@{/board/delete(no = ${boardDto.no})}"><button>삭제하기</button></a>
<a th:href="@{/board/update(no = ${boardDto.no})}"><button>수정하기</button></a>
<a href="/board/list"><button>리스트로 돌아가기</button></a>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>write</h1>
<form action="/board/writeProcess" method="post">
<div><input type="text" name="userName"></div>
<div><input type="text" name="subject"></div>
<div><textarea name="contents" id="" cols="30" rows="10"></textarea></div>
<div><button>확인</button></div>
</form>
</body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>UPDATE</title>
</head>
<body>
<h1>UPDATE</h1>
<form th:action="@{/board/updateProcess(no = ${boardDto.no})}" method="post">
<div>이름 : <input type="text" name="userName" th:placeholder="${boardDto.userName}"></div>
<div>제목 : <input type="text" name="subject" th:placeholder="${boardDto.subject}"></div>
<div>내용 : <textarea name="contents" id="" cols="30" rows="10" th:placeholder="${boardDto.contents}"></textarea></div>
<div><button>수정하기</button></div>
</form>
</body>
</html>
html의 input에서 Controller로 (Controller 안에 해당하는 경로의 작업으로) 넘길 때, Dto에 set을 하지 않더라도! 이미 Dao에서 파라미터를 Dto를 받기로 했다면! 나는 그냥 userName을 넘겼는데 Controller는 set 작업 없이, userName을 받았는데 저절로 boardDto.userName으로 해석해 받는다.
타임리프에서 태그를 사용할 때
<form th:action="@{/board/updateProcess(no = ${boardDto.no})}" method="post">
<a th:href="@{/board/delete(no = ${boardDto.no})}"><button>삭제하기</button></a>
처럼, 사용법이 특이하기 때문에 주의해서 사용할 것.
# 서버 포트 설정
server:
port: 9090
# 스프링 로그 예쁘게 나오기 설정
spring:
output:
ansi:
enabled: always
# 데이터 베이스 연동
datasource:
driver-class-name: oracle.jdbc.driver.OracleDriver
url: jdbc:oracle:thin:@localhost:1521:xe
username: "--------------"
password: "--------------"
#로그 찍기
logging:
level:
'[com.jjang051.ch04]': DEBUG
#mybatis 설정
mybatis:
mapper-locations: classpath:sqlmapper/**/*.xml
config-location: classpath:sqlmapper/config/config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias type="com.jjang051.ch04.dto.BoardDto" alias="BoardDto" />
</typeAliases>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jjang051.ch04.dao.BoardDao">
<select id="getAllBoard" resultType="BoardDto">
SELECT * FROM BOARD03
</select>
<select id="getOneBoard" resultType="BoardDto">
SELECT * FROM BOARD03 WHERE no = #{no}
</select>
<insert id="insertBoard" parameterType="BoardDto">
INSERT INTO BOARD03 VALUES (BOARD03_SEQ.NEXTVAL,
#{userName},
#{subject},
#{contents},
SYSDATE, 0)
</insert>
<update id="updateHit" parameterType="Integer">
UPDATE BOARD03 SET HIT = HIT + 1 WHERE NO = #{no}
</update>
<update id="updateBoard" parameterType="BoardDto">
UPDATE BOARD03 SET
USERNAME = #{userName}, SUBJECT = #{subject}, CONTENTS = #{contents}
WHERE NO = #{no}
</update>
<delete id="deleteBoard" parameterType="Integer">
DELETE FROM BOARD03 WHERE no = #{no}
</delete>
</mapper>