라즈베리파이반

라즈베리파이 등 컴퓨터계열 게시판입니다.

제목스프링 레거시로 게시판 만들기 (4) : Service, Controller 작성2022-10-23 10:18
작성자user icon Level 4
첨부파일service_controller.zip (7.6KB)

88x31.png


1. 서비스(Service) 작성


서비스에서는 DAO의 메소드를 호출하여 비즈니스 로직을 수행합니다. 


1) 서비스 스캔 등록


우선 서비스를 스캔할 수 있도록 스프링의 root-context.xml 파일에 서비스 컴포넌트 스캔 경로를 추가해줍시다. 아래 예시의 경우 kro.rubisco.service 패키지를 컴포넌트 스캔 경로로 등록했습니다. 


/src/main/webapp/WEB-INF/spring/root-context.xml 

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/beans 

https://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context 

http://www.springframework.org/schema/context/spring-context-4.3.xsd">

<!-- Root Context: defines shared resources visible to all other web components -->

<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" name="dataSource">

  <property value="oracle.jdbc.driver.OracleDriver" name="driverClassName"/>

  <property value="[DB 주소]" name="url"/>

  <property value="[DB 아이디]" name="username"/>

  <property value="[DB 암호]" name="password"/>

</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 

<property name="dataSource" ref="dataSource" />

<property name="configLocation" value="classpath:/mybatis-config.xml"></property>

<property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml"></property>

</bean>


<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">

<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>

</bean>

<context:component-scan base-package="kro.rubisco.service"></context:component-scan>

</beans>


2) 서비스 인터페이스 작성


이제 서비스의 인터페이스를 작성하겠습니다. Board, Category, Comment, Group, Member에 대하여 CRUD 기능을 넣었습니다. DAO와 유사합니다.


/kro/rubisco/service/BoardService.java 

package kro.rubisco.service;


import java.util.List;


import kro.rubisco.dto.BoardDTO;


public interface BoardService {


  public void regist(BoardDTO board) throws Exception;


  public BoardDTO read(Long documentId) throws Exception;


  public void modify(BoardDTO board) throws Exception;


  public void remove(Long documentId) throws Exception;


  public List<BoardDTO> listAll() throws Exception;

}


/kro/rubisco/service/CategoryService.java 

package kro.rubisco.service;


import java.util.List;


import kro.rubisco.dto.CategoryDTO;


public interface CategoryService {


  public void regist(CategoryDTO category) throws Exception;


  public CategoryDTO read(Long categoryId) throws Exception;


  public void modify(CategoryDTO category) throws Exception;


  public void remove(Long categoryId) throws Exception;


  public List<CategoryDTO> listAll() throws Exception;

}


/kro/rubisco/service/GroupService.java 

package kro.rubisco.service;


import java.util.List;


import kro.rubisco.dto.GroupDTO;


public interface GroupService {


  public void regist(GroupDTO group) throws Exception;


  public GroupDTO read(Long groupId) throws Exception;


  public void modify(GroupDTO group) throws Exception;


  public void remove(Long groupId) throws Exception;


  public List<GroupDTO> listAll() throws Exception;

}


/kro/rubisco/service/MemberService.java 

package kro.rubisco.service;


import java.util.List;


import kro.rubisco.dto.MemberDTO;


public interface MemberService {

  public void regist(MemberDTO member) throws Exception;


  public MemberDTO read(Long memberId) throws Exception;


  public void modify(MemberDTO member) throws Exception;


  public void remove(Long memberId) throws Exception;


  public List<MemberDTO> listAll() throws Exception;

}


3) 서비스 구현


이제 서비스 인터페이스의 구현체를 작성합니다. @Service 어노테이션을 통해 서비스 컴포넌트임을 선언하고, sqlSession에 DAO 인터페이스를 연결하여 DAO 구현체를 생성자 주입합니다.


DAO 구현체를 통해 CRUD 쿼리를 실행시킬 수 있으며, @Transactional 어노테이션을 통해 트랜잭션 처리를 할 수 있습니다.


/kro/rubisco/service/impl/BoardServiceImpl.java 

package kro.rubisco.service.impl;


import java.util.List;


import org.apache.ibatis.session.SqlSession;

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

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import kro.rubisco.dao.BoardDAO;

import kro.rubisco.dto.BoardDTO;

import kro.rubisco.service.BoardService;


@Service

@Transactional(readOnly = true)

public class BoardServiceImpl implements BoardService {


private final BoardDAO boardDAO;

@Autowired

public BoardServiceImpl(SqlSession sqlSession) {

this.boardDAO = sqlSession.getMapper(BoardDAO.class);

}

@Override

public void regist(BoardDTO board) throws Exception {

boardDAO.create(board);

}


@Override

public BoardDTO read(Long documentId) throws Exception {

return boardDAO.read(documentId);

}


@Override

public void modify(BoardDTO board) throws Exception {

boardDAO.update(board);

}


@Override

public void remove(Long documentId) throws Exception {

boardDAO.delete(documentId);

}


@Override

public List<BoardDTO> listAll() throws Exception {

return boardDAO.listAll();

}


}


/kro/rubisco/service/impl/CategoryServiceImpl.java 

package kro.rubisco.service.impl;


import java.util.List;


import org.apache.ibatis.session.SqlSession;

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

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import kro.rubisco.dao.CategoryDAO;

import kro.rubisco.dto.CategoryDTO;

import kro.rubisco.service.CategoryService;


@Service

@Transactional(readOnly = true)

public class CategoryServiceImpl implements CategoryService {


private final CategoryDAO categoryDAO;

@Autowired

public CategoryServiceImpl(SqlSession sqlSession) {

this.categoryDAO = sqlSession.getMapper(CategoryDAO.class);

}

@Override

public void regist(CategoryDTO category) throws Exception {

categoryDAO.create(category);

}


@Override

public CategoryDTO read(Long categoryId) throws Exception {

return categoryDAO.read(categoryId);

}


@Override

public void modify(CategoryDTO category) throws Exception {

categoryDAO.update(category);

}


@Override

public void remove(Long categoryId) throws Exception {

categoryDAO.delete(categoryId);

}


@Override

public List<CategoryDTO> listAll() throws Exception {

return categoryDAO.listAll();

}


}


/kro/rubisco/service/impl/CommentServiceImpl.java 

package kro.rubisco.service.impl;


import java.util.List;


import org.apache.ibatis.session.SqlSession;

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

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import kro.rubisco.dao.CommentDAO;

import kro.rubisco.dto.CommentDTO;

import kro.rubisco.service.CommentService;


@Service

@Transactional(readOnly = true)

public class CommentServiceImpl implements CommentService {


private final CommentDAO commentDAO;

@Autowired

public CommentServiceImpl(SqlSession sqlSession) {

this.commentDAO = sqlSession.getMapper(CommentDAO.class);

}

@Override

public void regist(CommentDTO comment) throws Exception {

commentDAO.create(comment);

}


@Override

public CommentDTO read(Long commentId) throws Exception {

return commentDAO.read(commentId);

}


@Override

public void modify(CommentDTO comment) throws Exception {

commentDAO.update(comment);

}


@Override

public void remove(Long commentId) throws Exception {

commentDAO.delete(commentId);

}


@Override

public List<CommentDTO> listAll() throws Exception {

return commentDAO.listAll();

}


}


/kro/rubisco/service/impl/GroupServiceImpl.java 

package kro.rubisco.service.impl;


import java.util.List;


import org.apache.ibatis.session.SqlSession;

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

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import kro.rubisco.dao.GroupDAO;

import kro.rubisco.dto.GroupDTO;

import kro.rubisco.service.GroupService;


@Service

@Transactional(readOnly = true)

public class GroupServiceImpl implements GroupService {

private final GroupDAO groupDAO;

@Autowired

public GroupServiceImpl(SqlSession sqlSession) {

this.groupDAO = sqlSession.getMapper(GroupDAO.class);

}


@Override

public void regist(GroupDTO group) throws Exception {

groupDAO.create(group);

}


@Override

public GroupDTO read(Long groupId) throws Exception {

return groupDAO.read(groupId);

}


@Override

public void modify(GroupDTO group) throws Exception {

groupDAO.update(group);

}


@Override

public void remove(Long groupId) throws Exception {

groupDAO.delete(groupId);

}


@Override

public List<GroupDTO> listAll() throws Exception {

return groupDAO.listAll();

}


}


/kro/rubisco/service/impl/MemberServiceImpl.java 

package kro.rubisco.service.impl;


import java.util.List;


import org.apache.ibatis.session.SqlSession;

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

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import kro.rubisco.dao.MemberDAO;

import kro.rubisco.dto.MemberDTO;

import kro.rubisco.service.MemberService;


@Service

@Transactional(readOnly = true)

public class MemberServiceImpl implements MemberService {


private final MemberDAO memberDAO;

@Autowired

public MemberServiceImpl(SqlSession sqlSession) {

this.memberDAO = sqlSession.getMapper(MemberDAO.class);

}

@Override

public void regist(MemberDTO member) throws Exception {

memberDAO.create(member);

}


@Override

public MemberDTO read(Long memberId) throws Exception {

return memberDAO.read(memberId);

}


@Override

public void modify(MemberDTO member) throws Exception {

memberDAO.update(member);

}


@Override

public void remove(Long memberId) throws Exception {

memberDAO.delete(memberId);

}


@Override

public List<MemberDTO> listAll() throws Exception {

return memberDAO.listAll();

}


}



2. 컨트롤러(Controller) 작성


컨트롤러에서는 사용자의 요청을 받아 서비스를 호출하고 DispatcherServlet에 ModelAndView를 반환합니다.


1) 루트 경로 변경 


우선 톰캣의 루트 경로를 수정합니다. 왼쪽 하단에 톰캣 서버를 더블클릭하세요.


 


Modules 탭을 선택하고 Edit... 버튼을 클릭합니다.



Path를 /로 설정하고 OK버튼을 클릭 후 서버를 재시작합니다.


 


2) 컨트롤러 스캔 등록


컨트롤러를 스캔할 수 있도록 스프링의 servlet-context.xml 파일에 컨트롤러 컴포넌트 스캔 경로를 추가해줍시다. 아래 예시의 경우 kro.rubisco.controller 패키지를 컴포넌트 스캔 경로로 등록했습니다.


/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml 

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/mvc"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:beans="http://www.springframework.org/schema/beans"

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd

http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->

<!-- Enables the Spring MVC @Controller programming model -->

<annotation-driven />


<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->

<resources mapping="/resources/**" location="/resources/" />


<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<beans:property name="prefix" value="/WEB-INF/views/" />

<beans:property name="suffix" value=".jsp" />

</beans:bean>

<context:component-scan base-package="kro.rubisco.controller" />


</beans:beans>


3) 필터 추가


한글 깨짐 문제를 해결하고 get, post 이외의 전송방식을 구현하기위해 web.xml 파일에 encodingFilterhttpMethodFilter를 추가합니다.


/src/main/webapp/WEB-INF/web.xml 

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">


<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/spring/root-context.xml</param-value>

</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<!-- Processes application requests -->

<servlet>

<servlet-name>appServlet</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>appServlet</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>

<filter>

<filter-name>encodingFilter</filter-name>

<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

<init-param>

<param-name>encoding</param-name>

<param-value>UTF-8</param-value>

</init-param>

</filter>

<filter>

<filter-name>httpMethodFilter</filter-name>

<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>encodingFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

<filter-mapping>

<filter-name>httpMethodFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

</web-app>


4) 매퍼 수정


게시글이 입력되면 id값이 BoardDTO에 반환될 수 있도록 boardMapperinsert 태그 아래에 selectKey 태그를 추가해줍니다.


boardMapper.xml 

...


<insert id="create">

insert into board (category_id, is_notice, title, content, member_id)

values (#{categoryId}, #{isNotice, typeHandler=kro.rubisco.config.YNTypeHandler}, #{title}, #{content}, #{memberId})

<selectKey keyProperty="documentId" resultType="Long" order="AFTER">

select max(document_id) as document_id from board

</selectKey>

</insert>

...


order 속성이 AFTER 이므로, insert 쿼리가 수행된 후에 selectKey 쿼리를 수행하여 BoardDTO의 documentId에 결과값을 반환합니다.


5) 컨트롤러 구현


BoardController, CommentController, MemberController, AdminController를 작성하도록 하겠습니다.


/kro/rubisco/controller/BoardController.java 

package kro.rubisco.controller;


import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.DeleteMapping;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PatchMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;


import kro.rubisco.dto.BoardDTO;

import kro.rubisco.service.BoardService;

import kro.rubisco.service.CategoryService;

import lombok.RequiredArgsConstructor;


@Controller

@RequiredArgsConstructor

@RequestMapping("/board")

public class BoardController {


private final BoardService boardService;

private final CategoryService categoryService;

@GetMapping()

public String getBoardListView(Model model) throws Exception {

model.addAttribute("boardList", boardService.listAll());

return "board/getBoardList";

}

@GetMapping("/{documentId}")

public String getBoardView(

@PathVariable("documentId") Long documentId, 

Model model

) throws Exception {

model.addAttribute("board", boardService.read(documentId));

return "board/getBoard";

}

@GetMapping(params = "act=write")

public String getInsertBoardView(

@RequestParam(value="documentId", required=false) Long documentId,

Model model

) throws Exception {

model.addAttribute("board", documentId == null ? new BoardDTO() : boardService.read(documentId));

model.addAttribute("categoryList", categoryService.listAll());

return "board/insertBoard";

}

@PostMapping()

public String insertBoard(BoardDTO board) throws Exception {

boardService.regist(board);

return "redirect:/board/" + board.getDocumentId();

}

@PatchMapping(params = "documentId")

public String updateBoard(BoardDTO board) throws Exception {

boardService.modify(board);

return "redirect:/board/" + board.getDocumentId();

}

@DeleteMapping(params = "documentId")

public String deleteBoard(

@RequestParam("documentId") Long documentId

)throws Exception {

boardService.remove(documentId);

return "redirect:/board";

}

}


/kro/rubisco/controller/CommentController.java 

package kro.rubisco.controller;


import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.DeleteMapping;

import org.springframework.web.bind.annotation.PatchMapping;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestMapping;


import kro.rubisco.dto.CommentDTO;

import kro.rubisco.service.CommentService;

import lombok.RequiredArgsConstructor;


@Controller

@RequiredArgsConstructor

@RequestMapping("/comment")

public class CommentController {

    

    private CommentService commentService;

    

    @PostMapping(params = "commentId")

    public String insertComment(CommentDTO comment) throws Exception {

        commentService.regist(comment);

        return "redirect:/board/" + comment.getDocumentId();

    }

    

    @PatchMapping(params = "commentId")

    public String updateComment(CommentDTO comment) throws Exception {

        commentService.modify(comment);

        return "redirect:/board/" + comment.getDocumentId();

    }

    

    @DeleteMapping(params = "commentId")

    public String deleteComment(CommentDTO comment) throws Exception {

        commentService.remove(comment.getCommentId());

        return "redirect:/board/" + comment.getDocumentId();

    }

}


/kro/rubisco/controller/MemberController.java 

package kro.rubisco.controller;


import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.DeleteMapping;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PatchMapping;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestMapping;


import kro.rubisco.dto.MemberDTO;

import kro.rubisco.service.MemberService;

import lombok.RequiredArgsConstructor;


@Controller

@RequiredArgsConstructor

@RequestMapping("/member")

public class MemberController {


private final MemberService memberService;

@GetMapping()

public String getMemberInfo() {

    return "member/getMemberInfo";

}

@GetMapping(params="act=signUp")

public String getSignUpView() throws Exception {

return "member/signUpForm";

}

@PostMapping()

public String insertMember(MemberDTO member) throws Exception {

    memberService.regist(member);

    return "redirect:/";

}

@PatchMapping()

public String updateMember(MemberDTO member) throws Exception {

    memberService.modify(member);

    return "redirect:/member";

}

@DeleteMapping()

public String deleteMember() throws Exception {

    return "redirect:/";

}

}


/kro/rubisco/controller/AdminController.java 

package kro.rubisco.controller;


import java.util.List;


import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PatchMapping;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestMapping;


import kro.rubisco.dto.CategoryDTO;

import kro.rubisco.dto.GroupDTO;

import kro.rubisco.service.CategoryService;

import kro.rubisco.service.GroupService;

import lombok.RequiredArgsConstructor;


@Controller

@RequiredArgsConstructor

@RequestMapping("/admin")

public class AdminController {


    private final CategoryService categoryService;

    private final GroupService groupService;

    

    @GetMapping("/category")

    public String getCategoryListView(Model model) throws Exception {

        List<CategoryDTO> categoryList = categoryService.listAll();

        model.addAttribute("categoryList", categoryList);

        return "category/getCategoryList";

    }

    

    @PostMapping("/category")

    public String insertCategory(CategoryDTO category) throws Exception {

        categoryService.regist(category);

        return "redirect:/admin/category";

    }

    

    @PatchMapping("/category")

    public String updateCategory(CategoryDTO category) throws Exception {

        categoryService.modify(category);

        return "redirect:/admin/category";

    }

    

    @GetMapping("/group")

    public String getGroupListView(Model model) throws Exception {

        List<GroupDTO> groupList = groupService.listAll();

        model.addAttribute("groupList", groupList);

        return "group/getGroupList";

    }

    

    @PostMapping("/group")

    public String insertGroup(GroupDTO group) throws Exception {

        groupService.regist(group);

        return "redirect:/admin/group";

    }

    

    @PatchMapping("/group")

    public String updateCategory(GroupDTO group) throws Exception {

        groupService.modify(group);

        return "redirect:/admin/group";

    }

}

#스프링 레거시# Service# Controller
댓글
자동등록방지
(자동등록방지 숫자를 입력해 주세요)