Java - Spring &&n SpringBoot

Spring Web 기본 정리 : 원리, spring 설정, DB 연결해서 데이터 보여주기

TerianP 2022. 7. 4.
728x90

나는 원래 spring을 spring boot 로 처음 접했었어서 그런지 spring 의 설정이 복잡하다는 것을 잘 몰랐었다. 다만 spring boot 가 spring 의 설정을 복잡함을 해결하고자 나왔다 라는 걸 알았기에 어느정도 복잡하구나...정도로 생각할 뿐이었다. 그런데 이번에 spring boot 가 아닌, 본래 spring 을 배우게 되었는데 세상에...엄청 복잡하다. 다만 복잡함에도 중요한 것들이 있었고, 이에 따라서 정리해보려고 한다.

 

1. 스프링 동작 원리

spring 실행순서

  1. web.xml을 로딩
  2. web.xml에 등록된 ContextLoaderListner 가 생성된다
  3. ContextLoaderListner가 root-context.xml을 로딩시작!
  4. root-context.xml에서 web 제외한 component를 스캔 및 생성
  5. 클라이언트 요청에 의해 DispatcherServlet 가 생성된다
  6. DispatcherServlet이 servlet-context.xml을 로딩!
  7. servlet-context.xml에서 web 관련 component를 스캔 및 생성

spring 구동 순서

  1. 클라이언트가 URL로 접근하여 정보를 요청
  2. DispatcherServlet이 클라이언트의 요청을 가로챈다.
  3. 가로챈 요청을 HandlerMapping으로 보내 해당 요청을 처리할 Controller 검색
  4. Controller에서 DB를 통해 정보를 가져온다.
  5. (Controller - Service - DAO - DB - DAO - Service - Controller)
  6. Controller에서 View를 리턴하면 ViewResolver를 통해 해당 View가 존재하는지 검색
  7. DispatcherServlet은 ViewResolver를 통해 접두사(prefix)와 접미사(suffix)가 붙은 JSP파일의 이름과 경로를 리턴받아 JSP를 실행, View에 결과를 보낸 후 DispatcherServlet은 최종결과를 클라이언트로 전송

 

※ Dispatcher Servlet 란?

  • DIspatcher Servlet 은 스프링에서 아주아주 중요한 역할을 담당한다.
  • 얘는 요청이 들어왔을 때 ‘자동으로’ 지정된 view 를 찾아서 model 에 담은 내용을 view 로 연결해주는 역할을 한다.
  • 참고로 view 를 찾아가는 역할을 하도록 도와주는건 viewResolver

 

spring Web 설정하기 : 아주 복잡하다

참고로 모든 xml 파일은 /spring_web01/src/main/webapp/WEB-INF 에 위치해야 한다

web.xml

servlet name 과 class mapping 을 적어준다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>spring_web02</display-name>
  
  <!-- 모든 요청은 servlet-name 에 선언된 dispatcher servlet 를 따라서 이동-->
  <!-- dispatcher servlet 를 이용한 이동을 좀 더 설정하기 위해 
  		/webapp/WEB-INF/dispatcher-servlet.xml 을 만들어주어야함
   -->
  <servlet>
    <servlet-name>dispatcher</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
  <!-- dispatcher 는 *.do 에 해당하는 모든 요청을 처리함 -->
  <servlet-mapping>
  	<servlet-name>dispatcher</servlet-name>
  	<url-pattern>*.do</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    <welcome-file>default.htm</welcome-file>
  </welcome-file-list>
</web-app>

disaptcher-servlet.xml

web.xml 에서 설정한 servlet-name 과 일치하도록하는게 중요하다.

즉 (servlet-name)-servlet.xml 로 파일명을 만들어야한다.

  • prefix : viewResolver 을 통해 찾아가는 view 의 위치
  • suffix : viewResolver 를 통해 찾아가는 view 파일의 확장자명
<?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:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- Handler Mapping 객체 설정 -->
	<bean id="beanNameUrlHandlerMapping"
		class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
	<!-- viewResolver 객체 설정 -->
	<bean id="internalResourceViewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<!-- prefix 는 접두사, suffix 는 접미사 -->
		<property name="prefix" value="/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
	<!--  여기서 id 에 해당하는 .do 의 위치는 결국 jsp 의 실제 위치를 적어주어야한다 -->
	<!--  만약 /views/hello.jsp 라면 id 에 /views/hello.do 가 되어야한다-->
	<!--  .do 인 이유는 요청 request 가 ~~.do 로 들어오기 때문!!-->


	<bean id="/hello.do"
		class="kr.kro.hjproject.control.HelloController"></bean>
	<bean id="/greeting.do"
		class="kr.kro.hjproject.control.HelloImpleController" p:name="pray"></bean>
</beans>

Controller & ModelAndView

  • 컨트롤러에 해당하는 클래스는 반드시!! Controller 를 구현하는 구현 클래스가 되어야함
  • 컨트롤러에서는 Model 과 View 를 합친 modelandView 객체를 사용함
  • addObject 를 통해서 key 와 value 를 담을 수 있고, setviewName 을 통해서 찾아가야하는 view 의 이름을 설정할 수 있음
  • 앞서 dispatcher-servlet 에서 설정한 prefix 가 / 였음으로 viewName 에 따로 /hello 로 작성할 필요는 없고 webapp 디렉토리에서의 위치와 파일명을 작성하면 됨
    • 이때 webapp/views 위치에 jsp 파일이 있는 경우 vewName 에는 /views/파일명 이 들어가야한다
package kr.kro.hjproject.control;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class HelloController implements Controller{

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		ModelAndView mav = new ModelAndView();
		mav.addObject("hello" , "fire"); // view 에 전달된 model 내용
		mav.setViewName("hello"); // 찾아가야하는 view 이름
		
		return mav;
	}

}

JSP

  • jsp 코드에서는 하이퍼링크 영역을 실제 페이지가 있는 영역으로 잡아야한다
  • .do 로 요청을 하면 dispatcher servlet.xml 이 해당 요청에 맞는 controller 에 맞춰서 알아서 찾아가준다
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<ul>
		<li><a href="./views/showdata.do">showdata</a></li>
	</ul>
</body>
</html>

활용 : Spring 에 DB 연결 후 내용 불러오기

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
	version="4.0">
	<display-name>spring_web05</display-name>

	<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- 초기화 할때 dispatcher 의 설정파일명을 변경한다는 의미
			/web-inf/webapp.xml 에 있는 파일이 설정파일이 됨
		-->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/webapp.xml</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>
	
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>
</web-app>

webapp.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:c="http://www.springframework.org/schema/c"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="beanNameUrlHandlerMapping"
		class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
	<bean id=""
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<!-- 시작 지점은 /  , 웹 확장자명은 .jsp -->
		<property name="prefix" value="/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>

	<!-- hjprojectDAO 를 Bean으로 생성 -->
	<bean id="hjproject" class="kr.kro.hjproject.dao.HjprojectDAO"></bean>
	<!-- showdata.do 에 대한 Controller 매핑 과 동시에 DI -->
	<bean id="/views/showdata.do"
		class="kr.kro.hjproject.control.ShowData"
		c:hjproject-ref="hjproject">
	</bean>

</beans>

MakeConnection : 싱글톤 패턴으로 생성한 DB 연결을 위한 Connection 객체

package makeConnction;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class MakeConnection implements ConnInfo{
	private static MakeConnection mc;
	private Connection conn = null;
	
	private MakeConnection() {}
	
	
	// 외부에서 getInstance 했을 때 mc 가 null 이면 mc 를 생성 후 return
	// null 이 아니면 그대로 이미 만들어졌던 mc return
	public static MakeConnection getInstance() {
		if(mc != null) {
			return mc;
		}else {
			mc = new MakeConnection();
			return mc;
		}
	}
	
	// 외부에서 getConnection 했을 때 내부에 Connection 이 없다면
	// Connection 을 만들고 return
	public Connection getConnection() {
		if(conn==null) {
			try {
				Class.forName("com.mysql.jdbc.Driver");
				conn = DriverManager.getConnection(url, user, passwd);
				
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return conn;
	}
}

DAO

package kr.kro.hjproject.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import kr.kro.hjproject.dto.HjprojectDTO;
import makeConnction.MakeConnection;

public class HjprojectDAO {

	Connection conn = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;

	StringBuffer sb = new StringBuffer();

	public HjprojectDAO() {
		// MakeConnection 은 싱글톤 패턴으로 생성
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn = MakeConnection.getInstance().getConnection();
			System.out.println("conn : " + conn);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public List<HjprojectDTO> selectAll() {
		List<HjprojectDTO> list = new ArrayList<HjprojectDTO>();
		sb.setLength(0);
		sb.append("SELECT * FROM MEMBER");

		try {
			pstmt = conn.prepareStatement(sb.toString());
			rs = pstmt.executeQuery();

			while (rs.next()) {
				int code = rs.getInt("member_code");
				String id = rs.getString("mid");
				String email = rs.getString("memail");

				list.add(new HjprojectDTO(code, id, email));
			}

		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return list;

	}
}

DTO

package kr.kro.hjproject.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class HjprojectDTO {
	int code;
	String id;
	String email;
}

showDataController

package kr.kro.hjproject.control;

import java.sql.Connection;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import kr.kro.hjproject.dao.HjprojectDAO;
import kr.kro.hjproject.dto.HjprojectDTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
// 컨트롤러는 컨트롤러 인터페이스를 구현!!
public class ShowData implements Controller{
	
	// 생성자로 DI
	private HjprojectDAO hjproject;

	
	
	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		// hjproject 에서 DB 의 전체 내역을 뽑아옴
		List<HjprojectDTO> list = hjproject.selectAll();
		
		// modelandView 에 도착할 페이지 위치, 데이터명, 테이터를 담기
		ModelAndView mav = 
				new ModelAndView("/views/showdata", "data", list);
		
		return mav;
	}

}

showdata.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<link
	href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css"
	rel="stylesheet"
	integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor"
	crossorigin="anonymous">
<script
	src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js"
	integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2"
	crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
	integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
	crossorigin="anonymous"></script>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>showdata</h1>
	<!-- EL 형식으로 해봤습니다 근데 EL 은 반복문없는건가 못하겠네요ㅠㅠ -->
	<div class="container">
		<table class="table table-bordered table-primary">
			<tr>
				<th>code</th>
				<th>아이디</th>
				<th>이메일</th>
			</tr>
			<tr>
				<th>${requestScope.data[0].code }</th>
				<th>${requestScope.data[0].id }</th>
				<th>${requestScope.data[0].email }</th>
			</tr>
			<tr>
				<th>${requestScope.data[1].code }</th>
				<th>${requestScope.data[1].id }</th>
				<th>${requestScope.data[1].email }</th>
			</tr>
			<tr>
				<th>${requestScope.data[2].code }</th>
				<th>${requestScope.data[2].id }</th>
				<th>${requestScope.data[2].email }</th>
			</tr>
		</table>
	</div>
</body>
</html>
 

 

댓글