RESTFul-API SampleCoding (Java,SpringBoot,Maven,myBatis) #3

 

 

   이번 포스팅에서는 앞서 만든 Project에 Model(DAO)를 추가하고 실제 DB에 저장된 데이터를 화면에 출력한다.   기본적으로 DB Table의 구조와 동일한 Model Class를 생성하고 해당 객체를 List 형태로 조회해서 사용하는 것을 기본 Base로 하겠다.

 

   먼저 PostgreSQL에서 Sample Database로 사용할 DVDRental 의 ERD를 살펴보자.

[ DVD Rental ER Model ]

   이번 포스팅에서 사용할 Table은 'staff' Table 이다.   관리자 정보가 보관되는 Table로 ID 및 Password 등에 대한 정보를 담고 있는 Table 이다.   Web Page에서의 Login 등의 처리에서 사용될 Data이다.   이번 포스팅에서는 단순하게 staff의 목록을 읽어와서 화면에 출력하는 과정에 대한 설명을 진행한다.

 

   'staff' Table의 상세 내용은 아래와 같다.

Column Name # Data Type Collation Not Null Default Java Type
staff_id 1 serial4   v nextval('staff_staff_id_seq'::regclass) int
first_name 2 varchar(45) default v   String
last_name 3 varchar(45) default v   String
address_id 4 int2   v   int
email 5 varchar(50) default     String
store_id 6 int2   v   int
active 7 bool   v true boolean
username 8 varchar(16) default v   String
password 9 varchar(40) default     String
last_update 10 timestamp   v now() Timestamp
picture 11 bytea       byte[]

 

 

    Model Class를 작성하기 위해서 필요한 Library를 'pom.xml'에 추가한다.   ToString을 위한 Library 와 @SerializedName 을 위한 Library 이다.   @SerializedName 애노테이션은 JSON Data Field의 이름과 Java Object의 이름 간의 Mapping을 지정할 때 사용된다.   주로 JSON Data와 Java Object 간의 Filed 이름이 다를 경우 유용하게 사용된다.

		<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
		<dependency>
		    <groupId>org.apache.commons</groupId>
		    <artifactId>commons-lang3</artifactId>
		</dependency>

		<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
		<dependency>
		    <groupId>com.google.code.gson</groupId>
		    <artifactId>gson</artifactId>
		</dependenc

pom.xml
0.00MB

   

 

   Project에 'kr.co.iamwhatiam.dvdrental.Common.Model' Package를 만들든 Folder를 생성해서 Directory 구조를 만들든 Common 밑에 Model Directory를 생성해서 그 안에 'DVDRentalBaseModel.java' , 'Staff.java' Class를 생성한다.

[ Model Class 생성 ]

 

 

일단 'DVDRentalBaseModel.java'는 아래의 코드처럼 빈상태로 둔다.   추후 공통으로 사용될 값들을 코딩하기 위해서 남겨 둔다.

package kr.co.iamwhatiam.dvdrental.Common.Model;

public class DVDRentalBaseModel {

}

 

 

그리고 'Staff.java' Class는 아래의 코드처럼 staff Table과 동일한 형태로 Model 을 구성한다.

package kr.co.iamwhatiam.dvdrental.Common.Model;

import java.sql.Timestamp;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import com.google.gson.annotations.SerializedName;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Staff extends DVDRentalBaseModel {

	@SerializedName("staff_id")
	private int staffId;

	@SerializedName("first_name")
	private String firstName;

	@SerializedName("last_name")
	private String lastName;

	@SerializedName("address_id")
	private int addressId;

	@SerializedName("email")
	private String email;

	@SerializedName("store_id")
	private int storeId;

	@SerializedName("active")
	private boolean active;

	@SerializedName("username")
	private String username;

	@SerializedName("password")
	private String password;

	@SerializedName("last_update")
	private Timestamp lastUpdate;

	@SerializedName("picture")
	private byte[] picture;

	public String toString() {
		  return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
	}

}

Staff.java
0.00MB

 

 

DB의 staff table의 목록을 화면에 출력하는 것을 실습으로 진행할 것이기 때문에 staff 관련 항목을 모아둘 package를 생성한다.   기본적으로 사용될 Controller, Mapper, Service Directory를 생성한다.

[ Staff Table에 관련된 항목을 처리할 파일을 모아둘 Package 구조를 생성한다. ]

 

 

   두개의 Method를 생성할 것이다.   하나는 특정 검색어에 의해서 Staff의 목록을 조회하는 Method이고, 다른 하나는 Staff ID를 조건으로 Staff의 상세 정보를 조회하는 Method를 만들 것이다.

   - getStaffInfoList : 특정 검색 조건에 의해 Staff 목록을 조회
   - getStaffInfoByUsername : Staff username를 기준으로 Staff의 상세 정보 조회

 

 

StaffMapper Interface 생성

   'src/main/java/kr/co/iamwhatiam/dvdrental/Web/Staff/Mapper'에 'StaffMapper.java' Interface를 만든다.   getStaffInfoList는 Map 객체로 인수를 받아서 List 형태로 Return 할 것이고, getStaffInfoByID는 Staff 구조체로 인수를 받아서 단일 Data로 Return 할 것이다.

[ StaffMapper Interface 생성 ]

package kr.co.iamwhatiam.dvdrental.Web.Staff.Mapper;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Mapper;

import kr.co.iamwhatiam.dvdrental.Common.Model.Staff;

@Mapper
public interface StaffMapper {

	public List<Staff>	getStaffInfoList(Map<String, String> parameter)	throws Exception;			// 검색어에 의해서 Staff 목록을 조회한다.

	public Staff		getStaffInfoByUsername(Staff staffInfo)				throws Exception;			// Staff ID를 검색조건으로 Staff의 상세 정보를 조회한다.

}

StaffMapper.java
0.00MB

 

 

StaffServiceImpl Class 생성

   'src/main/java/kr/co/iamwhatiam/dvdrental/Web/Staff/Service'에 'StaffServiceImpl.java' Class를 만든다.   이 Class 도 역시나 getStaffInfoList는 Map 객체로 인수를 받아서 List 형태로 Return 할 것이고, getStaffInfoByID는 Staff 구조체로 인수를 받아서 단일 Data로 Return 할 것이다.

[ StaffServiceImpl Class 생성 ]

package kr.co.iamwhatiam.dvdrental.Web.Staff.Service;

import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kr.co.iamwhatiam.dvdrental.Common.Model.Staff;
import kr.co.iamwhatiam.dvdrental.Web.Staff.Mapper.StaffMapper;

@Service
public class StaffServiceImpl implements StaffService {

	private static final Logger logger = LoggerFactory.getLogger(StaffServiceImpl.class);

	@Autowired
	private StaffMapper staffMapper;

	@Override
	public List<Staff> getStaffInfoList(Map<String, String> parameter)	throws Exception {
		logger.info("[StaffServiceImpl.getStaffInfoList] Start >> parameter >> " + parameter.toString());
		return staffMapper.getStaffInfoList(parameter);
	}

	@Override
	public Staff getStaffInfoByUsername(Staff staffInfo) throws Exception {
		logger.info("[StaffServiceImpl.getStaffInfoByUsername] Start >> parameter >> " + staffInfo.toString());
		return staffMapper.getStaffInfoByUsername(staffInfo);
	}

}

StaffServiceImpl.java
0.00MB

 

 

StaffService Interface 생성

   'src/main/java/kr/co/iamwhatiam/dvdrental/Web/Staff/Service'에 'StaffService.java' Interface를 만든다.   이 Interface 도 getStaffInfoList는 Map 객체로 인수를 받고, getStaffInfoByID는 Staff 구조체로 인수를 받을 것이다.

[ StaffService Interce 생성 ]

package kr.co.iamwhatiam.dvdrental.Web.Staff.Service;

import java.util.List;
import java.util.Map;

import kr.co.iamwhatiam.dvdrental.Common.Model.Staff;

public interface StaffService {

	// 검색어에 의해서 Staff 목록을 조회한다.
	public List<Staff> getStaffInfoList(Map<String, String> parameter) throws Exception;

	// Staff username을 검색조건으로 Staff의 상세 정보를 조회한다.
	public Staff getStaffInfoByUsername(Staff staffInfo) throws Exception;

}

StaffService.java
0.00MB

 

'src/main/java/kr/co/iamwhatiam/dvdrental/Web/Staff/Controller' Package 안에 'StaffController.java' Class를 생성하자.

[ StaffController Class 생성 ]

package kr.co.iamwhatiam.dvdrental.Web.Staff.Controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.ModelAttribute;
import kr.co.iamwhatiam.dvdrental.Common.Model.Staff;
import kr.co.iamwhatiam.dvdrental.Web.Staff.Service.StaffService;


@Controller
public class StaffController {

	private static final Logger logger = LoggerFactory.getLogger(StaffController.class);

	@Autowired
	private StaffService staffService;


	// get Staff Info List
	@GetMapping("/Staff/staff201.do")
	public String getStaffInfoList(@ModelAttribute("staffInfo") Staff staffInfo, Model model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		Map<String, String> parameter = new HashMap<String, String>();

		logger.info("[StaffController.getStaffInfoList] Start >> staffInfo >> " + staffInfo.toString());

		// Staff Info 검색 조건으로 Staff의 first_name 및 last_name 을 OR 검색조건으로 사용할 것이다.
		if (StringUtils.isEmpty(staffInfo.getFirstName())) {
			staffInfo.setFirstName("");
		}

		if (StringUtils.isEmpty(staffInfo.getLastName())) {
			staffInfo.setLastName("");
		}

		parameter.put("firstName", staffInfo.getFirstName());
		parameter.put("lastName" , staffInfo.getLastName());
		model.addAttribute("parameter", parameter);

		List<Staff> staffInfoList = staffService.getStaffInfoList(parameter);
		model.addAttribute("staffInfoList", staffInfoList);

		return "/Staff/staff201";
	}

}

StaffController.java
0.00MB


   Controller, Service, ServiceImpl, Mapper 에 대한 코드를 작성했다.   지금 작성한 순서는 흐름의 반대로 작성한 것이다.

   1. URL Request : Web Browser에서 '/Staff/staff201.do' URL로 요청을 보낸다.
   2. Controller : Request 받은 Controller는 전달된 Parameter를 정리하여 Service의 'getStaffInfoList' Method에 전달한다.
   3. Service : Service는 요청을 서비스 구현체(ServiceImpl)로 전달한다.
   4. Service Implement : Service Implement는 Mapper로 전달하여 MyBatis Framework를 통해 Database를 조회한다.
   5. Return : 조회된 Database 정보를 List 객체로 역방향으로 회신한다.

   그러면 왜 Spring Boot Application에서는 Controller, Service, Repository(Model), 그리고 그것들 간의 Directory를 구분하고 Service Interface와 Service Implement로 Code를 구분하는 이유를 알고 넘어가자.   한줄로 요약하자면 그 이유는 "보다 명확하고 유지보수 하기 쉬운 구조를 만들기 위함"이다.

 

   1. 역할 분리와 단일 책임 원칙 (Separatioin of Concerns, SoC)
      - Controller : HTTP 요청을 처리하고, Business Logic을 호출하여 적정한 응답을 생성한다.  Controller는 Client와 상호작용을 처리하는 역할을 담당한다.
      - Service : Business Logic을 수행하는 Interface를 정의한다.  여기서 Business Logic은 Data의 처리, 복잡한 계산, 외부 System과의 연동 등의 처리를 포함한다.
      - Repository(Model) : Database나 외부 Storage와의 직접적인 상호작용을 처리 한다.  Data 접근 계층(DAO) 역할을 수행하여 Data를 읽고 쓰는 작업을 담당한다.

 

   2. 유연성과 재사용성 증대
      - Service Interface : Business Logic의 추상화를 제공하여 여러 Implement가 Service를 구현할 수 있또록 한다.  이는 의존성 역전 원칙(Dependency Inversion Principle)을 따르고, Service Interface에만 의존하도록 설계함으로써 Code의 유연성과 재사용성을 높일 수 있다.
      - ServiceImpl : Service Interface의 구체적인 구현체로 실제 Business Logic을 구현한다.  이 Class는 Controller와 Repository 사이에서 중재자 역할을 하면서 복잡한 로직을 처리하는 데 집중한다.

 

   3. 단일 책임 원칙 (Single Reponsibility Principle, SRP)
      - 각 Class나 Interface는 하나의 명확한 책임을 가져야 한다는 조건에 하에 이는 Code를 이해하고 유지보수 하기 쉽게 만들고, 결국 Bug를 줄이고 Code의 Qulity를 향상시키는 효과가 발생한다.

 

   4. Test 용이성
      - 각 계층을 독립적으로 Test할 수 있다.  Controller는 MockMvc와 같은 Test 도구를 이용하여 HTTP Request Simulation 하고, Service는 Business Logic 을 직접 호출하여 Test 할 수 있게 된다.  이는 단위 Test와 통합 Test(SIT, UAT)에서 유리함을 가지게 된다.

   결론적으로 이러한 구조는 Application의 구조적 일관성을 유지할 수 있고, 유연성을 높이고, Code의 가독성과 유지보수의 용이성을 향상시킬수 있는 것이다.   뭐.. 교과서적인 이야기는 한번정도 읽어보는 수준이면 되고 왜 나누어 놓았는지 이해가 되었다면 그 흐름을 따라가는 것에 대해서 기억하기를 바란다.

 

 

   앞서 Model, Controller, Service, ServiceImpl, Mapper 에 대한 Code를 작성했다.  이제 실제로 Database의 정보를 조회하기 위해서 myBatis에 작성되는 Query 영역과 Web Browser에서 출력하기 위한 view 영역에 대한 Code를 살펴보자.

  application.properties 파일의 SpringBoot 설정 내용에 아래의 내용을 추가하자.  View File의 경로와 myBatis Setting 정보이다.

#JSP view resolver
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

# myBatis Setting;
mybatis.configuration.jdbc-type-for-null=null
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.type-aliases-package:kr.co.iamwhatiam.dvdrental.Common.Model
mybatis.mapper-locations:classpath:Mapper/**/*.xml

application.properties
0.00MB

 

   1. JSP View Resolver Setting

      - spring.mvc.view.prefix=/WEB-INF/views/ : Spring MVC에서 View File을 찾을 때 사용할 기본 경로(prefix)를 지정한다.  여기서는 /WEB-INF/views/ 디렉토리 아래에서 JSP 파일을 찾도록 설정했다.  View File의 경로를 명시적으로 지정하여 유지보수를 쉽게 하고, View File의 위치를 관리할 수 있기 때문이다.

      - spring.mvc.view.suffix=.jsp : View File의 확장자(suffix)를 지정했다.  여기서는 .jsp를 사용하여 JSP file을 view로 사용하도록 설정했다. 확장자를 명시적으로 지정하여 다양한 형식의 View File을 사용할 수 있도록 할 수 있다.

 


   2. MyBatis Setting

      - mybatis.configuration.jdbc-type-for-null=null : MyBatis에서 null 값을 처리할 때 사용할 JDBC 타입을 설정한다.  여기서는 null 값을 특별히 지정하지 않도록 설정했다.   Database와의 상호작용에서 null 값을 어떻게 처리할지 명시적으로 설정할 수 있다.  특정 Database에 따라 다르게 설정할 필요가 있을 수 있기 때문이다.

      - mybatis.configuration.map-underscore-to-camel-case=true : Database Field Name에 언더스코어(_)가 포함된 경우 이를 Java Object의 Camel Case Field Name으로 Mapping하도록 설정했다.  예를 들어, Database Field Name이 user_name을 Java Object의 userName Field로 자동 Mapping 한다.  Database와 Java Object 간의 Mapping을 자연스럽게 만들어 Code의 가독성과 유지보수성을 높일 수 있다.

      - mybatis.type-aliases-package=kr.co.iamwhatiam.dvdrental.Common.Model
: MyBatis에서 사용할 Type Alias를 지정할 Package를 설정한다.  kr.co.iamwhatiam.dvdrental.Common.Model Package 내의 Class 들이 Type Alias로 사용된다.  XML Mapper File에서 Java Type을 간단하게 지정할 수 있도록 해준다.

      - mybatis.mapper-locations=classpath:Mapper/**/*.xml
: MyBatis Mapper File의 위치를 지정한다.  classpath:Mapper/ Directory 아래의 모든 XML 파일(**/*.xml)을 Mapper File로 인식하게 된다.  MyBatis가 Mapper File을 찾아서 SQL Mapping을 할 수 있도록 경로를 명시적으로 지정하는 것이다.  이는 MyBatis가 올바르게 SQL Mapper File을 Load하여 Query를 실행할 수 있도록 도와준다.

   application.properties 설정을 추가적으로 했으면 실제 Database에서 조회할 Query 를 담당하는 MyBatis XML을 작성해보자.

 

myBatis XML 생성

   'src/main/resources' 아래로 'Mapper' 라는 Directory를 생성한다.   그 아래에 staff Table에 관련된 Query를 작성할 'staff.xml'  파일을 생성한다.  xml File의 내용에는 Service에서 작성한 Method 대로 2가지 Select Query를 아래와 같이 작성한다.

[ staff.xml 생성 ]

<?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="kr.co.iamwhatiam.dvdrental.Web.Staff.Mapper.StaffMapper">

    <!-- 검색어에 의해서 Staff 목록을 조회한다. -->
    <select id="getStaffInfoList" parameterType="java.util.Map" resultType="kr.co.iamwhatiam.dvdrental.Common.Model.Staff">
        	SELECT	staff_id
                    ,first_name
                    ,last_name
                    ,address_id
                    ,email
                    ,store_id
                    ,active
                    ,username
                    ,password
                    ,last_update
                    ,picture
            FROM	public.staff
            WHERE	1=1
            <if test='firstName != null and firstName != ""'>
              AND   ( first_name LIKE '%' || #{firstName} || '%' OR last_name LIKE '%' || #{firstName} || '%') 
            </if>
            <if test='lastName != null and lastName != ""'>
              AND   ( first_name LIKE '%' || #{lastName}  || '%' OR last_name LIKE '%' || #{lastName} || '%') 
            </if>
        ORDER BY	staff_id
    </select>


    <!-- Staff username을 검색조건으로 Staff의 상세 정보를 조회한다. -->
    <select id="getStaffInfoByUsername" parameterType="kr.co.iamwhatiam.dvdrental.Common.Model.Staff" resultType="kr.co.iamwhatiam.dvdrental.Common.Model.Staff">
        	SELECT	staff_id
                    ,first_name
                    ,last_name
                    ,address_id
                    ,email
                    ,store_id
                    ,active
                    ,username
                    ,password
                    ,last_update
                    ,picture
            FROM	public.staff
            WHERE	1=1
            <if test='username != null and username != ""'>
              AND   username = #{username}
            </if>
        ORDER BY	staff_id
    </select>


</mapper>

staff.xml
0.00MB

   간단한 Select Query 이고 parameterType 및 resultType 부분의 Model Object 경로, mapper namespace 경로로 StaffMapper 파일의 경로를 매칭시켜주면 된다.

 

 

View File 생성

   'src/main/webapp/WEB-INF/views/Staff' Directory를 생성한다.   그 안에 'staff201.jsp' 라는 이름으로 JSP File을 생성한다.  이 Project에서 사용되는 view file의 Naming Rule을 정하고 가겠다.  즉, 'staff201.jsp'  File은 Staff Table에 대한 Select View 화면이 되는 것이다.  실제는 Select 하기 위한 요청화면이 201이지만 DB의 정보를 정상적으로 가져오는지 테스트하기 위함이니 201을 사용하도록 하겠다.

   - 101 : Insert View
   - 102 : Insert Process
   - 201 : Select View
   - 202 : Select Process
   - 203 : Select Result
   - 301 : Update View
   - 302 : Update Process
   - 401 : Delete View
   - 402: Delete Process

[ staff201.jsp 생성 ]

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

<%@ taglib prefix="c"   uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn"  uri="http://java.sun.com/jsp/jstl/functions"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Staff Table Select</title>
    </head>

    <body>
        <table style="border: 1px solid #000; border-collapse: collapse; width: 100%;">
            <colgroup>
                <col style="width: auto; text-align: center;">
                <col style="width: auto; text-align: center;">
                <col style="width: auto; text-align: center;">
                <col style="width: auto; text-align: center;">
                <col style="width: auto; text-align: center;">
                <col style="width: auto; text-align: center;">
                <col style="width: auto; text-align: center;">
                <col style="width: auto; text-align: center;">
                <col style="width: auto; text-align: center;">
                <col style="width: auto; text-align: center;">
            </colgroup>

            <thead>
                <tr style="border: 1px solid #000; border-collapse: collapse; width: 100%;">
                   <th style="border: 1px solid #000;">Staff ID</th> 
                   <th style="border: 1px solid #000;">First Name</th> 
                   <th style="border: 1px solid #000;">Last Name</th> 
                   <th style="border: 1px solid #000;">Address ID</th> 
                   <th style="border: 1px solid #000;">email</th> 
                   <th style="border: 1px solid #000;">Store ID</th> 
                   <th style="border: 1px solid #000;">Active</th> 
                   <th style="border: 1px solid #000;">User Name</th> 
                   <th style="border: 1px solid #000;">Password</th> 
                   <th style="border: 1px solid #000;">Last Update</th> 
                </tr>
            </thead>

            <tbody>
                <c:if test="${fn:length(staffInfoList) > 0}">
                    <c:forEach items="${staffInfoList}" var="obj" varStatus="f">
                        <tr style="border: 1px solid #000;">
                            <td style="border: 1px solid #000;">${obj.staffId}</td>
                            <td style="border: 1px solid #000;">${obj.firstName}</td>
                            <td style="border: 1px solid #000;">${obj.lastName}</td>
                            <td style="border: 1px solid #000;">${obj.addressId}</td>
                            <td style="border: 1px solid #000;">${obj.email}</td>
                            <td style="border: 1px solid #000;">${obj.storeId}</td>
                            <td style="border: 1px solid #000;">${obj.active}</td>
                            <td style="border: 1px solid #000;">${obj.username}</td>
                            <td style="border: 1px solid #000;">${obj.password}</td>
                            <td style="border: 1px solid #000;">${obj.lastUpdate}</td>
                        </tr>
                    </c:forEach>
                </c:if>
            </tbody>
        </table>
    </body>
</html>

staff201.jsp
0.00MB

 

 

   jstl 등의 Library를 사용하기 위해서 pom.xml file에 몇가지 추가 Library 설정을 추가한다.

		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
		</dependency>

		<dependency>
			<groupId>jakarta.servlet.jsp.jstl</groupId>
			<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
			<version>3.0.0</version>
		</dependency>

		<dependency>
			<groupId>org.glassfish.web</groupId>
			<artifactId>jakarta.servlet.jsp.jstl</artifactId>
			<version>3.0.1</version>
		</dependency>

   - tomcat-embed-jasper : JSP Compile, JSP 실행, JSP 지원기능(EL, JSTL 등) 의 기능을 제공한다.

   - jakarta.servlet.jsp.jstl-api : JSTL(Jakarta Server Pages Standard Tag Library)의 API를 제공하는 Library.  Core Tag Library, Formatting Tag Library, SQL Tag Library, XML Tag Library, Function Tag Library 등의 JSP Page에서 자주 사용되는 기능을 제공한다.

   - jakarta.servlet.jsp.jstl : JSTL(Jakarta Server Pages Standard Tag Library)의 구현 Library.

pom.xml
0.00MB

 

 

   여기까지 단계별로 진행했다면 Web Service를 구동하고  http://localhost:8080/Staff/staff201.do 에 접속을 해보면 아래와 같이 dvdrental Database의 staff table의 목록이 출력될 것이다.

[ http://localhost:8080/Staff/staff201.do ]

 

 

   단계별로 진행하기 위해서 그때 그때 작성한 file을 중간중간 upload 했다. 코드 작성하면서 생길 수 있는 오타로 에러가 나는 경우가 종종 있을 수 있으니 이번 포스팅까지 진행한 Source 를 압축해서 Upload 해두도록 하겠다. 

DVDRental.zip
0.07MB

 

 

   다음 포스팅에서는 검색 조건을 입력하고 검색 조건에 따라 조회하고 조회 결과를 출력하는 과정을 Sample Database를 사용해서 진행해 보도록 하겠다.