
이 책은 스프링부트로 재빨리 웹 서버를 만드는 것을 목표로 하는 실무 안내서 역할을 하기 때문에 스프링의 역사나 이론적 배경은 다루지 않겠습니다. 기존의 자바 개발자들의 전통적인 클래스 객체 생성과 사용방법과 스프링 프레임워크를 이용한 클래스 객체 생성과 사용 방법이 매우 다릅니다.
전통적인 클래스 객체 사용법은 개발자가 직접 알아서 new를 통해 객체를 생성하고 사용하는 방식입니다. 전통적인 방식이 안 좋은 점을 몇가지 들면,
- 널 안정성(null safety)이 보장이 안됨.
- 싱글톤(Singleton)을 위해 추가 코드가 필요함.
- 개발자가 new를 해야 되는 번거로움
먼저 null 안정성을 보장해 주지 못합니다. 개발자는 클래스 객체를 사용할때 항상 null인지 몰라서 조건문을 사용하도록 강요받습니다. 자바 개발자라면 너무도 친숙한 Null Point Exception 오류를 자주 만났습니다. 스프링은 빈(Bean)으로 등록해두면 자동으로 객체생성(new)를 해주고 null 안정성을 보장해주므로 null인 객체를 넘겨주지 않습니다.
Member m = new Member("홍길동");
if( m != null ) {
System.out.println( m.getName() );
}
싱글톤은 어플리케이션 내에서 유일한 객체를 의미합니다. 계속해서 new을 사용하면 여러 객체 중에서 중요하고 유일한 값을 보장 받지 못합니다.
Member m = new Member("홍길동");
...
m = new Member("이순신");
...
if( m != null ) {
System.out.println( m.getName() );
}
m 객체에 저장된 이름은 홍길동인가요? 이순신인가요? ...? 이순신입니다.
new를 자주하게 되면 애플리케이션 내의 유일한 객체에 데이터 저장이 보장이 안될 수 있습니다. 개발자가 신중하게 코드를 짜야 되는데, 규모가 큰 코드와 여러 팀원간의 협업 체계에서는 이런 것이 힘들어질 수 있습니다. 따라서 개발자는 유일한 객체내의 데이터를 저장하기 위해 싱글톤 객체를 만들어야 합니다. 하지만 스프링 프레임워크는 모든 등록된 객체를 자동으로 싱글톤으로 생성합니다.
//회원정보 클래스
public class Member {
private String name;
public Member(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//싱글톤 만들기
private static Member m = null;
public static Member getInstance() {
if( m == null ) {
m = new Member("홍길동");
}
return m;
}
}
이 3가지 점에서 스프링 프레임워크가 자바 개발자에게는 편리한 개발환경을 선사합니다. 이외에도 다양하고 방대한 기능을 제공하는 라이브러리가 바로 스프링 프레임워크입니다.
일단 예제를 통해 간락히 기존 방식과 스프링 방식의 객체 생성에 대해서 알아보겠습니다.
https://start.spring.io 사이트에 가서 그림과 같이 설정하고 생성(GENERATE)버튼을 클릭하여 Ex03DI.zip파일을 다운로드 받습니다.
이전 예제 Ex01FirstApp과 설정이 동일하고, Group는 com.study이고 Artifact, Name이 Ex03DI, Package name이 com.study.springboot로 해줍니다. Artifact 이름을 Ex03으로 해주면, Package name이 com.study.Ex03DI로 자동 변경되어 있으니 다시 com.study.springboot로 바꿔줍니다. Package name이 동일해야 예제 프로젝트간 코드 복사가 쉽습니다. 디펜던시(Dependencies) 라이브러리는 Spring Web, Spring Boot Dev Tools, Lombok를 선택하면 됩니다.(각각의 라이브러리 설명은 다음에 하도록 하겠습니다)
다운받은 zip파일을 내문서>springboo폴더에 옮기고, 여기서 풀기를 하면 됩니다.
인텔리제이에서 파일>열기를 통해 프로젝트 폴더 Ex03DI를 지정하면 프로젝트가 열립니다.
resources폴더의 application.properties에 다음 내용을 추가합니다.
server.port=8090
서버 포트를 8080 기본포트에서 8090으로 변경합니다.
먼저 파일>새로 만들기>Java 클래스 메뉴를 통해 Member 클래스를 하나 생성합니다.


Member클래스의 내용은 아래와 같습니다. 학습을 위해서는 직접 타이핑하는 것이 좋지만, 오탈자가 염려된다면 다음 URL을 참조합니다. 예제 URL : https://bit.ly/3s2zd9T
//회원정보 클래스
public class Member
{
private String name;
public Member(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//싱글톤 만들기
private static Member m = null;
public static Member getInstance() {
if( m == null ) {
m = new Member("홍길동");
}
return m;
}
}
내용을 보면 전형적인 자바 클래스의 멤버 변수(필드), 멤버 메서드입니다. getter, setter 메서드와 싱글톤을 만드는 필드와 메서드가 정의되어 있습니다.
Ex03DiApplication.java에서 일단 스프링부트 애플리케이션 속성을 제거해서, 일반 자바 프로그램처럼 동작하도록 해보겠습니다. 예제 URL : https://bit.ly/3EXT1Tz
수정전 Ex03DiApplication 클래스에서
package com.study.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Ex03DiApplication {
public static void main(String[] args) {
SpringApplication.run(Ex03DiApplication.class, args);
}
}
아래 코멘트된 내용을 삭제합니다.
package com.study.springboot;
//import org.springframework.boot.SpringApplication;
//import org.springframework.boot.autoconfigure.SpringBootApplication;
//@SpringBootApplication
public class Ex03DiApplication {
public static void main(String[] args) {
// SpringApplication.run(Ex03DiApplication.class, args);
}
}
수정후 Ex03DiApplication 클래스는 아래와 같습니다.
package com.study.springboot;
public class Ex03DiApplication {
public static void main(String[] args) {
Member m = new Member("홍길동");
System.out.println( m.getName() );
}
}
수정된 코드는 Member클래스 생성자에 홍길동을 넣고 생성된 객체의 name필드를 얻어와 출력하는 프로그램합니다.
main함수 옆의 실행 버튼을 눌러 코드를 수행해보겠습니다.

원래는 홍길동이라고 한글이 정상적으로 출력되어야 됩니다.

하지만 콘솔에 한글이 깨져서 나올것이다.

아래와 같이 콘솔 환경설정을 하면 됩니다.
인텔리제이가 외국 제품인지라 콘솔에 한글을 설정하면 깨져보입니다.

아래 순서로 진행해 보겠습니다.
- 인텔리제이의 File Encoding 수정
- 인텔리제이의 vm options 수정
- 톰캣의 VM Options
- 캐시 무효화

-Dfile.encoding=UTF-8
-Dconsole.encoding=UTF-8



다시 실행하면 반가운 한글이 출력될 것입니다.

다시 돌아가서 코드를 보면, 전형적인 자바 코드에서 new를 통한 클래스 객체를 생성하고 값을 Getter함수에서 얻어와 출력하는 프로그램입니다.
package com.study.springboot;
public class Ex03DiApplication {
public static void main(String[] args) {
Member m = new Member("홍길동");
System.out.println( m.getName() );
}
}
코드를 추가해보자. 코드는 직접 입력해보는 것이 학습효과가 높습니다. 예제 URL은 다음과 같습니다. https://bit.ly/3rYWWHJ
package com.study.springboot;
public class Ex03DiApplication {
public static void main(String[] args) {
Member m = new Member("홍길동");
System.out.println( m.getName() );
//의존주입을 안쓰는 경우
printName1();
//의존주입을 사용한 경우
printName2( null );
printName2( new Member("이소룡") );
printName2( Member.getInstance() );
}
//의존주입을 안쓰는 경우
//직접 주입 new를 통한 객체 생성
public static void printName1() {
Member m = new Member("이순신");
System.out.println( m.getName() );
}
//의존주입을 사용한 경우
//외부로부터 객체(자바Bean)을 주입받는다.
public static void printName2( Member m ) {
//밖에서 주입을 받는다.
//널체크를 해서 null safety를 확보한다.
//그러나 Spring Framework에서는 기본으로 null safety를 보장해준다.
if( m == null ) {
System.out.println("m = null입니다.");
return;
}
System.out.println( m.getName() );
}
}
실행결과는 아래와 같습니다.
> Task :Ex03DiApplication.main()
홍길동
이순신
m = null입니다.
이소룡
홍길동
printName1 메서드는 자신 입장에서 외부로부터 객체를 주입(전달)받지 않고 스스로 new 생성해서 사용하고 있습니다. 직접 주입이라고 할 수 있습니다.
반면 printName2 메서드는 매개변수(인자)로 Member 클래스 객체를 주입(전달)받습니다. 외부에서 주입 받는 것을 의존 주입이라고 명명할 수 있습니다.
이때 주입받는 객체가 null일 수 있으므로, 조건문을 통해 null체크를 해줘야 정상적으로 사용가능합니다.
printName2( Member.getInstance() );
Member.getInstance()는 Member 클래스의 싱글톤을 얻어와서 전달해주는 부분입니다. 싱글톤을 사용하면 애플리케이션의 유일한 객체를 보장받을 수 있습니다.
이상으로 간단하게 기존의 자바 객체 생성 방법의 특징과 직접주입과 의존주입의 차이점에 대해서 배웠습니다.
스프링에서 자주 나오는 IoC라는 용어를 설명하겠습니다.
제어의 역전이란 약어로 IOC(Inversion of Control)라고 합니다. 제어의 역전이란 말 그대로 메소드나 객체의 호출작업을 개발자가 결정하는 것이 아니라, 외부에서 결정되는 것을 의미합니다. 스프링이 모든 의존성 객체를 스프링이 실행될때 다 만들어주고 필요한곳에 주입시켜줌으로써 Bean들은 싱글턴 패턴의 특징을 가지며, 제어의 흐름을 사용자가 컨트롤 하는 것이 아니라 스프링에게 맡겨 작업을 처리하게 됩니다.
아래 그림처럼 A클래스가 직접 new를 통해 B, C클래스를 생성하고 관리하는 구조입니다. A클래스 입장에서는 B, C 클래스가 변경시 의존적이 됩니다. 반면 스프링 프레임워크에서 B, C 클래스를 자동 생성해주고 A 클래스에게 의존 주입을 통해 제공하는 구조라면, A는 B, C 클래스의 변경 상황에 대한 보다 독립적인 위치가 되기 때문에 코드의 유지보수나 오류에 대해서 좀더 영향을 적게 받을 수 있습니다. 이런 관계로 보면 제어의 역전이라고 볼 수 있겠습니다.
