purplely88's home

시작에 앞서

9장을 완료해주세요.

 

데이터 바인딩 개요

데이터 바인딩이란 화면 입력항목과 오브젝트 필드와의 맵핑을 뜻합니다.

또한, 화면으로부터 전달받은 값을 필드 데이터형에 맞춰서 변환도 해줍니다.

 

스프링에서는 데이터 바인딩을 어느 정도 자동으로 시행해줍니다.

예를 들어, 화면에서 텍스트 항목에 입력한 수치를 오브젝트의 int형으로 변환해줍니다.

대신, 데이터 형 변환이 어려울 경우도 있습니다. 그런 경우에는 어노테이션을 사용하는 것으로 데이터 바인딩을 할 수 있습니다. 

어떤 방식으로 쓰는 거에는 이다음 실제 예를 들어서 설명하겠습니다.

 

데이터 바인딩 코딩

그러면 실제로 만들어보겠습니다.

데이터 바인딩에 필요한, 화면에서 컨트롤러 클래스에 오브젝트를 넘기는 방법에 대해서 배우겠습니다.

만들기 전에, 먼저 처리 개요에 대해서 설명하겠습니다.

로그인 화면에서는 아무것도 하지 않기 때문에, 유저 등록 화면의 처리 개요를 보도록 하겠습니다.

 

1,2의 처리

로그인 화면에서 유저 등록 화면으로 전이합니다. 그때에 유저 등록용 폼 클래스의 인스턴스(SignupForm)를 화면에 전달합니다.

 

3,4,5의 처리

유저 등록 버튼을 클릭하면, 폼 클래스의 인스턴스를 컨트롤러 클래스에 전달합니다.

폼 클래스의 인스턴스를 받은 후에는 /login에 리다이렉트 합니다.

유저 등록은 이루어지지 않습니다. 

(실제 등록은 데이터 베이스 쪽에서 다루겠습니다.)

 

즉, 유저 등록 화면에서 유저 등록용 폼 클래스에 데이터 바인딩을 합니다.

먼저 폼 클래스를 작성해보도록 하겠습니다. 

 

SignupForm.java

 

 

포인트 1

@DateTimeFormat 어노테이션을 필드에 붙임으로써, 화면으로 전달받은 문자열을 날짜 형식으로 변환해줍니다.

또한, pattern속성에 어떤 포맷으로 데이터를 전달받을지를 지정합니다.

 

데이터 바인딩용 어노테이션 @DateTimeFormat 외에도 @NumberFormat이라는 어노테이션이 있습니다.

사용방법은 구글링 하시면 쉽게 아실 수 있으십니다.

 

다음은 화면에서 전달받은 폼 클래스를 컨트롤러 클래스에서 받을 수 있도록 해보겠습니다.

 

SignupController.java

 

포인트 1

인수 폼 클래스에 @ModelAttribute 어노테이션을 붙임으로써, 자동으로 Model 클래스에 등록(addAttribute)해줍니다.

즉, 아래의 내용과 같습니다.

 

@GetMapping("/signup")

public String getSignUp(SignupForm form, Model model) {

  // 폼 클래스를 Model에 등록

  model.addAttribute("SignupForm", form);

}

 

또한, @ModelAttribute를 붙일 경우, 디폴트 값으로는 클래스 명의 앞글자가 소문자로 바꾼 문자열이 키 명으로 등록됩니다.

샘플 코드의 경우에는 "signupForm"이라고 하는 키 명에 등록됩니다.

혹시나 키 명을 바꾸고 싶을 경우에는 @ModelAttribute("<키명>")과 파라미터를 지정합니다.

 

포인트 2

데이터 바인딩 결과를 받을 경우에는 메서드의 인수에 BindingResult 클래스를 추가합니다.

이 클래스의 hasError() 메서드에 데이터 바인딩에 실패했는지를 알 수 있습니다.

또한, 이후에 등장할 밸리데이션(입력 체크)에 에러가 발생할 경우에도 이 BindingResult 클래스의 hasError() 메서드에 실패했는지 알 수 있습니다.

 

포인트 3

데이터 바인딩에 실패할 경우에는 BindingResult의 hasError() 메서드에 false가 반환됩니다.

위 코드에서는 데이터 바인딩에 실패했을 경우, 유저 등록 화면으로 돌아옵니다.

그 경우에는 getSignUp 메서드를 호출하고 있습니다.

이유는 라디오 버튼용 변수를 초기화해주기 때문입니다.

 

다음으로 유저 등록용 html을 수정합니다.

 

포인트 1

th:object 속성을 사용함으로써, Model에 등록된 오브젝트를 받는 것이 가능합니다.

위 코드로 말하자면, SignupForm 클래스를 받고 있습니다.

 

사용 방법은 다음과 같습니다.

th:oject="${<ModelAttribute의 키 명>}"

 

th:object를 붙인 태그 안에는 th:field에 그 오브젝트명을 생략할 수 있습니다.

th:field를 사용하면, 오브젝트 안에 필드를 취득할 수 있습니다.

 

th:field 사용법 첫 번째

th:field="${<ModelAttribute의 키 명. 필드명>}"

이것은 th:object속성을 쓰지 않았을 경우의 사용법입니다. 이 방법으로 위 코드를 다시 작성해본다면 아래와 같습니다.

th:field="${signupForm.userId}"

 

필드가 하나밖에 사용되지 않을 경우에는 th:object를 작성하지 않아도 값을 취득, 송신이 가능하다는 것입니다.

 

th:field 사용법 두 번째

th:field="${<필드명>}"

th:object가 붙은 태그 안에 있으면, 오브젝트 명을 생략할 수 있습니다.

이것은 위 코드와 같은 방법입니다. 화면에서 보낼 필드가 많을 경우에 유효한 방법입니다.

종래 필드가 늘 수 있다는 생각을 한다면 이 방법을 추천합니다.

 

포인트 2

에러 메시지를 정리해서 일람 표시하기 위해서는 라디오 버튼과 같은 th:each 속성을 사용합니다.

정리하지 않고 개별 메시지로 표시하는 방법에 대해서는 밸리데이션(입력 체크)에서 설명하겠습니다.

 

실행

스프링 부트를 실행하신 다음에 신규 등록 화면으로 들어오셔서 등록 정보를 입력합니다.

정상 등록일 시 로그인화면으로 전이
컨트롤러 클래스에서 작성한 println 내용

이것으로 두 가지가 가능해졌습니다.

1. 화면에서 폼 클래스에 값을 전달한다.

2. 폼 클래스를 컨트롤러 클래스에 전달한다.

 

하지만, 또 문제가 있습니다.

데이터 바인딩에 실패했을 경우입니다. 

예를 들어, 연령에 문자열이 입력되어 있다던지, 생일에 연도만 입력해보겠습니다.

 

이대로라면 메시지가 영어이면서 내용도 적절치 않습니다.

다음으로 에러 메시지를 수정해보도록 하겠습니다.

'프로그래밍 > 스프링 부트' 카테고리의 다른 글

9. 데이터 바인딩 & 밸리데이션 (1/4)  (0) 2020.06.20
8. DI (4/4)  (0) 2020.06.18
7. DI (3/4)  (0) 2020.06.16
6. DI (2/4)  (0) 2020.06.16
5. DI (1/4)  (0) 2020.06.14

시작

직접 WEB 애플리케이션(로그인 화면)을 제작해보면서 데이터 바인딩이 무엇인지, 밸리데이션이 무엇인지 알아보겠습니다.

 

데이터 바인딩

화면에 입력한 유저 ID와 패스워드 등을 오브젝트로 받는 방법에 대해 설명합니다.

 

밸리데이션 (입력 체크)

유저 등록 시에 밸리데이션 방법에 대해서 설명합니다.

 

라이브러리 추가

이번부터는 Bootstrap이라고 하는 CSS 프레임워크를 사용합니다.

Bootstrap을 사용함으로써 나름 이쁜 화면을 간단히 만드는 것이 가능합니다.

(개발 현장에서도 많이 사용합니다.)

 

Bootstrap을 사용하는 방법에는 몇 가지 방법 있습니다만, 여기서는 제일 간단한 방법으로 webjars라는 라이브러리를 사용하여 Booststrap을 설정합니다.

 

webjars

webjars란, maven등과 같은 빌드 툴로, JavaScript나 Bootstrap 등을 간단하게 관리할 수 있는 라이브러리입니다.

webjars를 사용하기 위해선, pom.xml의 dependencies 태그 안에 아래의 두 개의 라이브러리를 추가합니다.

 

jQuery

pom.xml 에는 Bootstrap 외에도 jQuery 라이브러리를 추가합니다.

jQuery라는 것은 JavaScript 라이브러리입니다. JavaScript 코드를 그대로 쓰는 것을 보다 쉽게 알 수 있기 때문에 개발 현장에서도 많이 사용됩니다.

Bootstrap에 일부에 jQuery가 사용됩니다. 그로 인해 Bootstarp을 사용할 때에는 jQuery가 필요합니다.

 

pom.xml을 변경한 다음에는 maven 갱신을 시행합니다.

maven을 갱신하면 추가한 라이브러리를 다운로드합니다.

maven 갱신
OK 버튼을 눌러 갱신 시작

 

다운로드 완료 확인

 

bootstrap 내용물 확인

보시면 아시겠지만, Webjars 에는 Bootstrap 등과 같은 파일을 직접 다운로드하고 있습니다.

이 jar파일의 경로를 지정하는 것으로 css 파일이나 js 파일을 읽는 것이 가능합니다.

이 이후에 실제로 사용하는 방법에 대해서 설명하겠습니다.

 

화면 작성

데이터 바인딩, 밸리데이션의 기능을 코딩하기 전에 HTML 화면 먼저 작성해보겠습니다.

그 이유는 화면 작성과 데이터 바인딩 코딩을 같이해버리면 복수의 설명이 되기 때문에..

 

그러면 이제부터 로그인과 신규등록에 관한 화면을 만들어보겠습니다.

먼저 구성은 아래와 같습니다.

 

로그인, 신규등록 컨트롤러 두개 작성과 동시에 html도 작성.

LoginController.java

 

내용을 말씀드리자면, http://localhost:8080/login에 GET 메서드, POST 메서드로 HTTP 요청(리퀘스트)이 온다면, login폴더 안에 있는 login.html에 화면을 전이한다는 것입니다.

각 메서드에 지정한 반환 값은 html 파일입니다.

html 파일은 src/main/resources/template 에서부터 지정되며, 그 이유로 return login/login이라는 것은 src/main/resources/template/login이라는 폴더 안에 login.html을 말하는 것입니다.

 

다음으로는 로그인 화면을 만들어보겠습니다.

 

login.html

포인트 1

Bootstrap을 사용하기 위해서는 jQuery와 Bootstrap의 파일을 읽어드릴 필요가 있습니다.

그렇기 때문에 <head> 태그 안에 추가했습니다.

통상 css 나 javascript 파일은 src/main/resources 안에 두어 파일을 읽습니다.

하지만 webjars를 사용하면 다운로드한 라이브러리 안에 파일을 src/main/resources안에 둔 것과 같이 파일을 읽을 수 있습니다. 또한 타임 리프에서 src/main/resources 안에 파일을 읽는 방법은 다음과 같습니다.

css 파일

th:href="@{src/main/resources부터의 파일 경로}"

js 파일

th:href="@{src/main/resources부터의 파일 경로}"

또한 jQuery의 js 파일을 읽은 후에 Bootstrap의 js 파일을 읽는 것과 같이 되어있습니다.

Bootstrap 파일 안에 jQuery를 사용하기 때문에, 먼저 jQuery 자기 자신을 읽지 않으면 안 됩니다.

Bootstrap을 사용하면 디자인성이 높은 화면을 간단히 제작할 수 있습니다. 

사용 방법은 각 태그의 class속성에 Bootstrap에서 제공하는 몇 가지 클래스를 SET만 하면 됩니다.

그로 인해 class="XXX"와 같이 되어있는 태그는 Bootstrap을 사용하고 있다고 생각해주세요.

 

포인트 2

타임 리프를 이용하여 앵커 태그를 사용할 경우에는 th:href 속성을 사용합니다.

사용하는 방법은 컨트롤러 클래스 안에 @GetMapping 어노테이션의 괄호 안의 문자열을 지정하는 거뿐입니다.

문자열을 지정할 경우에는 @{'/xxx'}라고 하는 형식으로 전이처를 지정합니다. 또한 앵커 태그이기 때문에 HTTP 요청(request)은 GET 메서드로 송신됩니다.

 

다음은 유저 등록 화면용 컨트롤러를 제작해보겠습니다.

 

SignupController.java

포인트 1

타임 리프에 라디오 버튼의 값을 동적으로 변경하기 위해서는 Map을 사용합니다.

그 Map에 들어간 키와 값을 화면에 표시하는 것이 가능합니다.

위 소스에서는 initRadioMarriage()라고 하는 메서드 안에 Map에 값을 넣습니다.

그리고 유저 등록 화면에 GET 요청(request)이 올 경우, Model 클래스에 Map을 등록합니다.

이러함에 있어 화면으로부터 Map의 값을 취득할 수 있게 됩니다.

타임 리프로 라디오 버튼을 만드는 방법은 차후에 설명하겠습니다.

 

포인트 2

리다이렉트를 할 경우, 메서드의 반환 값에 redirect:/전이처 경로를 지정합니다.

리다이렉트를 하면 전이처에 컨트롤러 클래스의 메서드가 호출됩니다.

위 소스의 경우 http://localhost:8080/login에 GET 메서드로 HTTP 요청(request)이 보내집니다.

그리고 LoginController의 getLogin 메서드가 호출됩니다.

 

위 코드를 다시 설명해드리자면, http://localhost:8080/signup에 GET 메서드로 HTTP 요청(request)이 온다면, signup.html (유저 등록 화면)으로 전이합니다.

 

다만, http://localhost:8080/signup에 POST 메서드로 HTTP 요청(request)이 온다면, 로그인 화면으로 리다이렉트 합니다. /login에 리다이렉트 하기 때문에 LoginController의 GET 메서드를 호출합니다.

 

다음은 유저 등록 화면을 작성해보겠습니다.

 

signup.html

포인트

라디오 버튼을 코딩하기 위해서는 th:each 속성을 사용합니다.

th:each 속성은 확장 for문과 같이 사용합니다. 

th:each

th:each="변수명 : ${<ModelAttribute의 키명>}" 형식으로 지정합니다. 

이것으로 Model에 등록된 값을 반복해서 호출하게 됩니다. 또한, th:each 가 붙은 태그 안에는 Model에 등록된 값을 변수명으로 간단히 취득할 수 있습니다.

위 코드에서는 th:each 속성이 붙은 div 태그 안에는 item이라고 하는 변수를 사용합니다.

이 item 변수 안에는 SignupController 클래스에서 취득한 Map이 들어가 있습니다.

th:text

th:text에는 화면에 표시할 문자열을 지정합니다.

위 코드에서는 Map 클래스의 키(기혼, 미혼)를 표시합니다.

th:value

th:value에는 화면으로부터 Controller 클래스에 보낼 값을 지정합니다.

위 코드에서는 Map 클래스의 값(true, false)을 보냅니다.

 

th:action

로그인 화면에서는 form 태그 안에 action="/login"이라는 형태로 요청(request)처 URL를 지정했습니다.

이것은 통상 HTML 사용법입니다. 

반면, 유저 등록 화면에서의 html에서는 th:action="@{/signup}"과 같이 요청(request)처 URL를 지정했습니다.

이것은 타임 리프의 사용 방법 중 하나입니다.

스프링 시큐리티를 사용하지 않는 경우에는 action="/login"과 같은 형태로 사용하여도 문제가 되지 않습니다.

하지만, 스프링 시큐리티를 사용할 경우에는 form태그의 th:action 속성을 사용하도록 해주세요.

관련 상세 내용은 추후에...

 

여기까지 작성이 완료되었으면 스프링 부트를 기동하여 아래의 URL을 입력해보겠습니다.

http://localhost:8080/login

 

로그인 버튼을 눌러도 아무 처리없이 로그인 화면으로 돌아옵니다.

신규등록은 여기로를 눌러보면...

이제 준비가 완료되었습니다.

다음 포스트부터는 본격적으로 데이터 바인딩, 밸리데이션에 대해서 설명해드리겠습니다.

'프로그래밍 > 스프링 부트' 카테고리의 다른 글

10. 데이터 바인딩 & 밸리데이션 (2/4)  (0) 2020.06.23
8. DI (4/4)  (0) 2020.06.18
7. DI (3/4)  (0) 2020.06.16
6. DI (2/4)  (0) 2020.06.16
5. DI (1/4)  (0) 2020.06.14

시작에 앞서

7장을 완료해주세요.

 

DI 라이프 사이클 관리 기능에 앞서

DI안에서는 인스턴스를 생성하고 그것을 변수에 넣는다는 것은 이제 충분히 이해하셨을 거라 봅니다.

하지만 DI 기능은 이거뿐만이 아닙니다.

 

DI에서는 라이프 사이클 관리도 행해지고 있습니다.

WEB 애플리케이션 개발하는 상에서는 꽤나 편리한 기능입니다만, 함정이 있습니다. (헉)

그럼 알아보도록 하겠습니다.

 

DI 라이프 사이클 관리

애초에 라이프 사이클 관리라는 것이 무엇이 나면, 인스턴스 생성과 파괴를 관리해주는 것입니다.

 

인스턴스를 생성할 때에 통상은 new를 붙여서 생성합니다. 반대로 인스턴스의 파괴는 변수에 null를 넣습니다.

사용하지 않게 된 변수에 null을 넣지 않으면, 메모리상에 그대로 남게 되어 낭비가 되기 때문입니다.

이것은 퍼포먼스에 영향을 준다는 지, 메모리 부족에서부터 시스템 정지까지도 일어날 수 있습니다.

 

그렇지만 @Autowired를 붙인 필드에 null을 일부러 넣을 필요는 없습니다.

스프링이 자동으로 해주기 때문에! 오홍

 

또한, Java에 Web 애플리케이션을 작성할 때에는 서블릿을 사용합니다.

서블릿을 사용할 때는 인스턴스를 Session 스코프나 Request 스코프에 등록합니다만, 이것도 스프링에서 해줍니다.

 

하지만, 그 인스턴스가 언제 파괴되는지에 대한 것을 꼭 파악해 둘 필요는 있습니다.

 

스코프

Session 스코프나 Request 스코프라고 하는 것은 인스턴스에 유효기간을 말합니다.

Java의 로컬변수를 상상해보세요. if문안에 선언한 변수는 if 안이 스코프이기 때문에, if문 밖에서는 사용할 수 없습니다.

 

각 스코프의 유효기간은 아래와 같습니다.

 

Session 스코프

유저가 로그인할때부터 로그아웃하기까지의 유효기간입니다.

예를들어, 유저 정보나 그것과 연관된 권한과 같은 정보를 Session 스코프로 가지고 있습니다.

 

Request 스코프

HTTP의 요청(Request)이 유효기간입니다.

예를 들어, 유저가 등록 화면에 각 항목을 입력한 후, 등록 버튼을 누르면, 입력 값을 출력되는 결과 화면이 표시됩니다. 

그 경우에 유저 등록 화면부터 결과 화면까지가 Request 스코프의 범위가 됩니다.

 

그러면 어떻게 해서 라이프 사이클 관리를 하는지는 @Scope 어노테이션을 붙입니다.

그리고 그 어노테이션에 어떤 스코프를 등록할지를 지정합니다. 

예를 들어, 아래와 같이 씁니다.

 

@Scope 샘플

 

@Component

@Scope("prototype")

public class SampleComponent {

 

}

 

빨간 글씨의 prototype은 Bean을 취득할 때마다 매회 새로운 인스턴스가 생겨나는 의미입니다.

※ prototype 외에도 singleton, session, sequest 등등 여러 가지가 있으니 검색!

 

@Component 외에도 @Bean이나 @Controller에서도 @Scope 어노테이션을 이용할 수 있습니다.

@Scope 어노테이션을 이용함으로써, 간단히 인스턴스 생성과 파괴가 가능해졌습니다.

 

DI 함정 첫 번째 - singleton

여기서부터는 스프링의 DI에 자주 있는 함정에 대해서 설명해보도록 하겠습니다.

먼저, singleton을 썼을 경우에 함정입니다.

 

스프링을 갓 배우기 시작하면, @Scope를 붙이는 것을 모른 채로 어노테이션을 만드는 경우가 있습니다.

특히 인터넷상에서는 간단하게 사용하는 방법을 배웠을 경우도 포함입니다.

 

@Scope를 붙이지 않으면, 인스턴스가 singleton에 작성됩니다.

singleton이란, Java의 디자인 패턴 중 한 가지입니다.

singleton이면 오브젝트의 인스턴스는 하나밖에 만들 수 없습니다.

 

singleton이 원인이 되어 버그가 발생되는 경우는 적습니다만, 아무리 조사해도 원인을 알지 못할 때에는

singleton이니까 발생한 것이 아닌가? 하는 의구심도 가질 필요가 있습니다.

 

※ @Controller, @Service, @Repository의 스코프는 통상 singleton으로 충분합니다.

 

DI 함정 두 번째 - 스코프의 차이

함정 두 번째는 스코프의 차이입니다.

예를 들어, singleton 스코프의 인스턴스가 prototype 스코프의 오브젝트를 가지고 있을 경우입니다.

 

prototype 스코프 샘플

@Component

@Scope("prototype")

public class PrototypeComponent {

 

}

 

singleton 스코프 샘플

@Component

public class SingletonComponent {

  @Autowired

  private PrototypeComponent component;

}

 

이렇게 함으로써 prototype 스코프를 설정한 Bean (PrototypeComponent)이 singleton스코프로 되어버립니다.

다른 예로는 request 스코프를 설정한 Bean을 session 스코프의 Bean에서 가질 때 위와 같은 상황이 되어버립니다.

 

스코프가 다른 Bean을 필드로써 가지는 경우는 주의가 필요합니다.

 

마무리

DI?

DI는 한 단어로 말해서 인스턴스 관리를 하는 것을 뜻한다.

인스턴스 관리는 인스턴스의 생성과 스코프 관리를 말한다.

@Controller와 같은 어노테이션이 붙은 클래스의 인스턴스를 DI 컨테이너에 등록한다.

DI컨테이너에 등록된 클래스를 Bean이라고 말한다.

@Autowired를 사용하면 DI 컨테이너로부터 getter로 인스턴스를 취득할 수 있다.

 

DI 실행 방법

1. 어노테이션 베이스

2. JavaConfig

3. xml

4. JavaConfig + 어노테이션 베이스

5. xml + 어노테이션 베이스

(주로 4번과 5번을 많이 사용한다.)

 

라이프 사이클 관리

DI에서는 인스턴스 스코프를 설정할 수 있다.

스코프는 @Scope 어노테이션에서 설정할 수 있다.

디폴트 값으로는 singleton으로 되며, 인스턴스가 하나밖에 생성되지 않는다.

스코프가 다른 인스턴스를 필드에서 가질 경우에는 주의가 필요하다.

'프로그래밍 > 스프링 부트' 카테고리의 다른 글

10. 데이터 바인딩 & 밸리데이션 (2/4)  (0) 2020.06.23
9. 데이터 바인딩 & 밸리데이션 (1/4)  (0) 2020.06.20
7. DI (3/4)  (0) 2020.06.16
6. DI (2/4)  (0) 2020.06.16
5. DI (1/4)  (0) 2020.06.14

시작에 앞서

6장을 완료해주세요.

DI 

오래 기다리셨습니다.

5장과 6장에서 의존성과 주입에 대해 알아보았습니다만, DI는 이 두 가지를 동시에 한다고 생각하시면 될 거 같습니다.

 

DI 내부 처리

일단 DI 내부 처리가 어떻게 일어나는지 한번 알아보겠습니다.

 

1. DI의 관리 대상 클래스를 찾는다. (컨포넌트 스캔)

알아보실 수 있으시겠죠..?

스프링을 기동 하면, 컨포넌트 스캔이라고 불리는 처리가 이루어집니다.

이것은 DI에서 관리하는 대상의 어노테이션이 붙은 클래스를 찾는 처리입니다.

 

컨포넌트 스캔 대상의 어노테이션은 다음과 같습니다.

@Component

@Controller

@Service

@Repository

@Configuration

@RestController

@ControllerAdvice

@ManagedBean

@Named

 

위에 어노테이션이 붙은 클래스가 DI의 대상이 됩니다.

또한 어노테이션이 붙은 클래스를 빈(Bean)이라고 합니다.

 

빈 (Bean)

물론, 어노테이션을 붙이지 않아도 DI 컨테이너에 클래스를 등록하는 것도 가능합니다

이로 인해, 정확히는 DI 컨테이너상에서 관리하는 클래스를 빈(Bean)이라고 합니다.

 

2. 인스턴스 생성과 주입

다음으로는 DI 컨테이너에 등록된 Bean의 인스턴스 생성과 주입을 한다고 생각하시면 됩니다.

어디까지나 간단한 입니다. 실제로는 다르니 이해로써만 참고해주세요.

DI 대상 클래스 (Bean)을 모은 후에는, 그것들의 인스턴스를 DI가 생성합니다.

그리고 생성한 인스턴스를 @Autowired 어노테이션이 붙은 필드 곳곳에 주입합니다.

 

위 이미지의 DI 컨테이너를 보시면, 안에 각 클래스의 인스턴스를 생성해 두고, 그 인스턴스를 getter로 취득하는 이미지입니다. 그리고 @Autowired가 붙어있는 필드들에는 DI 컨테이너의 getter를 호출하는 이미지입니다.

 

이것은 6장에서 만든 Factory 클래스와 상당히 비슷합니다.

하지만 Factory 클래스와 달리 매회 new 처리는 하지 않습니다만, 매회 new 처리하게 할 수도 있습니다.

 

즉, @Component 등의 어노테이션을 붙이는 것만으로도 하나하나 Factory 클래스를 만들 필요가 없다는 이야기가 됩니다.

 

@Autowired를 쓰는 곳

@Autowired를 쓰는 곳은 아래의 3곳입니다.

1. 필드 변수

2. 생성자 파라미터

3. setter 파라미터

이 외에는 @Autowired를 붙일 수 없습니다. 예를 들어 메서드 내부 로컬 변수에는 붙일 수 없습니다.

 

@Autowired를 쓰는 곳(2. 생성자 파라미터)

생성자 앞에 @Autowired를 붙임으로써, 파라미터에 인스턴스가 반환됩니다.

@Autowired

public class TestClass(xxxComponent component) {

  super();

  this.component = component;

}

 

@Autowired를 쓰는 곳(3. setter 파라미터)

setter 앞에 @Autowired를 붙임으로써, 파라미터에 인스턴스가 반환됩니다.

@Autowired

public class void setxxxComponent(xxxComponent component) {

  this.component = component;

}

 

이와 같이 클래스의 인스턴스를 생성하여 주입하는 것이 DI 컨테이너의 기능입니다.

 

DI 코딩 방법

 

DI를 코딩하는 방법은 총 5가지가 있습니다.

1. 어노테이션 베이스

2. JavaConfig

3. xml

4. JavaConfig + 어노테이션

5. xml + 어노테이션

 

지금까지 보여드린 방식은 1. 어노테이션 베이스입니다.

즉, @Controller, @Service만 사용한 코딩 방법입니다.

 

이번 코딩 방식에는 2번과 4번에 대해서 설명드리겠습니다.

 

JavaConfig

1. DI의 관리대상 클래스를 찾는다. (컨포넌트 스캔)

처음으로 DI 컨테이너에 등록하는 Bean을 정의합니다.

 

@Bean을 붙인 getter의 반환 값이 DI 컨테이너에 Bean으로 등록됩니다.

먼저 @Configuration 어노테이션을 붙인 클래스를 준비합니다.

그 클래스 안에는 @Bean 어노테이션이 붙은 getter 메서드를 준비하면, getter 반환 값이 Bean으로써 DI 컨테이너에 등록됩니다.

 

2. 인스턴스 생성과 주입

다음으로는 DI 컨테이너에 등록한 Bean의 인스턴스 생성과 주입을 한다고 생각하시면 됩니다.

이전 이미지를 그대로 가져왔습니다만, 꼭 집어서 추가될 부분은 DI 컨테이너 안에 private JavaConfig config = new JavaConfig(); 가 있겠네요.

DI 컨테이너 안에 JavaConfig 클래스와 Bean 인스턴스를 준비한 후, 각 인스턴스를 getter로 취득하는 이미지입니다.

그리고 @Autowired가 붙은 부분에 getter로 호출하는 이미지입니다.

이건 DI 코딩 방법 1. 어노테이션 베이스와 같습니다.

 

1. 어노테이션 베이스에서는 @Component 혹은 @Controller가 붙은 클래스를 스프링에서 자동으로 DI 컨테이너에 등록해줍니다. 한편, 2. JavaConfig에서는 @Bean 어노테이션이 붙은 getter의 반환 값을 DI 컨테이너에서 관리해줍니다.

즉, 2. JavaConfig에서는 DI 컨테이너에서 관리하는 인스턴스 분(개수)의 메서드를 준비하지 않으면 안 됩니다.

 

그럼 JavaConfig 방식은 굳이 안 써도 되지 않나?라고 생각하실 수 있습니다만, JavaConfig 방식은 상세한 설정이나 교체가 가능합니다. 예를 들어, 인스턴스를 생성할 때에 생성자에 전달하는 값을 설정할 수 있습니다.

혹은 JavaConfig 클래스 교체하여 개발환경용/본 환경용과 같이 교체하면서 사용이 가능합니다.

 

그래도 역시 개발규모가 크면 클수록 JavaConfig 안에 정의해야만 하는 메서드가 늘어갑니다.

이것을 회피하기 위해 통상은 1. 어노테이션 베이스 방식과 2. JavaConfig 방식을 같이 사용합니다. 

이 두 가지 방식을 사용하는 것이 4. JavaConfig + 어노테이션 방식입니다.

 

4. JavaConfig + 어노테이션

이 방식은 DI에서 관리하고 싶은 클래스에 @Controller와 같은 어노테이션을 붙이고, 상세한 설정을 하고 싶은 인스턴스만 JavaConfig에 설정하는 방식입니다.

보통 소규모 애플리케이션이라고 하면 1. 어노테이션 베이스 방식으로 개발합니다. 그리고 규모가 크거나 설정 교체와 같은 작업을 하고 싶을 때에는 4. JavaConfig + 어노테이션 방식으로 개발합니다.

 

추후에 4. JavaConfig + 어노테이션 방식으로 Bean정의로 코딩하는 것을 보여드리겠습니다.

 

엥!? xml 방식은...?

xml 은 JavaConfig에서 설정하는 내용을 단지 xml로 옮긴 거뿐입니다.

 

 

다음 포스트에서는 DI 라이프 사이클에 대해서 알아보겠습니다.

'프로그래밍 > 스프링 부트' 카테고리의 다른 글

9. 데이터 바인딩 & 밸리데이션 (1/4)  (0) 2020.06.20
8. DI (4/4)  (0) 2020.06.18
6. DI (2/4)  (0) 2020.06.16
5. DI (1/4)  (0) 2020.06.14
4. 웹 애플리케이션 작성 (데이터 베이스로부터 값 취득)  (0) 2020.06.14

시작에 앞서

5장을 완료해주세요.

 

주입

의존성 다음에는 주입에 대해서 이해를 해보도록 하겠습니다.

계속해서 5장의 자동차를 예를 들어, 이번에는 자동차를 대량 생산해보겠습니다.

 

대량생산에 맞춘 코드는 다음과 같습니다.

 

메인 클래스 (기본 생략)

 

// 엔진 생성

Engine hyundaiEngine1 = new HyundaiEngine();

Engine hyundaiEngine2 = new HyundaiEngine();

Engine kiaEngine1 = new KiaEngine();

 

// 자동차 생산

Car car1 = new Car(hyundaiEngine1);

Car car1 = new Car(hyundaiEngine2);

Car car1 = new Car(kiaEngine1);

 

여기서는 하나하나 생성한 인스턴스를 Engine인터페이스형의 변수에 넣고 있습니다.

이와 같이 인터페이스형 변수에 인스턴스를 넣는 것을 주입이라고 합니다.

 

이것으로 주입 설명은 끝...이라고 보이시겠지만, 실제로는 조금 문제가 있어 보입니다.

예를 들어 현대가 새로운 엔진을 만들고 거기에 맞춰 자동차를 생산하다고 생각해봅시다.

그러면 현대의 새로운 엔진 클래스를 준비하고, 메인 클래스를 수정해야 할 필요가 있습니다.

 

엔진 클래스 (현대 버전2)

public class HyundaiEngineVer2 extends HyundaiEngine {

 

  // 생성자

  public HyundaiEngineVer2() {

    super();

  }

 

  // 기동 메서드

  @Override

  public void boot() {

    System.out.println("현대 엔진 기동 (버전 2)");

 

  // 정지 메서드

  @Override

  public void stop() {

    System.out.println("현대 엔진 정지 (버전 2)");

  }

}

 

버전 2 클래스는 버전1 클래스를 상속합니다.

 

메인 클래스 (기본 생략)

 

// 엔진 생성

Engine hyundaiEngine1 = new HyundaiEngineVer2();

Engine hyundaiEngine2 = new HyundaiEngineVer2();

Engine kiaEngine1 = new KiaEngine();

 

// 자동차 생산

Car car1 = new Car(hyundaiEngine1);

Car car1 = new Car(hyundaiEngine2);

Car car1 = new Car(kiaEngine1);

 

엔진을 생성하는 부분에 버전2 엔진 클래스로 변경한 게 보이실 겁니다.

지금 하고 있는 건 새로운 엔진 클래스를 추가하는 처리와 같습니다.

하지만 엔진 생성하는 코드가 늘면 늘 수록 자칫하면 코드 수정이 대량으로 많아지는 의미로 볼 수 있습니다.

 

이 문제는 간단하게 해결할 수 있습니다. 그건 바로 Factory 클래스를 만드는 것입니다. 오호!

Factory 클래스란 인스턴스를 생산하는 클래스를 뜻합니다. 이건 자바에서 자주 사용되는 기법 중 하나입니다.

 

Factory 클래스

public class EngineFactory {

  

  // 현대 엔진 클래스를 생산하여 반환

  public static Engine createHyundaiEngine() {

    return new HyundaiEngineVer2();

  }

 

  // 기아 엔진 클래스를 생산하여 반환

  public static Engine createKiaEngine() {

    return new KiaEngine();

  }

}

 

메서드를 static으로 함으로써 Factory 클래스를 생성하지 않아도 메서드 호출이 가능해졌습니다.

 

메인 클래스 (기본 생략)

 

// 엔진 생성

Engine hyundaiEngine1 = EngineFactory.createHyundaiEngine();

Engine hyundaiEngine2 = EngineFactory.createHyundaiEngine();

Engine kiaEngine1 = EngineFactory.createKiaEngine();

 

// 자동차 생산

Car car1 = new Car(hyundaiEngine1);

Car car1 = new Car(hyundaiEngine2);

Car car1 = new Car(kiaEngine1);

 

Factory 클래스를 사용함으로써, 인스턴스를 생성하는 클래스가 변경될 시 메서드의 내용물만 바꾸는 것으로 마무리 지을 수 있습니다. 

즉, 변경에 강한 애플리케이션이 되었습니다. 얏호!

 

여기까지가 주입의 이야기입니다. 

여기까지 이해하셨다면 이것으로 DI를 이해하는데 충분한 기반이 완성되었습니다.

 

다음 포스트에서는 DI에 대해서 구체적으로 알아보겠습니다.

 

 

'프로그래밍 > 스프링 부트' 카테고리의 다른 글

8. DI (4/4)  (0) 2020.06.18
7. DI (3/4)  (0) 2020.06.16
5. DI (1/4)  (0) 2020.06.14
4. 웹 애플리케이션 작성 (데이터 베이스로부터 값 취득)  (0) 2020.06.14
3. 웹 애플리케이션 작성 (파라미터 전달)  (0) 2020.06.14

DI란

드디어 스프링 코어인 DI에 대해서 설명해보겠습니다.

 

DI는 Dependency Injection (의존성 주입)의 약어입니다.

DI는 사용함에 있어서는 매우 간단하지만 스프링 전체의 기반이기 때문에 너무나 중요합니다. 

 

이해를 돕기위해 간단한 코드를 기재합니다만, 직접 작성하여 컴파일 할 필요는 없습니다.

 

DI를 한 단어로 설명하자면..

DI가 무엇인가에 대해 한 단어로 설명하자면, 인스턴스 관리입니다.

@AutoWired 어노테이션을 필드쪽에 붙이면 DI 컨테이너로부터 인스턴스를 취득합니다.

 

이미지

점점 그림이 추악해진다...

인스턴스 관리라고 말하면 막연하기 때문에 아래의 두가지를 한다고 생각해주세요.

 

1. 인스턴스의 생성

DI 컨테이너 안에서 인스턴스를 생성합니다. 즉, 클래스를 new 합니다.

그리고 애플리케이션에서는 그것들의 인스턴스를 취득하여 이용합니다.

 

DI안에서 매회 new를 한 인스턴스를 애플리케이션에 넘길 것인가, 아니면 한번 new 한 인스턴스를 애플리케이션에 넘길 것인가와 같은 것도 관리해줍니다.

 

2. 인스턴스의 라이프 사이클 관리

인스턴스의 라이프 사이클 관리를 간단히 코딩할 수 있도록 되어있습니다.

이것은 웹 애플리케이션 개발을 더욱더 편리하게 해줍니다. 

 

예를들어 서블릿의 요청 스코프(request scope)나 섹션 스코프(session scope)에 인스턴스를 간단히 등록가능하다는 뜻입니다.

 

인스턴스의 생성과 라이프 사이클 관리(인스턴스 파괴)를 DI가 해주기때문에, 클래스를 new 하던지, 사용이 끝난 변수에 null을 넣을 필요가 없어집니다. 이러한 이점때문에 null 처리를 깜빡했을 경우를 방지해주며 코드의 가독성도 높아집니다.

 

이건 어디까지나 DI를 간단하게 설명한 것입니다. 

하지만 이것을 알고있는 것만으로도 다음 설명에 이해 정도가 다를 것입니다.

 

보충 : new

자바에서 클래스를 new 하는 것은 인스턴스가 메모리에 올라간다는 의미입니다.

그러므로 너무 많이 new 를 해버리면, 그 만큼 메모리를 사용하게 됩니다.

 

이것이 안좋다라는 의미는 아닙니다만, 같은 페이지에 대량의 요청을(request) 처리하게 된다면 같은 클래스를 그 만큼 new 할 필요가 있습니다.

 

더욱이, new 를 한 이후에는 기본적으로 그 변수에 null 을 입력합니다. null 을 입력함으로써, 가비지 컬렉션이 메모리상의 인스턴스를 깨끗하게 청소해주고 메모리를 비워주기 때문입니다. 

 

DI를 이용한다면 기본적으로 null을 입력할 필요가 없습니다.

null을 넣지않으면 안되는 경우도 분명히 있습니다만 그건 추후에 설명하겠습니다.

 

DI 설명 전에...

먼저 DI를 이해하기 전에 필수 지식부터 설명드리고 있습니다.

이것을 이해한다면 DI를 간단하게 이해할 수 있기 때문입니다.

 

DI에 대해서 이해하기 위해,  DI (의존성 주입)을 의존성과 주입. 이 두가지를 분석하겠습니다.

그리고 이 정도 이해가 되신다면 DI에 대해서 거진 다 이해하실 수 있으실 겁니다.

 

의존성이란 무엇인가

애초에 자바에 있어 의존이라는게 무엇인가? 라는 것부터 설명하겠습니다.

의존이라고 하는 것은 다른 클래스를 이용하고 있는가와 같은 것 입니다.

 

1. 다른 클래스를 로컬 변수로써 가지고 있다.

2. 다른 클래스가 메서드의 파라미터, 반환 값으로 되어있다.

 

자동차를 예를 들어 설명해보겠습니다.

자동차에는 엔진이 있습니다. 물론 타이어라던지 여러가지도 있습니다만, 이해하기 편하게 엔진만 가지고 있는 자동차라고 가정해보겠습니다. 이 때의 코드는 아래와 같을 것입니다.

 

자동차 클래스 (현대)

public class Car {

 

 // 현대 엔진

 private HyundaiEngine hyundaiEngine;

 

 // 생성자

 public class Car(HyundaiEngine hyundaiEngine) {

   // 현대 엔진 설정

   this.hyundaiEngine = hyundaiEngine;

 }

}

 

이 자동차 클래스는 현재 엔진을 사용합니다. 

그리고 이 자동차 클래스를 만드는 (new) 코드는 다음과 같습니다.

 

메인 클래스 (기본 생략)

  

  // 부품 생산

  HyundaiEngine hyundaiEngine = new HyundaiEngine();

  // 자동차 생산

  Car car = new Car(hyundaiEngine);

 

클래스 다이어그램으로 표현하자면...

일부 생략했습니다. 중요한 포인트는 따로있기때문에..

클래스 다이어그램에는 클래스간의 관계를 표현한 다이어그램입니다. 

클래스 다이어그램에는 의존을 나타낼때 점선 화살표를 이용하여 표현합니다.

 

자동차가 엔진을 사용하기 위해 자동차 클래스에는 현대 엔진 클래스를 의존하고 있습니다.

예를 들어, 자동차의 최고속도는 엔진에 의해 결정됩니다. 즉, 차의 성능은 엔진에 의존하고 있기때문입니다.

그렇기 때문에 의존원 자동차로부터 의존처의 엔진을 점선 화살표로 이어지게 됩니다.

 

이와 같이 어느 클래스가 다른 클래스를 이용하고 있는 것이 바로 의존입니다.

그리고 의존은 '정도'가 있습니다.

 

예를 들어, 아까와 같은 자동차에서는 엔진을 교환하기 위해서는 현대의 같은 엔진만 이용할 수 밖에 없습니다.

좀더 연비가 좋은 엔진이 있어도 교환이 안됩니다. 혹시나 이 자동차에 현대 엔진이 아닌 기아 엔진을 사용하게 된다면, 아래와 같이 수정할 수 밖에 없습니다.

 

자동차 클래스 (기아)

public class Car {

 

 // 기아 엔진

 private KiaEngine kiaEngine;

 

 // 생성자

 public class Car(KiaEngine kiaEngine) {

   // 기아 엔진 설정

   this.kiaEngine= kiaEngine;

 }

}

 

메인 클래스 (기본 생략)

  

  // 부품 생산

  KiaEngine kiaEngine= new KiaEngine();

  // 자동차 생산

  Car car = new Car(kiaEngine);

 

부품 일부를 바꾸는 것이 아닌 자동차 클래스와 메인 클래스 전부 수정하지 않으면 안됩니다.

이걸로는 간단하게 부품을 교체할 수가 없습니다. 또한, 자동차 클래스를 테스트할 때는 기아 엔진 클래스가 완성되지 않으면 테스트를 진행할 수 없습니다. 이대로라면 테스트도 어렵고 변경에 취약한 애플리케이션이 되버립니다.

이와 같이 의존도가 높은 상태를 tight coupling. 즉 결합이 밀접하다고 합니다.

 

여기서 의존도를 낮추는 방법에는 자바의 인터페이스를 사용합니다.

인터페이스를 사용함에 있어서 자동차의 부품을 자유롭게 교체할 수 있게 됩니다.

 

엔진 인터페이스

public interface Engine {

  public void boot();  // 기동 메서드

  public void stop();  // 정지 메서드

}

 

엔진의 인터페이스는 기동 메서드와 정지 메서드를 준비해 보겠습니다. 

어느쪽도 전 엔진에 필요한 메서드입니다. 인터페이스를 상속한 클래스에는 반드시 해당 메서드를 코딩하지 않으면(오버라이드) 안됩니다. 안그러면 컴파일 에러가 발생합니다.

 

엔진 클래스 (현대)

public class HyundaiEngine implements Engine {

  // 생성자

  public HyundaiEngine() {

    super();

  }

 

  // 기동 메서드

  @Override

  public void boot() {

    System.out.println("현대 엔진 기동");

 

  // 정지 메서드

  @Override

  public void stop() {

    System.out.println("현대 엔진 정지");

}

 

엔진 인터페이스를 상속한 현대 엔진 클래스에는 기동과 정지시의 구체적인 처리가 적혀져 있습니다. 

덧붙여서 인터페이스를 상속한 클래스를 서브 클래스라고 합니다.

 

자동차 클래스

public class Car {

 

 // 엔진

 private Engine engine;

 

 // 생성자

 public class Car(Engine engine) {

   // 기아 엔진 설정

   this.engine= engine;

 }

 

 // 시동을 건다

 public void engineStart() {

   engine.boot();

 }

 

 // 시동을 잠근다

 public void engineStop() {

    engine.stop();

  }

}

 

자동차 클래스에는 필드를 인터페이스로 변경합니다. 그리고 인터페이스의 기동, 정지 메서드를 이용하는 메서드를 준비합니다.

 

메인 클래스 (기본 생략)

  

  // 엔진 생산 (현대 엔진)

  Engine engine = new HyundaiEngine ();

 

  // 자동차 생산

  Car car = new Car(engine);

 

  // 시동을 건다

  car.engineStart();

 

  // 시동을 잠근다

  car.engineStop();

 

메인 클래스에서는 Engine형의 변수에 현대 엔진의 인스턴스를 넣고있습니다.

인터페이스형의 변수에는 상속한 서브클래스의 인스턴스를 넣는 것이 가능합니다.

 

엔진의 동작을 확인하기 위해, 시동을 걸고 정지합니다. 이 실행 결과는 다음과 같습니다.

 

결과

현대 엔진 기동

현대 엔진 정지

 

현대 엔진 클래스의 메서드가 실행되고 있다는 것을 알 수 있습니다.

여기서 다시 클래스 다이어그램으로 가보면..

 

슬슬 그림에 한계가 보인다..

자동차 클래스는 엔진 인터페이스를 의존하고 있습니다만, 현대 엔진에는 의존하고 있지 않습니다.

자동차 클래스의 점선은 엔진 인터페이스에만 향하고 있습니다. 

여기서 기아 엔진을 사용하고 싶을 경우에는 기아 엔진 클래스를 준비하여 메인 클래스를 조금 수정함에 있어 간단하게 끝납니다. 즉 자동차 클래스는 수정하지 않습니다.

 

엔진 클래스 (기아)

public class KiaEngine implements Engine {

  // 생성자

  public KiaEngine() {

    super();

  }

 

  // 기동 메서드

  @Override

  public void boot() {

    System.out.println("기아 엔진 기동");

 

  // 정지 메서드

  @Override

  public void stop() {

    System.out.println("기아 엔진 정지");

}

 

메인 클래스 (기본 생략)

  

  // 엔진 생산 (기아 엔진)

  Engine engine = new KiaEngine();

 

  // 자동차 생산

  Car car = new Car(engine);

 

  // 시동을 건다

  car.engineStart();

 

  // 시동을 잠근다

  car.engineStop();

 

메인 클래스에는 기아 엔진 클래스를 생성 (new) 하여 변경하고 있습니다.

그 결과는 아래와 같습니다.

 

결과

기아 엔진 기동

기아 엔진 정지

 

이와 같은 경우의 클래스 다이어그램을 다시 그리자면...

기아 엔진 추가

주목해야 할 부분은 기아 엔진을 추가해도 자동차 클래스의 의존은 오직 엔진 인터페이스만 의존하고 있습니다.

즉, 엔진 클래스를 추가하고 싶어도 자동차 클래스는 일절 변경을 하지않아도 된다는 것입니다. (오호!)

또한, 자동차 클래스의 테스트를 진행할려고 하면, 엔진 인터페이스를 상속한 더미 클래스를 준비하여 테스트를 진행할 수 있게 되었습니다.

 

이와 같이 인터페이스를 사용하면 의존'정도'를 낮출 수 있습니다. 

이와 같이 의존도가 낮은 상태를 loose coupling. 즉 결합이 느슨하다고 합니다.

 

자바의 인터페이스에 대한 얘기가 되었습니다만, 여기까지가 의존성의 얘기가 됩니다.

 

정리하자면...

1. 의존이라는 것은 타 클래스를 이용하고 있다는 것을 말한다.

2. 자바의 인터페이스를 사용함에 있어, 의존'정도'를 낮추는 것이 가능하다. (결합이 느슨하다.)

 

다음 포스트에서는 주입에 대해서 설명해보도록 하겠습니다.

시작에 앞서

3장을 완료해주세요.

 

데이터 베이스로부터 값 취득

다음으로는 데이터 베이스로부터 값을 취득해 보겠습니다.

이미지는 다음과 같습니다.

 

이미지

슬슬 그림에 한계가 오기 시작했다...

 

스프링에서는 그림과 같이 컨트롤러 클래스, 서비스 클래스, 리포지토리 클래스를 작성합니다. 

각 클래스의 역할은 다음과 같습니다.

 

리포지토리 클래스

데이터 베이스에 CRUD(Create Read Upload Delete) 조작을 하며, 그 결과를 반환합니다.

MVC 모델로 말하자면 Model에 해당합니다.

 

서비스 클래스

리포지토리 클래스와 같은 걸 사용하여 여러가지 서비스를 제공합니다.

MVC 모델로 말하자면 Model에 해당합니다.

 

컨트롤러 클래스

어떤 서비스를 사용할까를 지정하여, 서비스의 결과를 화면에 반환합니다.

MVC 모델로 말하자면 Controller에 해당합니다.

 

데이터 베이스

1장에서도 기술하였습니다만, 본 예제는 데이터 베이스를 따로 설치할 필요가 없이 사용할 수 있는 H2 데이터베이스를 이용합니다.

 

데이터 베이스의 설정과 같은 각종 설정은 기본적으로 application.properties 파일에 내용을 기재합니다.

H2 데이터 베이스를 이용하기 위해,  application.properties 파일에 다음과 같이 내용을 작성합니다.

 

application.properties

설정 내용은 H2 데이터 베이스에 접속할 때의 설정입니다.

8행과 9행에 있는 spring.datasource.schema=classpath:schema.sql spring.datasource.data=classpath:data.sql 는 데이터 베이스 접속할때의 설정이 아닌, SQL 실행에 관한 설정입니다.

 

스프링 부트를 재기동할 시에 classpath(src/main/resources/)에 있는 schema.sql, data.sql를 실행하는 의미입니다.

조금만 더 구체적으로 설명드리자면, schema.sql에서 테이블을 작성하고  data.sql에서 데이터를 등록하는 의미로 생각하시면 될 거 같습니다.

 

더욱이 H2 데이터 베이스는 메모리 상에 동작하는 In Memory 형식이기 때문에, 스프링 부트를 정지하면 테이블과 데이터가 없어집니다. 상기의 설정에는 스프링을 기동 할 때마다 schema.sql, data.sql 이 실행되기 때문에 스프링을 재기동하면 테이블/데이터의 초기화됩니다.

 

src/main/resources/schema.sql

src/main/resources/data.sql

이것으로 스프링 부트를 기동할때마다 테이블과 데이터가 작성됩니다.

다음으로는 리포지토리 클래스를 작성합니다. 

 

리포지토리

HelloRepository.java

포인트 1

컨트롤러 클래스에 @Controller 어노테이션을 붙인 것과 같이, 리포지토리 클래스에도 @Repository를 붙입니다.

어노테이션을 붙임으로 인해 DI에 등록되어집니다.

 

포인트 2

상기 프로젝트에는 스프링에서 가지고 있는 JDBC 접속용 클래스 JdbcTemplate를 사용하여, SELECT문을 실행합니다.

JdbcTemplate를 사용할 경우에는 @AutoWired 어노테이션을 붙입니다.

아직 이 시점에서는 @AutoWired를 붙임으로써 인스턴스를 생성해주는 걸로만 알고 계시면 될 거 같습니다.

(상세 설명은 추후에)

 

즉, 아래의 코드처럼 작성되었다고 생각하시면 됩니다.

private JdbcTemplate jdbcTemplate = new JdbcTemplate();

 

그러면 이제 검색 결과를 담는 employee 클래스를 작성합니다. 

 

Domain 클래스

Employee.java

 

포인트 1

@Data 어노테이션을 붙임으로써 getter/setter가 자동으로 완성됩니다. 

이건 이전에 설명한 Lombok 기능입니다.

 

부가설명 : domain 클래스

리포지토리 클래스나 서비스 클래스와 같은 사이에 전달하는 클래스를 스프링에서는 domain 클래스라 말합니다.

상기 Employee 클래스는 domain 클래스입니다. 

이외에도 model 클래스, DTO(Data Transfer Object)라고도 불립니다.

 

서비스 클래스

다음으로는 서비스 클래스를 작성합니다.

서비스 클래스에는 검색 결과를 Employee 클래스에 넣어 반환합니다.

 

HelloService.java

포인트 1

컨트롤러 클래스와 같이 서비스 클래스에도 @Service 어노테이션을 붙입니다.

또한, HelloRepository 클래스를 사용하기 위해 @Autowired 어노테이션을 붙입니다.

리포지토리 클래스에서 jdbcTemplate를 사용한 것처럼 현시점에서는 아래의 코드처럼 작성되었다고만 생각하시면 될 거 같습니다.

 

private HelloRepository helloRepository = new HelloRepository();

 

간단 설명

HelloRepository 클래스에는 jdbcTemplate의 queryForMap이라고 하는 메서드를 사용하여 검색을 실행하여, 그 검색 결과를 Map에 넣습니다.

Map의 Get 메서드에 테이블의 필드명을 지정하면, 값을 취득할 수 있습니다.

Map으로부터 취득한 값을 Employee 클래스의 인스턴스에 넣고 반환합니다.

 

다음은 hello.html부터 종업원 ID를 입력할 수 있도록 수정합니다.

 

HTML(기존)

hello.html

위 코드에서는 POST 송신처를 /hello/db라고 하는 Form 태그를 추가하였습니다.

 

다음은 컨트롤러 클래스에서 /hello/db 처리하는 메서드를 추가합니다.

 

컨트롤러 

HelloController.java

 

 

현재 문자열을 숫자로 변환하는 과정에서의 예외 발생시 처리는 제외했습니다.

앞에서와 같이 @RequestParam 어노테이션에 text2이라는 이름을 붙여, 파라미터를 받도록 합니다.

다음은 HelloService의 findOne 메서드에 상기의 값을 건네주면 그 검색 결과로 Employee 인스턴스가 반환됩니다.

 

Employee 클래스의 값을 model.addAttribute에 등록해두면 화면에서 받는 것이 가능합니다.

마지막으로 helloResponseDB.html을 작성합니다.

HTML(신규)

이것으로 DB로부터 값을 취득하는 코드를 작성하였습니다.

스프링 부트 애플리케이션을 기동하여 http://localhost:8080/hello에 접속해봅니다.

 

결과

마무리

스프링을 이용하여 간단하고 빠르게 웹 애플리케이션을 작성해 봤습니다.

다음 포스트부터는 조금 더 스프링을 심도 있게 다뤄보겠습니다.

'프로그래밍 > 스프링 부트' 카테고리의 다른 글

6. DI (2/4)  (0) 2020.06.16
5. DI (1/4)  (0) 2020.06.14
3. 웹 애플리케이션 작성 (파라미터 전달)  (0) 2020.06.14
2. 웹 애플리케이션 작성  (0) 2020.06.14
1. 시작  (1) 2020.06.14

시작에 앞서

2장을 완료해주세요.

 

파라미터 전달

다음으로는 화면에 값을 전달해보도록 하겠습니다.

코딩에 앞서 이미지는 다음과 같습니다.

 

이미지

POST 메서드가 추가되었네요!

 

먼저, 화면에 값을 전달하기 위하여, 이전에 작성한 hello.html을 수정합니다.

form 태그를 추가하여 값을 입력할 수 있도록 합니다.

포인트 1

th:value속성을 사용함으로써, 화면에서 컨트롤러 클래스에 값을 전달해주는 것이 가능합니다.

 

더불어, th: 가 붙은 속성은 타임 리프의 속성입니다. input 태그에는 value 속성이 이전부터 존재합니다.

그 속성에 th: 를 붙임으로써, 타임 리프의 속성임을 선언하게 됩니다.

 

간단 설명

form 태그의 경우, 통상 HTTP의 POST 메서드로 값을 전달합니다. (어느 메서드를 사용하는가는 form 태그의 method 속성에 지정합니다.)

 

HelloController.java

"helloResponse.html"에서  .html은 생략해도 됩니다.

포인트 1

@GetMapping과 같이 메서드에 @PostMapping 어노테이션을 붙입니다. 

이로 인해, POST 메서드로 송신됐을 경우 처리가 가능하게 됩니다.

 

포인트 2

메서드의 파라미터에 @RequestParam 어노테이션을 붙임으로 인해, 화면으로부터의 입력 내용을 받는 것이 가능합니다. 어노테이션의 파라미터는 html의 name속성의 값을 지정합니다.

 

즉, hello.html을 보자면..

<input type="text" name="text1" th:value="${text1_value}"/>

 

포인트 3

model.addAttribute에 키와 값을 설정합니다. 

이로 인해, 화면에서 지정한 키의 값을 받는 것이 가능합니다.

 

helloResponse.html

 

 

포인트 1

th:text 속성에 model.addAttribute로 등록한 키를 지정함에 있어, 컨트롤러 클래스로부터 값을 받는 것이 가능합니다.

아참. 키를 지정할 경우에는 ${<키 명>}을 입력합니다.

 

결과

이것으로 화면으로부터 값을 전달하는 웹 애플리케이션을 작성하였습니다.

해당 프로젝트를 스프링 부트 어플리케이션으로 기동하여 http://localhost:8080/hello로 접속해봅니다.

 

다음장은 데이터 베이스로부터 값을 취득해보겠습니다.

'프로그래밍 > 스프링 부트' 카테고리의 다른 글

6. DI (2/4)  (0) 2020.06.16
5. DI (1/4)  (0) 2020.06.14
4. 웹 애플리케이션 작성 (데이터 베이스로부터 값 취득)  (0) 2020.06.14
2. 웹 애플리케이션 작성  (0) 2020.06.14
1. 시작  (1) 2020.06.14

시작에 앞서

웹 애플리케이션 작성에 사용할 IDE는 STS이며 기초적인 설치 방법은 제외합니다.

getter/setter코드를 자동적으로 코딩해주도록 하기 위하여 Lombok을 설치해주세요.

 

프로젝트 생성

신규 스프링 부트 프로젝트
프로젝트 생성과 동시에 사용할 라이브러리

 

1. 스프링 부트 DevTools

스프링 부트 DevTools는 자동으로 웹 애플리케이션을 재기동해주는 개발 보충 툴입니다.

예를 들어, 웹 애플리케이션을 작성하고 실행하는 와중에 버그를 발견했을 시 보통 파일을 수정하여 보존 그리고 재기동을 해야 합니다만, 이 툴이 있으면 파일을 수정하여 저장하면 애플리케이션을 자동으로 재기동해줍니다.

 

2. Lombok

Lombok은 위에 시작에 앞서 부분에 간단히 설명한거와 같이 getter/setter 코드를 자동적으로 만들어줍니다.

 

3. 스프링 데이터 JDBC

※ 1장에서 설명

 

4. H2

H2데이터 베이스를 이용함으로써, 데이터 베이스 설치가 따로 필요 없어집니다. 

학습이나 테스트 환경의 데이터 베이스로써 사용되는 경우가 많습니다.

(여담으로 제가 실제 업무에서 사용한 것은 Postgre SQL입니다.)

 

5. Thymeleaf

타임리프는 스프링 부트 표준에서 사용되는 HTML 템플릿 엔진입니다. 

스프링이 보급하기전에는 JSP가 사용되어 왔습니다만, JSP는 HTML안에 자바 코드를 작성하기 때문에 HTML 가독성이 좋지 않습니다.

 

타임리프는 이 점을 개선해주기 때문에 HTML 가독성이 좋아집니다.

 

6. Spring Web

※ 1장에서 설명(MVC, 스프링 부트)

 

MVC 모델에 대하여

이제부터 웹 애플리케이션을 개발하기 위해 필수적인 지식이 있습니다.

바로 MVC 모델입니다.

 

MVC 모델을 간단히 설명하자면 다음과 같습니다.

 

Model

로직 처리를 담당합니다.

예를 들어, 데이터 베이스로부터 데이터 취득이나 여러 가지 계산을 처리

자바 클래스가 이 역할을 담당합니다.

 

View

화면 표시 역활을 담당합니다.

HTML이 이 역활을 담당합니다.

 

Controller

유저로부터의 요청(request)에 대해  Model 클래스에 처리를 의뢰합니다.

그리고 처리 결과에 대해 어느 View를 유저에 표시할 건지 정합니다.

 

자기 자신은 처리를 하지 않고, 처리 구간을 지휘하는 플레이메이커? 와 같은 역할을 담당합니다.

자바 클래스가 이 역활을 담당합니다.

 

이미지

웹 애플리케이션 처리 방식(마우스로만 그리기 어렵네요..)

1. 요청(request)

유저로부터 무언가의 요청을 서버에 송신합니다. 

예를 들어 유저 ID와 비밀번호를 입력하여 로그인 버튼을 누릅니다. 그러면 그 정보를 서버에 송신합니다. 

여기서 송신하는 곳. 즉 송신처가 Controller입니다.

 

2. 의뢰

요청을 받아 Model에게 처리를 의뢰합니다.

단, Model에게 의뢰하지 않는 경우도 있습니다.

 

3. 결과

Model은 처리 결과를 Controller에게 반환합니다. 결과를 받은 Controller는 어느 화면(View)을 유저에게 반환할 건지 정합니다.

 

4. 화면

유저가 보는 화면입니다. Model 처리결과를 표시하는 것도 가능합니다.

 

이와 같이 역할을 분단하게 하는 것이 MVC 모델입니다. 

그러면 왜 MVC 모델을 웹 애플리케이션에 작성하는 건가? 그건 바로 장점이 있기 때문입니다.

역할 분담을 해두면, 개별로 개발을 진행하는 것이 가능합니다. 게다가 재사용이나 확장도 쉽게 가능합니다.

역할을 분담하는 것은 자바와 웹 애플리케이션 개발만의 한해서의 얘기가 아닙니다.

 

Hello World 작성

그러면 이제부터 Hello World를 표시하는 웹 애플리케이션을 작성해보겠습니다.

 

이미지

Hello World 처리방식

HelloController 클래스에 HTTP의 GET 요청을 송신합니다.

HelloController 클래스로부터 hello.html을 반환합니다.

 

HTTP의 GET메서드란

HTML와 같은 것을 취득하기 위한 메서드입니다.

구글 크롬과 같은 브라우저에 URL을 입력하여 엔터키를 누르면, HTML 페이지가 표시됩니다.

브라우저에서 페이지를 취득할 때에는 GET메서드를 요청합니다.

 

패키지 구성

작성하는 건 아래와 같습니다.

com.example.demo.trySpring

HelloController.java

hello.html

 

SpringSampleApplication.java란

프로젝트 작성 시에 자동으로 작성되는 파일입니다.

보통 <프로젝트명> Application.java라는 이름으로 작성됩니다. 

이것이 스프링을 실행하는 프로그램입니다. 내용을 보면 main메서드가 있는 걸 확인하실 수 있습니다.

 

HelloController.java

포인트 1

스프링에서는 컨트롤러 클래스에 @Controller 어노테이션을 붙입니다.

@Controller 어노테이션을 붙임으로써, DI(의존성 주입)에 이용할 수 있게 됩니다.

 

포인트 2

@GetMapping 어노테이션을 메서드에 붙임으로써, HTTP 요청의 GET메서드를 처리할 수 있게 됩니다.

 

간단 설명

실제 위 작성 프로젝트는 localhost:8080/hello의 GET 요청에 대해 처리를 getHello메서드에서 이뤄지는 의미입니다.

 

또한, GET 요청의 경우, 메서드 이름을 맨 앞에 get을 붙이는 게 하나의 관습으로 되고 있습니다. 

(한국은 어떨지 모르겠네요..)

 

그리고 메서드의 반환 값에는 확장자 없이 html 파일명을 지정합니다.

 

hello.html

포인트 1

타임 리프를 사용하기 위하여, html 태그 안에 xmlns:th="http://www.thymeleaf.org"를 붙입니다.

실제 위 작성 프로젝트에서는 타임 리프 기능을 사용하지 않습니다.

 

실행

스프링 부트 애플리케이션 실행(일반 혹은 디버그 모드 상관없음)

http://localhost:8080/hello 접속

결과

hello.html이 표시됩니다.

'프로그래밍 > 스프링 부트' 카테고리의 다른 글

6. DI (2/4)  (0) 2020.06.16
5. DI (1/4)  (0) 2020.06.14
4. 웹 애플리케이션 작성 (데이터 베이스로부터 값 취득)  (0) 2020.06.14
3. 웹 애플리케이션 작성 (파라미터 전달)  (0) 2020.06.14
1. 시작  (1) 2020.06.14

개요

2019년 8월 말부터 시작한 신규 프로젝트에서 웹 프로그래밍 쪽에 프레임 워크를 스프링 부트로 선택한 후 2019년 12월에 릴리스를 완료했습니다.

 

지금까지 프레임 워크 스트럿츠 2 위주로 개발을 했던 저로써는 여러 가지 당황하면서 안절부절못했던 점이 있었습니다만, 여하튼 현재로써는 잘 마무리되어 한편으로는 뿌듯함이 남습니다.

 

이번 기회에 스프링 부트를 잘 정리하여 기초적인 마무리를 짓기 위해 이 글을 작성하였습니다.

 

애플리케이션 구성

이번에 개발해 볼 웹 애플리케이션의 구성은 다음과 같습니다.

 

스프링 : 5

스프링 부트 : 2.0.4

DB : H2

* 스프링 시큐리티, JDBC, Mybatis와 같은 건 필요시에 즉시 추가하는 구성으로 진행합니다.

 

스프링이란?

스프링은 자바 개발에 있어서의 하나의 프레임 워크입니다.

프레임 워크는 개발을 효율적으로 하기 위한 하나의 툴, 즉 도구입니다. 

프레임 워크를 이용하면 개발에 있어서 너무나도 편리해지기 때문에, 현장에서 프레임 워크를 이용하는 것이 대부분입니다.

 

한 가지 예를 들어보겠습니다.

데이터 베이스에 액세스 하기 위해 매번 새로운 클래스를 작성하는 건 상당히 고된 일입니다. 

이러한 자주 사용하는 기능들을 사용하기 쉽게 정리해둔 것이 프레임 워크입니다.

 

스프링은 이 외에도 여러 가지 특징을 가지고 있습니다.

 

스프링 특징

1. 변경(갱신)에 강하다.

DI(의존성 주입)을 사용함으로써, 각 클래스의 독립성을 높여줍니다. 

이러함에 있어서 클래스에 코드 내용 수정량도 극히 적어지게 됩니다.

(독립성이 높다는 말을 간단히 풀어보면, 다른 클래스와의 연결고리가 약해 해당 클래스만 수정하면 된다는 것입니다.)

 

2. 코드 가독성이 높다.

AOP(에스팩트 지향 프로그래밍)을 이용하여, 공통 처리를 모으는(정리하는)것이 가능합니다.

예를 들어, 로그 출력이나 시큐리티 설정과 같은 것을 하나의 클래스로 정리하는 것이 가능합니다.

이로 인해 본질적인 처리만 작성한 코드가 되어, 가독성을 높입니다.

 

3. 재이용성이 높다.

AOP(에스팩트 지향 프로그래밍)를 이용하면, 공통 처리와 본질적인 처리를 분리하는 것이 가능합니다.

이로 인해 공통 처리만 재이용하는 것이 가능할 뿐만 아니라 본질적인 처리만 재이용하는 것도 간단하게 가능합니다.

 

스프링의 이미지

[ 스프링 부트 ]

[ 시큐리티(스프링 부트), 데이터 액세스(스프링 데이터 JDBC), Web(스프링 MVC), 배치(스프링 배치) ]

[ 스프링 코어(스프링 DI, 스프링 AOP) ]

 

1. DI

DI는 Dependency Injection(의존성 주입)의 약어로, 간단히 말해 인스턴스 관리를 해주는 것이라고 생각하시면 됩니다. 여기가 스프링의 핵심 코어가 되는 부분입니다.

(상세 설명은 추후)

 

2. AOP

AOP는 Aspect Oriented Programming의 약어로, 에스팩트 지향 프로그래밍이라고 불립니다. 

공통 처리를 정리해두는 것이 가능하기 때문에, 본질적인 프로그래밍만 전념할 수 있습니다.

(상세 설명은 추후)

 

3. 스프링 MVC

웹 개발에 설계 방법에 있어 MVC 모델이라는 것이 있습니다.

MVC 모델을 이용하여, 웹 애플리케이션 개발이 가능하도록 하는 것이 스프링 MVC입니다.

 

4. 스프링 부트

스프링 MVC를 이용하여 웹 애플리케이션을 개발하려고 하면, 사전에 많은 설정이 필요합니다.

그 설정이 생각보다 복잡하고, 상당 부분 지식도 필요하기에 개발 시작단계부터 개발 속도가 상당히 떨어지게 됩니다.

 

이러한 문제점을 해소한 것이 바로 스프링 부트입니다.

 

스프링 부트를 이용하면, 처음부터 설정이 잘 되어있기 때문에 바로 개발을 진행할 수 있습니다.

또한, 설정을 변경하여도, 하나의 설정 파일을 수정하는 것만으로도 완료됩니다.

 

스프링으로 웹 개발을 할 때는, 스프링 부트를 이용하는 것이 일반적이라고 생각하시면 될 거 같습니다.

 

5. 스프링 시큐리티

웹 애플리케이션에 있어서 시큐리티 설정을 간단히 코딩하는 것이 가능합니다.

그 안에 내용은 인증과 허가입니다.

인증이란 로그인 기능, 허가는 유저 권한에 있어 기능 제한이라고 생각하시면 될 거 같습니다.

(상세 설명은 추후)

 

6. 스프링 데이터 JDBC

스프링 데이터란 데이터 베이스 제품별로 다른 조작을 간단히 사용할 수 있도록 하기 위한 프로젝트입니다.

이 스프링 데이터 프로젝트 안에 JDBC를 스프링에 합쳐서 이용할 수 있게 한 라이브러리가 바로 스프링 데이터 JDBC입니다.

 

JDBC란 자바를 사용하여 데이터 베이스에 액세스 하기 위한 표준 API입니다.

JDBC를 사용하면, Select문 하나만을 실행하기 위해도 상당수의 코드가 필요합니다. 

여기서 스프링 데이터 JDBC를 이용하면 SQL문 실행이 간단하게 가능합니다.

 

스프링 데이터에는 JDBC 외에도 JPA, MongoDB 등의 라이브러리도 있습니다.

 

마무리

다음 글부터는 웹 애플리케이션을 직접적으로 작성해보겠습니다.