본 글은 이전 글들과 이어진다.

2019/08/01 - [Spring] - Spring boot에서 maven을 통해 mybatis MySQL 사용하기 - select

2019/08/07 - [Spring] - Spring boot에서 maven을 통해 mybatis MySQL 사용하기 - insert

저번 글에서 문제 삼았던 부분이 롤 프로게임팀은 t1 뿐만 아니라 여러 팀이 있는데 나는 t1 하나만 보고 프로젝트를 작성했다. 그래서 xml에는 select * from t1, insert into t1 ~~~ 처럼 오로지 t1 테이블에만 접근이 가능한 쿼리문을 작성했다. 여기서 나는 GenG, Griffin 등 다양한 팀의 명단을 출력 및 선수 추가를 하고 싶다.

우선 GenG를 더 추가해주었다.
1. 데이터베이스에 GenG 테이블 추가
사용되는 쿼리문은 t1 테이블을 생성할 때와 같다.

CREATE TABLE lol.geng (id INT NOT NULL, 
name VARCHAR(100) NOT NULL, 
nickname VARCHAR(128) NOT NULL, 
position VARCHAR(100) NOT NULL, PRIMARY KEY(id));

2. 명단을 출력할 팀을 선택하는 페이지 생성
나는 selectTeam.jsp라는 페이지를 만들었다.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
         pageEncoding="EUC-KR"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
    <title>롤 프로게이머 팀</title>
</head>

<body>
    <form name="selectTeam" action="/list" method="post" id="select">
        <select name="team" id="team">
            <option value="t1"> T1 </option>
            <option value="geng"> GenG </option>
        </select>
        <input type="submit" name="selectBut" id="selectBut" value="팀 선택" />
    </form>
</body>

</html>

select box에서 팀을 선택하면 list 페이지에서 해당 팀의 명단을 출력해준다. 그러면 우리는 쿼리문에서 select * from t1; 이 아닌 여기서 선택한 팀이 select 되도록 수정해줘야 한다.

3. xml 수정

    <select id="getMember" resultType="com.chojpsh1.lol_pro.model.MemberModel">
		select * from ${team}
	</select>

xml에서 getMember 의 쿼리문을 수정해준다. 여기서 이전에 insert를 할때 사용자가 입력한 id, name, nickname, position 값들은 #{ }으로 쿼리문에 넣어주는데 테이블 명인 team은 ${ }으로 설정해준다. ${과 #{의 차이점은 무엇일까?
 결론은 #{은 Prepared Statement이고 ${은 그냥 준비되지 않고 직접 해당 값으로 대체된다. 그래서 #{은 안전하지만 ${은 안전하지 않다. 하지만 meta data(데이터 열, 행의 이름 또는 테이블 명)를 변경할 때에는 쿼리문이 수정되어야 한다. 그래서 해당 부분의 데이터를 변경하고 싶을 땐 ${을 사용해서 바꿔주는 데 ${는 원래 사용자 입력이 허용되지 않도록 하는 게 좋다.

- Prepared Statement와 Statement 차이점
 Statement는 실제 데이터베이스에 쿼리를 보내기 위해 필요한 객체이다. 이 객체는 Connect 객체의 연결 정보를 가져와서 DB에 접근하므로 이 객체를 사용하기 위해서는 Connection 객체가 먼저 존재해야 한다.
 Prepared Statement는 쿼리 수행마다 쿼리 문장 분석하고 컴파일 후 실행하는 statement와 달리 처음에만 해당 작업을 수행하고 캐시에 담아 재사용한다. 예를 들어, 삽입 쿼리문에 삽입되는 값이 변경되는 부분에 ? 로 작성하고 PreparedStatement 객체를 생성 후 해당 값이 int라면 setInt(?표 인덱스, 값) 함수를 통해 쿼리문을 재사용할 수 있다. 하지만 이렇다보니 한번에 쿼리문을 파악하기 힘들 수도 있다.

 그렇담 getMember에서 ${team}에 해당하는 데이터를 parameter로 받을 수 있게끔 수정해줘야 한다.
4. MemberDao 인터페이스에서 getMember 함수 수정

package com.chojpsh1.lol_pro.dao;

import com.chojpsh1.lol_pro.model.MemberModel;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface MemberDao {
    List<MemberModel> getMember(@Param("team") String team);
    void setMember(MemberModel member);
}

위와 같이 getMember 함수의 parameter 부분에 @Param annotation으로 team을 받는다. 그럼 getMember를 호출하는 Service에도 코드를 조금 수정해줘야 한다.

5. MemberService, MemberServiceImpl 수정
먼저 인터페이스 (MemberService)부터 수정한다.

package com.chojpsh1.lol_pro.service;

import com.chojpsh1.lol_pro.model.MemberModel;

import java.util.List;

public interface MemberService {
    List<MemberModel> printMember(String team);
    void insertMember(MemberModel member);
}

getMember 함수에게 전달해줄 team 데이터를 printMember 함수의 인자로 받는다.

package com.chojpsh1.lol_pro.service.Impl;

import com.chojpsh1.lol_pro.dao.MemberDao;
import com.chojpsh1.lol_pro.model.MemberModel;
import com.chojpsh1.lol_pro.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class MemberServiceImpl implements MemberService {

    @Autowired
    private MemberDao dao;

    @Override
    public List<MemberModel> printMember(String team) {
        List<MemberModel> member = dao.getMember(team);
        return member;
    }

    @Override
    public void insertMember(MemberModel member) {
        dao.setMember(member);
    }
}

MemberServiceImpl 클래스에서는 인자로 받은 team을 그대로 getMember 함수 인자로 전달만 해주도록 수정하면 끝이다.

6. Controller 수정
 Controller에서는 selectTeam.jsp 페이지를 띄우기 위해 selectTeam이 url에 요청되면 selectTeam String을 반환하는 selectTeam() 함수를 추가구현할 것이다. 그리고 list 요청 시 실행되는 list()함수를 수정해야 한다. 이전에는 페이지로부터 어떠한 데이터를 받는거 없이 그냥 쿼리문을 실행시켜 t1의 선수명단을 출력시켰는데 이제는 selectTeam.jsp 페이지에서 사용자가 선택한 팀 명단을 출력해야하므로 무슨 팀을 선택했는지 받아야한다. 받은 데이터를 printMember 함수 인자로 전달하도록 수정해주면 된다.

    @RequestMapping("/selectTeam")
    public String selectTeam(Model model) { return "selectTeam"; }

    @RequestMapping("/list")
    public String list(Model model, HttpServletRequest request) {
        String team = (String)request.getParameter("team");
        List<MemberModel> member = memberService.printMember(team);

        model.addAttribute("memberList", member);

        return "list";
    }

위 두 함수가 MemberController에서 추가 구현된 부분이다.

여기까지 하고 프로젝트 실행시켜 localhost:8080/selectTeam으로 이동하면 다음과 같이 선택창이 뜬다.

여기서 '팀 선택'을 누르면 list 페이지로 이동하면서 명단이 출력된다.

 원하는 테이블을 선택해서 select 하는 것은 되었다. 그럼 이제 원하는 테이블에 insert 하는 것을 해보자.

7. MemberModel 수정
 이건 나의 실수인 거 같은데 insert 할때에도 그냥 DAO클래스에서 인자에 @Param annotation을 이용해서 team 을 받으면 되었을 텐데, 이걸 알기 전에 insert 먼저 구현했다. (select를 나중에 구현하고) 그래서 insert 부분은 select와 다소 테이블 명을 넘겨주는 부분이 다르다. select를 구현하면서 @Param annotation을 이용하는 방법은 또 다른 방법이 있을까 싶어 구글링을 통해 알게된 내용이다. insert 부분은 일단 내가 생각하는 대로 작성해본 방법이다.

우선 MemberModel은 team 정보를 담고 있어야 한다. Controller 부분에서 Service의 함수를 호출해 DAO에서 쿼리문에 테이블 명을 넣을 때 MemberModel이 team 정보를 가지고 있어야 수훨하다고 생각했다.

package com.chojpsh1.lol_pro.model;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class MemberModel {

    private int id;
    private String name;
    private String nickname;
    private String position;
    private String team;
}

위와 같이 team 변수를 하나 추가시켜주었다.

8. MemberController 수정
마찬가지로 insert 함수에서 team 값을 set해주는 코드를 추가하였다. 이때 list 페이지를 띄울 때 '?team=팀이름 부분이 추가되었다. list()함수에서 team 명을 파라미터로 받아 select 쿼리문에 적용시켜 출력하기 때문에 team 이름을 전달해줘야 한다.

    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public ModelAndView insert(HttpServletRequest request) throws UnsupportedEncodingException {
        request.setCharacterEncoding("UTF-8");
        MemberModel member = new MemberModel();
        member.setId(Integer.parseInt(request.getParameter("id")));
        member.setName((String)request.getParameter("name"));
        member.setNickname((String)request.getParameter("nickname"));
        member.setPosition((String)request.getParameter("position"));
        member.setTeam((String)request.getParameter("team"));

        memberService.insertMember(member);
        ModelAndView result = new ModelAndView("redirect:/list?team="+member.getTeam());

        return result;
    }

 

9. xml 수정
xml에서 테이블 명 부분에 ${team}으로 하면 이 방법에서는 DAO 부분에 @Param annotation을 직접 인자로 주어 할 필요는 없다. 아래와 같이 쿼리문을 바꿔주면 된다.

	<insert id="setMember" parameterType="com.chojpsh1.lol_pro.model.MemberModel">
		INSERT INTO lol.${team} VALUES(#{id}, #{name}, #{nickname}, #{position})
	</insert>

 

selectMember 페이지에서 아래와 같이 정보를 입력하고

'추가하기' 버튼을 누르면 list 페이지로 이동하면서 geng 테이블의 정보가 출력된다.

데이터베이스에서 확인해보아도 데이터가 잘 삽입되어 있다.

 

지난 글에서 이어지는 글이다.

2019/08/01 - [Spring] - Spring boot에서 maven을 통해 mybatis MySQL 사용하기 - select

- 환경
   IntelliJ IDEA 2018.3.6 (Ultimate Edition)
   Mac OS

지난 글에서 select를 하기 위해 Model class인 MemberModel 에서 lombok을 이용하여 쉽게 Getter를 사용했다. insert를 위해서는 Setter가 필요하므로 똑같이 @Setter annotation을 추가해준다.
1. Setter 추가 (lombok 이용)

Setter를 생성했으면 DB에 접근해서 Setter로 받은 정보를 사용하는 함수를 DAO에 선언해준다.
2. DAO Interface에 setMember 함수 선언

DAO에 선언했으면 사용자가 입력한 정보를 토대로 DAO에 Model을 전달해줄 서비스가 필요하다. 그래서 MemberService interface에 해당 함수를 선언해준다.
3. MemberService interface에 insertMember 함수 선언

4. MemberServiceImpl에서 InsertMember 함수 구현

그럼 이제 xml에 DAO의 setMember 함수를 통해 DB에 접근하면 무슨 일을 수행할 지 정의해준다.
5. t1.xml에 insert 동작 정의

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.chojpsh1.lol_pro.dao.MemberDao">

    <select id="getMember" resultType="com.chojpsh1.lol_pro.model.MemberModel">
		select * from t1
	</select>

	<insert id="setMember" parameterType="com.chojpsh1.lol_pro.model.MemberModel">
		INSERT INTO lol.t1 VALUES(#{id}, #{name}, #{nickname}, #{position})
	</insert>
</mapper>

사용자가 추가할 프로게이머 선수의 id, 이름, 닉네임, 포지션을 입력하면 해당 정보를 setMember 함수를 통해 DB에 접근하여 삽입하게 된다.

 사용자가 url로 'addMember'를 요청하면 추가할 선수의 정보를 입력할 수 있는 페이지가 뜨도록 할 것이다. addMember페이지에서 사용자가 정보를 입력하고 '추가하기'라는 버튼을 누르면 입력한 정보들이 POST형식으로 'insert' 페이지에 submit 된다. 그럼 spring servlet 입장에서는 insert 페이지가 요청된 것이므로 해당 페이지 요청에 대해 controller 함수를 호출해 처리를 한다. 사용자가 입력한 정보를 토대로MemberModel을 하나 생성하여 memberService의 insertMember 함수에 넘겨줌으로써 DB에 삽입이 되도록 한다. 그리고 삽입이 된 결과를 확인하기 위해 이전 글에서 구현한 list 페이지를 띄워 DB에 저장된 선수들 목록을 보게한다.

 위가 내 프로젝트에서 삽입 부분에 대한 흐름이다. 우선 addMember.jsp부터 구현을 해보자.
6. addMember.jsp 작성

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
    <title>롤 프로게이머 추가하기</title>
</head>

<body>
<form name="inputBoard" id="inputBoard" method="post" action="/insert">
    <table border="1">
        <thead>
        <tr>
            <th>번호</th>
            <th>이름</th>
            <th>닉네임</th>
            <th>포지션</th>
        </tr>
        </thead>
        <tbody>
            <tr>
                <td><input type="text" id="id" name="id" /></td>
                <td><input type="text" id="name" name="name" /></td>
                <td><input type="text" id="nickname" name="nickname" /></td>
                <td><input type="text" id="position" name="position" /></td>
            </tr>
        </tbody>
    </table>
    <input type="submit" name="추가하기" value="추가하기" id="insert" />
</form>
</body>

</html>

 코드를 보면 submit을 하면 insert 페이지에 post 형식으로 입력 데이터들을 보내는 것을 알 수 있다.

7. MemberController 구현

package com.chojpsh1.lol_pro.controller;

import com.chojpsh1.lol_pro.model.MemberModel;
import com.chojpsh1.lol_pro.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.List;

@Controller
public class MemberController {

    @Autowired
    MemberService memberService;

    @RequestMapping("/list")
    public String list(Model model) {
        List<MemberModel> member = memberService.printMember();

        model.addAttribute("memberList", member);

        return "list";
    }

    @RequestMapping("/addMember")
    public String addMember(Model model) {
        return "addMember";
    }

    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public ModelAndView insert(HttpServletRequest request) throws UnsupportedEncodingException {
        request.setCharacterEncoding("UTF-8");
        MemberModel member = new MemberModel();
        member.setId(Integer.parseInt(request.getParameter("id")));
        member.setName((String)request.getParameter("name"));
        member.setNickname((String)request.getParameter("nickname"));
        member.setPosition((String)request.getParameter("position"));

        memberService.insertMember(member);
        ModelAndView result = new ModelAndView("redirect:/list");

        return result;
    }
}

Controller 구현 코드를 보면 list, addMember 요청이 들어왔을 때 처리하는 함수에서는 String을 반환한다. 하지만 insert 페이지가 들어오면 ModelAndView 객체를 반환한다. 

- Model객체와 ModelAnd View 객체 사용 시 차이점?
  ModelAndView객체는 말 그대로 Model 객체와 데이터를 넘길 페이지 값을 가진 return 값을 합친 것이다. list나 addMember 함수를 보면 Model 객체를 사용해서 String return 값으로 넘긴 페이지를 실행한다는 것을 알 수 있다. ModelAndView 객체를 사용하면 객체 생성 후 setViewName 함수로 실행할 페이지 이름을 지정해주거나 위의 insert 함수처럼 생성 시 페이지 이름을 지정해줄 수 있다. 그리고 return으로 ModelAndView 객체를 넘기면 servelt은 Model과 View 정보를 받고 해당 페이지에 정보를 뿌려주는 거다.
  이 둘은 상황에 따라 유동적으로 골라 사용하면 된다.

선수를 추가하기 전 데이터베이스의 모습은 아래와 같다.

addMember 페이지에서 아래와 같이 정보를 입력한 후 '추가하기' 버튼을 눌렀다.

그러면 list 페이지로 이동하고 새로 추가된 홍길동 선수가 같이 출력되는 것을 볼 수 있다.

데이터베이스에도 마찬가지로 정보가 새로 추가된 것 또한 당연히 볼 수 있다.

 

그런데 생각해보니 롤 프로게이머팀은 t1외에도 많다. 그래서 다음에는 어느 팀에 선수를 추가할지, 어느 팀의 선수 리스트를 뽑아올 지 사용자가 선택을 하면 spring 측에서 해당 팀 테이블에 접근해서 처리할 수 있도록 하는 과정을 올릴 것이다.

- 환경
   IntelliJ IDEA 2018.3.6 (Ultimate Edition)
   Mac OS

Spring boot을 사용하기 전에 Spring boot이 무엇인지 알아보자. 일단, Spring이 IoC/DI 기반으로 된 자바 프레임 워크다. IoC는 객체 생성 및 소멸이 알아서 대신 작동되는 것이고 DI는 interface의 method를 사용하면 나중에 주입하는 클래스에 따라 다른 기능을 하는 것이다.
그럼 Spring boot은 단순하게 스프링 프로젝트라고 생각하면 된다. 스프링이 점점 지원하는 라이브러리가 많아지니까 자주 사용하는 프로젝트 조합을 미리 만들어 빌드하는게 Spring boot이다. tomcat 서버를 내장하고 있기에 Spring에서 하던 연동 작업을 할 필요가 없다.

또, 제목에 있는 Maven은 무엇인지 알아보자. 내가 자바 프로젝트에서 필요한 라이브러리 jar 파일을 가지고 수행하다가 협업을 한다고 생각해보자. 그럼 내가 사용하는 jar 파일들을 수동으로 넘겨줘야 한다. 하지만 Maven을 사용하면 필요한 라이브러리를 pom.xml 파일에 정의함으로써 자동으로 프로젝트에 받아오게 한다. 

그럼 이제 Spring boot 프로젝트를 빌드해보자.

1. Spring boot 프로젝트 생성

 Intellij에서 'Spring Initializr'이 Spring boot을 말한다. 얘를 클릭하고 다음으로 넘어간다.

 그리고 group과 프로젝트명을 적고 넘어간다. 나는 롤 프로게이머 선수단 목록을 보여주는 웹 페이지를 만들 것이므로 lol_pro라고 했다.

 그리고 앞서 말한 프로젝트에 필요한 라이브러리를 프로젝트 빌드 시 같이 구축할 수 있다는 부분이 위 사진으로 설명된다. 여기서 나는 제목에 언급된 것 처럼 MyBatis로 MySQL를 사용할 것이므로 두 개를 선택해주고 Lombok도 선택해주었다.

 Next를 계속 누르고 오른쪽 하단에 해당 알림창이 뜨면 Enable Auto-Import를 선택해준다.

 그리고 프로젝트에 필요한 plugin을 설치하자. 우선 나는 Lombok을 사용하므로 Preference의(단축키는 'Command' + ',') Plugins 에서 lombok을 검색하면 나오는 'Lombok'을 설치했다. 

 그리고 MyBatis plugin도 검색해서 설치해준다.

2. 프로젝트 MVC 나누기
 MVC를 나누기 전에 Spring MVC에 대해 알고가면 프로젝트 흐름을 이해하는 데 도움이 된다. (당연히 알고 있어야 하는 것이고)
 웹 브라우저가 URL을 요청하면 DispatcherServlet으로 간다. 먼저 HandlerMapping에서 URL과 매핑되는 Controller를 검색해서 return해준다. Dispatcher Servlet에서 해당 controller에게 처리 요청을 하고 model, view를 return 받는다. 그리고 View에게 출력을 요청하면 View가 브라우저로 JSP를 생성한다.

다시 프로젝트로 돌아와서 일단은 해당 단계에서 View는 제외하고 Model, Controller, DAO, Service, Config 부분을 나누어 줄 것이다. 빌드된 프로젝트에 아래와 같이 패키지 및 클래스를 생성했다.

 2-1. config
  DataAccessConfig 파일은 Mapper에서 매칭된 쿼리문이 적힌 xml 파일을 가져와 데이터베이스의 데이터에 접근할 수 있도록 설정해주는 클래스이다.
  DataSourceConfig 파일은 application.properties 파일에 적힌 설정값을 토대로 데이터에 접근하기 위한 기본 작업을 하는 클래스이다.
 2-2. controller
  MemberController 파일은 브라우저가 요청한 URL에 맞는 함수를 실행해서 JSP에 나타낼 정보를 설정해준다.
 2-3. dao
 MemberDao 파일은 말 그대로 데이터를 가져오기 위한 인터페이스이다.
 2-4. model
 가져온 데이터를 저장하기 위한 클래스이다.
 2-5. service
 데이터를 저장한 MemberModel 객체를 dao 객체로부터 받아 특정 작업을 수행하기 위한 파일인데 선언부로 인터페이스를 두고 구현부로 Impl를 따로 두었다. 현재는 dao로 부터 받아온 모델 객체를 Controller에게 넘겨주기만 하면 된다.

3. MySQL 디비에 스키마 구축
나는 로컬에다가 MySQL를 설치하여 사용하였다. 
아래는 스키마 생성에 사용된 명령어이다.

CREATE SCHEMA lol DEAFAULT CHARACTER SET utf8;

아래는 테이블 생성에 사용된 명령어이다.

CREATE TABLE lol.t1 (id INT NOT NULL, name VARCHAR(100) NOT NULL,
			nickname VARCHAR(128) NOT NULL, position VARCHAR(100) NOT NULL,
                        PRIMARY KEY(id));

그리고 INSERT 문을 통해 데이터를 t1 테이블에 아래와 같이 삽입하였다.

4. 코드 작성 전 annotation 허용
 Annotation이란 @을 이용해 주석으로 자바 코드에 주석을 달아 특별한 의미를 부여한다. 컴파일러를 위한 정보를 제공하기 위한 용도이다. 
 Intellij에서 이를 처리하는게 프로젝트 빌드 초기 설정에는 허용되어 있지 않다. 그래서 Preference(다시 'Command' + ',') > Build, Execution, Deployment > Compiler > Annoation Processors 에서 Enable annotation processing 부분을 체크해준다.

5. application.properties에 접근할 MySQL DB 설정값 작성

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/lol?serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=password

 위와 같이 작성하면 되는데 나는 root 계정으로 접근해서 allowPublicKeyRetrieval 속성을 true로 주었다.
 그럼 이제 여기에 적힌 설정을 내용을 토대로 DataSourceConfig 클래스에 configure해주면 된다.

6. DataSourceConfig

package com.chojpsh1.lol_pro.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }
}

application.properties에 작성했던 datasource의 공통된 부분만 ConfigurationProperties annotation로 넘겨주면 DataSourceBuilder가 그에 맞게 수행할 것이다.

7. MemberModel
 데이터를 받아와 저장할 객체를 정의한다.

package com.chojpsh1.lol_pro.model;

import lombok.Getter;

@Getter
public class MemberModel {

    private int id;
    private String name;
    private String nickname;
    private String position;
}

 나는 Lombok을 이용해서 @Getter annotation을 통해 getter가 생성되도록 했다. Lombok을 사용하지 않는다면 public int getId() { return id; } 이렇게 getter를 구현해주면 된다.

8. MemberDao
 이제 데이터가 저장된 객체에 접근하는 객체를 정의해준다.

package com.chojpsh1.lol_pro.dao;

import com.chojpsh1.lol_pro.model.MemberModel;

import java.util.List;

public interface MemberDao {
    List<MemberModel> getMember();
}

 앞에 테이블에 삽입된 모습을 보면 알 수 있듯이 한 record가 아니라 여러 record가 있다. 한개만 있다면 그냥 MemberModel getMember();로 선언해도 되지만 여러 record를 뽑아오므로 List로 가져온다.

 이제 MySQL DB에서 수행할 쿼리문을 작성해보자. 앞에 말한 MVC 동작 과정을 다시 보면 Servlet에서 해당 URL에 대한 수행 작업을 알기 위해 Mapper에게 처리 요청을 한다. 그래서 어디에 있는 mapper에 가서 데이터를 접근할 지 설정하는 것이 DataAccessConfig이고, 데이터에 접근해서 무슨 동작을 할지 mapper 안에 정의 해준다. 
 그러기위해 resources 폴더 내에 mapper 폴더를 하나 만들어 t1 테이블에 접근하는 쿼리문을 작성할 xml 파일을 생성한다.
7. Mapper 쿼리문 작성

 내가 생성한 mapper 위치는 위와 같다. 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.chojpsh1.lol_pro.dao.MemberDao">

    <select id="getMember" resultType="com.chojpsh1.lol_pro.model.MemberModel">
		select * from t1
	</select>

</mapper>

8. DataAccessConfig

package com.chojpsh1.lol_pro.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "com.chojpsh1.lol_pro.dao")
public class DataAccessConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml")
        );
        return sessionFactoryBean.getObject();
    }

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

mapper에서 매칭될 파일의 경로가 getResources 함수로 들어간다.

이제 데이터를 저장하는 과정은 완료되었으니 이를 가져와 작업을 수행하는 코드를 작성해보자.
9. MemberService

package com.chojpsh1.lol_pro.service;

import com.chojpsh1.lol_pro.model.MemberModel;

import java.util.List;

public interface MemberService {
    List<MemberModel> printMember();
}

 일단은 그냥 데이터를 가져와 출력하는 게 목표이므로 데이터를 출력해주는 서비스를 할 함수를 인터페이스에 선언한다.

10. MemberServiceImpl

package com.chojpsh1.lol_pro.service.Impl;

import com.chojpsh1.lol_pro.dao.MemberDao;
import com.chojpsh1.lol_pro.model.MemberModel;
import com.chojpsh1.lol_pro.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class MemberServiceImpl implements MemberService {

    @Autowired
    private MemberDao dao;

    @Override
    public List<MemberModel> printMember() {
        List<MemberModel> member = dao.getMember();
        return member;
    }
}

 데이터를 받아 넘겨준다.(controller가 넘겨받을 것이다.)

11. MemberController

package com.chojpsh1.lol_pro.controller;

import com.chojpsh1.lol_pro.model.MemberModel;
import com.chojpsh1.lol_pro.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller
public class MemberController {

    @Autowired
    MemberService memberService;

    @RequestMapping("/list")
    public String list(Model model) {
        List<MemberModel> member = memberService.printMember();

        model.addAttribute("memberList", member);

        return "list";
    }
}

 RequestMapping annotation을 통해 URL에 /list 가 요청되면 list 함수를 실행할 것인데 인자값에 있는 spring 프레임워크에서 ui, 즉 View 로 값을 넘겨줄 수 있는 Model 객체로 데이터를 준다. 즉, 내 코드에서는 Model에 addAttribute 함수를 통해 'memberList'라는 id에데이터들의 집합 list를 매칭시켜주었다. 그럼 View 부분인 jsp에서 해당 id로 값을 사용할 수 있다.

근데 듣기론 Spring boot에서는 View로 jsp보단 다른 걸 사용한다고 들었는데..일단 jsp가 제일 기본이니까 Jsp로 했다. (Velocity도 많이 들었는데 일단 나는 초짜니까 jsp로 했다.)

12. pom.xml에 JSP View 사용 관련 dependency 추가
 Spring boot에서는 JSP를 View로 사용하는게 기본 설정이 아니여서 pom.xml에 이에 맞는 dependency를 추가해줘야한다. 

	<dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
	</dependency>
	<dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
	</dependency>

위 두개 dependency만 추가해주면 된다.

13. application.properties에 view 설정값 추가
 그리고 View를 담당할 jsp가 있을 파일 위치를 application.properties에 spring의 view로 설정해주면 된다.

spring.mvc.view.prefix=/WEB-INF/view/
spring.mvc.view.suffix=.jsp

 이렇게 하면 spring에서 main/webapp에 있을 WEB-INF/view/ 폴더 내에 확장자가 jsp파일을 view로 사용한다. 

 따라서 main폴더 내에 webapp 폴더를 만들고 application.properties에 작성한 경로대로 jsp 파일을 만들어준다.

 아까 11번 MemberController를 보면 "list"를 반환한다. 이는 Controller가 View 파일 이름을 반환하면 Servlet에서 반환받아 이에 해당하는 jsp 파일을 찾아 브라우저에 띄운다. 그래서 파일 이름을 list.jsp로 했다.

14. JSP View

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
         pageEncoding="EUC-KR"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
    <title>롤 프로게이머</title>
</head>

<body>
    <table border="1">
        <thead>
            <tr>
                <th>번호</th>
                <th>이름</th>
                <th>닉네임</th>
                <th>포지션</th>
            </tr>
        </thead>
        <tbody>
            <c:forEach items="${memberList}" var="member">
                <tr>
                    <td>${member.id}</td>
                    <td>${member.name}</td>
                    <td>${member.nickname}</td>
                    <td>${member.position}</td>
                </tr>
            </c:forEach>
        </tbody>
    </table>
</body>

</html>

 jsp 코드를 보면 아까 11번 MemberController에서 attribute로 준 memberList를 사용하고 있는 것을 볼 수 있다.

이렇게 하고 프로젝트를 실행한 후 localhot:8080/list로 접속하면 데이터베이스에 저장된 정보가 표로 출력되는 걸 볼 수 있다.

+ Recent posts