개발 공부/Spring

코드로 배우는 스프링 웹 프로젝트 - Part3: Chap07. 스프링 MVC 프로젝트의 기본 구성

maxlafe 2020. 7. 11. 13:03

코드로 배우는 스프링 웹 프로젝트 - 개정판

코드로 배우는 스프링 웹 프로젝트 - Part3: Chap07. 스프링 MVC 프로젝트의 기본구성.

2019년 7월 10일 인쇄판

 

Part3: 기본적인 웹 게시물 관리

 

Chapter07. 스프링 MVC 프로젝트의 기본 구성

 

일반적으로 웹 프로젝트는 3-tier 방식으로 구성한다.

 

Presentation - Business - Persistence tier

 

Presentation Tier는 화면에 보여주는 기술을 사용하는 영역. 책의 예제에선 SErvlet/JSP나 스프링 MVC가 담당하는 영역이 된다. Presentation Tier는 프로젝트의 성격에 맞춰 앱으로 제작하거나 CS(Client-Server)로 구성되는 경우도 있다.

 

Business Tier는 순수한 비즈니스 로직을 담고 있는 영역이다. 이 영역은 고객이 원하는 요구 사항을 반영하므로 중요하다. 이 영역은 주로 xxxService와 같은 이름으로 구성하고 메서드의 이름 역시 고객들이 사용하는 용어를 그대로 사용하는 것이 좋다.

 

Persistence Tier는 데이터를 어떤 방식으로 보관하고 사용하는가에 대한 설계가 들어간다. 일반적으로 데이터베이스를 많이 사용하지만 경우에 따라 네트워크 호출이나 원격 호출 등의 기술이 접목될 수 있다. 이 영역은 MyBatis와 mybatis-spring을 이용해 구성했던 파트1을 이용한다.

 

스프링 MVC 영역은 Presentation Tier를 구성하는데 각 영역은 사실 별도의 설정을 가지는 단위로 볼 수 있다. 이전 예제에선 root-context.xml, servlet-context.xml 등의 설정 파일에 해당 영역의 설정을 담았따. 스프링 Core은 흔히 POJO의 영역이다. 스프링의 의존성 주입을 이용해 객체 간의 연관구조를 완성해 사용한다. MyBatis 영역은 현실적으로 mybatis-spring을 이용해 구성하는 영역으로 SQL에 대한 처리를 담당한다.

 

7.1 각 영역의 Naming Convention(명명 규칙)

 

프로젝트를 위와 같이 3-tier로 구성하는 가장 일반적인 설명은 유지 보수에 대한 필요성 때문이다.

설계 당시부터 영역을 구분해서, 독립적으로 설계해 특정 기술이 변하더라도 필요 부분을 쉽게 교환할 수 있게 하기 위함이다. 해당 연결 부위는 interface를 이용해 설계하는 게 일반적.

 

- xxxController : 스프링 MVC에서 동작하는 Controller 클래스

- xxxService, xxxServiceImpl : 비즈니스 영역을 담당하는 인터페이스와, 그것을 구현한 클래스

- xxxDAO, xxxRepository : DAO(Data-access-object)나 Repository라는 이름으로 영역을 따로 구성하는 게 보편적. 다만 이 책의 예제는 별도의 DAO 구성 대신 MyBatis의 Mapper interface를 활용한다.

- VO, DTO : VO와 DTO는 일반적으로 유사한 의미로 사용되며 데이터를 담고 있는 객체를 의미. 다만 VO는 주로 Read Only 목적이 강하고 데이터 자체도 Immutable하게 설계하는게 정석. DTO는 데이터 수집 용도가 강하다.

 

 

7.1.1 패키지의 Naming Convention.

 

이 책의 구성

org.zerock.config : 프로젝트 설정 클래스

org.zerock.controller

org.zerock.service : Service 인터페이스와 구현 클래스들

org.zerock.domain : VO, DTO 클래스들

org.zerock.persistence : MyBatis Mapper 인터페이스

org.zerock.exception : 웹 관련 예외 처리

org.zerock.aop : AOP 관련

org.zerock.security

org.zerock.util : 각종 유틸리티 클래스 관련

 

 

7.2 프로젝트를 위한 요구사항

 

고객의 요구사항을 인식하고 이를 설계하는 과정이 필요. 흔히 요구사항 분석 설계라고 함.

요구사항은 온전한 문장으로 정리하는 게 좋음. 주어는 고객, 목적어는 대상(여기서 대상은 데이터의 베이스 설계와 시스템 설계에서 가장 중요한 용어가 된다, 도메인이라는 단어를 사용하는 경우도 많다)

 

 

7.2.1 요구사항에 따른 화면 설계

 

요구사항에 나오는 용어로 테이블이나 클래스의 이름이 정해지듯 화면도 요구사항에 영향을 받는다.

이러한 화면을 설계할 땐 Mock-up 툴을 사용하는 경우가 많다. PowerPoint나 Balsamiq studio, Pencil Mockup 등의 SW를 이용해 작성해 보면 좋다.

 

각 화면을 설계 하는 단계에서 사용자가 입력해야 하는 값과 전체 페이지의 흐름을 설계한다. 이 화면의 흐름을 URL로 구성하게 되는데 이 경우 GET/POST 방식에 대해 같이 언급하는 것이 좋다.

 

 

7.3 예제 프로젝트 구성

 

 

 

Package는 org.zerock.controller로 설정한다.

 

 

7.3.1 pom.xml의 수정

 

	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>5.0.7.RELEASE</org.springframework-version>
		<org.aspectj-version>1.6.10</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
        <dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
        
        <!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
		<dependency>
			<groupId>com.zaxxer</groupId>
			<artifactId>HikariCP</artifactId>
			<version>2.7.4</version>
		</dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.6</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.3.2</version>
		</dependency>
        
        <!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4 -->
		<dependency>
			<groupId>org.bgee.log4jdbc-log4j2</groupId>
			<artifactId>log4jdbc-log4j2-jdbc4</artifactId>
			<version>1.16</version>
		</dependency>
        
        <dependency><!-- 추가 -->
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.0</version>
			<scope>provided</scope>
		</dependency>
        
        
        
		<!-- Test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
		
		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>

Servlet-api 3.1로 수정.

junit 도 4.12로 수정.

maven-compiler-plugin 1.8로 수정

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>

 

Maven > Update Project.

 

마지막으로 Oraclec JDBC Driver를 Build Path > Libraries에 추가, Deployment Assembly 설정에도 추가한다.

(ojdbc6.jar)

 

7.3.2 테이블 생성과 Dummy 데이터 생성

 

SQL Developer로 book_ex 계정을 통해 테이블을 생성한다.

 

이 쯤에 DB 접속이 또 안 되서 아래 사이트 참고해서 오류 해결.

https://jhnyang.tistory.com/272

 

[DB Sql developer 에러] The Network Adapter could not establish the connection 원인,오류 해결법

안녕하세요~! 주말 내내 뒹굴거리다가... 이렇게 포스팅을 하러 왔습니다 ㅎㅎ SQL Developer를 설치하고 커넥션을 하려 하면, 이렇게 '상태:실패 -테스트 실패: IO 오류: The Network Adapter could not establi..

jhnyang.tistory.com

create sequence seq_board;

create table tbl_board(
bno number(10, 0),
title varchar2(200) not null,
content varchar2(2000) not null,
writer varchar2(50) not null,
regdate date default sysdate,
updatedate date default sysdate
);

alter table tbl_board add constraint pk_board primary key(bno);

 

더미 데이터의 추가

 

insert into tbl_board(bno, title, content, writer)
values (seq_board.nextval, '테스트 제목', '테스트 내용', 'user00');

 

 

7.4 데이터베이스 관련 설정 및 테스트

 

root-context.xml에 mybatis-spring namespace 추가, Part1에서 작성한 DataSource의 설정과 MyBatis 설정을 추가한다.

 

<?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:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
		<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
	<!-- <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property> -->
	<!--	<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:orcl"></property> -->
		<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
		<property name="jdbcUrl" value="jdbc:log4jdbc:oracle:thin:@localhost:1521:orcl"></property>
		<property name="username" value="book_ex"></property>
		<property name="password" value="book_ex"></property>
	</bean>
	
	<!--  HikariCP configuration -->
	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
		<constructor-arg ref="hikariConfig"/>
	</bean>
	
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<mybatis-spring:scan base-package="org.zerock.mapper"/>
</beans>

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

 

 

PART1의 org.zerock.persistence의 DataSourceTests.java와 JDBCTests.java 복사

 

 

7.5 JAVA 설정을 이용하는 경우의 프로젝트 구성

 

JAVA 설정을 이용하면 XML을 대신하는 클래스가 필요하다.

 

pom.xml을 제외하곤 모두 삭제, pom.xml plugins 내에 다음을 추가.

 

			<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-war-plugin -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>3.2.0</version>
				<configuration>
					<ailOnMissingWebXml>false</ailOnMissingWebXml>
				</configuration>
			</plugin>

package org.zerock.config;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

@Configuration
@MapperScan(basePackages= {"org.zerock.mapper"})

public class RootConfig {

	@Bean
	public DataSource dataSource() {
		HikariConfig hikariConfig = new HikariConfig();
		//hikariConfig.setDriverClassName("oracle.jdbc.driver.OracleDriver");
		//hikariConfig.setJdbcUrl("jdbc:oracle:thin:@localhost:1521:orcl");
		hikariConfig.setDriverClassName("net.sf.log4jdbc.sql.jdbcapi.DriverSpy");
		hikariConfig.setJdbcUrl("jdbc:log4jdbc:oracle:thin:@localhost:1521:orcl");
		hikariConfig.setUsername("book_ex");
		hikariConfig.setPassword("book_ex");
	
		HikariDataSource dataSource = new HikariDataSource(hikariConfig);
	
	return dataSource;
	}
	
	@Bean
	public SqlSessionFactory sqlSessionFacotory() throws Exception {
		SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
		sqlSessionFactory.setDataSource(dataSource());
		return (SqlSessionFactory) sqlSessionFactory.getObject();
	}
}
package org.zerock.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@EnableWebMvc
@ComponentScan(basePackages = {"org.zerock.controller"})
public class ServletConfig implements WebMvcConfigurer{
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		InternalResourceViewResolver bean = new InternalResourceViewResolver();
		bean.setViewClass(JstlView.class);
		bean.setPrefix("/WEB-INF/views/");
		bean.setSuffix(".jsp");
		registry.viewResolver(bean);
	}
	
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
	}
}
package org.zerock.config;

import javax.servlet.ServletRegistration;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer{

	@Override
	protected Class<?>[] getRootConfigClasses() {
		// TODO Auto-generated method stub
		return new Class[] {RootConfig.class};
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		// TODO Auto-generated method stub
		return new Class[] {ServletConfig.class};
	}

	@Override
	protected String[] getServletMappings() {
		// TODO Auto-generated method stub
		return new String[] {"/"};
	}
	
	@Override
	protected void customizeRegistration(ServletRegistration.Dynamic registration) {
		registration.setInitParameter("throwExceptionIfNoHandlerFound",  "true");
	}
}

 

ojdbc6.jar를 추가하고, properties 파일을 붙여넣기한다.