티스토리 뷰

https://url.kr/a1swod

 

코드로 배우는 스프링 웹 프로젝트:현업 개발을 위한 단계별 실습서

COUPANG

www.coupang.com

 

스프링 프레임워크의 간략한 역사
2000년대 초반부터 시작된 엔터프라이즈급의 개발은 안정된 품질의 개발이 절실했고, 그 결과 많은 프레임워크의 전성시대라고 할 수 있었다. 스프링은 비교적 그 시작이 조금 늦은 프로젝트였지만, 가장 성공적인 '경량 프레임워크'이다.

 

경량프레임워크?

경량프레임워크라는 용어는 90년대 말에 복잡한 구동환경과 하드웨어적인 구성이 필요한 프레임워크의 반대되는 개념으로 등장. 과거 J2EE 기술은 너무나 복잡하고 방대했기 때문에 이를 특정 기능을 위주로 간단한 jar파일 등을 이용해서 모든 개발이 가능하도록 구성된 프레임워크이다.

* 복잡함에 반기를 들어 만들어진 프레임워크
엔터프라이즈급 프레임워크들의 가장 큰 문제점으로는 복잡성이다. 다양한 경우를 처리할 수 있는 다양한 기능을 가지도록 만들다보니 하나의 기능을 위해서 너무 많은 구조가 필요한 상태가 되었다. 이러한 복잡성을 해결하기 위해서 나온 경량화된 프레임워크가 스프링이다. 일반적인 Java의 클래스와 인터페이스를 이용하는 구조이기 때문에 진입장벽이 높지 않고, EJB로 대표되는 복잡한 프레임워크에 비해 가볍기 때문에 빠르게 엔터프라이즈급의 시스템을 작성할 수 있게 되었다.

* 프로젝트 전체 구조슬 설계할때 유용한 프레임워크
다른 프레임워크들은 웹 영역이나 데이터베이스 영역 등의 전문적인 영역에 대해서만 지원하는 경우가 많았고, 비즈니스 로직 처리 부분에 대한 설계는 개발자의 역량에 맡기는 경우가 많았는데, 반면 스프링은 어느 한 분야에 집중하지 않고, 전체를 설계하는 용도로 사용될 수 있다. 스프링 프로젝트가 대부분 Web영역에서 많이 사용되기느 하지만, 근본적인 사상 자체는 OOP구조를 뒷받침하고 구조를 설계하는 사상이다. -> 의존성주입(Dependency Injection)에 대한 설명

* 다른 프레임워크들의 포용
다른 프레임워크들은 특정 프레임워크를 채택하면 해당 영역 전체를 수정해야 하는 고질적인 문제를 가지고 있었찌만, 스프링은 다른 프레임워크들과의 통합을 지원했기 때문에 최소한의 수정이 가능했다. 스프링의 최대 장점은 기본 뼈대를 흔들지 않고, 여러 종류의 프레임워크를 혼용해서 사용할 수 있다는 점이다.


 

프레임워크 : 말 그대로 '뼈대나 근간을 이루는 코드들의 묶음'

프레임워크를 이용한다는 의미는 프로그램의 기본 흐름이나 구조를 정하고, 모든 팀원이 이 구조에 자신의 코드를 추가하는 방식으로 개발한다는 것이다. 프레임워크 최대의 장점은 개발에 필요한 구조를 이미 코드로 만들어 놓았기 때문에, 반쯤 완성한 상태에서 필요한 부분을 조립하는 형태의 개발이 가능하다는 점이다.

 

스프링의 주요 특징 

  • POJO기반의 구성
    스프링의 성격 자체가 가벼운(light-weight) 프레임워크지만, 그 내부에는 객체간의 관계를 구성할 수 있는 특징을 가지고 있다. 스프링은 다른 프레임워크들과 달리 이 관계를 구성할 때 별도의 API 등을 사용하지 않는 POJO(Plain Old Java Object)의 구성만으로 가능하도록 제작되어 있다. 쉽게 말해서 일반적인  Java 코드를 이용해서 객체를 구성하는 방식을 그대로 스프링에서 사용할 수 있다는 의미. 이것이 중요한 이유는 -> 특정한 라이브러리나 컨테이너 기술에 종속적이지 않다는 것을 의미한다. 개발자는 가장 일반적인 형태로 코드를 작성하고 실행할 수 있기 때문에 생산성에서도 유리하고, 코드에 대한 테스트 작업 역시 좀 더 유연하게 할 수 있다는 장점이 생긴다.

  • 의존성 주입(DI)
    의존성(Dependency)이라는 것은 하나의 객체가 다른 객체 없이 제대로 된 역할을 할 수 없다는 것을 의미한다. 의존성은 하나의 객체가 다른 객체의 상태에 따라 영향을 받는 것을 의미한다. A객체가 B객체 없이 동작이 불가능한 상황 = A가 B를 의존하고 있다 = A는 B가 필요하다 라는 의미 이다.
    주입(Injection)은 말 그대로 외부에서 '밀어 넣는 것'을 의미한다. 어떤 음식점의 경우는 매일 가게를 열기 전 식재료를 구하기 위해 시장을 가지만, 프랜차이즈 식당들은 본사가 트럭 등을 이용해서 식재료를 공급한다. 이 두 가지 방식의 차이는 필요한 객체를 얻기 위해서 주체가 능동적인지 수동적인지에 대한 문제이다.

    의존성과 주입을 결합해서 생각해보면 '어떤 객체가 필요한 객체를 외부에서 밀어 넣는다'는 의미가 된다.

    Why '외부에서 객체를 주입하는 방식'을 사용할까?
    음식점의 예에서 직접 식재료를 사지 않고, 대행업체에서 배송해 주는 것을 사용하는 경우 얻는 장점은 무엇인가 에 대해서 고민해보면 '편리하다', '장사에만 집중할 수 있다'라는 장점들을 생각해 볼 수 있다.
    이를 코드에 대입해서 살펴보면 '주입을 받는 입장에서는 어떤 객체인지 신경 쓸 필요가 없다', '어떤 객체에 의존하든 자신의 역할은 변하지 않는다'와 같은 의미로 볼 수 있다.

    아래 그림을 보면 추가적인 하나의 존재가 필요하게 된다. 이 존재는 의존성이 필요한 객체를 찾아서 '주입'하는 역할을 하게 된다. 스프링은 이러한 구조를 만드는데 적합한 구조로 설계되어 있다. 

출처 : https://url.kr/bl8vo6

스프링에서는 ApplicationContext라는 존재가 필요한 객체들을 생성하고, 필요한 객체들을 주입하는 역할을 해 주는 구조이다. 따라서 스프링을 이용하면 개발자들은 기존의 프로그래밍과 달리 객체와 객체를 분리해서 생성하고, 이러한 객체들을 엮는(wiring) 작업을 하는 형태의 개발을 하게 된다. 스프링에서는 ApplicationContext가 관리하는 객체들을 '빈'이라는 용어로 부르고, 빈과 빈 사이의 의존관계를 처리하는 방식으로 XML 설정, 어노테이션 설정, Java설정 방식을 이용할 수 있다.

  • AOP
    좋은 개발환경의 중요 원칙은 '개발자가 비즈니스 로직에만 집중할 수 있게 한다' 이다. 이 목표를 이루기 위해서 몇가지 중요한 원칙이 있지만, 가장 쉽게 생각할 수 있는 것이 '반복적인 코드의 제거' 라고 할 수 있다. 스프링은 프레임워크를 이용한 개발에도 이러한 반복적인 코드를 줄이고, 핵심 비즈니스 로직에만 집중할 수 있는 방법을 제공한다.

    대부분의 시스템이 공통으로 가지고 있는 보안이나 로그, 트랜잭션과 같이 비즈니스 로직은 아니지만, 반드시 처리가 필요한 부분을 스프링에서는 '횡단 관심사'라고 한다. 스프링은 이러한 횡단관심사를 분리해서 제작하는 것이 가능하다. AOP(Aspect Oriented Programming)는 이러한 횡단 관심사를 모듈로 분리하는 프로그래밍의 패러다임이다.
    스프링은 AOP를 AspectJ의 문법을 통해서 작성할 수 있는데, 이를 통해 개발자는
    1) 핵심 비즈니스 로직에만 집중해서 코드 개발 가능
    2) 각 프로젝트마다 다른 관심사 적용할 때 코드의 수정을 최소화 가능
    3) 원하는 관심사의 유지보수가 수월한 코드 구성 가능

  • 트랜잭션의 지원 : 데이터베이스를 이용할 때 반드시 신경 써야 하는 부분은 하나의 업무가 여러 작업으로 이루어지는 경우의 트랜잭션 처리이다. 스프링은 이런 트랜잭션의 관리를 어노테이션이나 XML로 설정할 수 있기 때문에 개발자가 매번 상황에 맞는 코드를 작성할 필요가 없도록 설계되어 있다.

 

스프링은 클래스에서 객체를 생성하고 객체들의 의존성에 대한 처리 작업까지 내부에서 모든것이 처리된다. 스프링에서 관리되는 객체를 흔히 '빈(Bean)'이라고 하고, 이에 대한 설정은 XML과 Java를 이용해서 처리할 수 있다.

  • XML 을 이용하는 의존성 주입 설정
    STS의 Spring Legacy Project의 경우 기본적으로 XML을 이용해서 스프링에서 관리해야 하는 객체들을 처리한다.
    프로젝트의 src폴더 내에 'root-context.xml'은 스프링 프레임워크에서 관리해야 하는 객체(이러한 객체를 스프링에서는 빈(Bean)이라고 표현한다.)를 설정하는 설정 파일이다.
    'root-context.xml'을 클릭하면 아래쪽에 'NameSpaces'라는 탭이 보이는데, 이때 'context'라는 항목을 체크한다.
    'Source' 탭을 선택해서 아래의 코드를 추가한다. 저장 후 'Bean Graph' 탭을 선택해 보면 객체가 설정된 것을 확인할 수 있다.
<context:component-scan base-package="org.test.sample">
</context:component-scan>
  •  Java 설정을 이용하는 의존성 주입
    Java 설정을 이용하는 경우 'root-context.xml'을 이용하는 대신 RootConfig 클래스를 이용한다. XML로 설정된 내용은 RootConfig에서 @ComponentScan 어노테이션을 이용해서 처리할 수 있다.
@Configuration
@ComponentScan(basePackages = {"org.test.sample"})
public class RootConfig {
}

 

스프링이 동작하면서 생기는 일

  • 스프링 프레임워크가 시작되면 먼저 스프링이 사용하는 메모리 영역을 만들게 되는데 이를 컨텍스트(Context)라고 한다. 스프링에서는 ApplicationContext라는 이름의 객체가 만들어진다.
  • 스프링은 자신이 객체를 생성하고 관리해야 하는 객체들에 대한 설정이 필요하다. 이에 대한 설정이 root-context.xml 파일 이다.
  • root-context.xml에 설정되어 있는 <context:component-scan> 태그의 내용을 통해서 패키지를 스캔하기 시작한다.
  • 해당 패키지에 있는 클래스들 중에서 스프링이 사용하는 @Component라는 어노테이션이 존재하는 클래스의 인스턴스를 생성한다.
  • 객체가 필요하다는 어노테이션 @Autowired 등의 설정을 보고 스프링은 설정에 따라 객체의 레퍼런스를 객체에 주입한다. (생성자주입, setter) 생성자 주입의 경우 객체 생성시 의존성 주입이 필요하므로 좀 더 엄격하게 의존성 주입을 체크하는 장점이 있다. 스프링4.3이후에는 묵시적 생성자 주입이 가능해졌다.

 

@Component : 해당 클래스가 스프링에서 객체로 만들어서 관리하는 대상임을 명시하는 어노테이션
@Autowired : 스프링 내부에서 자신이 특정한 객체에 의존적이므로 자신에게 해당 타입의 빈을 주입하라는 표시이다.
                       (스프링한테 자동으로 주입해달라고 얘기하는 것). 스프링은 어노테이션을 보고 스프링 내부에 관리되는 객체들 중에 적당한 것이 있는지 확인하고, 자동으로 주입을 해준다. 이 때 필요한 객체가 존재하지 않는다면 스프링은 제대로 객체들을 구성할 수 없기 때문에 에러를 발생한다. NoSuchBeanDefinitionException : No qualifying bean of type...

- 테스트 관련 어노테이션 
@Runwith : 테스트 시 필요한 클래스를 지정한다. 스프링은 SpringJUnit4ClassRunner 클래스가 대상이 된다.
@ContextConfiguration : 스프링이 실행되면서 어떤 설정 정보를 읽어 들여야 하는지를 명시한다. XML 뿐만 아니라 Java 설정을 이용할 때도 사용할 수 있다.
@Test : junit에서 해당 메서드가 jUnit 상에서 단위 테스트의 대상인지 알려준다.

- Lombok관련 어노테이션  
@Setter : setter 메서드를 생성해 주는 역할을 한다.
@Data : Lombok에서 가장 자주 사용되는 어노테이션이다. @ToString, @EqualsAndHashCode, @Getter/@Setter, @RequiredArgsConstructor 를 모두 결합한 형태이다. 
@Log4j : 로그 객체를 생성한다. Lombok을 이용해서 로그를 기록하는 Logger를 변수로 생성한다. @Log4j는 Log4j 설정을 이용하고, Log4j가 존재하지 않은 경우에는 @Log를 이용할 수 있다.

@Log
public class LogExample { }

클래스로 변환된 후
public class LogExample {
	private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
}


@AllArgsConstructor : 인스턴스 변수로 선언된 모든 것을 파라미터로 받는 생성자를 작성하게 된다.
@RequiredArgsConstructor : 여러 개의 인스턴스 변수들 중에서 특정한 변수에 대해서만 생성자를 작성하고 싶을때 사용.
@NonNull 이나 final이 붙은 인스턴스 변수에 대한 생성자를 만들어 낸다.

스프링의 의존성 주입 방법 크게 2가지
1. 생성자 주입 : 객체 생성 시 의존성 주입이 필요하므로 좀 더 엄격하게 의존성 주입을 체크하는 장점이 있다.
2. Setter 주입

Oracle 이용시 참고

Oracle 11g의 경우 기본적으로 8080 포트를 이용해서 웹 환경으로도 데이터베이스를 접근할 수 있다. 문제는 웹 개발 시 많이 사용하는 Tomcat의 기본 포트가 8080이기 때문에 동시에 오라클과 Tomcat이 8080포트를 사용하는 문제를 가지게 된다. 이 문제를 해결하기 위해 오라클의 포트를 변경해 주는 것이 좋다.
select dbms_sdb.gethttpport() from daul;을 통해 현재 사용하는 포트 번호 확인 가능. 보통 1000번 이하의 포트는 예약 포트 이므로, 9090이나 9000등 원하는 번호로 변경해야 한다.

 

프로젝트의 JDBC 연결

우선 JDBC를 연결하려면 JDBC Driver가 필요하다. Oracle 데이터베이서의 JDBC Driver는 11g까지 공식적으로 Maven으로는 지원되지 않기 때문에 직접 jar 파일을 프로젝트에 추가시켜 줘야 한다. SQL Developer를 설치했다면 설치 경로에 jdbc/lib 폴더에 JDK 8버전용 ojdbc8.jar 파일이 존재한다. 추가할 프로젝트를 선택하고 Build Path를 이용해서 다운로드한 ojddbc8.jar 파일을 경로에 추가한다. 나중에 war파일로 만들어 질 때에도 jar파일이 포함될 수 있도록 Web Deployment Assembly 항목에도 jar파일을 추가한다.

 

커넥션 풀 설정

일반적으로 여러 명의 사용자를 동시에 처리해야 하는 웹 애플리케이션의 경우 데이터베이스 연결을 이용할 때는 '커넥션 풀(Connection Pool)'을 이용하므로, 아예 스프링에 커넥션 풀을 등록해서 사용하는 것이 좋다. Java에서는 DataSource라는 인터페이스를 통해서 커넥션 풀을 사용한다. DataSource를 통해 매번 데이터베이스와 연결하는 방식이 아닌, 미리 연결을 맺어주고 반환하는 구조를 이용하여 성능 향상을 시킨다.
커넥션 풀은 여러 종류가 있고, spring-jdbc 라이브러리를 이용하는 방식도 있지만 HikariCp방식도 있다. 

pom.xml을 수정해서 HikariCP를 추가할 수 있다.

 

스프링에서 root-context.xml은 스프링이 로딩되면서 읽어 들이는 문서이므로, 주로 이미 만들어진 클래스들을 이용해서 스프링의 빈(Bean)으로 등록할 때 사용된다. 일반적으로 프로젝트에 직접 작성하는 클래스들은 어노테이션을 이용하는 경우가 많고, 외부 jar 파일 등으로 사용하는 클래스들은 <bean> 태그를 이용해서 작성하는 경우가 대부분이다.

Java설정을 이용하는 경우에는 root-context.xml에 추가한 내용들을 RootConfig 클래스와 @Bean을 이용해서 처리한다.
@Bean은 XML 설정에서 <bean> 태그와 동일한 역할을 한다고 생각하면 된다. @Bean이 선언된 메서드의 실행 결과로 반환된 객체는 스프링의 객체(Bean)로 등록된다.

 

MyBatis

MyBatis는 흔히 'SQL 매핑(mapping) 프레임워크' 로 분류된다. MyBatis 역시 mybatis-spring이라는 라이브러리를 통해서 쉽게 연동작업을 처리할 수 있다. 

전통적인 JDBC 프로그램 MyBatis
- 직접 Connection을 맺고 마지막에 close()
- PreparedStatement 직접 생성 및 처리
- PreparedStatement의 setXXX(). 등에 대한 모든
  작업을 개발자가 처리
- SELECT의 경우 직접 ResultSet 처리
- 자동으로 Connection close() 가능
- MyBatis 내부적으로 PreparedStatement 처리
- #{prop}와 같이 속성을 지정하면 내부적으로 자동 처리
- 리턴 타입을 지정하는 경우 자동으로 객체 생성 및
   ResultSet 처리

 

MyBatis 관련 라이브러리 추가

  • spring-jdbc/spring-tx
  • mybatis/mybatis-spring

 

SQLSessionFactory

MyBatis에서 가장 핵심적인 객체는 SQLSession이라는 존재와 SQLSessionFactory 입니다. SQLSessionFactory는 내부적으로 SQLSession이라는 것을 만들어내는 존재인데, 개발에서는 SQLSession을 통해서 Connection을 생성하거나 원하는 SQL을 전달하고, 결과를 리턴 받는 구조로 작성하게 된다.

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

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource"></property>
</bean>

스프링에 SqlSessionFactory를 등록하는 작업은 SqlSessionFactoryBean을 이용한다. 패키지명을 보면 MyBatis 패키지가 아니라 스프링과 연동작업을 처리하는 mybatis-spring 라이브러리 클래스임을 알 수 있다.

 

log4jdbc-log4j2 설정

MyBatis는 내부적으로 JDBC의 PreparedStatement를 이용해서 SQL을 처리한다. 따라서 SQL에 전달되는 파라미터는 JDBC에서와 같이 '?'로 치환되어서 처리된다. 복잡한 SQL의 경우 '?'로 나오는 값이 제대로 되었는지 확인하기가 쉽지 않고 실행된 SQL의 내용을 정확히 확인하기 어렵다. SQL 로그를 제대로 보기 위해서는 log4jdbc-log4j2 라이브러리를 사용해야 한다.
pom.xml에 라이브러리를 추가한 후 1) 로그 설정 파일을 추가하는 작업 2) JDBC의 연결 정보를 수정 해야한다.

 

로그의 레벨 설정

log4jdbc에서 출력되는 로그를 조절하고 싶다면 추가적인 <logger>를 지정해서 처리한다. 기본 설정의 로그는 info 레벨이기 때문에 warn과 같이 좀 더 높은 레벨의 로그만 기록하게 수정하면 테스트 코드를 실행할 때 이전에 비해 로그의 양이 줄어드는 것을 확인할 수 있다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
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
글 보관함