데이터융합 JAVA응용 SW개발자 기업 채용연계 연수과정 57일차 강의 정리

misung·2022년 6월 8일
0

Spring

복습

스프링은 IoC 때문에 기존의 New 키워드를 사용한 객체의 생성보다는 .xml 에 객체를 bean 으로 등록해놓고 그때그때 가져다 쓴다.

이전 강의에서는 .xml 파일에 객체를 등록해 놓고 가져다 썼지만, 사실 값을 주입하거나 가져다 쓰는 것도 가능하니 오늘은 그것부터 시작하도록 한다.

실습

src/main/java 밑에 basic 패키지 아래에, ex02 패키지 생성. 그 아래에서 작업할 것

DataBaseInfo.java

package basic.ex02;

public class DataBaseInfo {
	
	private String url;
	private String uid;
	private String upw;
	
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public String getUid() {
		return uid;
	}
	public void setUid(String uid) {
		this.uid = uid;
	}
	public String getUpw() {
		return upw;
	}
	public void setUpw(String upw) {
		this.upw = upw;
	}
}

멤버변수 선언하고 Alt+Shift+S 로 getter, setter 메서드 자동 생성하면 됨

MemberDAO.java

package basic.ex02;

public class MemberDAO {

	private DataBaseInfo dbInfo;
	
	public void setDbInfo(DataBaseInfo dbInfo) {
		this.dbInfo = dbInfo;
	}
	
	public void showDbInfo() {
		System.out.println("URL: " + dbInfo.getUrl());
		System.out.println("UID: " + dbInfo.getUid());
		System.out.println("UPW: " + dbInfo.getUpw());
	}
}

MemberDAO 에서는 단순히 DB 커넥션 정보를 설정하고 보여주기만 하는 식으로 작성했음.

MainClass.java

package basic.ex02;

public class MainClass {

	public static void main(String[] args) {
		
		DataBaseInfo dbInfo = new DataBaseInfo();
		dbInfo.setUrl("jdbc:oracle:thin:@localhost:1521:xe");
		dbInfo.setUid("spring");
		dbInfo.setUpw("sss111");
		
		MemberDAO dao = new MemberDAO();
		dao.setDbInfo(dbInfo);
		dao.showDbInfo();
	}

}

이제 여기서부터가 조금 중요한데, 기존 방식대로라면 새 dbInfo가 필요할 때 마다 새로 new 키워드를 통해 선언하고 dao에 설정하거나 보여줄 dbInfo를 그때그때 갈아끼워야 한다.

우리는 이제 이 방식을 다시 스프링 방식으로 바꿔볼 것이다.

db-config.xml

전날 강의에서 만든 test-config.xml을 원래 .xml이 있던 디렉토리에 붙여넣기하면서 이름을 위와 같은 db-config로 바꾼다. (설정값을 그대고 끌어오기 위함) 그리고 안에 선언해뒀던 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans  
http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="db1" class="basic.ex02.DataBaseInfo">
		<!-- setter 주입 -->
		<property name="url" value="jdbc:oracle:thin:@Localhost:1521:xe"/>
		<property name="uid" value="spirng" />
		<property name="upw" value="sss123" />
	</bean>
	
	<bean id="dao" class="basic.ex02.MemberDAO">
		<property name="dbInfo" ref="db1" />
	</bean>
</beans>

ref="" <- 객체를 어떤 걸로 넣느냐는 물음에, 매개 변수를 객체로 넘겨주는 중

장점 : 자바 코드에 대한 지식이 없어도 xml의 일부 태그 내용만 고치면 동작을 쉽고 빠르게 변경할 수 있다.
(예. DB 커넥션 객체 2개 만들어놓고 1번(오라클) 2번(MySQL) 객체를 이름으로 지정만 하면 원하는 DB로 빠르게 접속이 가능한 등..)

MainClass.java 수정

package basic.ex02;

import org.springframework.context.support.GenericXmlApplicationContext;

public class MainClass {

	public static void main(String[] args) {
		
		GenericXmlApplicationContext ct =
				new GenericXmlApplicationContext("classpath:db-config.xml");
		
		MemberDAO dao = ct.getBean("dao", MemberDAO.class);
		dao.showDbInfo();
		
		ct.close();
	}
}

출력

URL: jdbc:oracle:thin:@Localhost:1521:xe
UID: spirng
UPW: sss123

싱글톤 (Singleton)

스프링 컨테이너에서 생성된 빈(Bean)객체의 경우 동일한 타입에 대해서는 기본적으로 한 개만 생성이 되며, getBean() 메소드로 호출될 때 동일한 객체가 반환된다.


스프링에서는 빈들이 기본적으로 싱글톤으로 등록되어 따로 싱글톤 패턴을 구축해 줄 필요가 없다고 한다.

프로토타입 (Prototype)

싱글톤 범위와 반대의 개념도 있는데, 이를 프로토타입 범위라고 한다. 프로토타입의 경우 개발자는 별도의 설정을 해 줘야 하는데, 스프링 설정 파일에서 빈(Bean) 객체를 정의할 때 scope 속성을 명시해 주면 된다.

예시)

<bean id="good" class="day01.SpringTest" scope="prototype" />

실습

basic.ex03 패키지 생성 후 아래의 클래스 생성

Person.java

package basic.ex03;

public class Person {
	
	private String name;
	private int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

prototype-config.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans  
http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="person" class="basic.ex03.Person">
		<property name="name" value="홍길동" />
		<property name="age" value="20" />
	</bean>
</beans>

MainClass.java

package basic.ex03;

import org.springframework.context.support.GenericXmlApplicationContext;

public class MainClass {

	public static void main(String[] args) {
		
		GenericXmlApplicationContext ct =
				new GenericXmlApplicationContext("classpath:prototype-config.xml");
		
		Person hong = ct.getBean("person", Person.class);
		Person kim = ct.getBean("person", Person.class);
		
		kim.setAge(30);
		kim.setName("김철수");
		
		// 객체가 두 개인 것 같지만, 사실은 아니다.
		System.out.println("hong의 이름 : " + hong.getName());
		System.out.println("hong의 나이: " + hong.getAge());
		System.out.println("kim의 이름: " + kim.getName());
		System.out.println("kim의 나이: " + kim.getAge());
	}

}

출력 :

hong의 이름 : 김철수
hong의 나이: 30
kim의 이름: 김철수
kim의 나이: 30

디폴트는 싱글톤이므로 객체는 컨테이너에 하나만 등록된다.
이 상태에서는 여러 곳에서 돌려 쓰는 상황이 되므로,
.xml 파일에서 scopeprototype 로 설정해 줄 필요가 있다.

prototype.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans  
http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="person" class="basic.ex03.Person" scope="prototype">
		<property name="name" value="홍길동" />
		<property name="age" value="20" />
	</bean>
</beans>

수정 후 MainClass.java 실행결과

hong의 이름 : 홍길동
hong의 나이: 20
kim의 이름: 김철수
kim의 나이: 30

의존객체 자동 주입

자동 주입이란?

스프링 설정 파일에서 의존 객체를 주입할 때 <constructor-org> 또는 <property> 태그로 의존 대상 객체를 명시하지 않아도 스프링 컨테이너가 자동으로 필요한 의존 대상 객체를 찾아서, 의존 대상 객체가 필요한 객체에 주입해 주는 기능이다.

구현 방법은 @Autowired@Resource 어노테이션을 이용해서 쉽게 구현할 수 있다.


음.. 개념만 봐서는 무슨 얘긴지 모르겠다.
여태까지 우리는 주입을 '직접' 했지만, 이제는 일일히 주입을 하는게 아니라 스프링이 알아서 찾아가게 하겠다는 거라는데..

실습

ex04 패키지 생성

Paper.java

package basic.ex04;

public class Paper {
	
	public String[] data = {
			"스프링 프레임워크",
			"자동 객체 주입",
			"Autowired는 객체의 타입을 검색하여 자동 주입"
	};
}

문자열 배열을 들고 있게 할 Paper 클래스

Printer.java

package basic.ex04;

public class Printer {

	private Paper paper;
	
	public void setPaper(Paper paper) {
		this.paper = paper;
	}
	
	public void showPaperInfo() {
		for (String info : paper.data) {
			System.out.println(info);
		}
	}
}

Paper 클래스를 객체로 들고, 안에 든 내용을 출력해줄 Printer 클래스

auto-config.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans  
http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="paper" class="basic.ex04.Paper" />
	
	<bean id="prt" class="basic.ex04.Printer">
		<property name="paper" ref="paper" />
	</bean>
	
</beans>

MainClass.java

package basic.ex04;

import org.springframework.context.support.GenericXmlApplicationContext;

public class MainClass {

	public static void main(String[] args) {
		
		GenericXmlApplicationContext ct =
				new GenericXmlApplicationContext("classpath:auto-config.xml");
		
		Printer printer = ct.getBean("prt", Printer.class);
		printer.showPaperInfo();
		
		ct.close();
	}
}

이제 MainClass 에서 showPaper() 메서드를 호출한다.

auto-config.xml 수정

야~ 누가봐도 페이퍼에는 세터메서드를 써야지~ 하고 스프링에 알려줄 것

<beans xmlns="http://www.springframework.org/schema/beans"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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">

기존의 <beans> 태그를 위의 태그로 덮어쓸 것.

이제 자동 스캔 명령을 추가할 것이다.

<context:annotation-config />

이렇게 한 줄만 추가해주면, 이제부터 자동 스캔 명령을 사용할 수 있게 된다.

이제 그러면 원래 코드를..

<bean id="paper" class="basic.ex04.Paper" />
	
<bean id="prt" class="basic.ex04.Printer">
	<property name="paper" ref="paper" />
</bean>

이렇게 직접 의존성 주입을 하고 있었지만

<bean id="paper" class="basic.ex04.Paper" />
<bean id="prt" class="basic.ex04.Printer" />

이제는 주입을 안 해도 된다.
단, 당연히 지금 상태에서 실행할 경우 NullPointerException이 뜬다.

따라서 우리는 Printer.java를 수정해야 한다.

Printer.java 수정

package basic.ex04;

import org.springframework.beans.factory.annotation.Autowired;

public class Printer {

	private Paper paper;
	
	@Autowired
	public void setPaper(Paper paper) {
		this.paper = paper;
	}
	
	public void showPaperInfo() {
		for (String info : paper.data) {
			System.out.println(info);
		}
	}
}

setter() 메서드에 @Autowired 어노테이션을 추가해준다.

이제 실행해보면 다음과 같은 결과를 얻는다.

출력 :

스프링 프레임워크
자동 객체 주입
Autowired는 객체의 타입을 검색하여 자동 주입

그리고 setter() 메서드 말고 생성자에도 @Autowired를 적용할 수 있다.

package basic.ex04;

import org.springframework.beans.factory.annotation.Autowired;

public class Printer {

	private Paper paper;
	
	@Autowired
	public Printer (Paper paper) {
		this.paper = paper;
	}
	
	/*
	@Autowired
	public void setPaper(Paper paper) {
		this.paper = paper;
	}
	*/
	
	public void showPaperInfo() {
		for (String info : paper.data) {
			System.out.println(info);
		}
	}
}

이래도 출력 결과는 똑같이 갖는다.
스프링에서 알아서 객체를 타입 으로 찾아서 자동 주입해주기 때문에, 직접 주입을 진행하지 않아도 알아서 진행되는 것이다.

심지어 이렇게 해도 똑같은 결과를 기대할 수 있다.

package basic.ex04;

import org.springframework.beans.factory.annotation.Autowired;

public class Printer {

	@Autowired
	private Paper paper;
	
	/*
	@Autowired
	public Printer (Paper paper) {
		this.paper = paper;
	}
	*/
	
	/*
	@Autowired
	public void setPaper(Paper paper) {
		this.paper = paper;
	}
	*/
	
	public void showPaperInfo() {
		for (String info : paper.data) {
			System.out.println(info);
		}
	}
}

그냥 멤버 변수에 @Autowired 어노테이션을 추가하기만 해도 된다. 생성자나 setter() 메서드가 없어도 가능하다.

@Autowired

  • 객체를 자동 주입할 때 사용하는 아노테이션이다.
  • 스캔 명령을 통해 객체를 찾아 주입하는데, 타입 이름으로 검색한다.
  • 타입을 찾아내지 못 하면, 이름(id 속성값)을 통해 검색한다.
  • 생성자, 메서드, 필드에 적용 가능하다.

@Qualifier (자동 주입하려는 와중에 객체가 두 개 이상 존재하는 경우)

  • @Autowired를 사용할 때, 동일 타입의 빈이 여러 개 있는 경우 어떤 빈을 주입해야 하는지 선택해 주는 추가 아노테이션이다.

auto-config.xml 에 다음과 같이 bean이 등록되어 있다고 하자.

<bean id="paper1" class="basic.ex04.Paper" />
<bean id="paper2" class="basic.ex04.Paper" />

이 경우에 실행을 해 보면, 에러가 뜨는데 single match를 예상했으나 객체가 두 개 있다고 에러가 뜬다.

이러한 경우 어떤 객체를 주입할 것인지 명시해 주어야 하는데, 다음과 같이 해결한다.

Printer.java

@Autowired
@Qualifier("paper2")
private Paper paper;

@Resource (Java 8 이하만 가능)

  • 자바 측에서 빈을 자동으로 주입해주는 아노테이션이다.
  • 필드, 메서드에서만 적용이 가능하고, 생성자에는 적용이 불가능하다.
  • name 속성을 통해 특정 bean의 id를 지목할 수 있다.

이를 알아보기 위해 Book.java라는 클래스를 만들고 bean으로 등록해보자.

Book.java

package basic.ex04;

import javax.annotation.Resource;

public class Book {
	
	@Resource(name="paper1")
	private Paper paper;
	
	public void setPaper(Paper paper) {
		this.paper = paper;
	}
	
	public Paper getPaper() {
		return paper;
	}
}

auto-config.xml 수정

<bean id="book" class="basic.ex04.Book" />

이 한 줄 추가.

실습 Quiz

/*
       1. quiz-config.xml 파일을 생성(auto-config.xml 파일을 복사)
        Monitor, Keyboard, Mouse, Computer 빈을 생성 후
       2. Computer 클래스에서 자동 주입을 설정해 보세요.
       (Computer는 나머지 부속품 객체들과 의존 관계에 있습니다.)
       3. MainClass에서 xml에 등록된 빈을 얻은 후 
       computerInfo() 메서드를 실행해 보세요. 
       computerInfo() 메서드는 각 부속품의 info()들을 한번에 
        출력해 주시면 됩니다.
*/

Keyboard.java

package basic.quiz;

public class Keyboard {

	public void info() {
		System.out.println("삼성 키보드!");
	}
}

Mouse.java

package basic.quiz;

public class Mouse {

	public void info() {
		System.out.println("로지텍 마우스!");
	}
}

Monitor.java

package basic.quiz;

public class Monitor {

	public void info() {
		System.out.println("LG 모니터!");
	}
}

Computer.java

package basic.quiz;

import org.springframework.beans.factory.annotation.Autowired;

public class Computer {
	
	private Keyboard keyboard;
	private Mouse mouse;	
	private Monitor monitor;
	
	@Autowired
	public Computer(Keyboard keyboard, Mouse mouse, Monitor monitor) {
		super();
		this.keyboard = keyboard;
		this.mouse = mouse;
		this.monitor = monitor;
	}
	
	public void computerInfo() {
		System.out.println("*** 컴퓨터의 정보 ***");
		keyboard.info();
		mouse.info();
		monitor.info();
	}
}

멤버변수에 일일히 @Autowired 를 붙여도 된다. 그 쪽이 훨씬 많이 쓰인다고도 하셨다. 물론 생성자를 만들어 저런 식으로 한 번에 해결해도 가능하다.

Mainclass.java

package basic.quiz;

import org.springframework.context.support.GenericXmlApplicationContext;

public class MainClass {

	public static void main(String[] args) {

		/*
	       1. quiz-config.xml 파일을 생성(auto-config.xml 파일을 복사)
	        Monitor, Keyboard, Mouse, Computer 빈을 생성 후
	       2. Computer 클래스에서 자동 주입을 설정해 보세요.
	       (Computer는 나머지 부속품 객체들과 의존 관계에 있습니다.)
	       3. MainClass에서 xml에 등록된 빈을 얻은 후 
	       computerInfo() 메서드를 실행해 보세요. 
	       computerInfo() 메서드는 각 부속품의 info()들을 한번에 
	              출력해 주시면 됩니다.
	    */
		
		GenericXmlApplicationContext ct =
				new GenericXmlApplicationContext("classpath:quiz-config.xml");
		
		Computer computer = ct.getBean("computer", Computer.class);
		
		computer.computerInfo();
		
		ct.close();
	}

}

스프링 MVC 웹서비스

1. 웹프로그래밍 설계모델

Model1

  • JSP 파일에 자바 코드를 작성하는 문제가 발생
  • ~con.jsp 파일이 너무 많이 생성됨

Model2

  • 요청을 Controller에서 파악
  • 요청에 맞는 Service 객체를 생성하고 Service에서 DAO와 연동하여 DB의 값을 넣거나 빼게 됨.
  • 그리고 객체를 VO로 포장하여 편하게 주고 받음
  • Service에서 session이나 request에 저장하여 응답하고 Controller로 보낸다
  • Controller에서는 forward 기능을 이용하여 사용자에게 보여질 View를 선택하는데 View는 JSP 파일에서 진행한다.

2. 스프링 MVC 프레임워크 동작 구조

매우 매우 중요하다
외우라고 강요할 정도까진 아니지만, 외워야 함

눈에 보이지 않는 부분이 많아 파악에 어려울 수 있지만, 이 흐름 파악을 잘 해야 한다.

가운데 부분은 Spring에서 지원해 주는 부분이고, dev의 부분이 우리가 직접 개발해야 하는 부분이다.

  1. 브라우저에서 사용자와의 상호작용을 통해 요청을 생성함

  2. DispatcherServlet이 요청을 받아 요청을 분석한다.

  3. 서블릿은 HandlerMapping, HandlerAdapter에게 방금 들어온 URL과 전송 방식이 무엇이었는지 전송한다.

  4. 그러면 두 녀석은 개발자가 만들어 둔, 방금 들어온 요청을 처리할 수 있는 컨트롤러가 존재하는지 찾아보기 시작한다.

  5. 찾았을 경우 Adapter는 서블릿에게 '이 요청은, 이 컨트롤러가 하는 것 같습니다' 라는 응답을 보낸다.

  6. 이번엔 서블릿이 HandlerMapping에 방금 전달받은 컨트롤러에게 가서 '지금 요청을 처리할 수 있는 메서드가 해당 컨트롤러 안에 있는지 검색해봐라' 라고 요청을 보낸다.

  7. 그러면 Mapping은 다시 Controller로 찾아가서, 요청을 처리할 수 있는 메서드를 호출하고 반환받은 결과값을 Servlet에게 보고한다.

  8. 그러면 서블릿은 ViewResolver에게 리턴값을 그대로 전달한다.

  9. Resolver는 처리결과를 출력할 View(jsp 파일)를 선택하고, 완성된 주소를 서블릿에게 반환한다.

  10. 서블릿은 완성된 주소를 가지고 실제 그 파일이 있는지를 검색한다. 만약 존재하는 경우 해당 파일에게 필요한 데이터를 전송하고, View 쪽에서는 전송받은 데이터로 화면을 꾸민 후 브라우저에 응답을 하는 형태로 진행된다.

스프링 개발환경 구축

1. 이클립스에 STS(Spring Tool Suit) 설치

스프링 홈페이지에서 설치해도 되고, 이클립스 마켓플레이스에서 설치해도 되는데, 이클립스 마켓플레이스에서 설치를 진행했다.

sts로 검색하면 나오는 Spring 3.9.14 릴리스 버전을 설치하면 된다. (JAVA SE 8, Eclipse 2020-06 기준)

설치가 완료되면 재시작하라고 뜰 거고, 재시작하면 창 우측 위에 Spring Perspective가 확인될 것이다.

2. 이클립스 개발환경 구축

  1. 설치 다 완료되었으면 우측 위 Perspective 부분을 눌러서 Spring Perspective로 전환

  2. 이클립스 상단 Window - Preferences 에서 enc 검색 후 HTML, CSS 등등 전부 인코딩 방식을 UTF-8로 전환

  3. 좌측 하단의 창의 서버 설정에서 Apache Tomcat 9.0으로 선택

  4. 전에 톰캣 설치한 디렉토리를 찾아가서 지정할 것. 9.0.62 버전임.

  5. 좌측 하단의 서버 창에서 서버 이름 더블클릭하여 상세설정 켜기

  6. Server Locations 설정을 User Tomcat Installation (가운데 체크박스) 으로 지정

  7. Server Options에 Publish Module ... 체크

  8. 상단의 탭에서 X를 눌러서 창을 끄려고 하면 뜨는 창에서 Save 눌러서 창 닫기.

  9. 기본 포트가 8181로 지정되어 있을 텐데, URL에 http://localhost:8181/ 넣고 접속하여 서버 잘 가동되고 있나 체크.

  10. 잘 작동되는 것 같으면 멈춤 버튼 눌러서 서버 중지.

3. 프로젝트 생성

  1. File - New - Spring Legacy Project 선택

  2. Persistance에 Spring MVC Project 선택 후 다운로드

  3. top-level 패키지 지정해야 하는데 com.spring.basic 넣고 Finish.

  4. 세팅 후 Maven Dependencies 확인하면 라이브러리들 버전이 너무 낮아서 갱신해 줄 것임.

4. pom.xml 수정

  • <java-version> 태그의 내용 1.61.8 로 수정

  • <org.springframework-version> 3.1.15.3.18 버전으로 수정
    (설치 경로는 C:/사용자/사용자명/.m2/repository/org/springframework/spring-context 이 디렉토리에서 확인 가능하다.)

  • servlet의 <dependency> 안의 일치하는 태그들을 다음과 같이 수정.

<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
  • <!-- Test --> 부분의 내부 태그 중, <plugin> 태그 부분을 다음과 같이 수정.
<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <!-- 버전을 3.5.1로 변경, 자바 버전도 1.8로 변경 -->
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>

이제 메이븐 프로젝트를 업데이트 해야 하는데,

SpringWebBasic 프로젝트 우클릭 - Maven - Update Project 선택한 후 OK를 눌러 업데이트할 것.

그 후 Maven Dependencies나, JRE Library가 원했던 버전으로 변경이 잘 되어있나 체크할 것.

5. 프로젝트를 서버에 추가

기본 창 좌측 밑의 Tomcat v9.0 서버에 우클릭 - Add and Remove ... 선택 후 - 현재 프로젝트를 클릭한 뒤 Add 하고 Finish한다.

6. 웹 페이지 띄워보기

디렉토리상에 home.jsp가 확인될텐데 더블 클릭 - 이클립스 상단의 Window에서 - Web Browser - Chrome 선택 후 Ctrl+F11 하여 서버 실행

아마 이 상태에서는 404 Error 가 뜨면서 제대로 접근이 안 될 것이다.

스프링에서는 jsp와 달리 MVC2 방식을 따르므로 어떤 경로로 직접 접근하고 있는지 공개되거나 접근할 수 없다. (jsp 파일을 직접 실행하지 않는다)

프로젝트 자체에 요청을 보내면 해결되는데, 프로젝트 명에 우클릭 - Run As - Run On Server 선택하면 제대로 페이지에 요청이 전송된다.

web.xml 설명

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- MVC와 관련된 스프링 설정 파일의 위치를 저장하는 태그 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>
	
	<!-- 스프링 프로젝트가 가동을 시작하면서, listener가 동작하고, 위에 선언 된 root-context.xml을
	모든 서블릿에서 공유하도록 스프링 설정 파일로 초기 선언. -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- 
		디스패처 서블릿을 appServlet으로 선언하고, 초기 파라미터로
		스프링 설정 파일위치를 선언. 우선 순위 또한 지정하여 가장 먼저 생성될 수 있도록 설정.
	 -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<!-- DispatcherServlet에 대한 설정 파일 끌어오는 중 -->
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<!-- 요청 받는 순위 -->
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<!-- 위에 선언된 appServlet을 모든 경로에 대해 맵핑 처리. -->
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<!-- url 패턴이 /면, 모든 요청을 다 받겠다는 뜻이다. -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

servlet-context.xml 설명

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet 관련 설정 -->
	
	<!-- 핸들러 어댑터, 핸들러 매핑 객체가 개발자가 작성한 컨트롤러 등을 찾을 수 있게 설정하는 태그. -->
	<annotation-driven />

	<!-- 정적 자원(html, css, js, img... 등)을 절대 경로로 쉽게 매핑해주는 태그.
		mapping에 작성된 경로 : 외부 노출되는 경로, location: 실제 파일의 경로 -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- 뷰 리졸버 빈 등록 설정. -->
	<!-- 
		- 컨트롤러가 리턴한 view 문자열을 해석하여 경로를 만들어서,
			파일을 찾아 응답하는 ViewResolver의 빈 등록 코드.
		- prefix, suffix 필드의 setter 주입을 통해서,
			컨트롤러에서 리턴된 문자열을 조립해 준다.
	 -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<context:component-scan base-package="com.spring.basic" />
	
	
	
</beans:beans>

<annotation-driven> 태그로 컨트롤러 찾을 수 있게 설정해 두면 컨트롤러를 찾을 수 있는데, 컨트롤러로 사용되는 클래스는 클래스 이름 위에 @Controller 라고 매핑 처리를 해 두어야 한다. 그러면 컨트롤러 객체를 찾아갈 수 있다.

이제 기본 프로젝트 기준에서 어떻게 찾아가냐 하면,
HomeController.java를 참고하면 된다.

package com.spring.basic;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
	}
	
}

보면

@Controller
public class HomeController

이렇게 컨트롤러로 지정되어 있고,

@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) { ... }

매핑이 지정한 URL로 요청이 들어오면 호출이 되게 되어 있는데, value가 "/" 임을 확인할 수 있다.

많이 중요!

<!-- 뷰 리졸버 빈 등록 설정. -->
	<!-- 
		- 컨트롤러가 리턴한 view 문자열을 해석하여 경로를 만들어서,
			파일을 찾아 응답하는 ViewResolver의 빈 등록 코드.
		- prefix, suffix 필드의 setter 주입을 통해서,
			컨트롤러에서 리턴된 문자열을 조립해 준다.
	 -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<!--
		아노테이션을 사용하여 자동으로 빈 등록을 하게 해 주는 설정 태그.
		com.spring.basic으로 경로가 시작되는 모든 클래스에서,
		아노테이션으로 지정되어 있는 클래스를 찾아 자동으로 빈 등록을 실행한다.
	-->
	<context:component-scan base-package="com.spring.basic" />

이것과, Homecontroller 클래스가 중요한데,

HomeController 클래스는 "home" 이라는 문자열을 반환 후 DispatcherServlet에 전달하고, 서블릿은 home이라는 문자열을 ViewResolver에 전달한다.

그러면 설정되어 있는 접두어, 접미어 (prefix, suffix)를 붙여서 완성된 경로를 완성하게 된다.

그러면 이렇게 /WEB-INF/views/home.jsp 라는 경로가 완성되고 응답된다.

컨트롤러 제작 실습 (요청 흐름 파악)

기본적으로 만들어져있는 HomeController.java 대신 우리가 직접 컨트롤러를 만들고 요청을 처리해보는 과정을 해볼 것.

RequestController.java

먼저 com.spring.basic.controller 라는 패키지를 만들고, 그 하위에 생성할 것.

package com.spring.basic.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RequestController {
	
	public RequestController() {
		System.out.println("RequestCon 생성!");
	}
	
	@RequestMapping("/request/test")
	public String testCall() {
		System.out.println("/request/test 요청이 들어옴!");
		return "test";
	}
}

이 클래스가 컨트롤러임을 알리기 위해 @Controller 매핑을 해 주고, 리퀘스트를 처리할 메서드에 @RequestMapping 아노테이션으로 매핑을 해 주었다.

/request/test 라는 요청이 들어오면 우리는 test 라는 문자열을 반환할 것이고, 그러면 Spring 프레임워크에서는 test.jsp 를 찾아나서게 된다.

test.jsp

src/main/webapp/WEB-INF/viewsrequest 라는 디렉토리를 하나 만들고 그 하위에 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2>Hello~</h2>
	<p>
		Spring 요청 흐름을 파악하는 연습 진행 중입니다~
	</p>
</body>
</html>

이제 이렇게 생성을 한 후, 프로젝트에 우클릭하여 Run As - Run on Server 선택 후 뜨는 브라우저 창에서 다음의 URL을 입력해보자.

http://localhost:8181/basic/request/test

입력하고 나서 엔터를 누르면 방금 만든 test.jsp 로 연결되는 것을 잘 확인할 수 있다.

GET, POST 요청 구분

기존의 방식대로에서는 <form> 태그에 의해 GET 방식으로 전송이 되는 것인지, POST 방식으로 전송이 되는 것인지 알 수가 없다. 따라서 다음과 같은 방식으로 작성하면 구분을 할 수 있다.

RequestController.java 수정

package com.spring.basic.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class RequestController {
	
	public RequestController() {
		System.out.println("RequestCon 생성!");
	}
	
	@RequestMapping("/request/test")
	public String testCall() {
		System.out.println("/request/test 요청이 들어옴!");
		return "test";
	}
	
	/*
    	만약 사용자가 /request/req 요청을 보내 왔을 때
    	views폴더 아래에 request폴더 안에 존재하는
    	req-ex01.jsp파일을 열도록 메서드를 구성해 보세요.
    */
	
	@RequestMapping("/request/req")
	public String req() {
		System.out.println("/request/req 처리중");
		return "req-ex01";
	}
	
	//@RequestMapping(value="/request/basic01", method=RequestMethod.GET)
	@GetMapping("/request/basic01")
	public String basicGet() {
		System.out.println("/request/basic01 요청이 들어옴! (GET 요청)");
		return "req-ex01";
	}
	
	//@RequestMapping(value="/request/basic01", method=RequestMethod.POST)
	@PostMapping("/request/basic01")
	public String basicPost() {
		System.out.println("/request/basic01 요청이 들어옴! (POST 요청)");
		return "req-ex01";
	}
}

@GetMapping @PostMapping 은 스프링 4버전부터 지원되는 아노테이션으로 정석적인 방법 (@RequestMapping) 보다 짧게 작성할 수 있다.

req-ex01.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>MWII 오늘 오전 2시에 첫 트레일러 공개~</h1>
	
	<form action="/basic/request/basic01">
		<input type="submit" value="GET 요청!">
	</form>
	
	<form action="/basic/request/basic01" method="post">
		<input type="submit" value="POST 요청!">
	</form>
</body>
</html>

이제 이 페이지에서 GET이나 POST를 누르면 어떤 요청이 전송되었는지 구분할 수 있다.

0개의 댓글