Chapter#01 : [Spring] STS 개발환경 - 다운로드 및 작업 경로 지정
Chapter#02 : [Spring] STS 개발환경 - IDE 환경 설정
Chapter#03 : [Spring] Spring Framework 웹 프로젝트 만들기
Chapter#04 : [Spring] Spring MVC 패턴( Model2 아키텍쳐 ) 구현 하기
Chapter#05 : [Spring] Component-Scan을 사용하는 Annotation 기반 설정
Chapter#06 : [Spring] MyBatis를 이용한 Oracle 데이터베이스 CRUD
#1. Model1 아키텍처
90년대 말부터 2000년대 초반까지 자바 기반의 웹 어플리케이션 개발에 사용된 아키텍처는 Model1이다.
Model1 아키텍처는 JSP와 JavaBeans만 사용하여 웹을 개발하는 구조로 전체적인 구조와 흐름은 다음과 같다.
Model1 아키텍처를 구성하는 요소 중에서 가장 먼저 확인할 것은 Model 기능의 JavaBeans다.
Maven Build 내역
자바에서 Bean이라는 용어는 객체를 의미하므로 JavaBean이라고 하면 결국
데이터베이스 연동에 사용되는 자바 객체를 의미한다.
Model1 아키텍처에서는 JSP 파일이 가장 중요한 역할을 수행하는데,
이는 JSP가 Controller와 View 기능을 모두 처리하기 때문이다.
Controller은 JSP 파일에 작성된 자바 코드를 의미하는데 JSP에 작성된 모든 자바 코드를 Controller라고 하지는 않는다.
일반적으로 Controller는 사용자의 처리와 관련된 자바 코드를 의미하며 대부분 다음과 같은 코드들로 구성된다.
기능 | 사용 예 |
사용자 입력 정보 추출 | String title = request.getParameter( "title" ); |
DB 연동 처리 | SampleVO vo = new SampleVO( ); vo.setTitle( title ); SampleDao sampleDAO = new SampleDAOJDBC( ); SampleVO sample = sampleDAO.selectSample( vo ); |
화면 내비게이션 | response.sendRedirect( "selectSampleList.jsp" ); |
그리고 JSP에서는 Model을 사용하여 검색한 데이터를
사용자가 원하는 화면으로 제공하기 위해서 다양한 마크업( markup )을 사용한다.
이때 사용되는 대표적인 마크업이 바로 HTML과 CSS이다.
Model1은 JSP Controller 기능과 View 기능을 모두 처리하기 때문에 역할 구분이 명확하지 않고,
JSP 파일에 대한 디버깅과 유지보수에 많은 어려움이 생길 수밖에 없다.
따라서 Model1 구조는 적은 개발 인력으로 간단한 프로젝트를 수행하는 때라면 사용할 수 있지만,
Enterprise급의 복잡한 시스템에는 부적절한 모델이라 할 수 있다.
그래서 등장한 것이 Model2, 즉 MVC( Model View Controller ) 아키텍처다.
Model2를 이니셜만 이용하여 MVC라고 부르는데,
이는 Model, View, Controller 요소로 기능을 분리하여 개발하기 때문이다.
결국, MVC는 Model1 구조의 단점을 보완하기 위해 만들어진 구조라고 생각할 수 있다.
#2. MVC 아키텍처( Model2 아키텍처 )
Model1 아키텍처는 시스템 규모가 크고, 기능이 복잡한 Enterprise System을 개발한다면
Model1 아키텍처는 적합하지 않다.
Model1 아키텍처가 엔터프라이즈 시스템에 적합하지 않은 가장 큰 이유는
JSP 파일에 Java로직과 화면 디자인이 통합되어 유지보수가 어렵기 때문이다.
Java 개발자 입장에서 JSP 파일에 자바 로직과 화면 디자인이 통합되어 있으면,
우선 수정할 자바 로직을 찾기부터가 쉽지 않다.
그리고 디자이너가 디자인을 변경할 때도 복잡한 코드들 때문에 어려움을 느낄 수 밖에 없다.
이런 Model1 아키텍처의 문제를 해결하기 위해 고안된 웹 개발 모델이 Model2 아키텍처, 즉 MVC 아키텍처다.
Model2 아키텍처에서 가장 중요한 특징은 Controller의 등장이며, 이 Controller는 서블릿 클래스를 중심으로 구현한다.
Model2 아키텍처에서는 기존에 JSP가 담당당했던 Controller 로직이 서블릿으로 구현한 Controller로 이동한 것이다.
따라서 기존에 Model1 아키텍처로 개발한 프로그램에서 JSP 파일에 있는
컨트롤 자바 코드만 Controller로 이동하면 Model2 아키텍처가 된다.
결과적으로 Controller 로직이 사라진 JSP에는 View와 관련된 디자인만 남게 되어 디자이너는 JSP 파일을 관리하고,
자바 개발자는 Controller와 Model만 관리하면 된다.
다음은 MVC 아키텍처에서 각 요소의 기능과 개발 주제를 정리한 표다.
기능 | 구성요소 | 개발 주체 |
Model | VO, DAO 클래스 | Java 개발자 |
View | JSP | Web 디자이너 |
Controller | Servlet 클래스 | Java 개발자 또는 MVC 프레임워크 |
MVC 아키텍처에서 가장 주용한 부분이 Controller다.
그리고 Struts나 Spring( MVC )같은 MVC프레임워크를 사용하는 이유는
바로 이런 효율적인 Controller를 제공해주기 때문이다.
스프링 MVC는 DispatcherServlet을 시작으로 다양한 객체들이 상호작용하며 클라이언트의 요청을 처리한다.
#3. Spring MVC 패턴을 적용한 샘플 구현
1) EL과 JSTL을 이용한 화면 처리
Model1 아키텍처를 Model2, 즉 MVC로 변화했던 결정적인 이유는 JSP 파일에서 자바 코드를 제거하기 위해서이다.
사실 Controller 로직은 사용자 입력 정보 추출, DB연동 처리, 화면 내비게이션 같은 자바 코드만을 의미하기 때문에
현재 JSP파일에 남아있는 자바 코드는 정확하게는 Controller에 해당하는 로직은 아니다.
만일 이런 자바 코드 조차도 JSP 파일에서 제거하고 싶다면, JSP에서 제공하는
EL( Expression Language )과 JSTL( JSP Standard Tag Library )를 이용하면 된다.
EL( Expression Language )과 JSTL( JSP Standard Tag Library )
EL은 JSP 2.0에서 새로 추가된 스크립트 언어로서, 기존의 표현식( Expression )을 대체하는 표현 언어다. 예를 들어 세션에 저장되어 있는 사용자 이름을 JSP 화면에 출력할 때, 기존에는 <%= session.getAttribute("userName") %> 이렇게 표현했다면 EL을 이용하면 ${userName}으로 표현할 수 있다. 따라서 JSP에서 표현식을 제거할 수 있다.
JSTL이란, JSP 프로그램을 개발하다 보면 스크립트릿( scriptlet )에서 if, for, switch등과 같은 자바 코드를 사용해야 하는 때가 있다. JSTL은 JSP에서 사용해야 하는 이런 자바 코드들을 태그 형태로 사용할 수 있도록 지원한다. 따라서 JSTL을 이용하면 JSP 파일에서 자바코드를 제거할 수 있다.
EL과 JSP에 관하여 더 많은 정보와 학습을 원한다면 tutorialspoint의 JSP Tutorial을 참조하기 바란다.
2) Servlet-API와 JSTL 라이브러리 추가
pom.xml을 수정하여 Maven Repository에 servlet과, JSTL 라이브러리 추가한다.
pom.xml
~~ 이 하 생 략 ~~
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
~~ 이 하 생 략 ~~
servlet-api, jstl을 추가해주고 Maven을 Build 한다.
※ 해당 포스팅은 Goals : package로 설정되 있다는 것을 전제로 진행한다.
Run Configurations 창을 열고 이전 Maven Build 내역을 확인해 보자.
Maven Build시 Goals : package로 설정되지 않았다면 아래와 같은 작업을 진행한다.
Edit Configuration 창이 팝업되면 Goals 항목에 package 값을 적어주고 Maven Build하면 된다.
Goals | Package |
pom.xml에서 마우스 오른쪽 버튼을 클릭하고 Run As → Maven Build( 위 )을 선택한다.
BUILD SUCCESS 되면 servlet-api, jstl 라이브러리가 추가되었다.
작업중인 프로젝트의 Maven Dependencies를 열고 확인해 보자.
servlet-api, jstl 라이브러리가 추가되었다.
3) Model 생성
① VO파일 생성
프로젝트에 service라는 Package를 새로 생성한다.
프로젝트의 src/main/java를 선택하고 마우스 오른쪽 버튼을 눌러 New → Package를 클릭한다.
New Java Package 팝업창이 나타나면 com.spring.web.example.service라고 입력한다.
service 패키지가 생성 완료되었다면 생성한 service 패키지를 선택하고 마우스 오른쪽 버튼을 클릭한다.
New → Class 를 선택한다.
New Java Class 팝업창이 나타나면 exampleVO라고 입력하고 Finish 버튼을 클릭한다.
그럼 exampleVO.java 파일이 생성된다.
exampleVO.java 파일을 열고 아래와 같이 변수를 선언한다.
ExampleVO.java
package com.spring.web.example.service;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
public class ExampleVO {
private String exampleNumber;
private String exampleId;
private String exampleName;
private String exampleTitle;
private String exampleInfo;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date exampleDate;
}
② Getter and Setter 만들기
ExampleVO.java 파일의 ExampleVO 클래스 안에서 마우스 오른쪽 버튼을 클릭한다.
Source → Generate Getters and Setters를 선택한다.
Generate Getters and Setters 팝업창이 오픈되면 우측의 나타나면 Select All 버튼을 클릭한다.
그럼 위에서 생성한 모든 변수가 체크된다.
Access modifier를 public으로 설정하고 Generate를 클릭하면
아래와 같이 ExampleVO.java 파일에 선언한 변수의 Getter와 Setter가 자동으로 생성된다.
ExampleVO.java
package com.spring.web.example.service;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
public class ExampleVO {
private String exampleNumber;
private String exampleId;
private String exampleName;
private String exampleTitle;
private String exampleInfo;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date exampleDate;
public String getExampleNumber() {
return exampleNumber;
}
public void setExampleNumber(String exampleNumber) {
this.exampleNumber = exampleNumber;
}
public String getExampleId() {
return exampleId;
}
public void setExampleId(String exampleId) {
this.exampleId = exampleId;
}
public String getExampleName() {
return exampleName;
}
public void setExampleName(String exampleName) {
this.exampleName = exampleName;
}
public String getExampleTitle() {
return exampleTitle;
}
public void setExampleTitle(String exampleTitle) {
this.exampleTitle = exampleTitle;
}
public String getExampleInfo() {
return exampleInfo;
}
public void setExampleInfo(String exampleInfo) {
this.exampleInfo = exampleInfo;
}
public Date getExampleDate() {
return exampleDate;
}
public void setExampleDate(Date exampleDate) {
this.exampleDate = exampleDate;
}
}
③ 생성자( Constructor ) 함수 만들기
ExampleVO.java 파일의 ExampleVO 클래스 내부의 가장 하단에서 마우스 오른쪽 버튼을 클릭한다.
Source → Generate Constructor using Fields를 선택한다.
Generate Constructor using Fields 팝업창이 오픈되면 우측의 나타나면 Select All 버튼을 클릭한다.
그럼 위에서 생성한 모든 변수가 체크된다.
Access modifier를 public으로 설정하고 Generate를 클릭하면
아래와 같이 ExampleVO.java 파일에 선언한 변수의 ExampleVO 함수( Constructor )가 생성된다.
ExampleVO.java
package com.spring.web.example.service;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
public class ExampleVO {
private String exampleNumber;
private String exampleId;
private String exampleName;
private String exampleTitle;
private String exampleInfo;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date exampleDate;
~~ 이 하 생 략 ~~
public ExampleVO(String exampleNumber, String exampleId, String exampleName, String exampleTitle, String exampleInfo, Date exampleDate) {
super();
this.exampleNumber = exampleNumber;
this.exampleId = exampleId;
this.exampleName = exampleName;
this.exampleTitle = exampleTitle;
this.exampleInfo = exampleInfo;
this.exampleDate = exampleDate;
}
}
Constructor 함수는 외부에서 해당 ExampleVO 클래스에 데이터를 전송할때 해당 함수를 호출하게 되는데,
이때 ExampleVO 클래스에 데이터를 전달받는 영학을 한다.
ExampleVO.java
package com.spring.web.example.service;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
public class ExampleVO {
private String exampleNumber;
private String exampleId;
private String exampleName;
private String exampleTitle;
private String exampleInfo;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date exampleDate;
public String getExampleNumber() {
return exampleNumber;
}
public void setExampleNumber(String exampleNumber) {
this.exampleNumber = exampleNumber;
}
public String getExampleId() {
return exampleId;
}
public void setExampleId(String exampleId) {
this.exampleId = exampleId;
}
public String getExampleName() {
return exampleName;
}
public void setExampleName(String exampleName) {
this.exampleName = exampleName;
}
public String getExampleTitle() {
return exampleTitle;
}
public void setExampleTitle(String exampleTitle) {
this.exampleTitle = exampleTitle;
}
public String getExampleInfo() {
return exampleInfo;
}
public void setExampleInfo(String exampleInfo) {
this.exampleInfo = exampleInfo;
}
public Date getExampleDate() {
return exampleDate;
}
public void setExampleDate(Date exampleDate) {
this.exampleDate = exampleDate;
}
public ExampleVO(String exampleNumber, String exampleId, String exampleName, String exampleTitle, String exampleInfo, Date exampleDate) {
super();
this.exampleNumber = exampleNumber;
this.exampleId = exampleId;
this.exampleName = exampleName;
this.exampleTitle = exampleTitle;
this.exampleInfo = exampleInfo;
this.exampleDate = exampleDate;
}
}
4) Controller 수정
이전 포스팅에서 생성한 ExampleController의 내용을 아래와 같이 수정하여 준다.
exampleInfo( ), exampleList( )라는 2개의 함수를 추가하였다.
ExampleController.java
package com.spring.web.example.controller;
import com.spring.web.example.service.ExampleVO;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
@Controller
public class ExampleController {
@RequestMapping(value = "/example.do", method = RequestMethod.GET)
public String ExampleMain() {
return "example";
}
@RequestMapping(value = "/exampleList.do", method = RequestMethod.GET)
public String exampleList(Model model) throws Exception {
// 포맷터
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
List<ExampleVO> exampleList = new ArrayList<>();
ExampleVO exampleVO = null;
exampleVO = new ExampleVO(
'EX00000001'
, "captain"
, "캡틴 아메리카"
, "First Avenger"
, null
, formatter.parse("2011-07-28")
);
exampleList.add(exampleVO);
exampleVO = new ExampleVO(
'EX00000002'
, "ironman"
, "아이언 맨"
, "I Am Iron Man"
, null
, formatter.parse("2008-04-30")
);
exampleList.add(exampleVO);
exampleVO = new ExampleVO(
'EX00000003'
, "thor"
, "토르"
, "God of Thunder"
, null
, formatter.parse("2011-04-28")
);
exampleList.add(exampleVO);
model.addAttribute("exampleTitle", "영화 타이틀");
model.addAttribute("exampleList", exampleList);
return "exampleList";
}
@RequestMapping(value = "/exampleInfo.do", method = RequestMethod.GET)
public String exampleInfo(HttpServletRequest request, Model model) throws Exception {
if(request.getParameter("number").isEmpty() == false) {
if(request.getParameter("number").equals("EX00000001") == true) {
model.addAttribute("exampleId", "captain");
model.addAttribute("exampleName", "캡틴 아메리카");
model.addAttribute("exampleTitle", "First Avenger");
model.addAttribute("exampleInfo",
"캡틴 아메리카의 '캡틴'은 초창기 코믹스에서는 말 그대로 미국의 대장이라는 뉘앙스로 쓰였으나,"
+ "<br/>시간이 흘러 캡틴이라는 말에는 어벤져스의 넘버 원이라는 의미도 포함되었다."
);
model.addAttribute("exampleDate", "2011-07-28");
}
if(request.getParameter("number").equals("EX00000002") == true) {
model.addAttribute("exampleId", "ironman");
model.addAttribute("exampleName", "아이언 맨");
model.addAttribute("exampleTitle", "I Am Iron Man");
model.addAttribute("exampleInfo",
"억만장자 천재 발명가인 토니 스타크가 심장에 치명적인 상처를 입은 자신의 목숨을 지키며"
+ "<br/>동시에 세계를 지킬 강화 슈트를 제작하고 과학의 결정체로 만들어진 슈트를 입고"
+ "<br/>아이언맨이 되어 범죄와 싸워나간다."
);
model.addAttribute("exampleDate", "2008-04-30");
}
if(request.getParameter("number").equals("EX00000003") == true) {
model.addAttribute("exampleId", "thor");
model.addAttribute("exampleName", "토르");
model.addAttribute("exampleTitle", "God of Thunder");
model.addAttribute("exampleInfo",
"아스가르드의 주신 오딘은 아들 토르에게 인간성과 겸손함을 배우게 하기 위해 기억을 지우고"
+ "<br/>절름발이 의사 도널드 블레이크의 육신에 토르를 내려보냈다."
);
model.addAttribute("exampleDate", "2011-04-28");
}
}
return "exampleInfo";
}
}
생성한 exampleInfo( ), exampleList( ) 메소드의 return 타입을 보면 String을 사용하며
메소드의 마지막 return을 실행할때 View( exampleInfo.jsp, exampleList.jsp ) 이름을 문자열로 리턴한다.
Srping 컨테이너는 리턴된 문자열에 ViewResolver를 적용하여 적절한 JSP를 찾아 실행한다.
그리고 매개변수로 사용한 Model View에 리턴할 정보를 모두 저장한다.
이렇게 Model에 저장된 데이터는 JSP에서 EL을 이용하여 출력할 수 있다.
5) View( JSP ) 생성
① 게시판 - 포스팅 상세 정보 페이지 제작
WEB-INF → views 디렉토리를 선택하고 마우스 우클릭하여 exampleList.jsp 파일을 생성한다.
New → JSP File을 선택한다.
New JSP File창이 팝업되면 File name을 exampleInfo.jsp 라고 작성한다.
exampleList.jsp 파일이 생성되면 아래와같이 내용을 작성한다.
exampleInfo.jsp
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>${exampleTitle}</title>
</head>
<style>
table, thead, tbody { border: 1px solid #000000;border-collapse:collapse; }
th, td { border:1px solid #000000;padding:10px; }
tfoot { text-align:right; }
</style>
<body>
<h3>${exampleName} : ${exampleTitle}</h3>
<table>
<tbody>
</tbody>
<tr>
<th>제목</th>
<td>${exampleTitle}</td>
</tr>
<tr>
<th>이름</th>
<td>${exampleName}(${exampleId})</td>
</tr>
<tr>
<th>개봉일</th>
<td>${exampleDate}</td>
</tr>
<tr>
<th>내용</th>
<td>${exampleInfo}</td>
</tr>
</tbody>
</table>
</body>
</html>
② 게시판 - 포스팅 목록 페이지 제작
WEB-INF → views 디렉토리를 선택하고 마우스 우클릭하여 exampleInfo.jsp 파일을 생성한다.
New → JSP File을 선택한다.
New JSP File창이 팝업되면 File name을 exampleList.jsp 라고 작성한다.
exampleList.jsp 파일이 생성되면 아래와같이 내용을 작성한다.
exampleList.jsp
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<title>영화 리스트</title>
</head>
<style>
table, thead, tbody, tfoot { border:1px solid #000000;border-collapse:collapse; }
tfoot { text-align:right; }
th, td { border:1px solid #000000;padding:10px; }
tbody > tr > td { cursor:pointer;cursor:hand; }
tbody > tr > td:first-child { text-align:center; }
button { cursor:pointer;cursor:hand; }
</style>
<body>
<h1>${headTitle}</h1>
<table>
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>이름</th>
<th>개봉일</th>
</tr>
</thead>
<tbody>
<c:forEach var="example" items="${exampleList}">
<tr onClick="window.location.href='./exampleInfo.do?number=${example.exampleNumber}'">
<td>${example.exampleNumber}</td>
<td>${example.exampleTitle}</td>
<td>${example.exampleName}(${example.exampleId})</td>
<td><fmt:formatDate value="${example.exampleDate}" pattern="yyyy-MM-dd"/></td>
</tr>
</c:forEach>
</tbody>
</table>
</body>
</html>
③ < jsp : forward >를 이용한 시작 페이지 설정
프로젝트의 시작 페이지를 변경한다.
index.jsp 파일을 열고 아래와 같이 <jsp:forward>설정을 추가하여 준다.
index.jsp
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<jsp:forward page="/exampleList.do"/>
이제 해당 웹 프로젝트를 열고 첫 페이지에 접속하면
게시판 목록 페이지가 기본 페이지로 설정될 것이다.
#4. Apachat Tomcat을 실행하여 Spring 프로젝트 결과 확인
STS에서 Apache Tomcat을 실행한다.
브라우저를 열고 http://localhost:8181/spring_web/ 혹은 http://localhost:8181/spring_web/exampleList.do를 입력한다.
그럼 위와같이 해당 포스팅에서 제작한 웹 사이트의 게시판의 기능을 수행하는
프로젝트의 결과가 노출되는것을 확인 할 수 있다.
'Spring Web > Spring Framework' 카테고리의 다른 글
[Spring] MyBatis를 이용한 Oracle 데이터베이스 CRUD 기능 사용 (2) | 2022.07.06 |
---|---|
[Spring] Component-Scan을 사용하는 Annotation 기반 설정 (0) | 2022.06.17 |
[Spring] Spring Framework 웹 프로젝트 만들기 (0) | 2021.12.23 |
[SpringBoot] Windows환경에 IntelliJ 설치하기 (0) | 2021.12.18 |
[Spring] STS 개발환경 - IDE 환경 설정 (0) | 2021.06.27 |