LostCatBox

SpringCore CH00

Word count: 2.4kReading time: 15 min
2022/12/24 Share

스프링 입문

References: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/dashboard

왜?

Django를 넘어서 java 와 spring 사용하여 서비스를 만들기위해 배움

전체 프로젝트 그림

https://github.com/lostcatbox/FisrtJavaLectureClone.git

컨트롤러 → 서비스 -1 리포.png

단축키 정리(인텔리J)

1
2
3
4
5
command+shift + T // 테스트 케이스 만들기
command +shift+enter //자동완성
command +D +방향키 // 선택영역 복사
command + option +V // 변수로 바로 생성
command + E //최근에 봣던 목록

기본 프로젝트 시작

  • start.spring.io를 통해 gradle설정하고, java 11버전을 선택
  • 붙임은 spring web, Thymeleaf 선택후 generate
  • build.gradle에서 볼수있듯 gradle은 필요한 설정들과 라이브러리를 땅겨오는 역할(dependencies)>> 이를ㄹ mavenCentral()이라는곳에서 모두 다운받아서 repo로 활용
  • .gitignore!! 무시되는것 확인해보기

라이브러리 살펴보기

  • build.gladedp dependencies확인 >> 의존관계에 따라 필요한것들 자동으로 다른 라이브러리 땅겨옴
  • external library를 확인해보면, 훨씬더 많은것이 확인됨
  • log 궁금하다면 slf4 와 logback 검색 ㄱ

View 환경설정

스크린샷 2022-06-06 오후 4.12.39.png

빌드 실행

콘솔로 이동

  1. ./gradlew build

  2. cd build/libs

  3. java -jar hello-spring-0.0.1-SNAPSHOT.jar

  4. 실행확인

정적 파일 접근시 경로

스크린샷 2022-06-06 오후 5.45.35.png

  • hello-static.html으로 왔다고 스프링에게 넘기면
  • controller에서 hello-static이라는 컨트롤러가없으므로
  • rescources: static안에서 찾음.. 반환!

MVC와 템플릿 엔진

  • mvc: model, view, controller
  • model 모델
  • model, controller는 비즈니스 로직, 내부처리 특화
  • view는 화면을 그리는것에 특화

스크린샷 2022-06-06 오후 6.02.08.png

API 방식

  • 문자열이 반환됨
1
2
3
4
5
@GetMapping("hello-string")
@ResponseBody //html의 body가 아닌,http 프로토콜에 header와 body부분에 body에 직접 내용넣겠다 선언
public String helloString(@RequestParam("name") String name) {
return "hello"+name; // view통과 없이 바로 이 문자열이 반환됨
}
  • 객체로 반환시 json 형식으로 반환(deflaut)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@GetMapping("hello-api")
@ResponseBody
public Hello helloApi(@RequestParam("name") String name) {
Hello hello = new Hello();
hello.setName(name);
return hello; // 객체로 반환시 json 형식으로 반환
}
static class Hello {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
  • @ResponseBody 사용원리

스크린샷 2022-06-06 오후 6.18.50.png

  • HTTP의 BODY에 문자 내용을 직접반환
  • viewResolver 대신에 HttpMessageConverter가 동작
  • 기본 문자처리: StringHttpMessageConverter
  • 기본 객체처리: MappingJackson2HttpMessageConverter
  • byte처리 등등 기타 여러 HttpMessageConverter가 기본으로 등록되어있음

참고로 클라이언트의 HTTP Accept헤더와 서버의 컨트롤러 반환 타입 정보 조합하여 다른 Converter가 선택될수있다.

회원관리 예제 - 백엔드 개발

  • 비즈니스 요구사항 정리
  • 회원 도메인과 리포지토리 만들기
  • 회원 리포지토리 테스트 케이스 작성
  • 회원 서비스 개발
  • 회원 서비스 테스트

비즈니스 요구사항 정리

  • 데이터: 회원ID, 이름
  • 기능: 회원 등록, 조회
  • 아직 데이터 저장소가 선정되지 않음(가상의 시나리오)

스크린샷 2022-06-06 오후 9.08.45.png

  • 클래스 의존관계

스크린샷 2022-06-06 오후 9.09.13.png

테스트 케이스 작성

  • 테스트 케이스 형식 주석깔기
1
2
3
4
5
//given 상황주어지면

//when 이걸로 실행했을때

//then 결과가 이게 나와야함

스프링 빈과 의존관계

  • 이제 서비스를 노출하기위해 컨트롤러랑 뷰 템플러가 필요함
  • 컴포넌트 스캔과 자동 의존관계 설정
  • 자바 코드로 직접 스프링 빈 등록하기

컴포넌트 스캔과 자동 의존관계 설정

  • 회원 컨트롤러가 회원서비스와 회원 리포지토리를 사용할수있게 의존관계 준비하자
  • 이너테이션 되어있지 않다면, 스프링 빈에 등록되어있지 않아 알수가없다.

스크린샷 2022-06-08 오전 11.56.58.png

스프링 빈을 등록하는 방법2가지

  • 컴포넌트 스캔과 자동 의존관계 설정
  • 자바 코드로 직접 스프링 빈 등록하기

컴포넌트 스캔 원리

1
@Controller //컨트롤러로 등록>> Sping이 자신의 컨테이너안에 객체로 생성해서 들고있음
  • @Component 애노테이션이 있으면 스프링 컨테이너에 스프링 빈이 자동 등록된다(생성자 DI 해결가능)
    • @Controller
    • @Service
    • @Repository
  • @Controller 컨트롤러가 스프링 빈으로 자동 등록된 이유도 컴포넌트 스캔 때문이다.
  • @Component 를 포함하는 다음 애노테이션도 객체로 스프링 빈으로 자동 등록된다
  • 컴포넌트 스캔은 main()있는 해당 패키지에서만 스캔함

참고: 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다(유일하게 하나만 등록해서 공유한다) 따라서 같은 스프링 빈이면 모두 같은 인스턴스다. 설정으로 싱글톤이 아니게 설정할 수 있지만, 특별한 경우를 제외하면 대부분 싱글톤을 사용한다.

자바 코드로 스프링 빈 등록하기

  • 기존 애노테이션 모두 제거
  • @Controller 와 그 @Controller안에 @Autowired는 남겨놔야함
  • 나머지는 SpringConfig 파일에 등록
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package hello.hellospring;

import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // config파일로 애너테이션함
public class SpringConfig {

@Bean //빈에 등록
public MemberService memberService(){
return new MemberService(memberRepository());
}

@Bean //빈에 등록
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
}

여기서는 향후 메모리 리포지토리를 다른 리포지토리로 변경할 예정이므로, 컴포넌트 스캔 방식 대신에
자바 코드로 스프링 빈을 설정하겠다.

  • XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않으므로 생략한다.
  • DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다. 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
    • 필드 주입은 변경 할수있는 어떤것이없다
    • setter주입의 단점은 public으로 세터가 열려있어야하므로, 변경될수도잇으므로노출위험
  • 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.
  • @Autowired 를 통한 DI는 helloConroller , memberService 등과 같이 스프링이 관리하는 객체에서만 동작한다.
    스프링 빈에 등록된것만 사용가능
  • 스프링 컨테이너, DI 관련된 자세한 내용은 스프링 핵심 원리 강의에서 설명한다.

Django의 requests가 Controller의 메서드의 Model이라는 타입의 객체를 파라미터로 받는다.!!! (request정보 및 session내장 객체에 정보 포함)
파라미터로 선언만 해주면 스프링이 알아서 만들어준다.

회원 관리 예제 - 웹 MVC개발

  • 회원 웹 기능 - 홈화면 추가
  • 회원 웹 기능 - 등록
  • 회원 웹 기능- 조회

회원 홈화면 추가

  • 컨트롤러추가
1
2
3
4
5
6
7
8
9
10
11
12
13
package hello.hellospring.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

@GetMapping("/")
public String home() {
return "home";
}
}
  • template추가
    • 회원 가입과 회원 목차를 링크로 걸어져있으니, 구현가능
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
<div>
<h1>Hello Spring</h1>
<p>회원 기능</p>
<p>
<a href="/members/new">회원 가입</a> <a href="/members">회원 목록</a>
</p></div>
</div>
</body>
</html>

회원 조회

  • controller
1
2
3
4
5
6
7
@GetMapping("/members")
public String list(Model model){
List<Member> members = memberService.findMembers();
model.addAttribute("members", members);
return "members/memberList";

}
  • template
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
<div>
<table>
<thead>
<tr>
<th>#</th>
<th>이름</th> </tr>
</thead>
<tbody>
<tr th:each="member : ${members}"> # th:each는 반복문으로 템플릿 문법
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
</tr>
</tbody>
</table>
</div>
</div> <!-- /container -->
</body>
</html>

스프링 DB 접근 기술

  • 스프링 데이터 엑세스

  • H2 데이터 베이스를 JDBC 로 연결

  • 스프링 JDBCTemplate sql편하게날림

  • sql을 아예 JPA가 만들어줌

  • 스프링 데이터 JPA

H2 데이터베이스 설치

  • 개발이나 테스트 용도로 가볍고 편리한 DB, 웹화면 제공
  • http://www.h2database.com/html/download-archive.html
  • 1.4.2 버전 다운로드
    권한 주기: chmod 755 h2.sh (윈도우 사용자는 x)
    실행: ./h2.sh (윈도우 사용자는 h2.bat)
    데이터베이스 파일 생성 방법
  • jdbc:h2:~/test (최초 한번)

/test.mv.db 파일 생성 확인
이후부터는 jdbc:h2:tcp://localhost/
/test 이렇게 접속

  • 접속후 member DB만들기
1
2
3
4
5
6
7
drop table if exists member CASCADE;
create table member
(
id bigint generated by default as identity,
name varchar(255),
primary key (id)
);

스크린샷 2022-06-08 오후 4.32.41.png

순수 JDBC

애플리케이션과 DB 연결

  • 설명은 그림 및 가장 중요한것은 무조건 getconnection을 spring을 통해서 하고 close도 마찬가지로 spring을 통해서 하는것이 매우중요
1
2
3
private Connection getConnection() {
return DataSourceUtils.getConnection(dataSource);
}
  • java의 객체 지향이 좋은것은 인터페이스로만 설계하고 인터페이스를 받는 것들만 수정해주면 모두 변경가능..

  • 아래 그림과같이 MemberRepository 인터페이스를 상속받은 jdbc로 빈에 등록하면 나머지 코드 수정할필요없이 동작가능

    스크린샷 2022-06-08 오후 5.28.11.png

  • 개방-폐쇄 원칙(OCP, Open-Closed Principle)
    확장에는 열려있고, 수정, 변경에는 닫혀있다.(인터페이스 다형성을 활용!!!)(매우중요)(구현체를 바꾸면서도 기존코드 수정 거의안함)

  • 스프링의 DI (Dependencies Injection)을 사용하면 기존 코드를 전혀 손대지 않고, 설정만으로 구현 클래스를 변경할 수 있다.
    회원을 등록하고 DB에 결과가 잘 입력되는지 확인하자.
    데이터를 DB에 저장하므로 스프링 서버를 다시 실행해도 데이터가 안전하게 저장된다.

스프링 통합테스트

  • @SpringBootTest : 스프링 컨테이너와 테스트를 함께 실행한다.

  • @Transactional : 테스트 케이스에 이 애노테이션이 있으면, 테스트 시작 전에 트랜잭션을 시작하고, 테스트 완료 후에 항상 롤백한다. 이렇게 하면 DB에 데이터가 남지 않으므로 다음 테스트에 영향을 주지 않는다.(테스트 반복가능)

  • 하지만 보통은 단위 테스트가 훨씬 빠르고 좋은 테스트일 확률이 높음

스프링 JdbcTemplate

  • 스프링 JdbcTemplate과 MyBatis 같은 라이브러리는 JDBC API에서 본 반복 코드를 대부분 제거해준다. 하지만 SQL은 직접 작성해야 한다.

  • 환경설정(build.gradle)

1
2
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'
  • 스프링 부트 데이터베이스 연결 설정 추가(application.properties)
1
2
3
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
  • JdbcTemplateMemberRepository 로 변경

JPA

  • SQL도 JPA가 처리할수있음

  • 객체 중심의 설계로 패러다임을 전환가능 >> 개발 생산성크게높일수있다.

  • build.gradle

1
2
3
4
5
6
7
8
9
10
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
//implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
  • resources/application.properties

    • show-sql : JPA가 생성하는 SQL을 출력한다.
      ddl-auto : JPA는 테이블을 자동으로 생성하는 기능을 제공하는데 none 를 사용하면 해당 기능을 끈다.

    create 를 사용하면 엔티티 정보를 바탕으로 테이블도 직접 생성해준다. 해보자.

1
2
3
4
5
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
  • JPA 엔티티 매핑 : JPA가 관리하는 엔티티(도메인) 이됨, pk 설정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package hello.hellospring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
  • 리포
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package hello.hellospring.repository;

import hello.hellospring.domain.Member;

import javax.persistence.EntityManager;
import javax.swing.*;
import java.util.List;
import java.util.Optional;

public class JpaMemberRepository implements MemberRepository{

private final EntityManager em;

public JpaMemberRepository(EntityManager em){
this.em = em;
}

@Override
public Member save(Member member) {
em.persist(member);
return member;
}

@Override
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}

@Override
public Optional<Member> findByName(String name) {
List<Member> result=em.createQuery("select m from Member m where m.name= :name", Member.class)
.setParameter("name",name)
.getResultList();
return result.stream().findAny();

}

@Override
public List<Member> findAll() {
List<Member> result =em.createQuery("select m from Member m", Member.class)
.getResultList();
return result;
}
}
  • JPA는 Transaction안에서 이뤄져야하므로 서비스에 @Transactional 해주기

  • SpringConfig에서 memberRepository반환값 JPA로 바꿔주기

스프링 데이터 JPA

스프링 부트와 JPA만 사용해도 개발 생산성이 정말 많이 증가하고, 개발해야할 코드도 확연히 줄어듭니다.
여기에 스프링 데이터 JPA를 사용하면, 기존의 한계를 넘어 마치 마법처럼, 리포지토리에 구현 클래스 없이
인터페이스 만으로 개발을 완료할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 스프링 데이터JPA가 모두 제공합니다.

따라서 개발자는 핵심 비즈니스 로직을 개발하는데, 집중할 수 있습니다.
실무에서 관계형 데이터베이스를 사용한다면 스프링 데이터 JPA는 이제 선택이 아니라 필수 입니다.

  • 주의: 스프링 데이터 JPA는 JPA를 편리하게 사용하도록 도와주는 기술입니다. 따라서 JPA를 먼저 학습한
    후에 스프링 데이터 JPA를 학습해야 합니다.

  • 앞전에 JPA 설정 그래돌 사용

  • repo 코드

1
2
3
4
5
6
7
8
9
10
11
package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
@Override
Optional<Member> findByName(String name);
}
  • JpaRepository가 왠만한것 모두 구현해줌,
  • 아래 오버라이드 findByName(String name)만 적어놓으면 인터페이스이름만으로도 완료
  • 인터페이스를 통한 기본적인 CRUD
    findByName() , findByEmail() 처럼 메서드 이름 만으로 조회 기능 제공 페이징 기능 자동 제공

참고: 실무에서는 JPA와 스프링 데이터 JPA를 기본으로 사용하고, 복잡한 동적 쿼리는 Querydsl이라는 라이브러리를 사용하면 된다. Querydsl을 사용하면 쿼리도 자바 코드로 안전하게 작성할 수 있고, 동적 쿼리도 편리하게 작성할 수 있다. 이 조합으로 해결하기 어려운 쿼리는 JPA가 제공하는 네이티브 쿼리를 사용하거나, 앞서 학습한 스프링 JdbcTemplate를 사용하면 된다.

스크린샷 2022-06-08 오후 8.06.20.png

AOP

AOP가 필요한 상황(왜?)

  • 모든 메소드의 호출 시간을 측정하고 싶다면?
  • 공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern)
  • 회원 가입 시간, 회원 조회 시간을 측정하고 싶다면?

기존 시간 측정 일일히 time-time 코드삽입시 문제점

  • 회원가입, 회원 조회에 시간을 측정하는 기능은 핵심 관심 사항이 아니다.
  • 시간을 측정하는 로직은 공통 관심 사항이다.
  • 시간을 측정하는 로직과 핵심 비즈니스의 로직이 섞여서 유지보수가 어렵다.
  • 시간을 측정하는 로직을 별도의 공통 로직으로 만들기 매우 어렵다.
  • 시간을 측정하는 로직을 변경할 때 모든 로직을 찾아가면서 변경해야 한다.

AOP적용

  • AOP: Aspect Oriented Programming
  • 공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern) 분리

스크린샷 2022-06-08 오후 8.26.44.png

AOP 등록

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package hello.hellospring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class TimeTraceAop {
@Around("execution(* hello.hellospring..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("START: " + joinPoint.toString());
try {
return joinPoint.proceed();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("END: " + joinPoint.toString()+ " " + timeMs +
}
}
}

해결

  • 회원가입, 회원 조회등 핵심 관심사항과 시간을 측정하는 공통 관심 사항을 분리한다.

  • 시간을 측정하는 로직을 별도의 공통 로직으로 만들었다.

  • 핵심 관심 사항을 깔끔하게 유지할 수 있다.

  • 변경이 필요하면 이 로직만 변경하면 된다.

  • 원하는 적용 대상을 선택할 수 있다. (보통 패키지 레벨로많이함)

  • 동작방식은 바로 가짜!!를 만든후 프록시!!! 기술 활용

스크린샷 2022-06-08 오후 8.44.39.png

추후학습

지금까지 스프링으로 웹 애플리케이션을 개발하는 방법에 대해서 얇고 넓게 학습했다. 이제부터는 각각의
기술들을 깊이있게 이해해야 한다.
거대한 스프링의 모든 것을 세세하게 알 필요는 없다. 우리는 스프링을 만드는 개발자가 아니다. 스프링을
활용해서 실무에서 발생하는 문제들을 잘 해결하는 것이 훨씬 중요하다. 따라서 핵심 원리를 이해하고,
문제가 발생했을 때, 대략 어디쯤 부터 찾아들어가면 될지, 필요한 부분을 찾아서 사용할 수 있는 능력이 더
중요하다.

스프링 완전 정복 시리즈(준비중)스프링을 완전히 마스터 할 수 있는 다음 시리즈를 준비중이다.
실제 실무에서 사용하는 핵심 스프링 기능 위주로 설명하고 실무에서 사용하지 않거나 오래된 기능은
과감하게 삭제했다. 그리고 실무 노하우 전수를 전수한다.

강의 목록

스프링 핵심 원리
스프링 웹 MVC
스프링 DB 접근 기술
스프링 부트

스프링 부트와 JPA 실무 완전 정복 로드맵최신 실무 기술로 웹 애플리케이션을 만들어보면서 학습하고 싶으면 스프링 부트와 JPA 실무 완전 정복
로드맵을 추천한다. URL: https://www.inflearn.com/roadmaps/149

강의 목록

자바 ORM 표준 JPA 프로그래밍
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
실전! 스프링 데이터 JPA
실전! Querydsl

CATALOG
  1. 1. 스프링 입문
  2. 2. 왜?
  3. 3. 전체 프로젝트 그림
  4. 4. 단축키 정리(인텔리J)
  5. 5. 기본 프로젝트 시작
    1. 5.1. 라이브러리 살펴보기
    2. 5.2. View 환경설정
    3. 5.3. 빌드 실행
    4. 5.4. 정적 파일 접근시 경로
  6. 6. MVC와 템플릿 엔진
  7. 7. API 방식
  8. 8. 회원관리 예제 - 백엔드 개발
    1. 8.1. 비즈니스 요구사항 정리
    2. 8.2. 테스트 케이스 작성
  9. 9. 스프링 빈과 의존관계
    1. 9.1. 컴포넌트 스캔과 자동 의존관계 설정
    2. 9.2. 스프링 빈을 등록하는 방법2가지
      1. 9.2.1. 컴포넌트 스캔 원리
      2. 9.2.2. 자바 코드로 스프링 빈 등록하기
    3. 9.3. 회원 관리 예제 - 웹 MVC개발
      1. 9.3.1. 회원 홈화면 추가
      2. 9.3.2. 회원 조회
  10. 10. 스프링 DB 접근 기술
    1. 10.1. H2 데이터베이스 설치
    2. 10.2. 순수 JDBC
      1. 10.2.1. 스프링 통합테스트
      2. 10.2.2. 스프링 JdbcTemplate
      3. 10.2.3. JPA
      4. 10.2.4. 스프링 데이터 JPA
  11. 11. AOP
    1. 11.1. AOP가 필요한 상황(왜?)
    2. 11.2. 기존 시간 측정 일일히 time-time 코드삽입시 문제점
    3. 11.3. AOP적용
      1. 11.3.1. AOP 등록
      2. 11.3.2. 해결
  12. 12. 추후학습