1.  스프링 Web MVC의 특징

출처 : https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/spring-mvc-intro.html
출처 : https://tvd12.com/posts/spring-mvc-request-lifecycle

 

스프링 Web MVC는 기본적으로 서블릿 API를 좀 더 추상화된 형태로 작성된 라이브러리지만, 서블릿 / JSP를 사용할 때 필요한 많은 기능들을 기본적으로 제공해서 개발의 생산성과 안정성을 높여줌

 

  📍 스프링 Web MVC는 이름에서 알 수 있듯이 Web MVC 패턴으로 구현된 구조
  📍 스프링 Web MVC가 기존 구조에 약간의 변화를 주는 부분

Front-Contoller 패턴을 이용해 모든 흐름의 사전 / 사후 처리를 가능하도록 설계된 점
어노테이션을 적극적으로 활용해서 최소한의 코드로 많은 처리가 가능하도록 설계된 점
HttpServletRequest / HttpServletResponse를 이용하지 않아도 될 만큼 추상화된 방식으로 개발 가능

 

구성요소 설명
DispatcherServlet 클라이언트 요청을 전달받아 요청에 맞는 컨트롤러가 리턴한 결과값을 View에 전달하여 알맞은 응답을 생성
HandlerMapping 클라이언트의 요청 URL을 어떤 Controller가 처리할 지 결정
Controller 클라이언트의 요청을 처리한 뒤, 결과를 DispatcherServlet에게 리턴
ModelAndView Controller가 처리한 결과 정보 및 View 선택에 필요한 정보를 담음
ViewResolver Controller의 처리 결과를 생성할 View를 결정
View Controller의 처리 결과 화면을 생성

 

출처 : https://velog.io/@codemcd/


1)  DispatcherServlet 과 FrontController

스프링 MVC에서 가장 주요한 사실은 모든 요청 Request이 반드시 DispatcherServlet이라는 존재를 통해서 실행
  ✓ 
객체지향에서 이렇게 모든 흐름을 하나의 객체를 통해서 진행되는 패턴을 퍼사드 facade 패턴이라 하는데  구조에서는 Front-Controller 패턴 이라도 부름
  ✓ 
Front-Controller 패턴을 이용하면 모든 요청이 반드시 하나의 객체를 지나서 처리되기 때문에 모든 공통적인 처리를 프론트 컨트롤러에서 처리할 수 있게 됨

 

 

 

👾  스프링 MVC에서는 DispatcherServlet이라는 객체가 프론트 컨트롤러의 역할을 수행
👾  프론트 컨트롤러가 사전 / 사후에 대한 처리를 하게 되면 중간에 매번 다른 처리를 하는 부분만 별도로 처리하는 구조를 만들게 됨 

     ➡️  스프링 MVC 에서는 이를 컨트롤러라고 하는데 @Controller를 이용해서 처리

 

 

 


2)  스프링 MVC 사용하기

👻  spring-webmvc 라이브러리는 미리 추가되었으므로 스프링 MVC관련 설정을 추가 

 

㉮  스프링 MVC관련 설정을 추가하기 위해 WEB-INF 폴더에 servlet-context.xml 파일을 생성
  ✓  root-context.xml을 이용할 수도 있지만, 일반적으로는 구조를 분리하듯이 웹을 다루는 영역이라 별도의 설정 파일을 이용하는 것이 일반적이다.

 

㉯  프로젝트 내부의 webapp 폴더 아래에 'resources'라는 폴더를 하나 더 생성해 둠

  ✓  나중에 정적 파일들(html, css, js, 이미지등)을 서비스하기 위한 경로

 

 servlet-context.xml  설정 코드
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
        
    <mvc:annotation-driven/>
    <mvc:resources mapping="/resources/**" location="/resources/"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

 

  ⚡️  <mvc:annotation-driven> 설정 : 스프링 MVC 설정을 어노테이션 기반으로 처리한다는 의미와 스프링 MVC의 여러 객체들을 자동으로 스프링의 빈으로 등록하게 하는 기능을 의미
  ⚡️  <mvc:resources> 설정 : 이미지나 html 파일과 같이 정적인 파일의 경로를 지정
        ✓  /resources 경로로 들어오는 요청은 정적 파일을 요구하는 것이라고 생각하고 스프링 MVC에서 처리하지 않는다는 의미
        ✓  location 속성 값은 webapp 폴더에서 만들어둔 폴더를 의미
        ✓  InternalResourceViewResolver라는 이름의 클래스로 빈이 설정되어 있는데,
             InternalResourceViewResolver는 스프링 MVC에서 제공하는 뷰(view)를 어떻게 결정하는지에 대한 설정을 담당
        ✓  prefix와 suffix는 뷰를 이용할때 파일명만 주어지면 조합해서 '/WEB-INF/view/파일명.jsp' 같은 형식을 만들 수 있음

 

 

web.xml의 DispatcherServlet 설정

 

스프링 MVC를 실행하려면 front controller 역할을 하는 DispatcherServlet을 설정해야하는데,  web.xml을 이용해서 처리

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/root-context.xml</param-value>
    </context-param>
    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

 

  ⚡️  <servlet> 설정과 <servlet-mapping> 설정이 추가
        ✓  <servlet> 설정은 DispatcherServlet을 등록하는데 DispatcherServlet이 로딩할 때 servlet-context.xml을 이용하도록 설정
        ✓  load-on-startup 설정의 경우 톰캣 로딩 시에 클래스를 미리 로딩해 두기 위한 설정
        ✓  <servlet-mapping> 설정모든 경로의 요청에 대한 처리를 담당하기 때문에 '/'로 지정

 


3)  스프링 MVC 컨트롤러 

스프링 MVC 컨트롤러는 전통적인 자바의 클래스 구현 방식과 다름

스프링 MVC의 컨트롤러 특징

 

  ✓  상속이나 인터페이스를 구현하는 방식을 사용하지 않고 어노테이션만으로 처리가 가능
  ✓  오버라이드 없이 필요한 메소드들을 정의
  ✓  메소드의 파라미터를 기본 자료형이나 객체 자료형을 마음대로 지정
  ✓  메소드의 리턴타입도 void, String, 객체 등 다양한 타입을 사용할 수 있음

 

㉮  controller 패키지를 추가하고 SampleController 클래스를 추가

@Log4j2
@Controller
public class SampleController {

    @GetMapping("/hello")
    public void hello() {
        log.info("hello(_)...");
    }
}

 

  ⚡️  몇 가지 특이한 어노테이션들이 사용
      ✓  @Controller는 해당 클래스가 스프링 MVC에서 컨트롤러 역할을 한다는 것을 의미하고 스프링의 빈으로 처리되기 위해서 사용
      ✓  @GetMapping은 GET 방식으로 들어오는 요청을 위해 사용

 


 

A) servlet-context.xml의 component-scan


controller 패키지에 존재하는 컨트롤러 클래스들을 스프링으로 인식하기 위해서는 해당 패키지를 스캔해서 @Controller 어노테이션이 추가된 클래스들의 객체들을 스프링이 빈으로 설정되게 만들어야 함

servlet-context.xml에 component-scan을 적용
<context:component-scan base-package="com.example.spring.controller"/>

 

WEB-INF에 view 폴더 생성후 hello.jsp 작성
<html>
<head>
    <title>Title</title>
</head>
<body>
  <h1>Hello JSP</h1>
</body>
</html>


 

B) @RequestMapping과 파생 어노테이션들


스프링 컨트롤러에서 가장 많이 사용하는 어노테이션은 @RequestMapping
  ⚡️  @RequestMapping은 말 그대로 '특정한 경로의 요청 Request을 지정'하기 위해서 사용
  ⚡️  컨트롤러 클래스의 선언부에도 사용할 수 있고, 컨트롤러의 메서드에도 사용할 수 있음

 

controller 패키지에 TodoController 클래스 추가
@Log4j2
@Controller
@RequestMapping("/todo")
public class TodoController {
    @RequestMapping("/list")
    public void list() {
        log.info("todo list...");
    }

    @RequestMapping(value = "/register", method = RequestMethod.GET)
    public void register() {
        log.info("todo register...");
    }
}

 

  ⚡️  TodoController의 @RequestMapping의 value 값은 '/todo'이고 list()는 '/list'이므로 최종경로는 '/todo/list' 
  ⚡️  JSP 파일이 없어서 에러가 나지만 로그 출력은 확인

 

  ⚡️  @RequestMapping을 이용하는 것만으로도 여러 개의 컨트롤러를 하나의 클래스로 묶을 수 있고, 각 기능을 메소드 단위로 설계할 수 있게 되므로 실제 개발에서 많은 양의 코드를 줄일 수 있음

  ⚡️  @RequestMapping에는 method 속성을 이용해서 GET / POST 방식을 구분해서 처리했지만, 스프링 4버전 이후에는 @GetMapping, @PostMapping 어노테이션이 추가되면서 GET / POST 방식을 구분해서 처리할 수 있게 됨
      ➡️  예를 들어 Todo 등록의 경우 GET 방식으로 '/todo/register'를 이용하면 입력 가능한 화면을 보여주고, POST방식은 처리를 해야 함

@Log4j2
@Controller
@RequestMapping("/todo")
public class TodoController {
    @RequestMapping("/list")
    public void list() {
        log.info("todo list...");
    }

//    @RequestMapping(value = "/register", method = RequestMethod.GET)
    @GetMapping("/register")
    public void register() {
        log.info("todo register...");
    }

    @PostMapping("/register")
    public void registerPOST(TodoDTO todoDTO) {
        log.info("POST todo register...");
        log.info(todoDTO);
    }
}

 


C)  파라미터 자동 수집과 변환


👩🏻‍💻  스프링 MVC가 인기를 끌게 된 여러 이유 중에는 개발 시간을 단축할 수 있는 편리한 기능들이 많기 때문
👩🏻‍💻  개발자들에게 가장 필요한 파라미터 자동 수집 기능

 

  ⚡️  파라미터 자동 수집 기능은 간단히 말해서 DTO나 VO등을 메서드의 파라미터로 설정하면 자동으로 전달되는

         HttpServletRequest의 파라미터들을 수집해 주는 기능
  ⚡️  단순히 문자열만이 아니라 숫자도 가능하고, 배열이나 리스트, 첨부 파일도 가능

 

파라미터 수집은 다음과 같은 기준으로 동작


  ✓  기본 자료형의 경우는 자동으로 형 변환처리가 가능
  ✓  객체 자료형의 경우는 setXXX()의 동작을 통해서 처리
  ✓  객체 자료형의 경우 생성자가 없거나 파라미터가 없는 생성자가 필요

 

단순 파라미터의 자동 수집

 

  📍 SampleController에 ex1() 추가

    @GetMapping("/ex1")
    public void ex1(String name, int age) {
        log.info("ex1() ...");
        log.info("name: " + name);
        log.info("age: " + age);
    }

 

  ✓  문자열 name과 int 타입의 age 선언
  ✓  브라우저를 이용해서 name과 age를 지정하면 'ex1?name=aaa&age=16'와 같이 호출 되었을 때 자동으로 처리되는 것 확인

 

 

  

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  MyBatis 

스프링 프레임워크의 중요한 특징 중 하나는 다른 프레임워크들을 쉽게 결합해서 사용할 수 있다는 점이다. 이것은 스프링 프레임워크가 웹이나 데이터베이스와 같이 특정한 영역을 구애받지 않고 시스템의 객체지향 구조를 만드는데 이용된다는 성격 때문.

 

데이터베이스와 관련해서 스프링 프레임워크는 자체적으로 'spring-jdbc'와 같은 라이브러리를 이용해서구현할 수도 있고, MyBatisJPA 프레임워크를 이용하는 방식도 존재


💫  MyBatis는 'Sql Mapping Framework'라고 표현

  • 'Sql Mapping'이라는 단어가 의미하는 바는 SQL의 실행 결과를 객체지향으로 '매핑'해 준다는 뜻
  •  MyBatis를 이용하면 기존의 SQL을 그대로 사용할 수 있고 다음과 같은 점들이 편리해짐
  PreparedStatement / ResultSet의 처리 : 기존에 프로그램을 작성해서 하나씩 처리해야 하는 파라미터나 ResultSet의 getXXX()를 MyBatis가 알아서 처리해 주기 때문에 기존에 비해서 많은 양의 코드를 줄일 수 있음
  Connection / PreparedStatement / ResultSet의 close() 처리 : MyBatis와 스프링을 연동해서 사용하는 방식을 이용하면 자동으로 close() 처리할 수 있음
  SQL의 분리 : MyBatis를 이용하면 별도의 파일이나 어노테이션 등을 이용해서 SQL을 선언함. 파일을 이용하는 경우에는 SQL을 별도의 파일로 분리해서 운영이 가능  ➡️ 자바 코드에 쿼리문을 넣지 않아도 됨

 

1)  MyBatis와 스프링의 연동 방식


👻  MyBatis는 단독으로도 실행이 가능한 완전히 독립적인 프레임워크이지만, 스프링 프레임워크는 MyBatis와 연동을 쉽게 처리할 수 있는 라이브러리와 API들을 제공
👻  스프링에서 제공하는 라이브러리를 이용하는지 여부에 따라서 다음과 같은 방식 중에 하나로 개발이 가능

  • MyBatis를 단독으로 개발하고 스프링에서 DAO를 작성해서 처리하는 방식
    ➡️  기존의 DAO에서 SQL의 처리를 MyBatis를 이용하는 구조로써 완전히 MyBatis와 스프링 프레임워크를 독립적인 존재로 바라보고 개발하는 방식
  • MyBatis와 스프링을 연동하고 Mapper 인터페이스만 이용하는 방식
    ➡️  스프링과 MyBatis 사이에 'mybatis-spring'이라는 라이브러리를 이용해서 스프링이 데이터베이스 전체에 대한 처리를 하고
    MyBatis는 일부 기능 개발에 활용하는 방식. 개발 시에는 Mapper 인터페이스라는 방식을 이용해서 인터페이스만으로 모든 개발이 가능

 

2) MyBatis를 위한 라이브러리들


👻  MyBatis를 이용하려면 다음과 같은 라이브러리들이 필요

스프링 관련 : spring-jdbc, spring-tx
MyBatis 관련 : mybatis, mybatis-spring
  ➡️  다른 스프링 관련 라이브러리들과 버전이 같도록 통일해서 추가


    📌  MyBatis 관련 라이브러리는 검색을 이용해서 추가. MyBatis 버젼과 mybatis-spring 라이브러리의 버전은 일치하지 않으므로 주의해서 사용

 



3) MyBatis를 위한 스프링 설정 - SqlSessionFactory


👻  MyBatis를 이용하기 위해서는 스프링에 설정해둔 HikariDataSource를 이용해서 SqlSessionFactory라는 빈을 설정
👻  root-context.xml에 'mybatis-spring' 라이브러리에 있는 클래스를 이용해서 <bean>을 등록

 

 


2. Mapper 인터페이스 활용하기

MyBatis는 SQL 파일을 별도로 처리할 수 있지만 인터페이스와 어노테이션으로도 처리가 가능

 

📌  프로젝트에 mapper라는 이름의 패키지를 구성하고 현재 시간을 처리하는 TimeMapper 인터페이스를 선언

 


🎃  TimeMapper는 데이터베이스의 현재 시각을 문자열로 처리하도록 구성

🎃  MyBatis에는 @Select 어노테이션을 이용해서 쿼리를 작성할 수 있는데, JDBC와 마찬가지로 ";"을 이용하지 않으므로 주의

 

 

 

🎃  작성된 인터페이스를 매퍼(Mapper) 인터페이스라고 하는데 마지막으로 어떠한 매퍼 인터페이스를 설정했는지 root-context.xml에 등록해 주어야 함
🎃 root-context.xml에는 <mybatis:scan> 태그를 이용해서 매퍼 인터페이스의 설정을 추가
🎃  root-context.xml 파일 상단의 xmlns, xsi 설정에 mybatis-spring 관련 설정이 추가 되어야 함.




 

1)  테스트 코드를 통한 확인


👾  test 폴더에 mapper 패키지와 TimeMapperTests 테스트 클래스를 작성해서 확인

import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import static org.junit.jupiter.api.Assertions.*;

@Log4j2
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")
class TimeMapperTest {
    @Autowired(required = false)
    private TimeMapper timeMapper;

    @Test
    public void testGetTimer() {
        log.info(timeMapper.getTime());
    }

}


  📍  테스트 코드에서는 @Autowired 내에 required 속성을 지정
      ✓  @Autowired(required = false)로 지정하면 해당 객체를 주입 받지 못하도라도 예외가 발생하지 않는데 인텔리제이의 경우@Service, @Repository 와 같이 직접 스프링의 빈으로 등록된 경우가 아니라면 경고가 발생하므로 이를 방지하기 위해서 사용

 


3.  XML로 SQL 분리하기

👩🏻‍💻  MyBatis를 이용할 때 SQL은 @Select와 같은 어노테이션을 이용해서 사용하기도 하지만 대부분은 SQL을 별도의 파일로 분리하는 것을 권장
👩🏻‍💻  XML을 이용하는 이유는 SQL이 길어지면 이를 어노테이션으로 처리하기가 복잡해지고, 어노테이션이 나중에 변경되면 프로젝트 전체를 다시 빌드하는 작업이 필요하기 때문에 단순 파일로 사용하는 것이 편리

 

XML과 매퍼 인터페이스를 같이 결합할 때는 다음과 같은 과정으로 작성
  1. 매퍼 인터페이스를 정의하고 메소드를 선언
  2. 해당 XML 파일을 작성 (파일 이름과 매퍼 인터페이스 이름을 같게)하고 <select>와 같은 태그를 이용해서 SQL을 작성
  3. <select>, <insert> 등의 태그에 id 속성 값을 매퍼 인터페이스의 메소드 이름과 같게 작성



🎃  TimeMapper2 매퍼 인터페이스를 정의
🎃  어노테이션이 없는 getNow() 메서드만을 작성

 

 

 

🎃  main/resources 폴더에 mapper 폴더를 추가

 

🎃  TimeMapper2.xml을 다음과 같이 작성 (매퍼 인터페이스와 같은 이름으로 대소문자 주의)
  ➡️ 스프링 버전이 업그레이드 되면서 인터페이스와 xml의 이름을 똑같이 하지 않아도 됨

  ➡️ 하지만 관리상의 목적으로 매퍼 인터페이스와 xml의 이름을 같게 하기도 함
🎃  <mapper> 태그의 namespace 속성에서 매퍼 인터페이스를 맞게 지정하면 됨
  ➡️ <mapper> 태그의 namespace 속성을 반드시 매퍼 인터페이스의 이름과 동일하게 지정

 

  🎃  <select> 태그는 반드시 resultType이나 resultMap이라는 속성을 지정
         ➡️  resultType은 말 그대로 select 문의 결과를 어떤 타입으로 처리할지에 대한 설정으로 java.lang.String과 같이 전체 이름을 써야 하지만 자주 사용하는 타입은 string과 같이 사용할 수 있음

 


🎃  마지막으로 root-context.xml에 있는 MyBatis 설정에 XML 파일들을 인식하도록 설정을 추가

 

  🎃  추가된 mapperLocations는 말 그대로 'XML 매퍼 파일들의 위치'를 의미
  🎃  resources의 경우 'classpath:' 접두어를 이용해서 인식되는 경로이고 mapper 폴더 밑에 폴더가 있어도 관계없도록 '**'와 모든 '.xml'을 의미라는 '*.xml'을 지정

 

 

📌  XML 설정이 정상적인지 테스트를 통해서 확인. 기존에 만들어진 TimeMapperTests를 이용.



 

 

 

[ 내용 참고 : IT 학원 강의 ]

 

 


1.  스프링의 시작 

스프링 프레임워크는 웹이라는 제한적 용도로만 쓰이는 것이 아니라 객체지향의 의존성 주입 dependency injection 기법을 적용할 수 있는 객체지향 프레임워크

 

  •   스프링 프레임워크는 로드 존슨이 2002년도에 집필했던 'J2EE 설계 및 개발 wrox'이라는 책의 예제 코드에서 시작되었는데 말 그대로 효과적이고 가볍게 J2EE를 이용할 수 있다는 것을 증명하면서 예제의 코드를 발전시킨 것
  • 2000년 당시 자바 진영에서 JavaEE의 여러가지 스펙을 정의하고 비대해지는 동안 스프링 프레임워크는 반대로 '경량 light weight 프레임워크'를 목표로 만들어짐
  • 스프링이 등장할 때 여러 종류의 프레임워크들이 비슷한 사상(경량화, 단순화)으로 등장했지만 다른 프레임워크들과 달리 스프링 프레임워크는 개발과 설계 전반에 관련된 문제들을 같이 다루었기 때문에 결론적으로 가장 성공한 프레임워크로 기록
  • 스프링 프레임워크는 가장 중요한 '코어core' 역할을 하는 라이브러리와  여러 개의 추가적인 라이브러리를 결합하는 형태로 프로젝트를 구성. 가장 대표적으로 웹 MVC구현을 쉽게 할 수 있는 'Spring Web MVC'나 JDBC 처리를 쉽게 할 수 있는 'MyBatis'를 연동하는 'mybatis-spring'과 같은 라이브러리가 그러한 예. 

2.  의존성 주입 

스프링이 객체지향 구조를 설계할 때 받아들인 개념은 '의존성 주입 dependency injection'이라는 사상.

    ✓ 의존성 주입은 어떻게 하면 '객체와 객체 간의 관계를 더 유연하게 유지할 것인가?'에 대한 고민에서 나옴

    ✓ 즉 의존성이란 하나의 객체가 자신이 해야 하는 일을 하기 위해서 다른 객체의 도움이 필수적인 관계를 의미

    ✓ 스프링 프레임워크는 바로 이런 점을 프레임워크 자체에서 지원. 다양한 방식으로 필요한 객체를 찾아서 사용할 수 있도록 XML 설정이나 자바 설정 등을 이용

 


3.  ApplicationContext 와 빈 Bean

👻  서블릿이 존재하는 공간을 서블릿 콘텍스트 ServletContext라고 했던 것 처럼, 스프링에서는 빈 Bean 이라고 부르는 객체들을 관리하기 위해서 ApplicationContext 라는 존재를 활용

코드의 경우 ApplicationContext는 root-context.xml을 이용해서 스프링이 실행

ApplicationContext 객체가 생성

root-context.xml을 읽으면 SampleService와 SampleDAO가 해당 클래스의 객체를 생성해서 관리

테스트를 실행하면 @Autowired가 처리된 부분에 맞는 타입의 빈 Bean 이 존재하는지를 확인

이를 테스트 코드 실행 시에 주입

멤버변수에 직접 @Autowired를 선언하는 방식을 '필드 주입 Field Injection' 방식

 


4.  SampleDAO 주입하기 

 

@Autowired를 이용하면 필요한 타입을 주입받을 수 있다는 사실을 이용해서 SampleService를 다음과 같이 변경

 

 

 

📌  Lombok의 @ToString을 적용한 부분과 SampleDAO의 변수로 선언하고 @Autowired를 적용

📌  테스트 코드를 실행하면 SampleService 객체 안에 SampleDAO 객체가 주입된 것을 확인

 

 

 

 

 

👾  스프링을 이용할 때는 클래스를 작성하거나 객체를 직접 생성하지 않음

👾 이 역할은 스프링 내부에서 이루어지며 ApplicationContext가 생성된 객체들을 관리

  • 서블릿의 url 매핑의 경우 web.xml에 <servlet>이라는 태그를 이용해서 서블릿 클래스의 이름과 경로를 전부 기록해야 했지만, 최근에는 @WebServlet 어노테이션이 이를 대신하는 것처럼, 스프링도 비슷한 방식으로 발전, 초기 스프링 버전에서는 XML 파일에 <bean>이라는 것을 이용해서 설정하는 방식이 2.5버전 이후에 어노테이션 형태로 변화되면서 예전에 비해 편리하게 설정이 가능

5.  @Repository, @Service 

👩🏻‍💻  서블릿에서도 @WebServlet이나 @WebFilter와 같이 다양한 어노테이션이 존재하듯이 스프링 프레임워크는 애플리케이션 전체를 커버하기 때문에 다양한 종류의 어노테이션을 사용하도록 작성 

 

@Controller      : MVC의 컨트롤러를 위한 어노테이션
@Service           : 서비스 계층의 객체를 위한 어노테이션
@Repository    : DAO와 같은 객체를 위한 어노테이션
@Component   : 일반 객체나 유틸리티 객체를 위한 어노테이션

 

💡  DAO(Data Access Object)   "실제로 DB의 데이터에 접근하는 객체"
  •  DAO는 Service와 DB를 연결하는 역할을 하며, 실제로 DB에 접근하여 data를 삽입, 삭제, 조회, 수정 등 CRUD 기능을 수행

💡 DTO(Data Transfer Object)   "계층 간 데이터 교환을 위한 객체"

  • 로직을 가지지 않고 getter/setter 메소드만 가진 순수한 데이터 객체 클래스(Java Beans)로 DB에서 데이터를 얻어 Service나 Controller 등으로 보낼 때 사용

💡 VO(Value Object)   "변경 불가능하며 오직 읽기만 가능 (Read-Only)"

  • DTO는 setter를 가지고 있어 값을 변경할 수 있지만, VO는 getter만을 가지기 때문에 읽기만 가능하고 수정은 불가능

 

출처: https://hstory0208.tistory.com/entry/Spring-DAO-DTO-VO란-각각의-개념에-대해-알아보자 

 

 

👩🏻‍💻  스프링이 사용하는 어노테이션의 경우 웹 영역뿐만 아니라 애플리케이션 전체에 사용할 수 있는 객체들을 포함

👩🏻‍💻  어노테이션을 이용하게 되면 스프링 설정은 <bean>이 아니라 '해당 패키지를 조사해서 어노테이션들을 이용'하는 설정으로 변경

 

설정 변경 전과 후

 

📌  기존 설정과 비교해 보면 XML 위쪽의 xmlns 네임 스페이스가 추가되고 schemaLocation이 변경

📌  내용에는 component-scan이 추가되었는데 속성값으로는 패키지를 지정 

📌  'component-scan'은 해당 패키지를 스캔해서 스프링의 어노테이션들을 인식

 


 

 

 

🎃  SampleDAO는 해당 클래스의 객체가 스프링에서 빈 Bean으로 관리될 수 있도록 @Repository 라는 어노테이션을 추가

 

 

 

 

 

 

 

 

 

🎃  SampleService에는 @Service 어노테이션을 추가

 

 

 

 

 

 

 


 

생성자 주입 방식 

 

👾  초기 스프링에서는 @Autowired를 멤버 변수에 할당하거나, Setter를 작성하는 방식을 많이 이용해 왔지만, 스프링 3 이후에는 생성자 주입이라고 부르는 방식을 더 많이 활용.

👾  생성자 주입 방식은 객체를 생성할 때 문제가 발생하는 지를 미리 확인할 수 있기 때문에 필드 주입이나 Setter 주입 방식 보다 선호되는 방식 

👾  Lombok에서는 생성자 주입을 보다 간단하게 작성할 수 있는데 @RequiredArgsConstructor를 이용해서  필요한 생성자 함수를 자동으로 작성할 수 있음

 

 

 

 

 

💫  생성자 주입 방식은 다음과 같은 규칙으로 작성

   

    - 주입 받아야 하는 객체의 변수는 final로 작성

     - 생성자를 이용해서 해당 변수를 생성자의 파라미터로 지정

 

 

 

 

 

 

 


6.  인터페이스를 이용한 느슨한 결합 

스프링이 의존성 주입을 가능하게 하지만  '좀 더 근본적으로 유연한 프로그램을 설계하기 위해서는 인터페이스를 이용'해서 나중에 다른 클래스의 객체로 쉽게 변경할 수 있도록 하는 것이 좋음.

    ⚡️  예를 들어 코드에서 SampleDAO를 다른 객체로 변경하려면 결론적으로 SampleService 코드 역시 수정해야 함. 추상화된 타입을 이용하면 이러한 문제를 피할 수 있는데 가장 대표적인 것이 인터페이스. 인터페이스를 이용하면 실제 객체를 모르고 타입만을 이용해서 코드를 작성하는 일이 가능해 짐. 

 

1)  SampleDAO를 인터페이스로 변경하기  

 

 

 

🎃  클래스로 작성된 SampleDAO을 인터페이스로 수정. @Repository 삭제

🎃  SampleService는 SampleDAO라는 인터페이스를 보게 되었지만 코드상의 변경은 필요하지 않음

 

 

 

 

 

 

 

 

🎃 SampleDAO 인터페이스는 구현 코드가 없기 때문에 구현한 클래스를 SampleDAOImpl 이라는 이름으로 선언

🎃 SampleDAOImpl에는 @Repository를 이용해서 해당 클래스의 객체를 스프링의 빈 Bean으로 처리되도록 구성

 

 

 

 

 

SampleService의 입장에서는 인터페이스만 바라보고 있기 때문에 실제 객체가 SampleDAOImpl의 인스턴스인지 알수 없지만, 코드를 작성하는 데에는 아무런 문제가 없음. 이처럼 객체와 객체의 의존 관계의 실제 객체를 몰라도 가능하게 하는 방식을 '느슨한 결합 Loose coupling'이라고 함. 느슨한 결합을 이용하면 나중에 SampleDAO 타입의 객체를 다른 객체로 변경하더라도 SampleService 타입을 이용하는 코드를 수정할 일이 없기 때문에 보다 유연한 구조가 됨. 


2) 다른 SampleDAO 객체로 변경해 보기 

 

 

🎃  예를 들어 특정한 기간에만 SampleDAO를 다른 객체로 변경해야 되는 경우 EventSampleDAOImpl이라는 클래스를 작성

 

 

 

 

 

 

 

 

이렇게 되면 SampleService에 필요한 SampleDAO 타입의 빈 Bean이 두 개 SampleDAOImpl, EventSampleDAOImpl 가 되기 때문에 스프링 입장에서는 어떤 것을 주입해야 하는지 알 수 없음테스트 코드를 실행하면 어떤 클래스의 객체를 사용해야 하는지 알 수 없으므로 다음과 같은 에러가 발생

 

Error creating bean with name 'sampleService' defined in file [E:\work\dev\projects\projects-spring intellij\springex\build\classes\java\main\com\nomadlab\springex\sample\SampleService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.nomadlab.springex.sample.SampleDAO' available: expected single matching bean but found 2: eventSampleDAOImpl,sampleDAOImpl 

 

 

스프링이 기대하는 것은 SampleDAO 타입의 객체가 하나 single 이길 기대했지만 2개가 발견. 

이를 해결하는 가장 간단한 방법은 두 클래스 중에 하나를 @Primary라는 어노테이션으로 지정해 주는 것

 


 

3) @Qualifier 이용하기 

 

👾  @Primary를 이용하는 방식 외에도 @Qualifier를 이용하는 방식도 있음

👾  @Qualifier는 이름을 지정해서 특정한 이름의 객체를 주입받는 방식

 

  📌  Lombok과 @Qualifier를 같이 이용하기 위해서는 src/main/java 폴더에 lombok.config 파일을 생성

 

 

 

 

🎃  동작을 확인하기 위해서 SampleDAOImpl과 EventSampleDAOImpl 에는 @Qualifier를 적용. 

🎃  SampleDAOImpl 에는 normal 이라는 이름을 지정 

 

 

 

 

 

 

 

🎃  EventSampleDAOImpl에는 event 라는 이름을 지정 

 

 

 

 

 

 

 

 

 

 

 

🎃  SampleService에는 특정한 이름의 객체를 사용하도록 수정

 

 

 

 

 

 

 

    🎃  normal 이름을 가진 SampleDAOImpl이 주입되는 것을 확인

 


4)  스프링의 빈 Bean 으로 지정되는 객체들 

 

👾  스프링 프레임워크를 이용해서 객체를 생성하고 의존성 주입을 이용할 수 있다는 사실을 알았지만 작성되는 모든 클래스의 객체가 스프링의 빈 Bean으로 처리되는 것은 아님

👾  스프링의 빈 Bean으로  등록되는  객체들은 쉽게 말해서 '핵심 배역'을 하는 객체들로 오랜 시간 동안 프로그램 내에 상주하면서 중요한 역할을 하는 '역할' 중심의 객체들

👾  반대로 DTO나 VO와 같이 '역할' 보다는 '데이터'에 중심을 두고 설계된 객체들은 스프링의 빈 Bean을 등록하지 않음

      특히 DTO의 경우 생명주기가 굉장히 짧고, 데이터 보관이 주된 역할이기 때문에 스프링에서 빈 Bean으로 처리하지 않음


5) XML이나 어노테이션으로 처리하는 객체 

 

👾  빈 Bean 으로 처리할 때 XML 설정을 이용할 수도 있고, 어노테이션을 처리할 수도 있지만, 이에 대한 기준은 '코드를 수정할 수 있는가'로 판단

👾  예를 들어 jar 파일로 추가되는 클래스의 객체를 스프링의 빈Bean으로 처리해야 한다면 해당 코드가 존재하지 않기 때문에 어노테이션을 추가할 수가 없다는 문제가 생김. 이러한 객체들은 XML에서 <bean>을 이용해서 처리하고, 직접 처리되는 클래스는 어노테이션을 이용하는 것이 좋음. 


7.   웹 프로젝트를 위한 스프링 준비 

💡  스프링의 구조를 보면 ApplicatonContext라는 객체가 존재하고 빈으로 등록된 객체들은  ApplicatonContext 내에 생성되어 관리되는 구조.

    ⚡️  이렇게 만들어진 ApplicatonContext가 웹 애플리케이션에 동작하려면 웹 애플리케이션이 실행될 때  스프링을 로딩해서 해당 웹 애플리케이션 내부에 스프링의 ApplicatonContext를 생성하는 작업이 필요하게 되는데 이를 위해서 web.xml을 이용해서 리스너를 설정

    ⚡️  스프링 프레임워크의 웹과 관련된 작업은 'spring-webmvc'라이브러리를 추가해야만 설정이 가능

 

 

 

 

▶️  build.gradle파일에 spring-mvc 라이브러리를 추가

 

 

 

 

▶️  WEB-INF 폴더 아래 web.xml에  <listener>설정과 <listener>에 필요한 <context-param>을 추가

 

 

 

 

💫  설정이 추가된 후에 톰캣을 실행하면 스프링과 관련된 로그가 기록되면서 실행 


8.  DataSource 구성하기 

톰캣과 스프링이 연동되는 구조를 완성했다면 데이터베이스 관련 설정을 추가

 

  📌  build.gradle에 MySQL과 HikariCP 관련 라이브러리를 추가

    // https://mvnrepository.com/artifact/mysql/mysql-connector-java
    implementation 'mysql:mysql-connector-java:8.0.33'

    // https://mvnrepository.com/artifact/com.zaxxer/HikariCP
    implementation 'com.zaxxer:HikariCP:5.0.1'

 

 

   📌  root-context.xml에 HikariCP 설정하기 

    <context:component-scan base-package="com.example.sping_class.sample" />

    <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/sample" />
        <property name="username" value="root"/>
        <property name="password" value="비밀번호"/>
        <property name="dataSourceProperties">
            <props>
                <prop key="cachePrepStmts">true</prop>
                <prop key="prepStmtCacheSize">250</prop>
                <prop key="prepStmtCacheSqlLimit">2048</prop>
            </props>
        </property>
    </bean>

    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <constructor-arg ref="hikariConfig" />
    </bean>

 

  • HikariCP를 사용하기 위해서는 HikariConfig 객체와 HikariDataSource를 초기화해야함
  • root-context.xml을 이용해서 HikariConfig 객체와 HikariDataSource 객체를 설정
  • hikariConfig에는 id 속성이 적용되어 있고 HikariDataSource는 <constructor-arg ref="hikariConfig" />로 id 값을 참조해서 사용
  • HikariDataSource는 javax.sql의 DataSource 인터페이스의 구현체이므로 테스트 코드를 통해서 설정에 문제가 없는지 확인

 

 

 

🎃  root-context.xml에 선언된 HikariCP를 주입받기 위해서 DataSource타입의 변수를 선언하고 @Autowired를 이용해서 주입 되도록 구성

 

 

 

 

 

 

 

 

 

 

 

 

 

 

👩🏻‍💻  스프링은 필요한 객체를 스프링에서 주입해 주기 때문에 개별적으로 클래스를 작성해서 빈으로 등록해 두기만 하면 원하는 곳에서 쉽게 다른 객체를 사용할 수 있음. 이런 특징으로 인해 스프링 프레임워크는 웹이나 데이터베이스 같은 특정한 영역이 아닌 전체 애플리케이션의 구조를 설계할 때 사용

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

+ Recent posts