====== Spring Framework (Java) ====== ===== Getting Started ===== * JDK 설치 (oracle.com) * Eclipse 설치(eclipse.org), STS(Spring Tool Suite) 설치 ([Help]->[Eclipse Marketplace]->Spring Tool Suite(STS) for Eclipse 설치) * eclipse.ini 파일; --vmargs 옵션 위에 -vm 옵션 추가 및 javaw의 정확한 위치 지정 * Tomcat 설치(tomcat.apache.org) 및 eclipse 연동 * 데이터베이스 구축, h2 (https://h2database.com) ===== New Project ===== * 프로젝트 생성 * [File] -> [New] -> [Spring Legacy Project] 또는 [Other] 선택 후 생성 창에서 [Spring] -> [Spring Legacy Project] 선택 * Project Name 입력, Templates 선택 'Spring MVC Project' -> com.reth.wiki 같은 형식의 패키지 명 입력 * 프로젝트 설정 * 프로젝트의 컨텍스트 메뉴 [Properties] 선택 -> 왼쪽 트리메뉴 [Project Facetes] 선택 Java 버전 선택 수정 * 오른쪽 [Runtimes] 탭에서 Tomcat 버전 선택 -> Apply -> OK * 확인 * [Java Build Path] -> [Libraries] 탭, 경로 확인 ===== Basic File Structures ===== * 프로젝트 * src/main/webapp/WEB-INF/web.xml; Servlet 컨테이너 설정 * pom.xml C:. │ .gitignore // gitignore 파일 │ build.gradle // 빌드 스크립트 │ gradlew // gradle 실행 sh 스크립트 │ gradlew.bat // gradle 윈도우 실행 배치 파일 │ LICENSE // 라이센스 정보 │ README.md // 간단한 설명 파일 │ settings.gradle // gradle 설정 파일, │ ├─.gradle ... 생략(자동 생성) ... ├─.idea ... 생략(자동 생성) ... ├─build ... 생략(자동 생성) ... ├─gradle // gradle wrapper │ └─wrapper │ gradle-wrapper.jar │ gradle-wrapper.properties └─src ├─main // 애플리케이션 메인 │ ├─java // 패키지 작성 │ └─resources // mapper xml, configuration yml, etc. │ application.yml // 기본 애플리케이션 설정 └─test // 애플리케이션 테스트 ├─java // 테스트 작성 └─resources // 테스트용 resources ^ 처리 영역 ^ 프레임워크 ^ 설명 ^ | Presentation | Struts | Struts 프레임워크는 UI Layer에 중점을 두고 개발된 MVC(Model View Controller) 프레임워크이다. | | ::: | Spring | Struts와 동일하게 MVC 아키텍처를 제공하는 UI Layer 프레임워크이다. 하지만 Struts 처럼 독립된 프레임워크는 아니고 Spring 프레임워크에 포함되어 있다. | | Business | Spring \\ (IoC, AOP) | Spring은 컨테이너 성격을 가지는 프레임워크이다. Springj의 IoC와 AOP 모듈을 이용하여 Spring 컨테이너에서 동작하는 엔터프라이즈 비즈니스 컴포넌트를 개발할 수 있다. | | Persistence | Hibernate or JPA | Hibernate는 완벽한 ORM(Object Relation Mapping) 프레임워크이다. ORM 프레임워크는 SQL 명령어를 프레임워크가 자체적으로 생성하여 DB 연동을 처리한다. JPA는 Hibernate를 비롯한 모든 ORM의 공통 인터페이스를 제공하는 자바 표준 API이다. | | ::: | Ibatis or Mybatis | Ibatis 프레임워크는 개발자가 작성한 SQL 명령어와 자바 객체(VO 혹은 DTO)를 매핑해주는 기능을 제공하며, 기존에 사용하던 SQL 명령어를 재사용하여 개발하는 차세대 프로젝트에 유용하게 적용할 수 있다. Mybatis는 Ibatis에서 파생된 프로임워크로서 기본 개념과 문법은 거의 같다. | * POJO(Plain Old Java Object); * IoC(Inversion of Control); 제어의 역행. 느슨한 결합, 낮은 결합도 * AOP(Aspect Oriented Programming); 관점지향 프로그래밍. 공통 로직을 분리하여 높은 응집도의 개발 지원 * polymorphism 다형성 * Design Patterns; Factory Pattern * 스프링 설정 파일 생성; 프로젝트의 src/main/resources 소스 폴더 선택 -> 컨텍스트 메뉴 [New] -> [Other] -> 'Spring' 폴더의 'Spring Bean Configuration File' 선택 -> File name에 'applicationContext' 입력 ... ... ... import org.springframework.context.support.AbstractAppicationContext; import org.springframework.context.support.GenericXmlApplicationContext; ... public class {class} { public static void main(String[] args) { // 1. Spring 컨테이너 구동 Abstract ApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml"); // 2. Spring 컨테이너로부터 필요한 객체를 요청(Lookup). TV tv = (TV)factory.getBean("tv"); } } ... ^ 구현 클래스 ^ 기능 ^ | GenericXmlApplicationContext | 파일 시스템이나 클래스 경로에 있는 XML 설정 파일을 로딩하여 구동하는 컨테이너이다. | | XmlWebApplicationContext | 웹 기방의 스프링 애플리케이션을 개발할 때 사용하는 컨테이너이다. | * beans 루트 엘리먼트 * import 엘리먼트 * bean 엘리먼트 * init-method 속성 * destryo-method 속성 * lazy-init 속성; boolean * scope 속성; singleton, prototype * 의존성 관리 * Dependency Lookup * Dependency Injection * Constructor Injection 생성자 인젝션 * Setter Injection * p 네임스페이스 사용; p:변수명-ref="참조할 객체의 이름이나 아이디" p:변수명="설정할 값" * 컬렉션 객체 설정 * list, set, map, properties something 1 something 2 something 1 something 2 something 2 key value 1 value 1 key value 2 value 2 value 1 value 2 * 이클립스에서 생성자 추가; 편집창에서 해당 클래스를 연 상태에서 + + -> [Generate Constructor using Fields] 선택 * 이클립스에서 인터페이스 자동 생성; 편집창에서 해당 클래스를 연 상태에서 + + -> [Extract Interface] * 이클립스에서 Setter 메소드 자동 생성; + + -> [Generate Getters and Setters] * 이클립스에서 p 네이스페이스 추가; 스프링 설정 파일에서 [Namespace] 탭 선택 -> p 네임스페이스 선택 * 인터페이스 생성 -> implements 클래스 작성 ===== Annotation 기반 설정 ===== * Context 네임스페이스 추가 * 스프링 설정 파일 [Namespaces] 탭 선택 'context' 체크 * 컴포넌트 스캔(component-scan) 설정 * * @Component("id"); 기본 생성자 필요, 클래스 선언 바로 앞에. * 의존성 주입 설정 ^ 어노테이션 ^ 설명 ^ | @Autowired | 주로 변수 위에 설정하여 해당 타입의 객체를 찾아서 자동으로 할당한다. \\ org.springframework.beans.factory.annotation.Autowired | | @Qualifier | 특정 객체의 이름을 이용하여 의존성 주입할 때 사용한다. \\ org.springframework.beans.factory.annotation.Qualifier | | @Inject | @Autowired와 동일한 기능을 제공한다. \\ javax.annotation.Resource | | @Resource | @Autowired와 @qualifier의 기능을 결합한 어노테이션이다. \\ javax.inject.Inject | * @Autowired; 변수 앞에 * @Qualifier("id"); 의존성 주입 대상이 두 개 이상일 때 @Autowired와 함께 * @Resource(name="id"); * 어노테이션과 XML 설정 병행하여 사용 ^ 어노테이션 ^ 위치 ^ 의미 ^ | @Service | XXXXServiceImpl | 비즈니스 로직을 처리하는 Service 클래스 | | @Repository | XXXDAO | 데이터베이스 연동을 처리하는 DAO 클래스 | | @Controller | XXXController | 사용자 요청을 제어하는 Controller 클래스 | * XxxVO, XxxDAO, XxxService, XxxServiceImpl * VO(Value Object) DTO(Data Transfer Object) 작성 데이터 전달 목적 * DAO(Data Access Object) 데이터 베이스 연동 * 드라이버 다운로드; pom.xml 태그 안에 추가 * JDBC Utility 클래스; Connection 획득 및 해제 작업 * 클래스 작성; @Repository("boardDAO"), insert, update, delete, get, list 메서드 작성 * Service 인터페이스 작성; DAO 클래스에서 + + * Service 구현 클래스 작성; * @Service("boardService") * @Autowired로 DAO 연결 * 실행; 컨테이너 구동 -> BoardServiceImpl 객체 Lookup -> VO 객체 -> insert/update/list,... -> 컨테이너 종료 ===== Spring AOP ===== * IoC -> 결합도, AOP(Aspect Oriented Programming) -> 응집도 * 관심 분리 Separation of Concerns; 횡단 관심 Crosscutting Concerns, 핵심 관심 Core Concerns * AOP 라이브러리 추가; pom.xml -> Maven Dependencies 확인 -> 스프링 설정파일 applicationContext.xml에서 [Namespaces] 탭 -> aop 네임스페이스 추가 -> applicationContext.xml 에 등록 -> aop 관련 설정 추가 ... ... * 조인포인트 Joinpoint; 클라이언트가 호출하는 모든 비즈니스 메소드 * 포인트컷 Pointcut; 필터링된 조인포인트 * expression 속성; {리턴타입} {패키지경로}{클래스명}.{메소드명 및 매개변수} * {*} {com.multicampus.biz..}{*Impl}.{*(..)} * {*} {com.multicampus.biz..}{*Impl}.{get*(..)} * 어드바이스 advice; 횡단 관심에 해당하는 공통 기능의 코드, 독립된 클래스의 메소드로 작성. * 어드바이스의 동작시점; before, after, after-returning, after-throwing, around * 위빙 Weaving; 포인트컷으로 지정한 핵심 관심 메소드가 호출될 때, 어드바이스에 해당하는 횡단 관심 메소드가 삽입되는 과정 * 처리 방식; 컴파일타임 Compiletime 위빙, 로딩타임 Loadingtime 위빙, 런타임 Runtime 위빙. 스프링에서는 런타임 위빙 방식만 지원. * Aspect는 Pointcut과 advise의 결합으로서, 어떤 포인트컷 메소드에 대해서 어떤 어드바이스 메소드를 실행할지 결정 * AOP 엘리먼트 * ; AOP 설정에서 루트 엘리먼트, 여러 번 사용가능, 하위에는 , 엘리먼트가 올 수 있다 * ; 포인트컷 지정을 위해 사용, 의 자식 엘리먼트로 사용 가능, 여러 개 정의 가능, 유일한 아이디를 할당하여 애스팩트를 설정할 때 포인트컷을 참조하는 용도로 사용 * ; 핵심 관심에 해당하는 포인트컷 메소드와 횡단 관심에 해당하는 어드바이스 메소드를 결합하기 위해 사용 * ; 포인트컷과 어드바이스를결합한다는 점에서 애스팩트와 같은 기능, 하지만 트랜잭션 설정 같은 몇몇 특수한 경우는 애스팩트가 아닌 어드바이저를 사용해야 한다. * 포인트컷 표현식 * 리턴타입 지정; 기본적인 방법은 * * 패키지 지정; 패키지 경로 지정할 때는 *, .. 캐릭터 이용 * 클래스 지정; *, + * 메소드 지정; 메소드 지정할 때는 주로 *, 매개변수를 지정할 때는 .. * 매개변수 지정; .., *, 또는 정확한 타입 지정 ^ 리턴타입 지정 ^^ ^ 표현식 ^ 설명 ^ | * | 모든 리턴타입 허용 | | void | 리턴타입이 void인 메소드 선택 | | !void | 리턴타입이 void가 아닌 메소드 선택 | ^ 패키지 지정 ^^ ^ 표현식 ^ 설명 ^ | com.reth.biz | 정확하게 com.reth.biz 패키지만 선택 | | com.reth.biz.. | com.reth.biz 패키지로 시작하는 모든 패키지 선택 | | com.reth..impl | com.reth 패키지로 시작하면서 마지막 패키지 이름이 impl로 끝나는 패키지 선택 | ^ 클래스 지정 ^^ ^ 표현식 ^ 설명 ^ | BoardServiceImpl | 정확하게 BoardServiceImpl 클래스만 선택 | | *Impl | 클래스 이름이 Impl로 끝나는 클래스만 선택 | | BoardService+ | 클래스 이름 뒤에 '+'가 붙으면 해당 클래스로부터 파생된 모든 자식 클래스 선택, 인터페이스 뒤에 '+'가 붙으면 해당 인터페이스를 구현한 모든 클래스 선택 | ^ 메소드 지정 ^^ ^ 표현식 ^ 설명 ^ | *(..) | 가장 기본 설정으로 모든 메소드 선택 | | get*(..) | 메소드 이름이 get으로 시작하는 모든 메소드 선택 | ^ 매개변수 지정 ^^ ^ 표현식 ^ 설명 ^ | (..) | 가장 기본 설정으로서 '..'은 매개변수의 개수와 타입에 제약이 없음을 의미 | | (*) | 반드시 1개의 매개변수를 가지는 메소드만 선택 | | (com.reth.user.UserVO) | 매개변수로 UserVO를 가지는 메소드만 선택. 이때 클래스의 패키지 경로가 반드시 포함되어야 함 | | (!com.reth.user.userVO) | 매개변수로 UserVO를 가지지 않는 메소드만 선택 | | (Integer, ..) | 한 개 이상의 매개변수를 가지되, 천 번째 매개변수의 타입이 Integer인 메소드만 선택 | | (Interger, *) | 반드시 두 개의 매개변수를 가지되, 첫 번째 매개변수의 타입이 Integer인 메소드만 선택 | * 어드바이스 동작 시점 ^ 동작 시점 ^ 설명 ^ | Before | 비즈니스 메소드 실행 전 동작 | | After | - After Returning: 비즈니스 메소드가 성공적으로 리턴되면 동작 \\ - After Throwing: 비즈니스 메소드 실행 중 예외가 발생하면 동작(마치 try-catch 블록에서 catch 블록에 해당) \\ - After: 비즈니스 메소드가 실행된 수, 무조건 실행(try~catch~finally 블록에서 finally 블록에 해당) | | Around | Around는 메소드 호출 자체를 가로채 비즈니스 실행 전후에 처리할 로직을 삽입할 수 있음 | * JoinPoint 메소드; 비즈니스 메소드 이름, 메소드가 속한 클래스와 패키지 정보를 알아야 할 때 사용 ^ 메소드 ^ 설명 ^ | Signature getSignature() | 클라이언트가 호출한 메소드의 시그니처(리턴타입, 이름, 매개변수) 정보가 저장된 Signature 객체 리턴 | | Object getTarget() | 클라이언트가 호출한 비즈니스 메소드를 포함하는 비즈니스 객체 리턴 | | Object[] getArgs() | 클라이언트가 메소드를 호출할 때 넘겨준 인자 목록을 Object 배열로 리턴 | * Signature가 제공하는 메소드 ^ 메소드명 ^ 설명 ^ | String getName() | 클라이언트가 호출한 메소드 이름 리턴 | | String toLongString() | 클라이언트가 호출한 메소드의 리턴타입, 이름, 매개변수를 패키지 경로까지 포함하여 리턴 | | String toShortString() | 클라이언트가 호출한 메소드 시그니처를 축약한 문자열로 리턴 | ==== 어노테이션 기반 AOP ==== * 어노테이션 기반 AOP 사용을 위한 스프링 설정 * 스프링 설정파일에 엘리먼트 선언 ^ Annotation 설정 | @Service \\ public class LogAdvice{} | ^ XML 설정 | | * 포인트컷 설정; 메소드 앞에 @PointCut("execution(* com.reth.biz..*Impl.*(..))") 와 같이 설정 * 어드바이스 설정; 메소드 앞에 @Before("allPointcut()") 과 같이 설정 ^ 어노테이션 ^ 설명 ^ | @Before | 비즈니스 메소드 실행 전에 동작 | | @AfterReturning | 비즈니스 메소드가 성공적으로 리턴되면 동작 | | @AfterThrowing | 비즈니스 메소드 실행 중 예외가 발생하면 동작(마치 try~catch 블록에서 catch 블록에 해당) | | @After | 비즈니스 메소드가 실행된 후, 무조건 실행(try~catch~finally 블록에서 finally 블록에 해당) | | @Around | 호출 자체를 가로채 비즈니스 메소드 실행 전후에 처리할 로직을 삽입할 수 있음 | * 애스팩트 설정; @Service 어노테이션과 클래스 선언 사이에 @Aspect 어노테이션을 삽입하여 설정, 포인트컷 + 어드바이스 설정이 되어 있어야 함 * 외부 Pointcut 참조; 어노테이션 설정으로 변경하고부터는 어드바이스 클래스마다 포인트컷 설정이 포함되면서, 비슷하거나 같은 포인트컷이 반복 선언되는 문제 -> 포인트컷을 외부에 독립된 클래스에 따로 설정 * 사용할 모든 포인트컷을 PointcutCommon 클래스에 등록 package com.reth.biz.common; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; @Aspect public class PointcutCommon { @Pointcut("execution(* com.reth.biz..*Impl.*(..))") public void allPointcut() {} @Pointcut("execution(* com.reth.biz..*Impl.get*(..))") public void getPointcut() {} } ... @Before("PointcutCommon.allPointcut()") public void beforeLog(JoinPoint jp) { ... } ... ===== Spring JDBC ===== * JdbcTemplate; JDBC 기반의 DB 연동, 템플릿 메소드 패턴(복잡하고 반복되는 알고리즘을 캡슐화해서 재사용하는 패턴) 적용 ==== 스프링 JDBC 설정 ==== * 라이브러리 추가; pom.xml 파일에 DBCP 관련 설정 추가 -> DBCP 라이브러리 정상 등록 확인 ... commons-dbcp Commons-dbcp 1.4 ... * DataSource 설정; JdbcTemplate 객체가 사용할 DataSource를 등록. 트랜젝션 처리나 Mybatis 연동, JPA 연동에서도 DataSource가 사용되므로 매우 중요한 설정 ... ... * 프로퍼티 파일을 활용한 DataSource 설정; PropertyPlaceholderConfigurer를 이용, 외부의 프로퍼티 파일을 참조하여 DataSource를 설정 * src/main/resource 소스 폴더에 config 폴더 생성 -> config 폴더에 database.properties 파일 작성 * 스프링 설정 엘리먼트 사용 jdbc.driver=org.h2.Driver jdbc.url=jdbc:h2:tcp://localhost/~/test jdbc.username=sa jdbc.password= ... ... ==== JdbcTemplate 메소드 ==== * SQL 구문에 ?로 값 대입; ?의 갯수만큼 값들을 차례로 나열하거나, ?의 수만큼 값들을 세팅하여 배역 객체로 전달 * update(); INSERT, UPDATE, DELETE 구문 처리. * queryForInt(); SELECT 구문으로 검색된 정수값을 리턴 받음. * queryForObject(); SELECT 구문의 실행 결과를 특정 자바 객체(Value Object)로 매핑하여 리턴 받음. 검색 결과가 없거나 검색 결과가 두 개 이상이면 예외(IncorrectResultSizeDataAccessException)를 발생. * 중요! 검색 결과를 자바 객체(Value Object)로 매핑할 RowMapper 객체를 반드시 지정해야 한다. package com.reth.biz.board.impl; import java.sql.ResultSet; import java.sql.SQLException; import com.reth.biz.board.BoardVO; import com.springframework.jdbc.core.RowMapper; public class BoardRowMapper implements RowMapper { public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException { BoardVO board = new BoardVO(); board.setSeq(rs.getInt("SEQ")); board.setTitle(rs.getString("TITLE")); board.setWriter(rs.getString("WRITER")); board.setContent(rs.getString("CONTENT")); board.setRegData(rs.getDate("REGDATE")); board.setCnt(rs.getInt("CNT")); return board; } } * query(); SELECT 문의 결과가 목록일 때 사용, 검색 결과를 VO 객체에 매핑하려면 RowMapper 객체 사용 ==== DAO 클래스 구현 ==== * JdbcDaoSupport 클래스 상속 ... @Repository public class BoardDAOSpring extends JdbcDaoSupport { ... @Autowired public void setSuperDataSource(DataSource dataSource) { super.setDataSource(dataSource); } ... } ... * JdbcTemplate 클래스 등록, 의존성 주입; 등록 -> DAO 클래스에서 @Autowired 어노테이션 이용, 의존성 주입 -> 서비스구현 *ServiceImpl 클래스가 DAO 객체를 이용하여 DB 연동 처리하도록 수정 ... ... ... @Repository public class BoardDAOSpring { @Autowired private JdbcTemplate jdbcTemplate; ... } ... ... @Service("boardService") public class BoardServiceImpl implements BoardService { @Autowired private BoardDAOSpring boardDAO; public void insertBoard(BoardVO vo) { boardDAO.insertBoard(vo); } ... } ... ===== 트랜잭션 처리 ===== * XML 기반의 AOP 설정만 사용 가능, 어노테이션 불가. * 애스팩트 설정; 사용 * 트랜잭션 네임스페이스 등록; 트랜젝션 관련 네임스페이스 추가 [Namespace] 탭 선택하고 tx 네임스페이스 추가 -> 루트 앨리먼트에 추가로 설정된 것 확인 * 트랜잭션 관리자 등록; 모든 트랜잭션 관리자는 PlatformTransactionManager 인터페이스를 구현한 클래스. commit(), rollback() * JDBC 기반; DataSourceTransactionManager 클래스 이용 * JPA를 이용한 DAO 구현; JPATransactionManager를 등록 ... ... ... * 트랜잭션 어드바이스 설정; 앨리먼트 사용. ... ... ^ 속성 ^^ ^ 속성 ^ 의미 ^ | name | 트랜잭션이 적용될 메소드 이름 지정 | | read-only | 읽기 전용 여부 지정(기본값 false) | | no-rollback-for | 트랜잭션을 롤백하지 않을 예외 지정 | | rollback-for | 트랜잭션을 롤백할 예외 지정 | * AOP 설정을 통한 트랜잭션 적용; txPointcut으로 지정한 메소드가 호출될 때, txAdvice로 등록한 어드바이스가 동작하여 트랜잭션을 관리하도록 설정 ... ... * 트랜잭션 설정의 동작 - Client에서 insertBoard() 호출 - Container의 BoardServiceImpl에서 해당 비즈니스 로직 수행 - txAdvice에 의해 After 어드바시으가 동작 - txManager의 commit() 혹은 rollback() 메소드를 호출 ===== Model 1 아키텍처 ===== * Client <-> Container(JSP as a Controller + View, JavaBeans as a Model) <-> DBMS * JSP 파일이 가장 중요 역할 수행; Controller + View 기능 처리 -> 역할 구분이 명확하지 않음, JSP 파일에 대한 디버깅과 유지보수에 많은 어려움 * Model 1 구조의 단점 보완 -> Model 2 == MVC; Model, View, Controller * 포워드 방식은 RequestDispatcher를 이용하여 응답으로 사용할 JSP 화면으로 넘겨서, 포워드된 화면이 클라이언에 전송되는 방식. 한 번의 요청과 응답으로 처리. 실행속도는 빠르지만 클라이언트 브라우저에서 URL이 바뀌지 않아 응답이 어디에서 들어왔는지 확인 불가 * 리다이렉트는 요청된 JSP에서 일단 브라우저로 응답 메시지를 보냈다가 다시 서버로 재요청하는 형식. 포워드 방식과 달리 일단 응답이 브라우저로 들어간 다음, 재요청하는 방식. 응답이 들어온 파일로 브라우저의 URL이 변경, 두 번의 요청과 응답으로 처리되므로 실행 속도는 포워드 방식보다 느림. ===== Model 2 아키텍처 ===== * Model 1 아키텍처는 엔터프라이즈 시스템에 부적합 * Client <-> Container(Servlet as a Controller, JSP as a View, JavaBeans as a Model) <-> DBMS * Controller 구현 - 서블릿 생성 및 등록; 프로젝트 탐색창 src/main/java 컨텍스트 메뉴 -> [New] -> [Servlet] -> Java Package에 com....view.controller, Class name에 DispatcherServlet 입력 -> [Next] -> Name에 action, URL mappings에 /action을 더블 클리하여 Pattern을 *.do로 설정 -> [Finish] => DispatcherServlet 클래스가 만들어지는 순간 WEB-INF/web.xml 파일에 서블릿 관련 설정이 자동으로 추가됨. , 의미없는 설정이므로 제거 - Controller 서블릿 구현; doGet(), doPost(), process(). doPost()에 한글 처리 인코딩 추가 request.setCharacterEncoding(""); process()에서 분기 처리 ===== MVC 프레임워크 ===== * Controller를 서블릿 클래스 하나로 구현하는 것은 DispatcherServlet 클래스가 복잡하게 구현되어 복잡도 증가 -> 프레임워크의 Controller를 사용 * Container * DispatcherServlet -> HandlerMapping * DispatcherServlet -> Controller * DispatcherServlet <- String <- Controller * DispatcherServlet -> ViewResolver ^ 클래스 ^ 기능 ^ | DispatcherServlet | 유일한 서블릿 클래스로서 모든 클라이언트의 요청을 가장 먼저 처리하는 Front Controller | | HandlerMapping | 클라이언트의 요청을 처리할 Controller 매핑 | | Controller | 실질적인 클라이언트의 요청 처리 | | ViewResolver | Controller가 리턴한 View 이름으로 실행될 JSP 경로 완성 | * MVC 프레임워크 구현 - Controller 인터페이스 작성 - Controller 구현체 - HandlerMapping 클래스 작성 - ViewResolver 클라스 작성 - DispatcherServlet 수정 * EL(Expression Language); JSP 2.0에서 추가된 스크립트 언어, 기존의 표현식(Expression)을 대체하는 표현 언어. * <%= session.getAttribute("userName") %> -> ${username} 과 같이 표현 * JSTL(JSP Standard Tag Library); Scriptlet에서 if, for, switch 등과 같은 자바 코드를 사용해야 할 때, 자바 코드을을 태그 형태로 사용할 수 있도록 지원 * http://www.tutorialpoint.com/jsp/ ===== Spring MVC 구조 ===== - Client -> (request) -> DispatcherServlet - DispatcherServlet -> HandlerMapping - DispatcherServlet -> Controller - DispatcherServlet <- ModelAndView <- Controller - DispatcherServlet -> ViewResolver - DispatcherServlet -> View - 클라이언트로부터의 모든 ".do" 요청을 DispatcherServlet이 받는다 - DispatcherServlet은 HandlerMapping을 통해 요청을 치리할 Controller를 검색. - DispatcherServlet은 검색된 Controller를 실행하여 클라이언트의 요청을 처리. - Controller는 비즈니스 로직의 수행 결과로 얻어낸 Model 정보와 Model을 보여줄 View 정보를 ModelAndView 객체에 저장하여 리턴 - DispatcherServlet은 ModelAndView로부터 View 정보를 추출하고, ViewResolver를 이용하여 응답으로 사용할 View를 얻어냄. - DispatcherServlet은 ViewResolver를 통해 찾아낸 View를 실행하여 응답 전송 - DispatcherServlet 등록 및 스프링 컨테이너 구동 - DispatcherServlet 등록; WEB-INF/web.xml - 스프링 컨테이너 구동; DispatcherServlet.java - 스프링 설정 파일 등록; 프로젝트 탐색창 WEB-INF 컨텍스트 메뉴 -> [New] -> [Other] -> Spring 폴더에서 Spring Bean configuration file 선택 -> [Next] -> File name에 action-servlet.xml 파일 명입력 -> [Finish] -> 생성된 파일의 이름과 경로 확인 - 스프링 설정 파일 변경; WEB-INF 폴더 아래 config 폴더 생성 -> 위에 생성한 파일 action-servlet.xml 파일 이동 -> presentation-layer.xml로 이름 변경 -> web.xml 파일 열고 DispatcherServlet 클래스 등록한 곳에 성정 추가 action org.springframework.web.servlet.DispatcherServlet contextConfigLocation /WEB-INF/config/presentation-layer.xml public class DispatcherServlet extends HttpServlet { private String contextConfigiLocation; public void init(ServletConfig config) throws ServletException { contextConfigLocation = config.getInitParameter("contextConfigLocation"); new XmlWebApplicationContext(contextConfigLocation); } } - 인코딩설정; web.xml 파일에 CharacterEncodingFilter 클래스를 필터로 등록 ... characterEncoding org.springframework.web.filter.CharacterEncodingFilter encoding EUC-KR characterEncoding *.do ... ===== Spring MVC 적용 ===== * 기존의 com....view.controller의 패키지 삭제 * Controller의 handleRequest의 리턴타입 String -> ModelAndView - Controller 구현; *Controller.java - HandlerMapping 등록; presentation-layer.xml에 HandlerMapping과 Controller를 등록 .... login .... * ViewResolver 활용; JSP를 View로 사용하는 경우에는 InternalResourceViewResolver 사용 - /WEB-INF/board/ 생성, getBoardList.jsp, getBoard.jsp 파일 이동 - Controller 수정 .... .... ===== 어노테이션 기반 MVC ===== * 어노테이션 관련 설정; 루트 엘리먼트에 context 네임스페이스 추가. HandlerMapping, Controller, ViewResolver 클래스에 대한 등록 삭제하고 엘리먼트로 대체 * 모든 Controller 클래스가 스캔 범위에 포함되도록 엘리먼트의 base-package 속성에 Controller 클래스들이 있는 가장 상위 패키지인 'com.reth.view'를 등록 * 또한, 어노테이션 활용에 집중하기 위해, 스프링 설정 파일에 등록한 ViewResolver 설정을 삭제. jsp파일의 위치도 원래대로 src/main/webapp 폴더 밑으로 ==== @Controller ==== * @Component를 상속한 @Controller는 @Controller가 붙은 클래스의 객체를 메모리에 생성하는 기능 제공. 단순히 객체를 생성하는 것에 그치지 않고, DispatcherServlet이 인식하는 Controller로 만들어 준다. * 만일 @Controller를 사용하지 않는다면, 모든 컨트롤러 클래스는 반드시 스프링에서 제공하는 Controller 인터페이스를 구현해야 한다. 그리고 handleRequest() 메소드를 반드시 재정의하여 DispatcherServlet이 모든 Controller의 handleRequest() 메소드를 호출할 수 있도록 해야한다. import org.springframework.stereotype.Controller; @Controller public class InsertBoardController { } 만약 이미 다른 Controller가 import에 등록되었다면, 클래스 위에 @Controller를 설정할 때 자동완성이 다음처럼 이상하게 입력될 수 있다. import org.springframework.web.servlet.mvc.Controller; @org.springframework.stereotype.Controller public class InsertBoardController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { } } 이때는 import된 Controller를 제거하고 다시 @Controller를 자동완성하면 깔끔하게 처리된다. 또는 이클립스의 ++ 단축키를 눌러 import를 정리해도 된다. ==== @RequestMapping ==== * @RequestMapping을 이용하여 HandlerMapping 설정을 대체 .... import org.springframework.web.bind.annotation.RequestMapping; .... @RequestMapping(value="/insertBoard.do") public void insertBoard(HttpServletRequest request) { } .... ==== 클라이언트 요청 처리 ==== * 서블릿 객체의 service() 메소드가 호출되는 과정 * Client -> HTTP요청 -> Servlet Container - HTTP요청; Start line, Message Header, Message Body; * 서블릿 컨테이너는 클라이언트의 HTTP 요청이 서버에 전달되는 순간 - HttpServletRequest * HttpServletRequest 객체를 생성하고 HTTP 프로토콜에 설정된 모든 정보를 추출하여 HttpServletRequest 객체에 저장 - DispatcherServlet의 service 메서드의 HttpServletRequest 인자 * 그리고 이 HttpServletRequest 객체를 service() 메소드를 호출할 때, 인자로 전달 * Controller 객체의 메소드가 호출되는 과정 * Client -> Spring Container - 객체생성; 매개변수에 해당하는 VO 객체 생성 - 사용자 입력 값 전달; 사용자가 입력한 파라미터 값들을 추출하여 VO 객체에 저장. 이 때, VO 클래스의 setter 메소드들이 호출된다. - 사용자가 요청한 액션에 해당 메소드를 호출할 때, 사용자 입력값들이 설정된 VO 객체가 인자로 전달된다. Controller 메소드가 실행되고 View 경로를 리턴하면 기본이 포워딩 방식이므로 url은 변경되지 않는다. 따라서 포워딩이 아닌 리다이렉팅을 원할 때는 "redirect:"라는 접두사를 붙여야 한다. * @RequestMapping(value="/login.do", method=RequestMethod.GET) * value; url 위치 * method; RequestMethod.GET, RequestMethod.POST * JSP에서 Command 사용; ${...} 구문 * @ModelAttribute; Command 객체의 이름을 변경, View(JSP)에서 사용할 데이터를 설정하는 용도 * @RequestParam; 파라미터 정보 추출 * @SessionAttributes; ===== 프레젠테이션 레이어와 비즈니스 레이어 통합 ===== * Spring MVC 기반; request -> Servlet(DispatcherServlet <-LOADING- presentation-layer.xml) -> Spring Container(Controller -use-> DAO) * Controller는 DAO 객체를 직접 이용해서는 안되며, 반드시 비즈니스 컴포넌트를 이용. * 비즈니스 컴포넌트; VO, DAO, Service Interface, Service 구현 클래스 - DAO 클래스 교체 - AOP 설정 적용 - 비즈니스 컴포넌트 의존성 주입 * 2-Layered 아키텍처 * Presentation Layer(MVC); presentation-layer.xml -LOADING-> DispatcherServlet (-> Controller & -Data-> View(JSP)) * Business Layer; Controller -> ServiceImpl(<-implements- Service Interface) -> use DAO & use VO * ContextLoaderListner; web.xml 파일에 설정 추가 ===== 파일 업로드 ===== - 파일 업로드 입력 화면 - Command 객체 수정; VO 클래스에 org.springframework.web.multipart.MultipartFile 타입의 변수 추가 - FileUpload 라이브러리 추가; pom.xml 에 dependency 추가 - MultipartResolver 설정; CommonsMultipartResolver 를 등록 - 파일 업로드 처리 * 예외처리 * 어노테이션 기반 예외처리; @ControllerAdvice, @ExceptionHandler 컨트롤의 메소드 수행 중 발생하는 예회를 일괄적으로 처리. presentation-layer.xml에 예외 처리 관련 어노테이션 사용을 위한 설정 추가 * XML 기반 예외처리; presentation-layer.xml 파일에 SimpleMappingExceptionResolver 클래스를 등록 ===== 다국어 처리 ===== * 메시지 파일 작성; Java Resources/sr/main/resources/message/*_.properties * MessageSource 클래스 등록 * LocaleResolver 등록; AcceptHeaderLocaleResolver, CookieLocaleResolver, SessionLocaleResolver, FixedLocaleResolver * Local 변경; LocaleChangeInterceptor * JSP 파일 작성 ===== 데이터 변환 ===== * JSON(JavaScript Object Notation)으로 변환; '키:값' 형태로 표현 * Jackson2 라이브러리; 추가 * HttpMessageConvertor 등록; MappingJackson2HttpMessageConverter - Start Line * HTTP/1.1 200 OK - Message Header * Content-Type: text/html * Connection: close * Server: Apache Tomcat/8.0.32 (HTTP/1.1 Connector) * Last-Modified: Thu, 25 Nov, 2021 23:21:49 GMT - Message Body * .... * XML로 변환 * JAXB2 설정 추가 ===== Mybatis 프레임워크 ===== - Java ORM Plugin 설치; [Help] -> [Eclipse Marketplace] -> 검색 창에 'orm' 입력 후 검색 결과 화면 -> Java ORM 플러그인 Install - 프로젝트 생성; [File] -> [New] -> [Spring Legacy Project] -> 프로젝트 이름 입력, Template 선택 -> JRE 버전 수정 -> DB 연동을 위한 라이브러리 다운 받기위해 추가 - VO 클래스 작성 - SQL Mapper XML 파일 작성; 프로젝트 컨텍스트 메뉴 [New] -> [Other] -> Java ORM Plugin 폴더 - Mybatis Mapper XML 선택 [Next] -> 파일 이름 입력 [Finish] -> src/main/resource 소스 폴더에 mappings 패키지 생성 후 파일 이동 -> 파일 내용 작성 - Mybatis 환경설정 파일; 프로젝트 컨텍스트 메뉴 [New] -> [Other] -> Java ORM Plugin 폴더 - Mybatis Configuration XML 선택 [Next] -> 파일 이름(일반적으로 sql-map-config.xml) 입력 [Finish] -> db.properties, xml 파일 src/main/resource 폴더로 이동 -> db.properties 파일 수정, xml 파일 수정 - SqlSession 객체 생성; Mybatis를 이용하려면 SqlSession 객체 필요. SqlSessionFactory 객체 필요. - DAO 클래스 작성 - 테스트 클라이언트 작성 ==== Mapper XML 파일 설정 ==== * SQL Mapper XML 기본 설정 * Mybatis 구조 * Mapper XML 파일 구조 *