Icednut's Note

스프링 퀵 스타트 Day 01

2017-07-19

순서

  • 개발 환경 셋팅
  • Spring Overview
  • IoC, Dependency Injection
  • 실습 Day 01



개발 환경 셋팅

  1. JDK 1.8 설치
  2. H2 Database 설치
  3. Gradle Build Tool 설치
  4. IntelliJ IDEA 2017 설치
  5. Spring Initializer를 통한 실습 프로젝트 생성
    • XML 설정 프로젝트 생성 실습
    • Spring-boot 프로젝트 생성 실습



Spring Overview

왜 스프링인가?

스프링을 써서 개발하면 어플리케이션 개발을 빠르고 효율적으로 할 수 있도록

  • 어플리케이션의 바탕이 되는 틀
  • 공통 프로그래밍 모델과 기술 API

등을 제공해준다.

  • 어플리케이션의 기본 틀: 스프링 컨테이너
  • 공통 프로그래밍 모델: IoC/DI, 서비스 추상화, AOP
  • 기술 API: Spring MVC, Sprign JDBC

한 마디로 스프링을 써서 웹 어플리케이션을 개발하면 기본틀 위에서 개발하기 때문에 효율적인 코드 작성이 가능하다는 소리

스프링이 없을 땐 웹 어플리케이션을 어떻게 개발하고 운영했을까?

Java 환경에서 스프링이 없을 때는 아래와 같이 웹 어플리케이션 개발은 Java Servlet을 통해 개발을 했었다.

  1. Java Servlet Class 작성 (javax.servlet.http.HttpServlet을 상속받아 HTTP_METHOD별 처리 메소드를 만듬)
  2. web.xml 파일에 URI와 앞에서 만든 Servlet 클래스를 정의
  3. war로 패키징
  4. Tomcat의 webapp 폴더에 배포
  5. Tomcat 실행
web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.skplanet.servlet.exercise.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
HelloServlet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.skplanet.servlet.exercise;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletOutputStream outputStream = resp.getOutputStream();
outputStream.write("Hello, world!".getBytes());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// do something
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// do something
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// do something
}
}

TV 제어 API를 개발한다고 치자.

web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
...
<servlet>
<servlet-name>samsungTvServlet</servlet-name>
<servlet-class>com.skplanet.servlet.exercise.SamsungTvServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>samsungTvServlet</servlet-name>
<url-pattern>/samsung-tv</url-pattern>
</servlet-mapping>
</web-app>

Tv.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public interface Tv {
public void powerOn();
public void powerOff();
public void volumeUp();
public void volumeDown();
}
class SamsungTv implements Tv {
public void powerOn() {
System.out.println("SamsungTV -- 전원켠다.");
}
public void powerOff() {
System.out.println("SamsungTV -- 전원끈다.");
}
public void volumeUp() {
System.out.println("SamsungTV -- 소리올린다.");
}
public void volumeDown() {
System.out.println("SamsungTV -- 소리내린다.");
}
}
class LgTv implements Tv {
public void powerOn() {
System.out.println("LgTV -- 전원켠다.");
}
public void powerOff() {
System.out.println("LgTV -- 전원끈다.");
}
public void volumeUp() {
System.out.println("LgTV -- 소리올린다.");
}
public void volumeDown() {
System.out.println("LgTV -- 소리내린다.");
}
}
SamsungTvServlet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SamsungTvServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
SamsungTv tv = new SamsungTv();
String action = req.getParameter("action");
switch(action) {
case "POWER_ON":
tv.powerOn();
break;
case "POWER_OFF":
tv.powerOff();
break;
case "VOLUME_UP":
tv.volumeUp();
break;
case "VOLUME_DOWN":
tv.volumeDown();
break;
}
}
}
LgTvServlet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class LgTvServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
LgTv tv = new LgTv();
String action = req.getParameter("action");
switch(action) {
case "POWER_ON":
tv.powerOn();
break;
case "POWER_OFF":
tv.powerOff();
break;
case "VOLUME_UP":
tv.volumeUp();
break;
case "VOLUME_DOWN":
tv.volumeDown();
break;
}
}
}

결합도를 낮추기 위해 디자인패턴을 적용해보자.
TV가 S사, L사는 쓸 수 없고 다른 회사의 제품으로 교체해야 된다고 했을 때 각 서블릿마다 들어가서 고쳐야 되는 것인가?

요구사항에 좀 더 유연하게 대응하기 위해서 Factory Method 패턴을 적용하자.

1
2
3
4
5
6
7
8
9
10
public class TvFactory {
public Tv getTv(String tvName) {
if (tvName.equals("samsung")) {
return new SamsungTv();
} else if (tName.equals("lg")) {
return new LgTv();
}
return null;
}
}
SamsungTvServlet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SamsungTvServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
TvFactory tvFactory = new TvFactory();
Tv tv = tvFactory.getTv("samsung");
String action = req.getParameter("action");
switch(action) {
case "POWER_ON":
tv.powerOn();
break;
case "POWER_OFF":
tv.powerOff();
break;
case "VOLUME_UP":
tv.volumeUp();
break;
case "VOLUME_DOWN":
tv.volumeDown();
break;
}
}
}
LgTvServlet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class LgTvServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
TvFactory tvFactory = new TvFactory();
Tv tv = tvFactory.getTv("lg");
String action = req.getParameter("action");
switch(action) {
case "POWER_ON":
tv.powerOn();
break;
case "POWER_OFF":
tv.powerOff();
break;
case "VOLUME_UP":
tv.volumeUp();
break;
case "VOLUME_DOWN":
tv.volumeDown();
break;
}
}
}




IoC, Dependency Injection

IoC

Tv 인스턴스를 클라이언트(서블릿)에서 한게 아니라 Factory 클래스에게 요청하면 그 때서야 인스턴스를 반환하는 방식으로 변경이 되었는데,
이걸 제어의 역전(Inversion of Control, IoC)라고 부른다.

스프링의 핵심 기술 중 하나가 IoC를 전문적으로 해주는 IoC 컨테이너를 제공한다는 것이다.

SamsungTvServlet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SamsungTvServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
Tv tv = (Tv) factory.getBean("samsung");
String action = req.getParameter("action");
switch(action) {
case "POWER_ON":
tv.powerOn();
break;
case "POWER_OFF":
tv.powerOff();
break;
case "VOLUME_UP":
tv.volumeUp();
break;
case "VOLUME_DOWN":
tv.volumeDown();
break;
}
}
}
applicationContext.xml
1
2
3
4
5
6
7
8
<?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="samsungTv" class="com.skplanet.spring.plalab.SamsungTv"></bean>
<bean id="lgTv" class="com.skplanet.spring.plalab.LgTv"></bean>
</beans>

Dependency Injection

이번엔 스피커를 추가하여 Tv 볼륨을 제어해보자.

SonySpeaker.java
1
2
3
4
5
6
7
8
9
10
11
12
13
public class SonySpeaker implements Speaker {
public SonySpeaker() {
System.out.println("===> Sony Speaker 객체 생성");
}
public void volumeUp() {
System.out.println("Sony Speaker -- 소리 올린다.");
}
public void volumeDown() {
System.out.println("Sony Speaker -- 소리 내린다.");
}
}
SamsungTv.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class SamsungTv implements Tv {
private Speaker speaker;
public SamsungTv(Speaker speaker) {
this.speaker = speaker;
}
public void powerOn() {
System.out.println("SamsungTV -- 전원켠다.");
}
public void powerOff() {
System.out.println("SamsungTV -- 전원끈다.");
}
public void volumeUp() {
this.speaker.volumeUp();
}
public void volumeDown() {
this.speaker.volumeDown();
}
}

예전 TvFactory를 사용한다면 Factory 클래스에서 인스턴스화 할 때 생성자로 넘겨(주입해)준다.

1
2
3
4
5
6
7
8
9
10
11
12
public class TvFactory {
public Tv getTv(String tvName) {
if (tvName.equals("samsung")) {
Speaker speaker = new SonySpeaker();
return new SamsungTv(speaker);
} else if (tName.equals("lg")) {
Speaker speaker = new SonySpeaker();
return new LgTv(speaker);
}
return null;
}
}

이와 같이 Tv에 의존관계(Dependency)가 있는 Speaker를 생성자로 주입(Injection)해 줬다고 해서 이를 Dependency Injection이라고 부른다.

이걸 스프링으로는 어떻게 할까?
XML로 직접 연결하거나, 어노테이션(@Autowired, @Resource)를 사용하여 알아서 주입되게 한다.

  1. XML을 통해 직접 Dependency Injection 정의하기

    applicationContext.xml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?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: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.xsd">
    <bean id="samsungTv" class="com.skplanet.spring.plalab.SamsungTv">
    <property name="speaker" ref="sonySpeaker"></property>
    </bean>
    <bean id="sonySpeaker" class="com.skplanet.spring.plalab.SonySpeaker">
    </bean>
    </beans>
  2. 자동으로 Dependency Injection 하기

    applicationContext.xml
    1
    2
    3
    4
    5
    6
    7
    8
    <?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: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.xsd">
    <context:component-scan base-package="com.skplanet.spring.plalab"/>
    </beans>
SonySpeaker.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class SonySpeaker implements Speaker {
public SonySpeaker() {
System.out.println("===> Sony Speaker 객체 생성");
}
public void volumeUp() {
System.out.println("Sony Speaker -- 소리 올린다.");
}
public void volumeDown() {
System.out.println("Sony Speaker -- 소리 내린다.");
}
}
SamsungTv.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class SamsungTv implements Tv {
@Autowired
private Speaker speaker;
public void powerOn() {
System.out.println("SamsungTV -- 전원켠다.");
}
public void powerOff() {
System.out.println("SamsungTV -- 전원끈다.");
}
public void volumeUp() {
this.speaker.volumeUp();
}
public void volumeDown() {
this.speaker.volumeDown();
}
}

실습 Day 01

Page 109 ~ 139

Tags: Spring JPA