Spring Test & Project Setting

Let's Just Go·2022년 7월 12일
0

Spring

목록 보기
9/26

Spring

Board Test

Mapper

  • Mapper.xml
    • Mapper를 작성하여 DataBase와 Spring이 정상적으로 동작하는지 Test 진행

      <?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.spring.mvc.board.repository.IBoardMapper">
      
      	<!-- DB와 VO객체 간의 변수명이 다를 수 있으므로 해당 변수명 맞춰주는 작업 
      		 property는 VO 객체의 변수명 column은 DB 변수명  -->
      	<resultMap type="com.spring.mvc.board.model.BoardVO" id="BoardMap">
      		<id property="boardNo" column="board_no" />
      		<!-- pk라서 id 태그로 작성 -->
      		
      		<result property="regDate" column="reg_date"/>
      		<result property="viewCnt" column="view_cnt"/>
      		<!-- pk가 아닌 데이터는 result 태그로 진행 -->
      		
      	</resultMap>
      	
      	<insert id="insert">
      		<!-- IBoardMapper와 연동하여 DB접근하여 DML 수행   -->
      		INSERT INTO mvc_board 
      		(board_no, title, content, writer)
      		VALUES(board_seq.NEXTVAL, #{title}, #{content}, #{writer})		
      	</insert>
      	
      	<select id="getArticleList" resultMap="BoardMap">
      		SELECT * FROM mvc_board ORDER BY board_no ASC
      	</select>
      	
      	<select id="getArticle" resultMap="BoardMap">
      		SELECT * FROM mvc_board WHERE board_no = #{num}
      	</select>
      	
      	<update id="updateArticle">
      		UPDATE mvc_board SET title = #{title}, content = #{content} WHERE board_no = #{boardNo}
      	</update>
      	
      	<delete id="deleteArticle">
      		DELETE FROM mvc_board WHERE board_no = #{boardNo}
      	</delete>
      
      </mapper>

Board Test

  • Board 기능 동작 테스트
    • src/test/java에서 test 진행

    • JUnit 라이브러리를 활용

    • @RunWith, @ContextConfiguration, @Test Annotation을 통해 test 수행

    • 실제 DB와 연동이 되어 데이터의 조회, 삭제, 삽입, 수정이 가능한지 테스트

    • test 파일과 InterfaceMapper, Mapper.xml 연동하여 테스트

      package com.spring.mvc.board;
      
      import java.util.List;
      
      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.test.context.ContextConfiguration;
      import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
      
      import com.spring.mvc.board.model.BoardVO;
      import com.spring.mvc.board.repository.IBoardMapper;
      
      @RunWith(SpringJUnit4ClassRunner.class)
      // 테스트 환경에서 Mapper 객체 활용을 위해 빈 등록 설정이 있는 xml 파일 로딩
      @ContextConfiguration(locations= {"file:src/main/webapp/WEB-INF/spring/root-context.xml"})
      // XML 파일 설정을 가져옴 
      // root-config.xml에 있는 Mapper설정을 가져옴
      public class BoardMapperTest {
      	
      	@Autowired
      	private IBoardMapper mapper;
      		
      	// 게시글 등록 단위 테스트
      	@Test
      	public void insertTest() {
      		for (int i = 0; i <= 323; i++) {
      			BoardVO board = new BoardVO();
      			board.setTitle("테스트 제목" + i);
      			board.setWriter("문테스트" + i);
      			board.setContent("테스트 중입니당" + i);
      			mapper.insert(board);
      			// 테스트 값이 실제 DB에 들어가는지 확인
      			// root-config.xml을 가져왔으므로 될 것 같은데
      			// 테스트 성공
      		}
      	}
      	
      	//게시글 목록 전체 조회 테스트
      	//게시물 개수 몇개인지 출력하시고, 게시글 모든 내용을 toString()으로 출력
      	@Test
      	public void listTest() {
      		List<BoardVO> boards = mapper.getArticleList();
      		// mapper와 연동해서 전체 게시물 가져옴 
      		
      		System.out.println("전체 게시물 개수 : "+ boards.size());
      		for(BoardVO board : boards) {
      			System.out.println(board.toString());
      			// 전체 게시물의 내용 모두 출력
      		}
      	}
      
      	//게시글 단일 조회 테스트	
      	@Test
      	public void getBoard() {
      		int num = 38;
      		BoardVO board = mapper.getArticle(num);
      		// 게시물 번호로 특정 게시물 가져옴 
      		System.out.println(num + "번째 게시물 내용 : " +board.getContent());
      	}
      
      	//게시글 수정 테스트
      	//수정 내용(글 제목, 글 내용)을 입력하고 수정을 진행 (1번글의 제목, 내용)
      	@Test
      	public void upBoard() {
      		BoardVO upBoard = new BoardVO();
      		upBoard.setBoardNo(1);
      		upBoard.setTitle("수정 테스트 글 제목");
      		upBoard.setContent("수정 테스트 글 내용");
      		mapper.updateArticle(upBoard);
      		// 특정 게시물 번호로 게시물 수정 
      		
      		BoardVO board = mapper.getArticle(1);
      		System.out.println(board.getTitle());
      		System.out.println(board.getContent());
      		// 수정이 정상적으로 완료되었는지 확인
      	}
      
      	//게시글 삭제 테스트
      	//3번글을 삭제하세요. 삭제 후 상세보기를 통해 3번글을 가져왔을 때 null이 리턴되는지
      	//조건문으로 확인해서 결과를 출력
      	@Test
      	public void delBoard() {
      		int num = 3;
      		mapper.deleteArticle(num);
      		// 특정 게시물 번호로 게시물 삭제 
      		
      		// delete이후 실제 해당 게시물 번호로 값이 존재하는지 유무 파악
      		if (mapper.getArticle(num) == null) {
      			System.out.println("삭제 성공 게시글 없음");
      		} else {
      			System.out.println("삭제 실패");
      		}
      	}
      }

Controller Test

Service

  • Interface 구축
    • Service 계층을 구현하기 위한 Interface 생성

      package com.spring.mvc.board.service;
      
      import java.util.List;
      
      import com.spring.mvc.board.model.BoardVO;
      
      public interface IBoardService {
      
      	// 게시글 등록 기능 
      	void insert(BoardVO board);
      	
      	// 게시글 전체 조회 (페이징 전)
      	List<BoardVO> getArticleList();
      	
      	// 게시글 상세 조회 기능
      	BoardVO getArticle(int boardNo);
      	
      	// 게시글 수정 기능 
      	void updateArticle(BoardVO board);
      	
      	// 게시글 삭제 기능 
      	void deleteArticle(int boardNo);
      	
      	// 게시글 수 조회 기능
      }

  • Service 클래스 생성
    • Interface를 상속받아 Service 클래스 생성

    • Container에 Bean 등록을 하기 위해 @Service annotation 지정

    • Service클래스는 Mapper가 필요하므로(dependency) Interface Mapper를 가져오고 @Autowird를 통해 의존성 주입

      package com.spring.mvc.board.service;
      
      import java.util.List;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      
      import com.spring.mvc.board.model.BoardVO;
      import com.spring.mvc.board.repository.IBoardMapper;
      
      @Service
      public class BoardService implements IBoardService {
      	
      	@Autowired
      	private IBoardMapper mapper;
      	
      	
      	@Override
      	public void insert(BoardVO board) {
      		mapper.insert(board);
      	}
      
      	@Override
      	public List<BoardVO> getArticleList() {
      		
      		return mapper.getArticleList();
      		// Controller에 값을 보냄
      	}
      
      	@Override
      	public BoardVO getArticle(int boardNo) {
      		return mapper.getArticle(boardNo);
      	}
      
      	@Override
      	public void updateArticle(BoardVO board) {
      		mapper.updateArticle(board);
      	}
      
      	@Override
      	public void deleteArticle(int boardNo) {
      		mapper.deleteArticle(boardNo);
      	}
      
      }

Controller

  • Controller 클래스 생성
    • Container를 Container에 Bean으로 등록을 하기 위해 @Controller annotation 지정

    • Container는 Service 계층과 연동하여 데이터를 전달해야함으로 Interface Service를 가져오고 @Autowird를 통해 의존성 주입

      package com.spring.mvc.board.controller;
      
      import java.util.List;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.PostMapping;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RequestParam;
      import org.springframework.web.servlet.mvc.support.RedirectAttributes;
      
      import com.spring.mvc.board.model.BoardVO;
      import com.spring.mvc.board.service.IBoardService;
      
      @Controller
      @RequestMapping("/board")
      public class BoardController {
      	
      	@Autowired
      	private IBoardService service;
      	
      	// 게시글 등록 
      	@PostMapping("/write")
      	public String writeBoard(BoardVO board) {
      		System.out.println("/board/write : POST");
      		service.insert(board);
      		return "redirect:/board/list";
      		// board/list로 다시 재요청을 보내 리스트를 확인할 수 있도록 함
      		// 글 등록이후 다시 list로 재요청 
      	}
      
      	// 게시글 목록 불러오기 
      	@GetMapping("/list")
      	public String listBoard(Model model){
      		System.out.println("/board/list : GET");
      		model.addAttribute("articles", service.getArticleList());
      		// model에 articles라는 이름으로 전체 게시물을 담아줌
      		// model은 지정된 url로 자동 이동
      		return "board/list";
      		// 특정 url로 보냄
      	}
      	
      	
      	// 게시글 상세보기 
      	@GetMapping("/content")
      	public String contentBoard(@RequestParam("boardNo") int boardNo, Model model) {
      		System.out.println("/board/content : GET");
      		model.addAttribute("article", service.getArticle(boardNo));
      		return "board/content";
      	}
      	
      	// 게시글 수정 
      	@PostMapping("/modify")
      	public String modifyBoard(BoardVO board) {
      		System.out.println("/board/modify : POST");
      		service.updateArticle(board);
      		return "redirect:/board/content?boardNo=" + board.getBoardNo();
      	}
      	
      	// 게시글 삭제 
      	@PostMapping("/delete")
      	public String deleteBoard(@RequestParam("boardNo") int boardNo, RedirectAttributes ra) {
      		System.out.println("/board/delete : POST");
      		
      		service.deleteArticle(boardNo);
      		ra.addFlashAttribute("msg", "deleteSuccess");
      		return "redirect:/board/list";
      	}
      
      }

Controller Test

  • Test
    • Controller가 제대로 동작하는지 확인하기 위해 Test 진행

    • @Before Annotation을 작성하여 가상 구조를 세팅

    • 화면(View)이 없어도 Controller도 테스트 진행할 수 있음

    • @Before를 사용해서 가상환경을 구축하여 실제 Controller가 구동하는 것과 같이 테스트 진행 가능

      package com.spring.mvc.board;
      
      import org.junit.Before;
      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.test.context.ContextConfiguration;
      import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
      import org.springframework.test.context.web.WebAppConfiguration;
      import org.springframework.test.web.servlet.MockMvc;
      import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
      import org.springframework.test.web.servlet.setup.MockMvcBuilders;
      import org.springframework.web.context.WebApplicationContext;
      
      import com.sun.org.apache.xalan.internal.xsltc.compiler.Parser;
      
      import lombok.extern.log4j.Log4j;
      
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration(locations= {"file:src/main/webapp/WEB-INF/spring/root-context.xml", 
      								  "file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
      // controller는 servlet에 controller에 대한 설정이 있으므로 그것도 가져옴 
      
      @WebAppConfiguration
      @Log4j
      public class BoardControllerTest {
      	
      	@Autowired
      	private WebApplicationContext ctx;
      	/*
      	 WebApplicationContext
      	 - test환경에서 가상의 DispatcherServlet을 사용하기 위한 객체 자동 주입
      	 - 가상 환경을 만들어서 controller가 요청을 받을 수 있도록 해줌
      	 - Spring에서 제공되는 servlet들을 사용할 수 있도록 정보를 저장하는 객체
      	 - @WebAppConfiguration 통해서 가져올 수 있음
      	 */
      	
      	// MockMvc는 웹 어플리케이션을 서버에 배포하지 않아도 스프링 MVC 동작을 
      	// 구현할 수 있는 가상의 구조를 만들어 줌
      	private MockMvc mockMvc;
      	
      	@Before
      	public void setUp() {
      		
      		// 가상 구조 세팅 
      		this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
      		
      		// 가상의 mvc 구조 완성 
      		// Spring container에 등록된 모든 bean과 의존성 주입까지 load해서 사용 가능
      	}
      	// 위의 3개는 Spring에 있는 모든 Controller를 사용할 때의 Setup
      	
      // 만약 하나의 Controller만 사용하고 싶을 때 
      //	@Autowired
      //	private BoardController controller;
      	
      	// private MockMvc mockMvc;
      	// setup메서드에 지정 
      //	this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
      	// 이렇게 지정하면 하나의 Controller만 테스트 가능 
      	
      	@Test
      	public void testList() throws Exception {
      		log.info(
      					this.mockMvc.perform(MockMvcRequestBuilders.get("/board/list"))
      					.andReturn()
      					.getModelAndView()
      					.getModelMap()
      					// 가상환경에서도 get 요청을 보낼 수 있고 요청을 받아서 model을 꺼내서 확인하는 작업
      					// getModelMap()은 map형식으로 확인할 수 있게 해주는 것`
      					// controller에서 return 했을 때 요기로 와서 연산이 제대로 되는지 확인
      					// 즉, Controller가 제대로 동작하는지 확인
      				);
      	}
      	
      	@Test
      	public void testWrite() throws Exception {
      		
      		String viewPage = this.mockMvc.perform(MockMvcRequestBuilders.post("/board/write")
      				.param("content", "controller 테스트")
      				.param("writer", "controller 테스트")
      				).andReturn().getModelAndView().getViewName();
      		// 매개변수가 있는 메서드를 테스트할 때에는 .param("이름", "값")을 통해 넣어줄 수 있음
      		// getViewName()을 통해 어디로 return되는지 확인
      				
      		log.info(viewPage);
      		// controller를 통해 실제 요청이 되었고 db에 들어가는 것을 확인
      		// 이 뜻은 Mapper와 연결되어 실제로 진행된 것 
      	}
      	
      	   //게시글 상세보기 요청 넣어보기.
      	   //컨트롤러에서는 DB에서 가지고 온 글 객체를 model에 담아서 jsp로 이동시킬 것입니다.
      	   //42번글을 보여달라는 요청을 넣으시고, 요청 결과가 들어있는 model을 출력해 보세요.
      	   // /board/content -> get
      	@Test
      	public void testContent() throws Exception {
      		log.info(this.mockMvc.perform(MockMvcRequestBuilders.get("/board/content")
      				// 해당 요청을 Controller에 보냄
      				.param("boardNo", "35"))
      				// 파라미터가 있으므로 파라미터도 추가 
      				.andReturn()
      				.getModelAndView()
      				.getModelMap()
      				);
      	}
      	
      	   //5번글의 제목과 내용을 수정하는 요청을 보낼 예정입니다.
      	   //전송방식은 post 방식입니다.
      	   //수정 후 이동하는 페이지는 해당 글의 상세보기 페이지입니다.
      	   //컨트롤러가 리턴하는 viewName을 출력해 주세요. ("/board/modify")
      	@Test
      	public void testModify() throws Exception {
      		String viewName = this.mockMvc.perform(MockMvcRequestBuilders.post("/board/modify")
      				.param("title", "test title 수정 테스트123")
      				.param("content", "test content 수정 테스트123")
      				.param("boardNo", "5")
      				).andReturn().getModelAndView().getViewName();
      		
      		log.info(viewName);
      	}
      
      	   //42번글을 삭제하세요.
      	   //전송 방식은 post방식이고, 이동하는 곳은 목록 요청이 재요청될 것입니다.
      	   //viewName을 출력해 주세요.   
      	@Test
      	public void testDelete() throws Exception {
      		String viewname = this.mockMvc.perform(MockMvcRequestBuilders.post("/board/delete")
      				.param("boardNo", "33"))
      				.andReturn()
      				.getModelAndView()
      				.getViewName();
      		
      		log.info(viewname);
      	
      	}
      }

  • 정적 자원 URI 절대 경로 사용
    • servlet-context.xml에서 resources 태그 사용

    • resources 태그안에 mapping과 location property를 이용하여 경로 설정 가능

      <!-- 정적 자원(html, css, js, img ...) 등을 URI 절대 경로로 사용하기 위한 Mapping 처리  -->
      	<!-- mapping에는 사용자에게 노출되는 경로, location에는 실제 파일 경로 -->
      	<resources mapping="/resources/**" location="/resources/" />
      	<resources mapping="/css/**" location="/resources/css/" />
      	<!-- css/로 된 경로가 있으면 실제로는 /resources/css/에서 찾아라  -->
      	<resources mapping="/js/**" location="/resources/js/" />
      	<resources mapping="/img/**" location="/resources/img/" />
      	<resources mapping="/scss/**" location="/resources/scss/" />
      	<resources mapping="/vendor/**" location="/resources/vendor/" />

  • URL에 파라미터 표시하지 않고 값을 보내는 방법
    • url에 ?boardno = ~~~ 말고 식별자로 가져올 수 있음

    • 기존 방법을 사용하게 되면 url에 boardNo=번호 표시되어 보여짐 (데이터가 사용자에게 보여진다는 것이 좋지 않음)

    • controller에 해당 요청이 있는 메서드에서 받은 파라미터 앞에 @PathVariable을 작성해서 url 경로에 변수를 포함시켜 파라미터명으로 url을 표현할 수 있음

    • href="<c:url value='/board/content/${board.boardNo}' />"를 통해 url에 boardNo를 묻혀서 보내면 url이 훨씬 깔끔해짐

      // 게시글 상세보기 (?를 써서 파라미터를 보여주는 것이 아니라 특정 구분자로 url에 보여주는 방법)
      	// 받은 parameter명에 @PathVariable annotation 지정
      		// @PathVariable은 URL 경로에 변수를 포함시켜 주는 방식 
      		// 만약 파라미터 값에 .이 포함되어 있다면 .뒤의 값은 잘림
      		// {}안에 변수명 넣고 @PathVariable 괄호 안에 영역을 지목해서 값을 받아옴
      	// 요청 url뒤에 /{받은 파라미터 명}
      	@GetMapping("/content/{boardNo}")
      	public String contentBoard(@PathVariable int boardNo, Model model) {
      		// 파라미터명과 매개변수 명이 같으면 @RequestParam 안적어도 됨
      		System.out.println("/board/content : GET");
      		model.addAttribute("article", service.getArticle(boardNo));
      		return "board/content";
      	}

Project Setting

  • New Project
    • pom.xml에서 버전 조정 및 사용할 라이브러리 추가
    • maven update
    • DB Table 만들기
    • table과 매칭되는 객체 디자인 (VO)
    • web.xml에 encoding filter 설정
      • 만약 root-context와 servlet-context의 경로와 이름을 바꾸면 web.xml에 context-param태그에서 root-context 경로와 이름을 변경해주고 servlet 태그의 경로와 이름을 바꿔주면 됨
    • root-context에 설정을 작성하고 namespace에서 jdbc, mvc, mybatis-spring 선택
    • Backend 기능 동작 테스트
      • Mapper interface 구축
      • Mapper 연동 테스트 진행
    • 테스트 완료 후 Controller와 Service 구축
      • Controller 테스트 진행
profile
안녕하세요! 공부한 내용을 기록하는 공간입니다.

0개의 댓글