03. Spring 웹 사이트 만들어보기!(feat. 스프링 MVC 하루만에 배우기)

min seung moon·2021년 5월 4일
2

1. 책입력 기능 만들기

01. 개요

  • 책 정보를 입력하기 위한 기능을 만든다
  • 생성 화면 /create에서 제목, 분류, 가격을 입력하고 저장 버튼을 클릭하면 서버가해야 하는 일을 정의하는 것이다

02. 책 매퍼 XML 생성

  • 쿼리를 작성하는 XML파일을 만든다
  • 마이바티스는 XML에 설정된 쿼리를 읽어서 실행시키게 된다





<?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="book"></mapper>
  • namespace 항목은 쿼리가 여러 개일 때 이름 공간을 분리하는 역할을 한다
  • 쿼리 .XML 파일은 보통 여러개 생성되기 때문에 이를 구별하는 용도로 사용

03. 책 입력 기능 쿼리 작성

  • 데이터 입력 SQL
INSERT INTO 테이블이름(키1, 키2) values(키1값, 키2값)
  • 쿼리를 매퍼 XML에 옮긴다, <mapper namespace="book">코드와</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="book">
<insert id="insert" parameterType="hashMap" useGeneratedKeys="true" keyProperty="book_id">
	<![CDATA[
	insert into book
	(title, category, price)
	values
	(#{title}, #{category}, #{price})
	]]>
</insert>
</mapper>
  • <insert 태그는 MYBatis에서 데이터 입력(insert)을 나타내는 태그다.
  • id 항목은 네임스페이스 안에서 쿼리를 구분하는 유일한 식별자 역할을 한다
  • parameterType은 쿼리에 적용할 파라미터 타입이다, 우리는 Map 타입을 이용한ㄷ
  • useGeneratedKeys와 keyProperty는 한 쌍이다, useGeneratedKey가 true로 설정되면 마이바티스는 insert 쿼리 실행 후 생성된 PK를 파라미터 객체의 keyProperty속성에 넣어준다.
  • <![CDATA[ 항목은 원리(Raw) 문자열을 나타낸다. <![CDATA[안에 있는 문자열은 <등의 태그 문자가 있더라도 태그로 인식하지 않는다. ]]>로 원시 문자열을 끝낸다
  • 마이바티스는 쿼리가 실행될 때 파라미터를 치환한다
  • #{title}은 파라미터로 입력된 키를 값으로 치환한다, 예를 들어서 파라미터 map.get("title) == "제목" 형태가 마이바티스 쿼리 XML에 전달되면 MyBatis는 #{title}을 '제목'으로 자동 변환한다
  • (#{title}, #{category}, #{price}) 이 코드가 ('제목', '분류', '가격')으로 치환된다

04. 책 DAO 클래스 생성

  • 매퍼 XML을 실행히키는 DAO(Data Access Object) 클래스를 생성한다
  • src/main/java 하위 항목 smaple.spring.yse 패키지를 우클릭해서 BookDao 클래스를 만들면 된다


package sample.spring.yse;

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

@Repository
public class BookDao {
	@Autowired
	SqlSessionTemplate sqlSessionTemplate;
}
  • 클래스 정의 위 @Repository 어노테이션을 붙여서 데이터에 접근하는 클래스임을 명시
  • @Repository 어노테이션이 있는 클래스는 스프링이 데이터를 관리하는 클래스라고 인지해서 자바빈으로 등록해서 관리
  • 매퍼 XML을 실행시키기 위해서 SqlSessionTemplate 객체를 멤버 변수로 선언
  • SqlSessionTemplate 선언 위에 @Autowired 어노테이션을 붙여서 sqlSessionTemplate 객체를 사용할 수 있게한다
    • SqlSessionTemplate 객체는 new 키워드를 통해 직접 생성하지 않는다, 대신 의존성 주입(DI - Dependency Injection)을 통해 주입 받는다
    • @Autowired 어노테이션을 붙이면 의존성을 주입하라는 뜻
    • 스프링은 미리 만들어 놓은 sqlSessionTemplate 타입 객체를 BookDao 객체에 주입한다
    • 개발자가 직접 sqlSessionTemplate 객체를 생성하는 일 없이 곧바로 사용할 수 있다
    • 참고로 SqlSessionTemplate 객체는 root-context.xml에서 정의해둔 객체다
    • 스프링은 서버가 시작할 때 미리 XML을 읽어서 객체를 인스턴스화 해둔다
    <bean id="sqlSessionTemplate"
    • 멤버변수(필드)위에 @Autowired 어노테이션으로 의존성 주입하는 방식을 필드 인젝션(Field Injection)이라고 부른다

05. 책 입력 기능 DAO 메소드 생성

  • 책 데이터 입력 쿼리를 실행하는 DAO 메소드를 만든다
package sample.spring.yse;

import java.util.Map;

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

@Repository
public class BookDao {
	@Autowired
	SqlSessionTemplate sqlSessionTemplate;
	
	public int insert(Map<String, Object> map) {
		return this.sqlSessionTemplate.insert("book.insert", map);
	}
}
  • sqlSessionTemplate.insert 메소드의 첫번째 파라미터는 SQL Mapper의 id다.
  • book_SQL.xml 파일에서 namespace="book", id="insert"였다
    • MyBatis는 네임스페이스+id 조합으로 쿼리를 찾아서 실행한다
    • 따라서 book.insert가 매퍼 쿼리 이름이 된다
    • 참고로 매퍼 SQL 구조는 아래와 같다
  • sqlSessionTemplate.insert 메소드의 두번째 인수는 쿼리에 전달할 데이터이다
    • 예제에서는 Map 타입 데이터 map을 전달했다
    • map은 insert(Map map) 메소드가 전달받은 파라미터이므로 전달받은 파라미터를 그대로 매퍼 XML에 전달한 셈이다
  • 쿼리가 실행되고 나면 파라미터로 전달된 map 객체에 book 테이블의 PK인 book_id 항목이 생긴다
    • 쿼리가 실행되기 전의 테이터는 이런식으로 담겨져있었다고 하자
    {
    	"title" : "제목",
      	"category" : "IT",
          	"price" : 10000
    }
    • 쿼리가 실행된 후의 데이터는 이렇게 된다
    {
    	"title" : "제목",
      	"category" : "IT",
          	"price" : 10000
            "book_id" : 1
    }
    • 이 설정은 매퍼SQL에서 결정했다
  • sqlSessionTemplate.insert 메소드의 반환ㄱ밧은 쿼리의 영향을 받은 행수(row count)다
    • ISERT 쿼리의 경우 성공하면 1개의 행(row)이 생기므로 1, 실패하면 0이 된다
    • BookDao 클래스의 insert 메소드는 XML 매퍼가 반환한 값을 바로 리턴한다

06. 책 서비스 클래스 생성

  • 서비스 클래스는 비즈니스 클래스가 위치하는 곳이다
  • Spring MVC 구조에서 서비스 클래스는 컨트롤러와 DAO를 연결하는 역할을 한다


package sample.spring.yse;

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

@Service
public class BookServiceImpl implements BookService {
	@Autowired
	BookDao bookDao;
}
  • @Service 어노테이션을 붙여서 스프링 서비스 클래스임을 알려준다
  • BookService 인터페이스 사용을 선언한다
    • 일반적으로 서비스 레이어는 인터페이스와 클래스를 함께 사용한다
    • 스프링은 직접 클래스를 생성하는 것을 지양하고 인터페이스를 통해 접근하는 것을 권장하는 프레임워크 때문에
    • 물론 아직 BookService 인터페이스는 생성되지 않은 상태이기 때무에 오류가 난다
    • 일단 클래스를 생성을 완료하고 인터페이스를 생성하겠다
  • 데이터베이스 접근을 위해 BookDao 인스턴스를 주입받는다
  • 클래스 이름이 Impl로 끝나는 것은 Implement의 약자로 관습에 가깝다
    • 인터페이스와 클래스를 함께 사용하는 경우 접미어로 Impl을 붙여두면 클래스인지 인터페이스인지구별하기가 쉬워진다

07. 책 서비스 인터페이스 생성

  • 책 서비스 클래스에서 선언한 BookService 인터페이스를 생성
  • 서비스 인터페이스는 직접 탐색기에서 생성하지 않고 클래스에서 생성하는 방법을 취한다
  • 서비스 클래스 BookServiceInpl.java의 BookService 부분에 빨간줄이 가있을것이다
    • 이후 팜업 메뉴가 나오면 create interface 'BokkService' 항목을 클릭
    • 새 팝업에서 Source folder를 확인하고 Finish 버튼을 누른다
    • 예제에서는 서비스 클래스와 같은 패키지 안에 둘 것이므로 경로는 수정하지 않는다


08. 책 입력 기능 서비스 클래스 메소드 생성

  • BookDao.insert 메소드를 실행시키는 서비스 메소드를 작성한다
  • 서비스 메소드는 BookServiceImpl 클래스에 작성하면 된다
package sample.spring.yse;

import java.util.Map;

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

@Service
public class BookServiceImpl implements BookService {
	@Autowired
	BookDao bookDao;
	
	@Override
	public String create(Map<String, Object> map) {
		int affectRowCount = this.bookDao.insert(map);
		if(affectRowCount == 1) {
			return map.get("book_id").toString();
		}
		return null;
	}
}
  • bookDao.insert 메소드를 실행시킨다
  • affectRowCount 변수에는 영향 받은 행수가 담긴다, insert 구문은 입력이 성공합면 1, 실패하면 0을 반환
  • 입력에 성공한 경우 map 인스턴스에 book 테이블의 PK인 book_id가 담겨져 있을것이다, book_id 값을 리턴
  • 실패할 경우 null을 리턴해서 실했음을 호출한 곳에 알린다

09. 책 입력 기능 서비스 인터페이스 메소드 시그니쳐 생성

  • @Ovjerride 어노테이션은 자바에서 기본으로 내장된 어노테이션으로 상위 인터페이스에 정의된 것을 재정의(Override) 한다는 뜻이다
  • 이클립스에서 상위 인터페이스에 메소드 시그니쳐가 없을 경우 오류를 표시해 주면서 자동으로 인터페이스 시그니쳐를 만들어주는 기능이 있다
  • 이 기능을 이용하기 위해 @Override 어노테이션을 사용
  • 서비스 메소드 빨간 줄에 마우스를 올리고 create 'create' in super type 'BookService'를 클릭한다

10. 책 입력 기능 컨트롤러 서비스 빈 추가

  • 책입력 기능 서비스를 호출하기 위햇 서비스 빈을 추가한다
  • 클래스 선언 public class BookController { 바로 아래에 추가하면 된다
  • 서비스를 호출하기 위해 BookService를 의존성을 주입한다
    • 이 때 BookService 인터페이스가 사용되었음을 주의하자

11. 책 입력 기능 컨트롤러 메소드 추가

  • 서비스를 이용해 책을 입력하는 컨트롤러 메소드를 만든다
package sample.spring.yse;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
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;

@Controller
public class BookController {
	@Autowired
	BookService bookservice;
	
	@RequestMapping(value = "/create", method = RequestMethod.GET)
	public ModelAndView create() {
		return new ModelAndView("book/create");
	}
	
	@RequestMapping(value = "/create", method = RequestMethod.POST)
	public ModelAndView createPost(@RequestParam Map<String, Object> map) {
		ModelAndView mav = new ModelAndView();
		
		String bookId = this.bookservice.create(map);
		if (bookId == null) {
			mav.setViewName("redirect:/create");
		}else {
			mav.setViewName("redirect:/detail?BookId="+bookId);
		}
		
		return mav;
	}
}
  • 데이터 변경(입력)이 얼어나므로 http 메소드는 POST 방식으로 처리한다
  • @RequestParam 어노테이션을 통해 HTTP 파라미터를 map 변수에 자동으로 바인딩한다.
    • HTTP 파라미터는 브라우저에서 서버로 전달하는 데이터를 말한다
    • 책을입력할 때는 제목, 분류, 가격 등의 정보가 서버로 전달되는데, 이런 것을 HTTP 파라미터라고 한다
  • 스프링은 객체 타입이나 스칼라 타입일 경우 특별한 어노테이션 없이도 HTTP 파라미터를 자바 메소드의 파라미터로 변환해서 컨트롤러 메소드를 호출한다
    • 하지만 Map 타입의 경우는 예외여서 @RequestParam 어노테이션을 붙여야만 HTTP 파라미터의 값을 map 안에 바인딩해 준다
  • 서비스 메소드를 호출
  • 데이터 입력이 실패일 경우 다시 데이터를 입력받아야 하므로 생성 화면으로 리다이렉트 한다
    • ModelAndView 객체는 setViewName 메소드를 통해 뷰의 경로를 지정할 수 있다
    • 만약 뷰의 경로가 redirect:로 시작하면 스프링은 뷰 파일을 찾아가는 것이 아니라 웹 페이지의 주소를 변경한다
    • 위 코드는 /create 경로로 웹페이지의 주소를 변경한다
  • 데이터 입력이 성공하면 상세 페이지로 이동한다
  • 브라우저에서 http://localhost:8080/create에 접속해 실제로 데이터를 입력해본다!
  • 아직 상세 페이지는 만들지 ㅇ낳았기 때문에 데이터가 입력되더라도 브라우저에서는 http 404 not found 오류가난다
  • 데이터 입력이 성공했는지 확인!
profile
아직까지는 코린이!

0개의 댓글