관리 메뉴

거니의 velog

231116_SPRING 1 (2) 본문

대덕인재개발원_웹기반 애플리케이션

231116_SPRING 1 (2)

Unlimited00 2023. 11. 16. 08:52

- http://localhost/book/form.do


[form.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="${pageContext.request.contextPath}/resources/css/bootstrap.min.css" rel="stylesheet" />
    <title>책 생성하기</title>
</head>

<body>
    <div class="jumbotron">
        <div class="container">
            <h2 class="display-4">책 생성하기</h2>
        </div>
    </div>
    <div class="container">
        <form action="/book/form.do" method="post">
            <div class="row">
                <div class="col-md-12 mb-2">
                    <div class="row">
                        <div class="col-md-2">
                            <label for="title" class="col-form-label">제목</label>
                        </div>
                        <div class="col-md-10">
                            <input type="text" class="form-control" name="title" id="title" />
                        </div>
                    </div>
                </div>
                <div class="col-md-12 mb-2">
                    <div class="row">
                        <div class="col-md-2">
                            <label for="category" class="col-form-label">카테고리</label>
                        </div>
                        <div class="col-md-10">
                            <input type="text" class="form-control" name="category" id="category" />
                        </div>
                    </div>
                </div>
                <div class="col-md-12 mb-2">
                    <div class="row">
                        <div class="col-md-2">
                            <label for="price" class="col-form-label">가격</label>
                        </div>
                        <div class="col-md-10">
                            <input type="text" class="form-control" name="price" id="price" />
                        </div>
                    </div>
                </div>
                <button type="submit" class="btn btn-warning">등록</button>
                <a href="" class="btn btn-primary">목록</a>
            </div>
        </form>
    </div>
</body>

</html>

* 인터페이스는 앞에 I가 붙고, 이를 상속 받는 클래스는 끝에 Impl로 명칭하는 것이 개발자들 사이의 관례이다.

[IBookService]

package kr.or.ddit.book.service;

import java.util.Map;

public interface IBookService {
	
	public String insertBook(Map<String, Object> map);
	
}

[BookServiceImpl]

package kr.or.ddit.book.service;

import java.util.Map;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import kr.or.ddit.book.dao.BookDAO;

/*
 * 일반적으로 서비스 레이어는 인터페이스와 클래스를 함께 사용한다.
 * 스프링은 직접 클래스를 생성하는 것을 지양하고 인터페이스를 통해 접근하는 것을 권장하는 프레임워크이다.
 */
@Service
public class BookServiceImpl implements IBookService {

	/*
	 * Service 클래스는 비즈니스 클래스가 위치하는 곳이다.
	 * 스프링 MVC 구조에서 서비스 클래스는 컨트롤러와 DAO를 연결하는 역할을 한다.
	 * 
	 * 어노테이션(@) Service는 스프링에 서비스 클래스임을 알려준다.
	 * 
	 * 데이터베이스 접근을 위해 BookDAO 인스턴스를 주입받는다.
	 * 클래스의 이름이 Impl로 끝나는 것은 implements의 약자로 관습에 따른다.
	 * Impl이 붙고 안붙고에 따라 클래스인지 인터페이스인지 구별하기 쉽다.
	 */
	@Inject
	private BookDAO dao;
	
	/**
	 * <p>책 등록</p>
	 * @since SampleSpringYse
	 * @author PC_23
	 * @param map 등록할 책 데이터
	 * @return 성공시 책ID, 실패시 null
	 */
	@Override
	public String insertBook(Map<String, Object> map) {
		// status 변수에는 영향받은 행 수가 담긴다.
		// insert 구분은 입력이 성공하면 1, 실패하면 0을 리턴한다.
		int status = dao.insert(map);
		if(status == 1) {
			// 결과가 성공일 시, map 인스턴스에 book 테이블의 pk인 book_id가 담겨있다.
			return map.get("book_id").toString();
		}
		return null;
	}

}

[BookDAO.java]

package kr.or.ddit.book.dao;

import java.util.Map;

import javax.inject.Inject;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;

/*
 * 어노테이션(@) Repository는 데이터에 접근하는 클래스임을 명시한다.
 * 해당 어노테이션이 있는 클래스는 스프링이 데이터를 관리하는 클래스라고 인지하며
 * 자바 빈(Java Bean)으로 등록해서 관리한다.
 * 
 * SqlSessionTemplate 객체를 멤버 변수로 선언하는 이유는 mapper xml을 실행시키기 위해서이다.
 * 해당 객체 위에 @Inject 또는 @Autowired를 붙여서 sqlSessionTemplate 객체를 사용할 수 있도록 한다.
 * 이러한 형태를 '의존성 주입'이라고 한다. (필드 인젝션, Field Injection)
 * 
 * SqlSessionTemplate 객체는 new 키워드를 통해 직접 생성하지 않고, 의존성 주입(Dependency Injection - DI)을 통해 주입받는다.
 * 스프링은 미리 만들어 놓은 SqlSessionTemplate 타입 객체를 BookDAO 클래스 안에서 사용한다.
 * 해당 과정은 스프링에서 자동 실행되며 개발자가 직접 SqlSessionTemplate 객체를 생성하는 일 없이 곧바로 사용할 수 있다.
 * 
 * SqlSessionTemplate 객체는 root-context.xml에서 정의해둔 객체이기도 하고, 서버가 시작될 때 스프링은 미리 xml을 읽어 객체를 인스턴스화 해둔다.
 * 
 */
@Repository
public class BookDAO {
	
	/*
	 * 매퍼 xml을 실행시키기 위해서 SqlSessionTemplate 객체를 멤버 변수로 선언한다.
	 * @Inject를 붙여서 SqlSessionTemplate 객체를 사용할 수 있게 한다.
	 */
	@Inject
	private SqlSessionTemplate sqlSession;
	
	/*
	 * sqlSessionTemplate.insert()
	 * 1) 첫 번째 파라미터는 SQL Mapper의 id이다.
	 * 	book_SQL.xml에서 namespace로 설정한 'Book'과 insert 쿼리를 실행하기 위해 만든 insert 문의 id의 값 'insert'이다.
	 * 	mybatis는 네임스페이스 + id 조합으로 쿼리를 찾아서 실행한다.
	 * 2) 두 번째 파라미터는 쿼리에 전달할 데이터이다.
	 * 	mapper 내 insert 쿼리를 실행하기 위해 전달되어 지는 parameterType이 map이다.
	 * 
	 * 외부에서 Dao까지 map에 title, category, price가 담겨져서 온다.
	 * 그리고, useGeneratedKeys와 keyProperty의 설정 덕분에 book 테이블의 pk인 book_id 항목이 생긴다.
	 */
	public int insert(Map<String, Object> map) {
		/*
		 * useGeneratedKeys와 keyProperty 설정에 따라서 쿼리가 실행되고 나면 파라미터로 전달된 map 객체에 book 테이블의 PK인 book_id 항목이 생김
		 * 
		 * 기존 Map :::
		 * {
		 * 	"title" : "제목", "category" : "카테고리", "price" : 1000
		 * }
		 * 
		 * 쿼리 실행 후 Map:::
		 * {
		 * 	"title" : "제목", "category" : "카테고리", "price" : 1000, "book_id" : 1
		 * }
		 * 
		 * sqlSessionTemplate.insert()의 반환값은 쿼리의 영향을 받은 행 수(row count)이다.
		 * insert 쿼리의 경우 성공하면 1개의 행(row)이 생기므로 1을 리턴하고 실패하면 0을 리턴한다.
		 */
		return sqlSession.insert("Book.insert", map);
	}
	
}

[book_SQL.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">
  
<!--  
	[참고 사이트] 마이바티스
	- https://mybatis.org/mybatis-3/ko/getting-started.html
	
	마이바티스는 무엇인가요?
	- 마이바티스는 개발자가 지정한 SQL, 저장 프로시저 그리고 몇 가지 고급 매핑을 지원하는 퍼시스턴스 프레임워크입니다.
		마이바티스는 JDBC로 처리하는 상당 부분의 코드와 파라미터 설정 및 결과 매핑을 대신해준다.
		마이바티스는 데이터베이스 레코드에 원시타입과 Map 인터페이스 그리고 자바 POJO를 설정해서
		매핑하기 위해 xml과 어노테이션을 사용할 수 있습니다.
		
		*** POJO란?
		- POJO(Plain Old Java Object)란, 순수한 자바 객체를 말한다.
		
			그리고, 객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트
			객체지향원리를 기반으로 설계된 프로젝트(getter/setter를 이용한 VO들)
			
	namespace 항목은 쿼리가 여러 개일 때 이름 공간(namespace)을 분리하는 역할을 한다.
	쿼리 xml 파일은 보통 여러 개 생성되기 때문에 이를 구별하는 용도로 사용한다.
-->
<mapper namespace="Book">

	<!--  
		데이터 입력 sql 쿼리 작성방법
		insert into 테이블명(컬럼1, 컬럼2 ...) values (값1, 값2 ...)
		id항목은 namespace 안에서 쿼리를 구분하는 유일한 식별자 역할을 한다.
		parameterType은 쿼리에 적용할 파라미터 타입(현재 Map타입 사용)
		useGeneratedKeys와 keyProperty는 하나의 쌍으로 작성된다.
		useGeneratedKeys가 true로 설정되면 mybatis는 insert 쿼리 실행 후 생성된 pk를 파라미터 객체의 keyProperty 속성에 넣어준다.
		useGeneratedKeys : 시퀀스로 자동 증가된 번호값을 가져올 것인지에 대한 여부 설정
		keyProperty : 여부 true 설정 시, 어떤 필드 값으로 받을 것인지 설정
		
		mybatis는 쿼리를 실행할 때 파라미터를 치환하는데 #{title}은 파라미터로 입력된 키를 값으로 치환한다.
	-->
	<insert id="insert" parameterType="hashMap" useGeneratedKeys="true">
		<selectKey keyProperty="book_id" resultType="int" order="BEFORE">
			select seq_book.nextval from dual
		</selectKey>
		insert into book(
			book_id, title, category, price
		) values (
			#{book_id}, #{title}, #{category}, #{price}
		)
	</insert>

</mapper>

[BookInsertController.java]

package kr.or.ddit.book.web;

import java.util.Map;

import javax.inject.Inject;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import kr.or.ddit.book.service.IBookService;

/*
 * @Controller 어노테이션이 있는 클래스는 스프링이 브라우저의 요청(request)을 받아들이는 컨트롤러라고 인지해서
 * 자바 빈(Java Bean)으로 등록해서 관리한다.
 */
@Controller
@RequestMapping("/book")
public class BookInsertController {
	
//	private IBookService service = new BookServiceImpl();
	
	// DI : 의존성 주입, @Autowired도 병행하여 사용
	/*
	 * 서비스를 호출하기 위해 BookService를 의존성 주입한다.
	 * 의존성 주입을 통한 결합도 낮추기
	 */
	@Inject
	private IBookService service;
	
	/*
	 * @RequestMapping
	 * - 요청 URL을 어떤 메소드가 처리할 지 여부를 결정한다.
	 * 	> 클래스 라인에 들어있다면 시작 URL을 처리한다.
	 * 	> 메소드 라인에 들어있다면 최종 목적지 URL을 처리한다.
	 * 
	 * method 속성은 http 요청 메소드를 의미한다.
	 * 일반적인 웹 페이지 개발에서 GET 메소드는 데이터를 변경하지 않는 경우에, POST 메소드는 데이터가 변경될 때 사용된다.
	 * 
	 * ModelAndView는 컨트롤러가 반환할 데이터를 담당하는 모델(Model)과 화면을 담당하는 뷰(View)의 경로를 합쳐놓은 객체이다.
	 * ModelAndView의 생성자에 문자열 타입 파라미터가 입력되면 뷰의 경로라고 간주한다.
	 * 
	 * 뷰의 경로를 'book/form'과 같은 형태로 전달하는 이유는 요청(request)에 해당하는 url의 mapping되는 화면의 경로 값을 ViewResolver라는 녀석이
	 * 제일 먼저 받게 된다. 받아서 suffix, prefix 속성에 의해서 앞에는 'WEB-INF/views/'를 붙이고 뒤에는 '.jsp'를 붙여 최종 위치에 해당하는
	 * jsp 파일을 찾아준다.
	 */

	// bookForm() 메서드는 '/book/form.do'를 받는 최종 목적지이다.
	// 이 때, return 으로 나가는 정보가 'book/form' 이라는 페이지 정보를 리턴한다.
	// 문자열로 이뤄진 페이지 정보기 때문에 리턴 타입을 String으로 설정한 것이다.
	// 페이지 정보를 리턴하는 방법은 여러 가지가 존재한다.
	// - 문자열 그대로를 리턴하는 String, 문자열을 리턴타입으로 설정
	// - ModelAndView 객체를 이용한 리턴 설정
	@RequestMapping(value="/form.do", method = RequestMethod.GET)
	public ModelAndView bookForm() {
		return new ModelAndView("book/form"); // 기본적으로 포워드 방식
	}
	
	/*
	 * 데이터의 변경이 일어나므로 http메소드는 POST방식으로 처리
	 * 어노테이션(@) RequestParam은 HTTP 파라미터를 map 변수에 자동으로 바인딩한다.
	 * Map타입의 경우는 어노테이션(@) RequestParam을 붙여야만 HTTP 파라미터 값을 map에 바인딩해준다.
	 */
	@RequestMapping(value="/form.do", method = RequestMethod.POST)
	public ModelAndView bookInsert(@RequestParam Map<String, Object> map) {
		ModelAndView mav = new ModelAndView();
		
		// 서비스 메소드 insertBook을 호출한다.
		// 서비스에서 bookId를 리턴받는다 (책 등록 후 얻어온 최신 책 ID)
		String bookId = service.insertBook(map);
		if(bookId == null) {
			// 데이터 입력이 실패할 경우 다시 데이터를 입력받아야 하므로 생성 화면으로 redirect 한다.
			// ModelAndView 객체는 .setViewName 메소드를 통해 뷰의 경로를 지정할 수 있다.
			mav.setViewName("redirect:/book/form.do"); // 리다이렉트 방식
			// 뷰의 경로가 redirect:로 시작하면 스프링은 뷰 파일을 찾아가는 것이 아니라 웹 페이지의 주소(/book/form.do)를 변경한다.
		}else {
			// 데이터 입력이 성공하면 상세 페이지로 이동한다.
			mav.setViewName("redirect:/book/detail.do?bookId=" + bookId); // 상세보기 페이지로 아이디 값을 가지고 이동
		}
		
		return mav;
	}
	
}

@RequestParam으로 Map을 처리해야 파라미터 값이 들어온다.
마이바티스에서 book_id를 가져오는 처리를 따로 해줬다...
DB에도 잘 반영되었다.


 

'대덕인재개발원_웹기반 애플리케이션' 카테고리의 다른 글

231120_SPRING 1 (4)  (0) 2023.11.20
231117_SPRING 1 (3)  (0) 2023.11.17
231115_SPRING 1 (1)  (0) 2023.11.15
231114_JSP 개론 14  (1) 2023.11.14
231113_JSP 개론 13  (0) 2023.11.13