[Spring] Spring Boot + JdbcDaoSupport

김대현·2024년 10월 22일

📚 공부_Spring

목록 보기
5/8

2024.10.23
어째서인가 어제 작성한 JdbcDaoSupport의 앞부분이 오늘 작성한 MyBatis의 내용으로 덮어씌워져 있었다.... ㅠ
주말간에 복구를 해야겠다....

목차

1. JdbcDaoSupport 클래스란?
2. Spring Boot에서 JdbcDaoSupport 클래스 사용하기
3. Object를 사용하는 메소드
4. 최종 결과물
5. 마무리

1. JdbcDaoSupport 클래스란?

2. Spring Boot에서 JdbcDaoSupport 클래스 사용하기

1) 상황 가정하기

간단한 상황을 가정해서 JdbcDaoSupport 클래스를 사용하는 상황을 가정해 보자. 회사에서 각 부서별 or 직급별로 검색을 해서 해당 직원의 정보를 출력하고자 한다. 추가로 공백을 입력시 전체 직원의 정보를 출력해야한다. DB는 2가지로 나누어져 있으며, 부서 정보가 담겨있는 buser Table, 직원 정보가 담겨져 있는 jikwon Table로 이루어져 있다.

2) 필요한 상황 점검하기

위의 조건대로 어플리케이션을 만들기 위해서는 입력 데이터, 출력데이터간에 필요한 요소와 진행 과정을 우선적으로 체크해봐야한다. 체크를 해보니 크게 5단계의 진행이 필요했다.

  1. VIEW에서 데이터 입력. (직급, 부서) → input, post방식
  2. CONTROLLER로 직급과 부서의 정보를 갖고 이동, 해당 정보가 유효한지 검사
  3. 검사 이후 해당 정보를 갖고 MODEL로 이동. DB에 접근, query에서 조건문 설정함으로 원하는 데이터 꺼내오기 (WHERE절)
  4. 꺼낸 데이터를 가지고 CONTROLLER로 다시 돌아와서 해당 데이터를 가지고 VIEW로 Forwarding
  5. VIEW에서 해당 데이터를 출력.

3) VIEW 작성

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2>직급으로 직원 찾기!</h2>
	<form method="post" action="controller/findLevel">
		직급 입력 : <input type="text" name="level"> <input type="submit"
			value="확인">
	</form>
	<h2>부서로 직원 찾기!</h2>
	<form method="post" action="controller/findDepart">
		부서 입력 : <input type="text" name="depart"> <input type="submit"
			value="확인">
	</form>
</body>
</html>

4) CONTROLLER 작성

CONTROLLER에서 필요한 요건은 유저가 입력한 데이터, 즉 파라미터를 받아오기, 해당 입력값이 유효한지의 검사다. 파라미터를 받아오는 방법은 어노테이션 (@RequestParam)을 사용했고, 유효성 검사는 부서와 직급이 저장된 List를 통해 유저가 입력한 정보가 해당 리스트안에 있는지 contains 메소드를 사용해 검사했다. (이부분은 MODEL로 넘어가서 검사할 수도 있다.)

package pack.controller;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;

import pack.model.JikwonDto;
import pack.model.JikwonInter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping(value = "controller")
public class JikwonController {

	@Autowired
	private JikwonInter jikwonInter;
	
	//직급 리스트
	private String[] level = {"","사원","대리","부장","과장","이사"};
	private List<String> levelList = Arrays.asList(level);
	
	//부서 리스트
	private String[] depart = {"","총무부","영업부","전산부","관리부"};
	private List<String> departList = Arrays.asList(depart);
	
    //직급으로 사원의 정보 가져오기
	@RequestMapping(value = "findLevel", method=RequestMethod.POST)
	public String getJikwonL(@RequestParam("level")String level, Model model) {
		ArrayList<JikwonDto> list=null;
		//유저가 입력한 값이 직급 리스트에 있는지 확인
		if(levelList.contains(level)) {
			list = (ArrayList<JikwonDto>)jikwonInter.findJikwonL(level);
			model.addAttribute("data", list);
			model.addAttribute("msg", null);
			model.addAttribute("display", level);
		}
		else {
			model.addAttribute("data", list);
			model.addAttribute("msg", "잘못 입력하셨습니다");
		}
		return "show";
	}
	
    //부서명으로 사원의 정보 가져오기
	@RequestMapping(value = "findDepart", method=RequestMethod.POST)
	public String getjikwonD(@RequestParam("depart")String depart, Model model) {
		ArrayList<JikwonDto> list=null;
		//유저가 입력한 값이 부서 리스트에 있는지 확인
		if(departList.contains(depart)) {
			list = (ArrayList<JikwonDto>)jikwonInter.findJikwonD(depart);
			model.addAttribute("data", list);
			model.addAttribute("msg", null);
			model.addAttribute("display", depart);
		}
		else {
			model.addAttribute("data", list);
			model.addAttribute("msg", "잘못 입력하셨습니다");	
		}
		return "show";
	}	
}

4) MODEL 작성

직원의 경우 WHERE 조건문을 통해서 해당 직급을 조회한후 Return하면 된다. 그러나 부서의 경우 부서명이 jikwon Table에는 없고 buser Tabel에 있기 때문에 JOIN을 사용해야 한다.
또한 Statement를 사용하면 SQL Injection의 위험이 있기 때문에 PreparedStatement 객체를 사용해야한다.

package pack.model;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;

@Repository
public class JikwonImpl extends JdbcDaoSupport implements JikwonInter {

	//JdbcDaoSupport에 dataSource 설정하기
	@Autowired
	public JikwonImpl(DataSource dataSource) {
		setDataSource(dataSource);
	}

	//직급으로 직원 찾기
	@Override
	public List<JikwonDto> findJikwonL(String level) {
		String sql;
		List<JikwonDto> list;
		if(level.equals("")) {
			sql = "SELECT jikwonno, jikwonname, jikwongen, jikwonpay FROM jikwon";
			list  = (ArrayList<JikwonDto>) getJdbcTemplate().query(sql,(RowMapper) (rs, rowNum) -> {
				JikwonDto dto = new JikwonDto();
				dto.setJikwonno(rs.getString("jikwonno"));
				dto.setJikwonname(rs.getString("jikwonname"));
				dto.setJikwongen(rs.getString("jikwongen"));
				dto.setJikwonpay(rs.getString("jikwonpay"));
				return dto;
			});			
		}
	
		//PreparedStatement로 조건 추가 => Object 사용
		else {
			sql = "SELECT jikwonno, jikwonname, jikwongen, jikwonpay FROM jikwon WHERE jikwonjik=?";
			list  = (ArrayList<JikwonDto>) getJdbcTemplate().query(sql, new Object[] {level}, (RowMapper) (rs, rowNum) -> {
				JikwonDto dto = new JikwonDto();
				dto.setJikwonno(rs.getString("jikwonno"));
				dto.setJikwonname(rs.getString("jikwonname"));
				dto.setJikwongen(rs.getString("jikwongen"));
				dto.setJikwonpay(rs.getString("jikwonpay"));
				return dto;
			});			
		}
		return list;	
	}
	
	//부서명으로 직원 찾기
	@Override
	public List<JikwonDto> findJikwonD(String depart) {
		String sql;
		List<JikwonDto> list;
		if(depart.equals("")) {
			//jikwon Table에는 부서명이 없기 때문에 INNER JOIN으로 buser Table의 부서명을 가져오기.
			sql = "SELECT j.jikwonno, j.jikwonname, j.jikwongen, j.jikwonpay FROM jikwon AS j INNER JOIN buser AS b ON j.busernum = b.buserno";
			list  = (ArrayList<JikwonDto>) getJdbcTemplate().query(sql,(RowMapper) (rs, rowNum) -> {
				JikwonDto dto = new JikwonDto();
				dto.setJikwonno(rs.getString("jikwonno"));
				dto.setJikwonname(rs.getString("jikwonname"));
				dto.setJikwongen(rs.getString("jikwongen"));
				dto.setJikwonpay(rs.getString("jikwonpay"));
				return dto;
			});			
		}
		
		//PreparedStatement로 조건 추가 => PrepareStatementSetter 사용
		else {
			//jikwon Table에는 부서명이 없기 때문에 INNER JOIN으로 buser Table의 부서명을 가져오기.
			sql =  "SELECT j.jikwonno, j.jikwonname, j.jikwongen, j.jikwonpay FROM jikwon AS j INNER JOIN buser AS b ON j.busernum = b.buserno WHERE b.busername=?";
			//PreparedStatement와 RowMapper는 추상메소드를 1개만 가지는 Interface이다. 즉 Lambda 함수를 사용 할 수 있다.
			list  = (ArrayList<JikwonDto>) getJdbcTemplate().query(sql, pss -> pss.setString(1, depart), (RowMapper) (rs, rowNum) -> {
				JikwonDto dto = new JikwonDto();
				dto.setJikwonno(rs.getString("jikwonno"));
				dto.setJikwonname(rs.getString("jikwonname"));
				dto.setJikwongen(rs.getString("jikwongen"));
				dto.setJikwonpay(rs.getString("jikwonpay"));
				return dto;
			});			
		}
		return list;
	}
}

3. Object를 사용하는 메소드

그런데 PrepareStatement 객체에 값을 넣기 위해 query(sql,object, RowMapper) 메소드를 사용하는 순간 해당 코드가 Deprecated임을 발견하게 되었다. 이후 다른 메소드를 찾아봤는데 PrepareStatement 에 Object를 통해 값을 입력하는 모든 메소드가 deprecated가 되어있음을 발견하게 되었다. 정보를 찾아보니 해당 메소드가 deprecated가 된 이유는 가독성 향상과 Java에서 Object 타입을 사용하면 컴파일 타임에 타입 체크가 이루어지지 않아 런타임 오류가 발생할 수 있는 위험이 커지기 때문이라고 한다. 그래서 대체할 방법을 찾아봤고 곧바로 'PreparedStatementSetter Interface'를 발견하게 되었다.

PreparedStatementSetter Interface

해당 인터페이스는 JdbcTemplate class 안에서 PreparedStatement의 값을 입력하는데 사용하는 인터페이스 이다. 그리고 해당 인터페이스는 serValues라는 단 1개의 추상메소드만 갖는다.
해당 인터페이스의 사용법은 아주 간단한데 setValues 메소드를 사용하거나 기존 PreparedStatement와 같이 setString, setInt등의 메소드를 사용하면 된다.

또한 RowMapper인터페이스 역시 1개의 추상메소드만 갖는 인터페이스이다. 즉 둘다 람다 표현식을 사용 가능하다.

4. 최종 결과물

5. 마무리

  • Spring Boot와 JdbcDaoSupport로 CRUD를 하는 과정에대한 이해가 온전하지 못했다. 하지만 오늘 수업과 공부를 통해서 이해가 되었다.
  • 내일부터는 Mybatis와 JPA를 기준으로 DB를 운용하게 된다. Spring Boot로 수준있는 웹 개발을 하기 위해서 꾸준히 공부하고 꾸준히 훈련해야겠다.
profile
안녕하세요. 날마다 성장하는 김대현입니다 :)

0개의 댓글