/ LECTURE

Spring - Intro

이 페이지는 다음에 대한 공부 기록입니다

JAVA(자바), Python(파이썬) 기반의

AI 활용 응용 소프트웨어 개발자 양성 과정

2021.11.10. ~ 2022.05.18.

찾으시는 정보가 있으시다면
주제별reference를 이용하시거나
우측 상단에 있는 검색기능을 이용해주세요

61일차 수업

Framework

App들의 공통점을 찾아 기본 하부 구조를 제공해 줌

디자인 패턴과 함께 패턴이 적용 된 기반 클래스 라이브러리를 제공

Spring

Java enterprise 개발을 편하게 해주는 app 프레임워크

발전

  1. EJB의 대안으로 소개, IoC(DI), AOP, XML 기반의 빈 정의

  2. Spring security, DI 와 MVC를 Annotation 방식 설정

  3. RESTful 프레임워크 사용

  4. 웹소켓

  5. 리액티브 프로그래밍

주요 프로젝트 및 라이브러리

module description
spring-beans 스프링 컨테이너를 이용해서 객체를 생성하는 기본 기능 제공
spring-context 객체 생성, 라이프 사이클 처리, 스키마 확장 등의 기능 제공
spring-aop AOP 기능을 제공
spring-web REST Client, 데이터 변환처리, 서블릿 필터, 파일 업로드 지원등 웹 개발에 필요한 기반 제공
spring-webmvc 스프링 기반의 MVC 프레임워크, 웹 앱을 개발하는데 필요한 controller, view 구현 제공
spring-websocket 스프링 MVC에서 웹 소켓 연동을 지원
spring-oxm 트랜잭션 처리를 위한 추상 레이어를 제공
spring-jdbc jdbc 프로그래밍을 보다 쉽게 할 수 있는 템플릿을 제공
spring-orm 하이버네이터,JPA, MyBatis 등과의 연동을 지원
spring-jms JMS 서버와 메세지를 쉽게 주고 받을 수 있도록 하기위한 템플릿,어노테이션 등을 제공
spring-context-support 스케줄, 메일 발송, 캐시연동, 벨로시티 등 부가기능을 제공

IoC 컨테이너 기능을 제공

  • IoC(Invension of Control) : 객체의 생성, 생명주기의 관리까지 모든 객체에 대한 제어권이 바뀜

  • DL과 DI로 분류

DI

Dependency Injection

Setter, Constructor, Method Injection

각 클래스 간의 의존 관계를 bean 설정 정보를 바탕으로 컨테이너가 자동으로 연결해 주는 것

  • DL(Dependency Lookup) : 저장소에 저장되어있는 Bean에 접근하기 위해 컨테이너가 제공하는 API를 이용하여 Bean 을 Lookup하는 것, 사용시 컨테이너 종속성이 증가하여 주로 DI 사용

객체 레퍼런스를 컨테이너로부터 주입 받아서, 실행 시에 동적으로 의존관계가 생성

  • 코드가 단순해지고, 컴포넌트 간의 결합도가 제거 된다

관련용어

  • Bean : 스프링이 IoC 방식으로 관리하는 객체, 직접 생성과 제어를 담당

  • BeanFactory : IoC를 담당하는 핵심 컨테이너

  • Application Context : BeanFactory를 확장한 IoC 컨테이너, 스프링의 부가서비스를 추가 제공

Bean

XML 기반

  • <bean> 요소로 Bean을 정의, <constructor-arg><property> 요소로 의존성을 주입
<!-- applicationContext.xml 파일 에서-->
<beans>
    <!-- 빈을 생성하는 다양한 방법들 -->
    <!-- DI : 빈 정의 -->
    <bean id="ko" class="aDI.firstTime.MessageBeanKoImpl"/>
    <bean id="en" class="aDI.firstTime.MessageBeanEnImpl"/>

    <!-- 생성자의 인자로 빈을 생성-->
    <bean id="member" class=" aDI.firstTime.MemberBean">
        <constructor-arg value="홍길국"/>
        <constructor-arg value="23" type="int"/>
        <constructor-arg>
            <value>안녕하세요</value>
        </constructor-arg>
    </bean>

    <!-- 생성자의 setter를 이용하여 생성-->
    <bean id="member2" class=" aDI.firstTime.MemberBean">
        <property name="name" value="홍길도"/>
        <property name="age">
            <value>55</value>
        </property>
        <property name="message" value="즐거운 스프링"/>
    </bean>

    <!--
    생성자의 인자로 빈을 생성
    DAO에서 활용할 memberBean의 형태로 만든 빈을 참조하는 방법
    객체를 참조할때는 ref, 값을 입력할 때는 value
    -->
    <bean id="dao1" class="aDI.firstTime.MemberDAO">
        <constructor-arg ref="member"/>
    </bean>

    <bean id="dao2" class="aDI.firstTime.MemberDAO">
        <constructor-arg name="member" ref="member2"/>
    </bean>

    <bean id="beanList" class="aDI.firstTime.ListBean">
        <property name="intList">
            <list>
                <value>22</value>
                <value>33</value>
                <value>44</value>
            </list>
        </property>
        <property name="memberList">
            <list>
                <ref bean="m1"/>
                <ref bean="m2"/>
                <bean class="aDI.firstTime.MemberBean"/>
            </list>
        </property>
    </bean>
    <bean id="m1" class="aDI.firstTime.MemberBean"/>
    <bean id="m2" class="aDI.firstTime.MemberBean"/>

</beans>
<!-- Class 에서-->
public class MainApp {
    public static void main(String[] args) {
        // 스프링의 설정파일을 연결
        ApplicationContext context = new ClassPathXmlApplicationContext("member-context.xml");
    
        // 빈(bean) 객체명은 클래스명과 동일하되 첫글자만 소문자
        // 만일 BCyrup 처럼 두번째 글자가 대문자라면 객체명은 소문자 시작이 아닌 BCyrup 동일명으로  
        // DI 컨테이너에서 빈 가져오기
        MemberDAO dao = context.getBean("dao", MemberDAO.class);
        dao.insert();
    }
}

Annotation 기반

@Component 같은 어노테이션이 부여된 클래스를 탐색하여 DI 컨테이너에 빈을 등록

@Component // memberBean 앞자리 소문자로 해서 만들어 줌
public class MemberBean {

    private String name;
    private int age;
    private String message;
}

@Component("dao")	// 이름을 지정할 수도 있음
public class MemberDAO {

    // (3) 필드 기반 의존성 주입 방식
    @Autowired // MemberBean 객체가 있으면 찾아서 주입
    private MemberBean member;
}
<!-- applicationContext.xml 파일 에서, 컴포넌트 스캔 범위를 설정해줄 수 있다 -->
<!-- annotation이 붙은 클래스를 탐색하여 DI 컨테이너에 자동으로 등록 -->
<context:component-scan base-package="lastlast.aDI"/>

Java 기반

자바 클래스에 @Configuration, 메소드에 @Bean 어노테이션을 사용해 빈을 정의

의존성 주입 어노테이션

  • @Autowired : 스프링 제공, 생성자, 메소드, 변수 모두 사용가능 하지만 주로 변수에 설정하여 해당 타입의 객체를 자동으로 할당

  • @Qualifier : 스프링 제공, 특정 객체의 이름으로 의존성 주입할 때 사용

  • @Resource : javax.annotation.Resource, @Autowired 와 @Qualifier 기능을 결합한 역할

  • @Inject : java.inject.Inject, @Autowired와 동일한 기능

@Component를 상속한 어노테이션

  • 스프링에서 클래스를 분류하기 위해 제공

  • @Service : 비즈니스 로직을 처리하는 Service 클래스

  • @Repository : 데이터베이스 연동을 처리하는 DAO 클래스

  • @Controller : 사용자 요청을 제어하는 Controller 클래스

MVC

Model-View-Controller, 아키텍쳐 패턴, 주 목적은 Business 와 Presentation의 logic을 분리, 서로 영향을 최소화해 유지보수를 편리하게 하기 위함

기본 흐름

  • 클라이언트의 요청이 있으면 -> 컨트롤러가 받아서 -> 모델을 호출하고, 결과를 받아 -> 뷰에게 화면생성을 요청하고 -> 컨트롤러가 결과화면을 받아 클라이언트에게 응답

  • Model : app의 정보(data, Business logic)

  • View : 화면(Presentation logic)

  • Controller : M과 V 사이의 상호 작용 관리

Spring MVC

  • DI, AOP, 서블릿 기반의 웹개발을 위한 MVC 프레임 워크 제공

  • 주요 구성 요소

구성 요소 설명
DispatcherServlet 클라이언트의 요청을 받아 Controller에게 전달, 결과값을 View에 전달, 응답 생성
HandlerMapping url과 요청정보를 기준으로 어떤 핸들러 객체를 사용할지 결정하는 객체
Controller 클라이언트 요청 처리한 뒤, model 호출하고 결과를 DispatcherServlet 에게 알려줌
ModelAndView Controller가 처리한 데이터 및 화면에 대한 정보를 보유한 객체
View Controller의 처리 결과 화면에 대한 정보를 보유한 객체
ViewResolver Controller가 리턴한 뷰 이름을 기반으로 Controller 처리 결과를 생성할 뷰를 결정

DB 연결해보기

<!-- database.properties 파일을 생성해 db정보를 입력해주고 -->
jdbc.dbDriver = oracle.jdbc.OracleDriver
jdbc.dbUrl = jdbc:oracle:thin:@ ~~~
jdbc.dbId = admin
jdbc.dbPw = 1q2w3e4r5t^Y
<!-- database-context.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"
       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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 내가 만든 database에 관한 변수들을 현재 파일에서 사용하겠다-->
    <context:property-placeholder location="classpath: database.properties"/>
    <!-- org.apache.commons.dbcp.BasicDataSource-->
    <!-- DataSource 설정 DB 와의 연결-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.dbDriver}"></property>
        <property name="url" value="${jdbc.dbUrl}"></property>
        <property name="username" value="${jdbc.dbId}"></property>
        <property name="password" value="${jdbc.dbPw}"></property>
    </bean>

    <!-- Spring JDBC 설정-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 해당 경로에 존재하는 컴포넌트를 스캔하여 bean 객체를 생성-->
    <context:component-scan base-package=" lastlast.db.board"/>
</beans>
// DAO 클래스 파일에서
@Repository
public class boardDAOSpring {

    // SQL 명령어들
    private final String BOARD_INSERT = "insert into board(seq, title, writer, content,regdate, cnt) "
            + " values(board_seq.nextval,?,?,?,sysdate, 0)";

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insertBoard(BoardVO vo) {
        System.out.println("==>> JDBC를 통한 insert 기능");
        jdbcTemplate.update(BOARD_INSERT,vo.getTitle(),vo.getWriter(),vo.getContent());
    }
}

Mybatis 사용해 연결해보기

<!-- pom.xml 에 관련 dependency를 추가해준다-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>

        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>

<!—mybatis-config.xml 파일을 생성해 -->
        <?xml version='1.0' encoding='UTF-8'?>
        <!DOCTYPE configuration
                PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- properties 설정-->
<properties resource=" database.properties"/>
<!--alias 설정-->
<typeAliases>
    <typeAlias type="com.system.lastlast.A0208.db.board.BoardVO" alias="board"/>
</typeAliases>
<!-- dataSource 설정-->
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.dbDriver}"></property>
            <property name="url" value="${jdbc.dbUrl}"></property>
            <property name="username" value="${jdbc.dbId}"></property>
            <property name="password" value="${jdbc.dbPw}"></property>
        </dataSource>
    </environment>
</environments>

<!--sql mapper 설정-->
<mappers>
    <mapper resource="board-mapping.xml"/>
</mappers>
</configuration>
<!-- board-mapping.xml 파일을 생성해-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- sql문을 작성해준다-->
<mapper namespace="BoardDAO">
    <insert id="insertBoard" parametertype="board">
        INSERT INTO board(seq, title, writer, content, regdate, cnt)
        VALUES (board_seq.nextval, #{title}, #{writer}, #{content}, SYSDATE, 0)
    </insert>
</mapper>
<!SqlSessionFactoryBean 담당하는 클래스를 하나 만들어주고-->
public class SqlSessionFactoryBean {
    private static SqlSessionFactory sessionFactory = null;
    static {
        try {
            if (sessionFactory == null) {
                Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
                sessionFactory = new SqlSessionFactoryBuilder().build(reader);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlSessionInstance() {
        return sessionFactory.openSession();
    }
}
<!DAO 클래스에서-->
public class BoardDAO {

    private SqlSession mybatis;

    public BoardDAO() {
        // 호출한다
        mybatis = SqlSessionFactoryBean.getSqlSessionInstance();
    }

    public void insertBoard(BoardVO vo) {
        mybatis.insert("BoardDAO.insertBoard", vo);
        mybatis.commit();
    }
}