행위

스프링 프레임워크(Spring Framework) 외부연수

라이언의 꿀팁백과

Ryanyang (토론 | 기여)님의 2023년 5월 17일 (수) 16:32 판 (→‎커리큘럼)

1 강사님

오정임 purum01@naver.com

2 목표

  • 스프링 프레임워크 이해
    • Spring Framework 은 결국 JSP & Servlet을 더 효율적으로 사용하도록 돕는 도구
  • 웹 시스템 유지보수 및 개발역량 함양

3 수업방식

  • 100% 실습


※ (주의사항) Java 기본문법을 알고 JSP & Servlet(=Web)에 대한 기본이해가 있다는 것을 전제로 하고 수업을 함

4 커리큘럼

교육일자 회차 내용 비고
2023.5.15(월) 1일 JSP &Servlet(=Web) 이해 Spring Frameowrk 선수과정
2023.5.16(화) 2일 Cookie, Session, Filter, Listener, DB, JSP 태그, MVC
2023.5.17(수) 3일 Bean, MVC 복습, IoC 컨테이너, Bean, Dependency Injection(DI), Annotation, SpringAOP Spring Framework 수업 착수
2023.5.18(목) 4일
2023.5.19(금) 5일

5 필기노트

5.1 1일차

5.1.1 Java SE vs. Java EE

자바 언어를 활용한 개발은 크게 두 가지로 나뉜다.

  • Java SE : Standalone 프로그램 개발 시 활용
  • Java EE : 다른 프로그램(컨테이너, 엔진, 서버)에 의존해서 실행하는 프로그램 개발 시 활용 (예: JSP, Servlet)


정확히 말하자면 자바 언어를 활용한 개발은 정확히는 크게 네 가지로 나뉜다.

Differences between Java EE and Java SE


Java technology is both a programming language and a platform. The Java programming language is a high-level object-oriented language that has a particular syntax and style. A Java platform is a particular environment in which Java programming language applications run.


There are several Java platforms. Many developers, even long-time Java programming language developers, do not understand how the different platforms relate to each other.


The Java Programming Language Platforms


There are four platforms of the Java programming language:

  • Java Platform, Standard Edition (Java SE)
  • Java Platform, Enterprise Edition (Java EE)
  • Java Platform, Micro Edition (Java ME)
  • JavaFX


All Java platforms consist of a Java Virtual Machine (VM) and an application programming interface (API). The Java Virtual Machine is a program, for a particular hardware and software platform, that runs Java technology applications. An API is a collection of software components that you can use to create other software components or applications. Each Java platform provides a virtual machine and an API, and this allows applications written for that platform to run on any compatible system with all the advantages of the Java programming language: platform-independence, power, stability, ease-of-development, and security.


Java SE

When most people think of the Java programming language, they think of the Java SE API. Java SE's API provides the core functionality of the Java programming language. It defines everything from the basic types and objects of the Java programming language to high-level classes that are used for networking, security, database access, graphical user interface (GUI) development, and XML parsing.

In addition to the core API, the Java SE platform consists of a virtual machine, development tools, deployment technologies, and other class libraries and toolkits commonly used in Java technology applications.


Java EE

The Java EE platform is built on top of the Java SE platform. The Java EE platform provides an API and runtime environment for developing and running large-scale, multi-tiered, scalable, reliable, and secure network applications.


Java ME

The Java ME platform provides an API and a small-footprint virtual machine for running Java programming language applications on small devices, like mobile phones. The API is a subset of the Java SE API, along with special class libraries useful for small device application development. Java ME applications are often clients of Java EE platform services.


JavaFX

JavaFX is a platform for creating rich internet applications using a lightweight user-interface API. JavaFX applications use hardware-accelerated graphics and media engines to take advantage of higher-performance clients and a modern look-and-feel as well as high-level APIs for connecting to networked data sources. JavaFX applications may be clients of Java EE platform services.

ChatGPT는 Java SE 와 JavaEE 차이점을 아래와 같이 대답했다.

What are the differences between Java SE and Jave EE?

5.1.2 WAS

WAS는 Web Application Server으로 JSP, Servlet을 처리하는 미들웨어 소프트웨어다. 우리는 수업 때, 전통적으로 많이 쓰이는 Tomcat 중 버전 8.5를 쓴다. JDK는 8을 쓴다. ChatGPT 설명을 참고하면 좀 더 이해가 쉽다. 쉽게 말해서 WAS는 웹 서버와 애플리케이션 서버를 연결하는 역할을 한다. WAS가 애플리케이션 서버는 아니다.

ChatGPT-Answer-02.png

5.1.3 JDK

Sun 시절에는 JDK 8 버전까지는 오픈소스이나 그 이후부터는 Oracle 에서 라이선스 구독이 필요하다. 그러나 무료로 사용할 수 없는 것은 아니다. JDK는 OracleJDK, OpenJDK로 나뉜다. 이름에서 알 수 있듯이 OpenJDK는 무료다. OpenJDK의 종류로는 Azul 등이 있음.


참고로 JDK와 Tomcat 버전은 동일하게 맞춰야 제대로 작동한다. 정확히는 Tomcat 의 아래 표 중 Supported Java Versions를 살펴본 후, Tomcat을 설치해야 한다.

Apache Tomcat Versions.png

Oracle10gExpressEdition

5.1.4 개발환경 구성

  1. JDK 설치 (JDK 1.8.0_91)
  2. WAS 설치 (Tomcat 8.5)
  3. IDE 설치 (Eclipse Java EE Neon 2)
  4. Eclipse Plugin 설치 (OJDBC)
  5. DB 설치 (Oracle 10g DB Express Ediition)

5.1.5 web.xml 관련

WEB-INF/web.xml 파일은 컨테이너 환경설정 파일이다. Servlet 2.5 에는 항상 환경설정을 web.xml 에서만 해야했음. 그러나 Servlet 3.0 부터는 소스코드에 Annotation을 추가함으로써 환경설정을 추가할 수 있는 기능을 제공했다.

5.1.6 context

웹 애플리케이션 이름이라고 생각하면 됨

5.1.7 WEB-INF 디렉토리

  • 기본적으로 애플리케이션 서버는 Context Root d아래, WEB-INF 이하 classes 에서 *.class 파일을, lib 에서 *.jar 파일을 찾음.
  • Maven, Gradle 과 같은 패키지 관리 도구는 필요한 패키지(*.jar)를 lib 폴더에 자동으로 다운로드 및 복사를 함.

5.1.8 서블릿

웹에서 요청해서 실행할 수 있는 유일한 자바 프로그램을 서블릿이라고 한다. 서블릿은 아래와 같은 특성을 갖는다.

  1. 반드시 javax.servlet.http.HttpServlet 상속

(Lunch Time)


javax.servlet 과 javax.servlet.http 기억하기


컨테이너가 최초 호출이 되면 아래와 같은 일이 일어남.

  1. 객체 생성
  2. init() 호출
  3. HttpServletRequest(①), HttpServletRespons(②) 객체 생성
  4. service(①, ②)


두 번째 부터는 아래와 같은 일만 일어남.

  1. HttpServletRequest(①), HttpServletRespons(②) 객체 생성
  2. service(①, ②)


HttpServlet은 GenericServlet을 상속하고 있음. init() 함수는 GenericServlet에 있고, service() 함수는 HttpServlet에 있다.

5.1.9 서블릿 경로 만들기

  1. web.xml 수정
  2. 어노테이션 @WebServlet("경로") 사용


<?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_3_1.xsd" id="WebApp_ID" version="3.1">

  <display-name>edu</display-name>

  <servlet>

  <servlet-name>first</servlet-name>

  <servlet-class>com.edu.test.FirstServlet</servlet-class>

  </servlet>

 

  <servlet-mapping>

  <servlet-name>first</servlet-name>

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

</servlet-mapping>

</web-app>

5.1.10 Tomcat의 GET 방식 한글처리

server.xml 파일 내 <Connector> 태그의 속성으로 URIEncoding="UTF-8" 라는 코드와 같이 클라이언트와 동일하게 맞추면 된다. 참고로 기본값은 UTF-8 이다.

5.1.11 Servlet 저장소

  1. ServletContext
  2. HttpSession
  3. HttpServletRequest


데이터 저장과 적재를 위해서는 아래 두 함수를 사용한다.

  • (저장) void setAttribution(String name, Object value)
  • (추출) Object getAttribute(String name)
  • (삭제) removeAttribute(String name)
저장소명 사용범위 생성시점 삭제시점 생성개수 용도
ServletContext 하나의 애플리케이션 서버 시작 시 서버 종료 시 웹 애플리케이션 수(=1개) Application 단위로 데이터 공유,

Application 변수 사용

HttpSession 하나의 클라이언트 클라이언트(=브라우저) 접속 시


HttpServletRequest 객체

- getSession()

- getSession(boolean)

1. 클라이언트(=브라우저 등) 종료 시


2. HttpSession.invalidate() 호출

클라이언트 수 로그인, 장바구니
HttpServletRequest 하나의 요청 요청 시 응답 시 요청 수만큼 일반적인 요청 및 응답 (가장 많이 쓰임)


<init-param>은 변수를 선언하는 용도이며 web.xml 안에서 정의할 수 있다. <servlet> 안에서 지정한 <init-param>은 FirstServlet 에서만 접근이 가능하다. <web-app> 안에서 지정한 <context-param>은 전체 애플리케이션에서 접근할 수 있다.

<?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_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>edu</display-name>
  <context-param>
  	<param-name>config</param-name>
  	<param-value>layout.xml</param-value>
  </context-param>
  
  <servlet>
	  <servlet-name>first</servlet-name>
	  <servlet-class>com.edu.test.FirstServlet</servlet-class>
	  <init-param>
	  	<param-name>code</param-name>
	  	<param-value>utf-8</param-value>	  
	  </init-param>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>first</servlet-name>
  	<url-pattern>/exam01</url-pattern>
  </servlet-mapping>
</web-app>


<param-name>이 변수명이고 <param-value>는 값이다. 아래와 같은 코드로 변수 값을 가져올 수 있다.

package com.edu.test;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class FirstServlet extends HttpServlet {
	@Override
	public void init(ServletConfig config) throws ServletException {
		String code = config.getInitParameter("code");
		ServletContext sc = config.getServletContext();
		System.out.println("FirstServlet's init() 요청" + " : init-param 'code' = " + code + " : " + "context-param 'config' = " + sc.getInitParameter("config"));
		
	}
	
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		System.out.println("FirstServlet's service() 요청");		
	}
}

5.1.12 페이지 이동

RequestDispatcher 클래스는 페이지 이동 시 이전 페이지의 request, response를 그대로 사용할 때 사용한다. 그런데 여기에는 두 개 메소드가 있다.

  • RequestDispatcher.forward(req, resp) : 이동하고 다시 돌아오지 않음 (이 때 요청 URL은 변경되지 않음)
  • RequestDispatcher.inclue(req, resp) : 이동했다가 다시 돌아옴


※ include는 최초 요청받은 페이지에서 응답을 보낼 때 사용하고, forward는 이동한 페이지에서 응답을 보내고자 할 때 사용

RequestDispatcher 객체는 HttpServletRequest.getRequestDispatcher() 함수를 통해 얻을 수 있다.

페이지 이동 시 새로운 request, response를 생성할 때 사용한다.

  • HttpServletResponse.sendRedirect(path) : 이전 페이지의 request, response를 사용하지 않음. (만약, 내가 요청한 페이지의 URL이 변경되었다면 sendRedirect() 메소드를 사용한 것이다.)

5.2 2일차

스프링 프레임워크에서는 페이지 이동 시 기본적으로 RequestDispatcher 객체를 사용한다.

5.2.1 데이터 관리

클라이언트 단위로 데이터를 유지하는 방법은 아래와 같다.

구분 저장위치 저장형태 보안 사용방법
HttpSession 서버 Object (쿠키에 비해) 매우 좋음
Cookie 클라이언트 text (name=value) 매우 취약 - (생성) new Cookie(name, value)

- (전송) void HttpServletResponse.addCookie(Cookie)

- (추출) Cookie[] HttpServletRequest.getCookies()

5.2.2 쿠키

아래 코드는 쿠키를 생성하고 응답 객체인 HttpServletResponse에 쿠키를 추가한다.

package com.edu.test;

import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

@WebServlet("/cookie1")
public class CookieTest1 extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();		
		out.println("<h1>Cookie 전송</h1>");
		
		Cookie c1 = new Cookie("code", "Java");
		resp.addCookie(c1);
		
		Cookie c2 = new Cookie("city", "Seattle");
		resp.addCookie(c2);		
		c2.setMaxAge(60*60*3); // 유효시간 설정 : 양수(초), 음수(세션과 동일), 0(삭제), default(세션)
		resp.addCookie(c2);
		
		out.close();
	}
}

5.2.3 세션

HttpServletRequest 객체의 getSession() 혹은 getSession(true)을 호출하면 세션 ID 가 생성이 된다. 참고로 getSession(false)와 getSession(true)의 차이점은 다음과 같다.

  • HttpServletRequest.getSession(true) : 세션 ID가 없으면 생성해서 리턴한다.
  • HttpServletRequest.getSession(false) : 세션 ID가 없으면 null을 리턴한다.


세션 객체 생성 및 삭제, 세션 내 데이터 저장 및 삭제 등은 아래 코드를 통해 수행이 가능하다.

package com.edu.test;

import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

@WebServlet("/session")
public class SessionTest extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String param = req.getParameter("p");
		
		HttpSession session = null;
		String msg = null;
				
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();		
//		out.println("<h1>HttpSession 테스트</h1>");
//		out.println("<h3>" + param + "</h3>");
//		out.close();
		
		if (param.equals("create")) {
			session = req.getSession();
			
			if(session.isNew()) {
				msg = "새로운 세션 객체가 생성되었습니다. " + session.getId();
			} else {
				msg = "기존 세션 객체가 반환되었습니다. " + session.getId();
			}
		} else if (param.equals("destory")) {
			session = req.getSession(false);
			if (session == null) {
				msg = "삭제할 세션 객체가 없습니다.";	
			} else {				
				msg = "세션 객체를 삭제했습니다. " + session.getId();
				session.invalidate();
			}
		} else if (param.equals("add")) {
			session = req.getSession(true);
			session.setAttribute("id", "guest");
			msg = "세션에 id 등록을 완료했습니다. " + session.getAttribute("id");
		} else if (param.equals("get")) {
			session = req.getSession(false);
			
			if (session != null) {
				String id = (String)session.getAttribute("id");
				msg = "세션에 등록한 id 객체의 값을 가져왔습니다. " + session.getAttribute("id");
			} else {
				msg = "세션이 존재하지 않습니다.";
			}
			
		} else if (param.equals("remove")) {
			session = req.getSession(false);
			
			if (session != null) {				
				msg = "세션에 등록한 id 객체의 값을 삭제했습니다. " + session.getId() + " : " + session.getAttribute("id");
				session.removeAttribute("id");
			} else {
				msg = "세션이 존재하지 않습니다.";
			}
		}
		
		out.println("<h3>" + msg + "</h3>");
	}
}

5.2.4 Filter 와 Listener

5.2.4.1 Filter

Filter는 요청을 처리하는 서비스 전(=request 필터) 혹은 후(=response 필터)에 항상 실행해야 하는 기능을 추가할 때 사용하며, 아래와 같은 절차를 통해 수행할 수 있다.

  1. Filter 생성 - Filter 인터페이스를 구현 (... implements Filter)
  2. Filter 서버 등록
  3. Filter 맵핑

request 필터는 인증, 로깅, 감사, 인코딩 등으로 사용하고, response 필터는 데이터 압축, 총 서비스 시간 측정 등으로 사용한다..


아래는 Filter 생성 및 삭제 관련 함수다.

함수명 용도 시점
init() 필터 객체 생성 시 서버 시작 시
doFilter() 매핑 페이지 요청 시
destory() 필터 객체 삭제 시 서버 종료 시

Filter 등록은 web.xml 에 하며 아래와 같은 형식으로 하거나 Filter 클래스에 어노테이션으로 @WebFilter("/*"); 등을 추가해서 설정할 수 있다.

...

<filter>
    <filter-name>필터이름</filter-name>
    <filter-class>클래스 경로</filter-class>
</filter>

...

Filter 생성은 Spring Framework에서 자동으로 하기 때문에 개발자는 2,3번만 직접 수행하면 된다. 그리고 Filter 맵핑은 아래와 같이 하면 된다.

...

<filter-mapping>
    <filter-name>필터이름</filter-name>
    <url-pattern>요청 URL</url-pattern>
</filter-mapping>

...


Filter를 시각화하면 아래와 같다.

Filter 클래스

5.2.4.2 Listener

~Listener 클래스가 있다면 아래와 같은 특성이 있다고 기억하자.

  1. 인터페이스
  2. 콜백(callback) 메소드


Javadoc에 보면 아래와 같은 인터페이스를 볼 수 있다.

  • HttpSessionActivationListener
  • HttpSessionAttributeListener
  • HttpSessionBindingListener
  • HttpSessionIdListener
  • HttpSessionListener
  • ServletContextListener


어디서 어떤 상황이냐? ~Listener 앞에 있는 명칭 통해 유추 가능.

ChatGPT의 자바 웹 기술의 Filter와 Listener 설명


아래는 Listener 객체를 사용하기 위한 코드다.

package com.edu.test;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class MyListener implements ServletContextListener {

	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		System.out.println("MyListener's contextDestroyed() 호출!");
	}

	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		System.out.println("MyListener's contextInitialized() 호출!");
	}
}


Listener를 사용하려면 web.xml에 관련 설정 값을 추가해야한다. 아래 코드 중 <listener> ... </listener> 부분을 추가하면 된다.

<?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_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>edu</display-name>
  <context-param>
  	<param-name>config</param-name>
  	<param-value>layout.xml</param-value>
  </context-param>

  <listener>
  	<listener-class>com.edu.test.MyListener</listener-class>
  </listener>

  <filter>
    <filter-name>f1</filter-name>
    <filter-class>com.edu.test.MyFilter</filter-class>
  </filter>
  
  <filter-mapping>
    <filter-name>f1</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <servlet>
	  <servlet-name>first</servlet-name>
	  <servlet-class>com.edu.test.FirstServlet</servlet-class>
	  <init-param>
	  	<param-name>code</param-name>
	  	<param-value>utf-8</param-value>	  
	  </init-param>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>first</servlet-name>
  	<url-pattern>/exam01</url-pattern>
  </servlet-mapping>
</web-app>
Filter-Listener-JSP.png

5.2.5 데이터베이스

책에서는 H2 DB를 사용하지만 수업에서는 Oracle 10g Express Edition을 사용한다. 관리자 계정은 sys이며 암호는 DB 설치 시 설정했던 admin123으로 로그인한다. 웹사이트 주소는 http://127.0.0.1:8080/apex 이다.

5.2.5.1 사용절차

자바에서 DB 처리는 java.sql 패키지에 있는 라이브러리를 사용한다. 여기의 라이브러리는 모두 인터페이스다. 실제 구현체는 각 DB 업체에서 제공하는 JDBC 드라이버에 있다. DB 처리는 아래와 같은 순서대로 수행한다.

  1. JDBC Driver 파일 준비 (로딩)
  2. DB 서버 접속하기
  3. Statement or PreparedStatement 객체 생성하기
  4. SQL 문 실행하기
  5. 자원 해제하기

5.2.5.2 예제코드

아래는 DB에 접속해서 테이블을 추가하는 예제 코드다.

package com.edu.test;

import java.sql.*;

public class DBTest {
	public static void main(String[] args) {
		Connection conn = null;
		String id = "samoyed7";
		String pwd = "milk7";
		
		try {
			// 1. JDBC Driver 파일 준비 (로딩)
			Class.forName("oracle.jdbc.driver.OracleDriver");
			// 2. DB 서버 접속하기
			conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "scott", "tiger");
			// 3. Statement or PreparedStatement 객체 생성하기
//			Statement stmt = conn.createStatement();
			PreparedStatement pstmt = conn.prepareStatement("INSERT INTO TEST VALUES(?,?)");
			pstmt.setString(1, id);
			pstmt.setString(2, pwd);
			pstmt.executeUpdate();
			// 4. SQL 문 실행하기
//			stmt.executeUpdate("CREATE TABLE TEST(ID VARCHAR2(20) PRIMARY KEY, PWD VARCHAR2(20))");
//			stmt.executeUpdate("INSERT INTO TEST VALUES('happydog','11')");
//			stmt.executeUpdate("INSERT INTO TEST VALUES('ryanyang','22')");

//			ResultSet rs = stmt.executeQuery("SELECT * FROM TEST");
			pstmt = conn.prepareStatement("SELECT * FROM TEST");
			ResultSet rs = pstmt.executeQuery();
			while (rs.next()) {
				System.out.println(rs.getString(1) + " : " + rs.getString(2));
			}
			
			// 5. 자원 해제하기
			rs.close();
//			stmt.close();
			pstmt.close();
			conn.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("Okay : " + conn);
	}
}

5.2.5.3 Auto Commit 설정

Connection 객체의 setAutoCommit(boolean) 메소드를 통해 Auto Commit 옵션을 설정할 수 있다. 기본값은 Auto Commit 이다.

5.2.6 JSP Container

JSP Container는 jsp 를 java로 변환한 후 class 파일로 컴파일을 한다.

5.2.6.1 JSP 태그

  • <% ... %> ← service() 메소드에 들어가는 코드
  • <%= ... %> ← service() 메소드에 out.print()로 들어가는 코드
  • <%@ ... %> ← package 정의 및 import 관련 코드로 변환이 됨
  • <%! ... %> ← 멤버변수 및 멤버함수 정의하는 코드로 변환이 됨 (즉, class { ... } 사이에 들어감


모든 JSP 코드는 java 코드로 변환이 되는데 이 때 항상 아래 세 가지 객체가 암묵적으로 추가된다.

  • HttpServlet Request request = ...
  • HttpServlet Response response = ...
  • HttpSession session = ...


위에서 진하게 표기한 변수명으로 각 객체에 접근할 수 있다.

<%@ page contentType="text/html; charset=UTF-8" import="java.util.*, java.io.*" %>

<h1>Test</h1>
<h1>스프링</h1>

<% Date d = new Date(); %>

<%= d %>

<%
	request.setAttribute("msg", "Hello JSP 테스트 메시지입니다.");
	RequestDispatcher rd = request.getRequestDispatcher("test2.jsp");
	rd.forward(request, response);
%>

5.2.6.2 XML 태그

  • 표준 액션 태그
  • 사용자 태그


사용자 태그의 경우 <태그라이브러리 : 태그이름> 으로 사용할 수 있는데 Spring Framework 에서는 <spring : 태그이름> 과 같이 사용할 수 있다. 아래는 JSP 코드 예제이다.

5.2.6.3 JSP Expression Language(EL)

EL의 경우 request.getAttribute("xxx")로 데이터를 찾을 때, 가장 먼저 ServletRequest에서 해당 값을 찾는다. 만약에 여기에 없다면 Session에서 해당 값을 찾는다. 만약 여기에도 없다면 ServletContext에서 "xxx"에 해당하는 데이터를 찾는다.

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

<%
	String v = (String)request.getAttribute("msg");
%>

<%= v %>

<hr/>

${ msg }

5.2.7 MVC 디자인 패턴

  • Model, View, Controller 패턴으로 웹 개발을 많이 함. Model-View-ViewModel 혹은 MVVM 이라는 패턴도 많이 씀.
  • edu_MVC.zip 파일을 Eclipse에 존재하는 프로젝트로 import를 한 후, 관련 코드를 작성함.
  • MVC를 구현하면 자연스럽게 3-Tier Architecture가 구현됨

5.2.7.1 View

클라이언트가 보는 화면으로 HTML, CSS, JavaScript, JSP, VueJS, React 등이 있음

5.2.7.2 Model

VO(Value Object) 혹은 DTO(Data Transfer Object)로 Getter, Setter 그리고 toString()을 구현한다.

5.2.7.3 Controller

비즈니스 로직을 처리하는 부분으로 아래와 같은 처리를 함 (우리가 학습한 예제 중심으로 설명)

  1. Parameter 추출 (필요 시 유효성 검증)
  2. 서비스 메소드 호출
  3. View로 이동

5.2.7.4 Model1, Model2

MVC Model1 은 JSP에 View와 Controller가 혼용되어 있다. MVC Model2는 View는 JSP에서 Controller는 Servlet에서 처리한다.

5.2.7.5 Model : POJO

POJO("포조")라고 하는 Plain Old Java Object로 모델을 만든다. 그리고 모델의 객체는 통상 두 개로 나뉜다.

  1. 서비스 처리 객체
  2. DB 처리 객체(=DAO "다오", Data Access Object)

5.3 3일차

Spring Framework는 "IoC와 AOP를 지원하는 경량의 컨테이너 프레임워크"다. 오늘부터 Spring Framework를 본격적으로 학습함. 가장 먼저 STS를 설치했다.

5.3.1 MVC 복습

MVC는 3-Tire Architecture를 구현하도록 하는 시스템 아키텍처 패턴이다.

MVC구분 기능 처리흐름 처리구분 관련 프레임워크
View 클라이언트에게 보여지는 화면 1 (요청받음) Presentation SpringMVC
Controller 1. Parameter 추출

2. 서비스 메소드 호출

3. 출력부 이동

2 (요청확인 및 라우팅) Business SpringAOP
Model Service 객체 - 서비스 구현 3 (서비스 처리) Persistent SpringJDBC, MyBatis, SpringDataJPA(=ORM)
DAO 객체 - DB처리 4 (필요 시 DB 등 Data Source 조회)

3-Tier Architecture를 ChatGPT에게 설명을 요청했다.

ChatGPT's answer to what 3-tier architecture is

5.3.2 프레임워크(Framework)

5.3.2.1 장점 및 단점

Framework 은 반제품으로 ready-made 된 building blocks가 많아서 생산성이 높다. 그러나 이를 사용하려면 Framework을 알아야 한다.

5.3.2.2 프레임워크 예시

Presentation 영역의 프레임워크는 SpringMVC가 있다. SpringAOP는 Controller 영역의 프레임워크다. Model 영역의 프레임워크는 SpringJDBC, MyBatis, JPA가 있다. JPA의 경우, Java 객체만 만들면 SQL은 프레임워크가 만들어준다. JPA와 같은 프레임워크를 ORM 이라고 부른다. JPA는 ORM을 위한 자바 EE 표준이며 SpringDataJPA는 JPA를 쉽게 사용하기 위해 스프링에서 제공하고 있는 프레임워크다.

5.3.2.3 Spring Framework

결론적으로 Spring Framework 의 Architecture Diagram은 아래와 같다.

Spring Framework Architecture Diagram
Spring Framework Artifacts


ChatGPT의 Spring Framework 설명을 살펴보자.

ChatGPT's explanation on Spring Framwork

5.3.3 SQL 명령줄에서 Oracle DB 10g XE 접속

아래와 같이 conn 사용자명/암호 를 입력라면 DB에 접속이 된다.

Sql-명령줄-오라클.png

5.3.4 IoC 컨테이너

서블릿(Servlet)은 서블릿 컨테이너(Servlet Container)가 실행한다. 즉, 프로그램 흐름은 서블릿 컨테이너(Servlet Container)가 한다. 개발자가 할 수 없다. 개발자는 서블릿 컨테이너(Servlet Container)가 사전에 정한 프로세스를 준수해야 한다. 이러한 컨테이너를 IoC 컨테이너라고도 부름. 참고로 IoC는 Inversion of Control의 약자다.

5.3.5 Bean이란

Bean은 컨테이너가 관리하는 자바 객체다. 여기서 관리한다는 것은 객체를 생성하거나 삭제하는 일 그리고 메소드를 호출하는 일 등을 말한다. 스프링 프레임워크에서 Bean 을 사용하면 스프링 프레임워크가 Bean을 생성하고 초기화한다.


Bean 객체는 각 Layer별로 설정할 수 있다. 즉, Presentation, Business, Persistent 각각 설정이 가능하다. 개발을 하면 Bean 객체가 많이 늘어난다. 이 경우에는 <import> 태그를 통해 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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://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
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<!--<bean id="tv" class="polymorphism.SamsungTV"></bean>-->

	<import resource="context-tv.xml"/>
</beans>


위에서 import 한 context-tv.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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://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
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
	<bean id="tv" class="polymorphism.SamsungTV" init-method="init" destroy-method="destroy"></bean>
	
</beans>

Bean 객체 생성 시 lazy-init 속성을 주면 해당 bean을 미리 생성하지 않고, 클라이언트가 요청하는 시점에 생성한다. 결국, 메모리 관리를 더 효율적으로 할 수 있다. 아래와 같이 bean 객체에 lazy-init 속성을 "true"로 하면, 객체 생성을 요청하는 경우에만 init-method인 init()이 호출됨을 알 수 있다. lazy-init 속성의 기본값은 "false"이며 GenericXmlApplicationContext 객체가 생성이 될 때, 즉 스프링 컨테이너가 구동이 될 때, bean 객체를 생성한다.

<?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"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://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
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
	<bean id="tv" class="polymorphism.SamsungTV" init-method="init" destroy-method="destroy" lazy-init="true"></bean>
	
</beans>


그리고 scope 속성을 통해 생성하는 Bean을 singleton(기본값)으로 할지 아니면 요청하는 만큼 새로 생성할지(prototype)를 설정할 수 있다.

<?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"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://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
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
	<bean id="tv" class="polymorphism.SamsungTV" init-method="init" destroy-method="destroy" lazy-init="true" scope="prototype"></bean>
	
</beans>

5.3.6 이클립스 꿀팁

  • Ctrl-Alt-↑ 또는 ↓ : 현재 선택한 줄이 화살표 방향에 맞게 그대로 복사가 된다.
  • Alt + Shift + T : 인터페이스 자동 생성
  • Alt + Shift + S : Getters 와 Setters 자동 생성

5.3.7 스프링 프레임워크가 해주는 일

  1. Inversion of Control 혹은 IoC라는 제어의 역행을 지원한다.
  2. Bean 객체를 자동으로 생성한다.
  3. 컨테이너가 생성한 객체를 검색하는 Dependency Lookup을 지원한다.
  4. Dependency Injection 혹은 DI라고 하는 의존성 주입을 통해 객체 사이의 의존관계를 자동으로 설정한다.
  5. AOP를 통해 관심 분리(separtion of concerns)를 하여 횡단 관심(crosscutting concerns)이 아닌 핵심 관심(core concerns)에 집중하게 한다.

5.3.8 인젝션 방법

인젝션은 멤버변수를 초기화하는 것을 의미한다. 인젝션생성자 인젝션Setter 인젝션 두 가지가 있다.

5.3.8.1 생성자 인젝션

아래와 같이 constructor-arg 태그를 활용한다.

<?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"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://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
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
	<bean id="tv" class="polymorphism.SamsungTV" init-method="init" destroy-method="destroy" lazy-init="true" scope="prototype">
		<constructor-arg index="0" ref="apple"></constructor-arg>
		<constructor-arg index="1" value="2700000"></constructor-arg>
	</bean>
		
</beans>

5.3.8.2 Setter 인젝션

Setter 인젝션의 경우 두 가지 방법으로 설정이 가능하다.

5.3.8.3 Property 태그 사용

아래와 같이 property 태그를 활용한다.

<?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"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://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
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
	<bean id="tv" class="polymorphism.SamsungTV" init-method="init" destroy-method="destroy" lazy-init="true" scope="prototype">
		<property name="speaker" ref="apple"></property>
		<property name="price" value="2700000"></property>
	</bean>
		
</beans>

5.3.8.4 p 네임스페이스 사용

네임스페이스만 적절히 선언하고 사용하면 된다. (상세 코드는 생략)

  • (참조형 변수) p:변수명-ref="참조할 객체의 이름이나 아이디"
  • (기본형 번수) p:변수명="설정할 값"

5.3.9 Annotation

스프링 프레임워크에서 의존성을 주입할 때, 앞에선느 XML 설정을 통해 처리했지만, 그 방법 외에 어노테이션(Annotation)을 통해서도 의존성 주입이 가능하다.

의존성 주입 방법 장점 단점
XML 1. 자바 소스 수정 불필요

2. 유지보수 간편

1. XML 설정 부담

2. XML 설정 해석 필요(소스 코드로는 객체 의존성 관계 확인 불가)

Annotation 1. XML 설정 부담 없음

2. 직관적 (의존관계에 대한 정보가 자바소스에 있음)

1. 자바 소스를 수정하지 않고서는 의존성 관계 수정 불가

2. 설정이 소스 코드 전체에 흩어져 있음

5.3.9.1 스프링 프레임워크가 추가로 제공하는 어노테이션

컨트롤러 객체는 @Controller, 서비스 개체는 @Service, DAO 객체는 @Repository로 등록을 한다. 스프링 프레임워크에서는 @Component를 상속하여 다음과 같은 세 개의 어노테이션을 추가로 제공한다.

어노테이션 위치 의미
@Service XXXServiceImpl 비즈니스 로직을 처리하는 Service 클래스
@Repository XXXDAO 데이터베이스 연동을 처리하는 DAO 클래스
@Controller XXXController 사용자 요청을 제어하는 Controller 클래스

5.3.10 DTO vs. VO

과거에는 실무에서 항상 Value Object, 즉 VO라고만 불렀으나 해당 VO가 여러 Layer를 돌아다니게 되면서 Data Transfer Object, 즉 DTO라고 부르게 되었다고 함. 그래서 현재 실무에서는 항상 DTO라고 부름. 학술적으로는 DTO와 VO가 다르지만, 실무에선느 혼용해서 사용했음. 과거에는 VO라고 했으나, 현재는 DTO라고 부름.


※ 정확한 설명은 아니지만 개념적으로 이렇게 이해해도 된다. "VO는 Setter가 존재하지 않는다"는 등의 설명이 있지만 실무적으로는 "동일하며, 모두 다 DTO다." 라고 생각하는 게 나을 수도 있다. (관련 링크 : https://mosei.tistory.com/entry/DTO-vs-VO)

5.4 4일차

업데이트 예정

5.5 5일차

업데이트 예정

6 강의자료

https://naver.me/5DbzAFTA

7 참고자료