공부기록/Study

[자바 웹개발 워크북] 4장 스프링과 스프링 WebMVC

메델 2023. 2. 14. 14:09

1. 의존성 주입

 

의존성

하나의 객체가 자신이 해야 하는 일을 하기 위해서 다른 객체의 도움이 필수적인 관계

 

과거에는 의존성을 해결하기 위해 컨트롤러에서 직접 서비스 객체를 생성하거나 하나의 객체만을 생성해서 활용하는 등의 다양한 패턴을 설계함 → 현재는 스프링 프레임워크 자체에서 지원 

 

스프링 프레임워크: 자체적으로 객체를 생성하고 관리하면서 필요한 곳으로 객체를 주입하는 역할

빈(Bean): 스프링이 관리하는 객체, 스프링의 빈 설정은 XML을 이용하거나 별도의 클래스를 이용하는 자바 설정이 가능

ApplicationContext: 스프링에서 Bean이라고 부르는 객체들을 관리하기 위해 ApplicationContext라는 존재 활용

 

@Autowired 

스프링에서 사용하는 의존성 주입 관련 어노테이션, 만일 해당 타입의 Bean이 존재하면 여기에 주입해 주기를 원한다는 의미

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

 

@Extendwith(SpringExtensions.class)

JUnit5버전에서 'spring-test'를 이용하기 위한 설정, JUnit4에서는 @Runwith 사용

 

@ContextConfiguration

스프링의 설정 정보를 로딩하기 위해서 사용, XML로 설정되어 있는 경우 locations 속성 이용, java 설정을 이용하는 경우 classes 속성 이용

 

<context:component-scan>

스프링을 이용할 때는 클래스 작성 X, 객체 직접 생성 X → 이 역할은 스프링 내부에서 이루어지며 ApplicationContext가 생성된 객체들을 관리 

 

@Controller

MVC의 컨트롤러를 위한 어노테이션, 스프링의 빈으로 처리되기 위해 사용 

 

@Service

서비스 계층의 객체를 위한 어노테이션

 

@Repository

DAO와 같은 객체를 위한 어노테이션

 

@Component

일반 객체나 유틸리티 객체를 위한 어노테이션

 

생성자 주입방식

 

초기 스프링에서는 @Autowired를 멤버 변수에 할당하거나, Setter를 작성하는 방식 많이 이용

→ 스프링 3 이후에는 생성자 주입 방식이라고 부르는 방식을 더 많이 활용, 객체를 생성할 때 문제가 발생하는지를 미리 확인할 수 있기 때문에 다른 방식보다 선호되는 방식

 

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

 

  • 주입 받아야하는 객체의 변수 final로 작성
  • 생성자를 이용해서 해당 변수를 생성자의 파라미터로 지정 

Lombok에서는 생성자 주입을 간단하게 작성 가능

→ @RequiredArgsConstructor를 이용해서 필요한 생성자 함수를 자동으로 작성

 

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

 

인터페이스를 이용해서 나중에 다른 클래스의 객체로 쉽게 변경할 수 있도록 함

→ 인터페이스를 이용하면 실제 객체를 모르고 타입만을 이용해서 코드 작성 가능,

    객체와 객체의 의존 관계의 실제 객체를 몰라도 가능하게 하는 방식 = 느슨한 결합

 

Service에 필요한 DAO 타입의 Bean이 두개가 될 때 스프링은 어떤 것을 주입해야하는지 알 수 없음 

→  두 클래스 중에 하나를 @Primary 지정하거나 @Qualifier를 이용

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

 

 

스프링 빈으로 지정되는 객체들

 

모든 클래스의 객체가 스프링의 Bean으로 처리되는 것 X

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

 

즉,  DTO/VO와 같이 역할보다는 데이터에 중점을 두고 설계된 객체들은 스프링의 빈으로 등록 X

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

 

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

 

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

 

ex) jar 파일에 추가되는 클래스의 객체를 스프링의 빈으로 처리해야할 때 해당 코드가 존재하지 않아 어노테이션을 추가할 수 없는 경우 XML에서 <bean>을 이용해서 처리하고, 직접 작성되는 클래스는 어노테이션 이용

 

 

2. MyBatis와 스프링 연동

 

MyBatis

MyBatis = 'Sql Mapping Framework'

Sql Mapping이란 SQL의 실행 결과를 객체지향으로 매핑해준다는 의미

 

MyBatis 이용시 장점

 

  • 기존의 SQL을 그대로 사용할 수 있음
  • PreparedStatement/ResultSet의 처리 : 기존에 프로그램을 작성해서 하나씩 처리해야 하는 파라미터나  ResultSet의 getXXX()를 MyBatis가 알아서 처리해 주기 때문에 기존에 비해서 많은 양의 코드를 줄일 수 있음
  • Connection/PreparedStatement/ResultSet의 close() 처리: MyBatis와 스프링 연동해서 사용하는 방식을 이용하면 자동으로 close() 처리
  • SQL의 분리: MyBatis를 이용하면 별도의 파일이나 어노테이션 등을 이용해서 SQL을 선언, 파일을 이용하는 경우 SQL을 별도의 파일로 분리해서 운영 가능

 

MyBatis와 스프링의 연동 방식 

 

MyBatis는 단독으로 실행이 가능한 완전한 독립적인 프레임워크이나 스프링 프레임워크는MyBatis와 연동을 쉽게 처리할 수 있는 라이브러리와 API 제공

 

스프링에서 제공하는 라이브러리를 이용하는지 여부에 따라서 아래와 같은 방식 중에 하나로 개발 가능

 

MyBatis를 단독으로 개발하고 스프링에서 DAO를 작성해서 처리하는 방식

: 기존의 DAO에서 SQL의 처리를 MyBatis를 이용하는 구조로써 완전히 MyBatis와 스프링 프레임워크를 독립적인 존재로 바라보고 개발하는 방식

 

MyBatis와 스프링을 연동하고  Mapper 인터페이스만 이용하는 방식

: 스프링과 MyBatis 사이에 'mybatis-spring'이라는 라이브러리를 이용해서 스프링이 DB 전체에 대한 처리를 하고 MyBatis는 일부 기능 개발에 활용하는 방식, 개발 시에는 Mapper 인터페이스라는 방식을 이용해서 인터페이스만으로 모든 개발이 가능한 방식, 개발자가 실제 동작하는 클래스와 객체를 생성하지 않고, 스프링에서 자동으로 생성되는 방식

→ 스프링에서 자동으로 생성된 객체를 이용하기 때문에 개발자가 직접 코드를 수정할 수 없다는 단점 존재, 인터페이스만으로도 개발을 완료할 수 있다는 장점도 존재 

 

XML로 SQL 분리하기

 

MyBatis를 이용할 때 SQL은 @Select와 같은 어노테이션을 이요해서 사용하기도 하지만 대부분 SQL을 별도의 파일로 분리하는 것을 권장

 

XML을 이용하는 이유 →  SQL이 길어지면 어노테이션으로 처리하기 복잡, 어노테이션이 나중에 변경되면 프로젝트 전체를 다시 빌드하는 작업이 필요하기 때문 

 

XML과 매퍼 인터페이스를 같이 결합할 때 다음과 같은 과정으로 작성

 

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

 

3. 스프링 WebMVC 기초

 

스프링 Web MVC의 특징

 

: 스프링 WebMVC는 WebMVC 패턴으로 구현된 구조 

 

스프링 MVC가 기존 구조에 약간의 변화를 주는 부분은 아래와 같음

 

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

 

스프링 MVC의 전체 흐름 

 

 

 

DispatcherServlet과 Front Controller

 

  • 스프링 MVC에서 가장 중요한 사실은 모든 Request는 반드시 DispatcherServlet이라는 존재를 통해서 실행
  • Front-Controller 패턴을 이용하면 모든 요청이 반드시 하나의 객체를 지나서 처리되기 때문에 모든 공통적인 처리를 프론트 컨트롤러에서 처리할 수 있게 됨
  • 스프링 MVC에서는 DispatcherServlet이 Front-Controller의 역할 수행
  • 프론트 컨트롤러가 사전/사후에 대한 처리를 하게 되면 중간에 매번 다른 처리를 하는 부분만 별도로 처리하는 구조를 만든다 →  스프링에서는 이를 controller라고 함 

 

servlet-context.xml의 설정

 

<mvc:annotation-driven> 설정

→ 스프링 MVC 설정을 어노테이션 기반으로 처리한다는 의미와 스프링 MVC의 여러 객체들을 자동으로 스프링의 Bean으로 등록하게 하는 기능

 

<mvc:resources> 설정

→ 이미지나 html 파일과 같이 정적인 파일의 경로 지정

 

InternalResourceViewResolver 클래스로 빈 설정 

→ InternalResourceViewResolver는 스프링 MVC에서 제공하는 뷰를 어떻게 결정하는지에 대한 설정 담당,

 

 

web.xml의 설정

 

<servlet> 설정

→ DispatcherServlet을 등록하는데 DispatcherServlet이 로딩할 때 servlet-context.xml을 이용하도록 설정

 

load-on-startup 설정

→톰캣 로딩 시에 클래스를 미리 로딩하기 위함

 

<servlet-mapping>설정

→ DispatcherServlet이 모든 경로의 요청에 대한 처리를 담당하기 때문에 '/'로 지정

 

스프링 mvc 컨트롤러

 

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

 

@RequestMapping와 파생 어노테이션들

 

@RequestMapping

 

  • 특정한 경로의 요청을 지정하기 위해 사용 
  • 컨트롤러 클래스의 선언부에 사용 가능
  • 컨트롤러의 메서드에도 사용 가능 
  • method라는 속성을 이용해서 GET/POST 방식을 구분해서 처리했지만 스프링 4 이후로는 @GetMapping, @PostMapping이 추가되어 GET/POST 방식을 구분해서 처리할 수 있게 됨

 

파라미터 자동 수집과 변환

 

파라미터 자동 수집 

 

  • DTO나 VO 등을 메소드의 파라미터로 설정하면 자동으로 전달되는 HttpServletRequest의 파라미터들을 수집해 주는 기능 
  • 문자열, 숫자, 배열, 리스트, 첨부 파일도 가능 
  • 동작기준
    • 기본 자료형의 경우 자동으로 형 변환처리 가능
    • 객체 자료형의 경우 setXXX()의 동작을 통해서 처리
    • 객체 자료형의 경우 생성자가 없거나 파라미터가 없는 생성자가 필요(Java Beans)

 

@RequestParam

 

스프링 MVC의 파라미터가 전달되지 않으면 문제가 발생할 수 있음

→ RequestParam 사용, defaultValue 속성을 이용해 기본값을 지정해 줄 수 있음

 

Formatter를 이용한 파라미터의 커스텀 처리

 

HTTP는 문자열로 데이터를 전달하기 때문에 컨트롤러는 문자열을 기준으로 특정한 클래스의 객체를 처리하는 작업 진행 

→ 가장 문제가 되는 타입 : 날짜 관련 타입

  특정한 타입을 처리하는 Formatter 이용, Formatter은 문자열을 포맷을 이용해서 특정한 객체로 변환하는 경우에 사용

 

객체 자료형의 파라미터 수집  

 

객체 자료형을 파라미터로 처리하기 위해서는 객체가 생성되고 setXXX()를 이용해서 처리

Lombok을 이용하는 경우 @Setter/ @Data 이용

 

Model이라는 특별한 파라미터

 

스프링 MVC는 기본적으로 웹 MVC와 동일한 방식이므로 Model을 JSP까지 전달해야함

순수한 서블릿 방식에서는 request.setAttribute()를 이용해서 데이터를 담아 JSP까지 전달했지만 스프링 MVC 방식에서는 Model이라는 객체를 이용해서 처리할 수 있음 

Model에는 addAttribute()를 통해 View에 전달할 '이름'과 '값' 지정 가능 

 

Java Beans와 @ModelAttribute

 

스프링 MVC의 컨트롤러는 파라미터로 getter/setter를 이용하는 JavaBeans의 형식의 사용자 정의 클래스가 파라미터인 경우에는 자동으로 화면까지 객체를 전달 

 

자동으로 생성된 변수명 todoDTO 이름 외에 달느 이름을 사용하고 싶다면 명시적으로 @ModelAttribute() 사용

 

RedirectAttributes와 리다이렉션

 

POST 방식으로 처리하고 Redirect 해서 GET 방식으로 특정한 페이지로 이동하는 PRG 패턴을 처리하기 위해서

→ RedirectAttributes 사용

 

  • addAttribute(키, 값): 리다이렉트할 때 쿼리 스트링이 되는 값을 지정, 데이터를 추가하면 리다이렉트할 URL에 쿼리 스트링으로 추가
  • addFlashAttribute(키, 값): 일회용으로만 데이터를 전달하고 삭제되는 값을 지정, addFlashAttribute()를 이용하면 URL에 보이지 않지만 JSP에서는 일회용으로 사용할 수 있음

 

다양한 리턴 타입

 

컨트롤러 내에 선언한느 메서드의 리턴 타입 다양하게 사용 가능

주요 사용하는 리턴 타입은 void, 문자열, 객체나 배열, 기본 자료형, ResponseEntity

 

void는 컨트롤러의 @RequestMapping 값과 @GetMapping 등 메서드에서 선언된 값을 그대로 뷰의 이름으로 사용 

void는 주로 상황에 관계없이 동일한 화면을 보여주는 경우에 사용 

 

문자열은 상황에 따라서 다른 화면을 보여주는 경우 사용

redirect: 리다이렉션을 이용하는 경우

forward:브라우저의 URL을 고정하고 내부적으로 다른 URL을 처리하는 경우

→ 특별한 경우가 아니라면 'redirect:'만을 이용 

 

스프링 MVC에서 주로 사용하는 어노테이션

 

  • 컨트롤러 선언부에서 사용되는 어노테이션
    • @Controller: 스프링 빈의 처리됨을 명시
    • @RestController: REST 방식의 처리를 위한 컨트롤러임을 명시
    • @RequestMapping: 특정한 URL 패턴에 맞는 컨트롤러인지 명시
  • 메소드 선언부에 사용하는 어노테이션
    • @GetMapping/@PostMapping/@DeleteMapping/@PutMapping.. : HTTP 전송 방식에 따라 해당 메소드를 지정하는 경우에 사용, 일반적으로 @GetMapping, @PostMapping이 주로 사용
    • @RequestMapping: GET/POST 방식 모두 지원하는 경우에 사용
    • @ResponseBody: REST 방식에서 사용
  • 메소드의 파라미터에 사용하는 어노테이션
    • @RequestParam: Request에 있는 특정한 이름의 데이터를 파라미터로 받아서 처리하는 경우에 사용
    • @PathVariable: URL 경로의 일부를 변수로 삼아서 처리하기 위해 사용
    • @ModelAttribute: 해당 파라미터는 반드시 Model에 포함되어서 다시 View로 전달됨을 명시(주로 기본 자료형이나 Wrapper  클래스, 문자열에 사용)
    • 기타: @SessionAttribute, @Valid, @RequestBody 등.. 

 

스프링 MVC의 예외처리 

 

@ControllerAdvice는 컨트롤러에서 발생하는 예외를 처리하는 가장 일반적인 방식 

@ControllerAdvice가 선언된 클래스  역시 스프링 빈으로 처리 

 

@ControllerAdvice의 메서드들에는 @ExceptionHandler 메서드 사용 가능 

→ 이를 이용해서 전달되는 Exception 객체들을 지정하고 메서드의 파라미터에서 이를 이용할 수 있음 

 

(참고: 자바 웹 개발 워크북)