관련된 개념들을 조금씩 정리했다.
반복되는 코드는 클래스로 분리해라 -> 한번만 수정하면되니까 유지보수가 편하다
코드가 중복되지 않도록 노력해야 한다
요청을 처리하는 클래스를 분리한다 -> 서비스 클래스
특정 기능을 가져 여러 클래스가 접근하는 코드도 클래스로 분리한다 -> DAO ( Data Access Object )
하위폴더를 적극적으로 사용하는것이 개발과정과 유지보수에 이롭다
get - 검색, 링크 // 데이터가 uri에 있다 ( 노출됨 )
get은 QueryString
을 이용해서 데이터 전송 ( posts?keyword=keywordname
)
쿼리스트링은 보통 테이블의 조건으로 사용 ( endpoint 는 테이블이름으로 사용됨 )
post - submit, 로그인, 회원가입 // 데이터가 바디에 있다
클라이언트가 할수있는 4가지 요청 - SELECT(GET)
, INSERT(POST)
, UPDATE(PUT)
, DELETE(DELETE)
( http1.1 버전 )
폼은 get
과 post
방식만 가능 ( 자바스크립트는 put
, delete
도 가능 )
html은 post
를 이용해서 다른 요청 처리
( 예를들어 user/update
, user/insert
, user/delete
를 이용해 작업 처리 )
( Rest 공부하기 전에 이렇게 했었음 )
스프링의 기본파싱전략 ( MYME TYME ) ->
x-www-form-urlencoded
( key = value 형태 )
폼 태그로 보내는 데이터를 디폴트로key = value
로 받는다
MIME TYPE
이란? - 바디에 넣을 데이터를 설명 -> 브라우저에게 알려줌
get 데이터에는 바디가 없다 -> MIME TYPE
도 없음 -> 서버에서 기본파싱으로 읽음
JSP 의 contentType
은 JSP 의 MIME TYPE
을 설정 ( 디폴트 - text/html )
네트워크에서 주고 받는 데이터는 페이로드( body )에 헤더가 합쳐진것
URL 요청은 웹서버 ( 아파치 ) 에서 처리 -> 정적리소스 - html
( 리소스폴더는 클래스패스로 지정되어 있음, 웹서버가 접근 가능 )
URI 요청은 웹어플리케이션 서버 ( 톰캣 ) 에서 처리
톰캣은 서블릿을 생성하여 요청을 처리
( jsp페이지
-> .java
-> .class( 서블릿 )
)
서버가 초기에 요청을 받으면 스레드 생성 - 요청 처리
-> 스레드 초기화에 시간이 소모되므로 서버 실행시 스레드풀을 생성해야함
- PAGE 영역 - 하나의 JSP 페이지 범위
- REQUEST 영역 - 한번의 요청 - 요청을 처리하는 모든 JSP 페이지를 포함
요청마다 request객체/ response객체 생성
- SESSION 영역 - 하나의 클라이언트와 관련된 모든 요청을 포함
- APPLICATION 영역 - webapp 폴더 범위, 위 3가지 영역을 모두 포함
MVC 패턴에서는 REQUEST / SESSION 기본 객체를 많이 사용한다.
쿠키
브라우저가 도메인 요청을 통해 서버에 연결되면 서버에서는 세션을 생성하고 세션의 ID( Key )를 응답헤더 ( Set-Cookie
)에 담아 브라우저에게 보내게 되는데 이때 보내는 Key가 JSSESSIONID
가 된다.
쿠키는 브라우저의 요청헤더에 담겨서 서버로 가는데 서버에서는 세션저장소의 세션 ID와 쿠키의 세션 ID를 비교 - 같으면 동일한 연결이라 판단한다.
쿠키는 도메인( 오리진 )별로 관리된다 - 접속한 도메인에서만 쿠키를 받고 보낸다.
기본적으로 방문에 대한 기록을 남기는 쿠키도 있겠지만 로그인상태에 관한 쿠키도 존재한다.
쿠키를 사용할때는 Null인지 확인부터 - NullPointer익셉션이 발생한다.
( 쿠키의 수명은 setMaxAge()
로 설정 )
메모리 쿠키라면 브라우저 종료시 서버에 저장된 세션 id는 소멸
브라우저는 쿠키를 자동저장하게 만들어져 있다. 앱개발자는 쿠키를 저장하는 기능을 만들어야 한다.
세션
세션은 클라이언트의 상태를 저장해서 상태를 저장하지 않는 stateless
한 http를 stateful
로 만들어주는 기술이다.
서버는 응답헤더의 쿠키헤더( Set-cookie
)에 쿠키를 넣어서 리턴
-> JSESSIONID
의 값으로 세션의 ID ( key ) 를 브라우저와 공유
세션저장소에 세션은 ( key:value - HashMap 구조
)로 저장됨
세션저장소에 유저 오브젝트를 저장( value )하고 요청헤더의 쿠키( JSESSIONID
)와 비교해서 로그인상태 확인 - 인증
로그인세션은 로그아웃시 삭제됨 ( invalidate()
)
로그인과 상관없이 브라우저 세션도 저장함 ( 단순 방문 )
세션의 값은 오직 서버에만 존재하기 때문에 보안에 좋다
모든 유저의 세션을 저장하면 서버부하가 커진다 -> 토큰으로 보완
스프링을 알아보기 전에 JSP 모델과 MVC 패턴을 먼저 알아보자
모델 1
모델 2
MVC 패턴 | JSP 모델2 구조 |
---|---|
컨트롤러 | 서블릿 |
모델 | 로직 처리 클래스, 자바빈 |
뷰 | JSP |
컨트롤러
MVC 패턴은 모든 요청을 컨트롤러( 서블릿 )로 보낸다
( 전체 웹 어플리케이션에서 일괄적으로 적용되는 기능은 컨트롤러에서 처리 )
컨트롤러는 입력을 분석, 처리 ( 모델 사용 - 모델의 메소드 호출 ) 하고 흐름을 제어한다
요청을 서비스클래스에 전달하고 결과를 뷰에 전달한다
모델에서 반환된 데이터를 객체에 저장 - 뷰로 포워딩
서블릿은 모델이 어떻게 로직을 처리하는지 알 필요가 없다. 자기 역할만 하면 된다.
컨트롤러가 뷰를 부르는것은 호출이 아닌 통신 - 객체에 데이터 넣고 전달하기 때문에
뷰
사용자에게 보여줄 화면을 만든다 ( jsp 페이지 )
뷰는 화면을 출력하기도 하지만 다시 컨트롤러로 요청을 보내기도 한다. ( form태그 )
새로운 사용자가 생성되면 ( 예를들어 모바일 접속 ) 뷰만 추가하면 된다
모델
모델은 서비스 클래스나 DAO 클래스를 이용해서 비즈니스 로직을 수행
모델은 외부 DB나 서버와 통신을 한다 - 모델의 책임 = 데이터 접근
MVC 패턴의 핵심은 기능 분리 -> 서로 독립적이라 영향을 주지 않는다 -> 유지보수와 확장을 쉽게 한다
컨텍스트
컨텍스트( Context
) - 어떤 객체를 핸들링하기 위한 접근 수단
예를들어 IoC 컨테이너 -> ApplicationConText
라고 표현함
컨텍스트를 가지고 있다는 것은 대상에 대한 모든것을 안다는것이다.
웹 컨텍스트 = 웹 어플리케이션 = 서블릿 컨텍스트 ( 웹어플리케이션 단위로 생성 )
웹 어플리케이션은 정적인컨텐츠와 동적인컨텐츠들의 집합
서버 실행하면 웹 컨텍스트( 웹 애플리케이션 )마다 한 개의 서블릿 컨텍스트 객체를 생성
애플리케이션 전체의 공통 자원이나 정보를 미리 바인딩해서 서블릿들이 공유하여 사용
프로젝트명을 contextPath
라고 한다. ( getContextPath()
로 프로젝트 경로 얻음 )
서블릿
서블릿은 웹페이지를 동적으로 생성하는 서버( 톰캣 ) 측 프로그램이다.
서블릿은 컨트롤러를 사용한다.
서블릿은 단순히 자바에서 동적 페이지를 편하게 만들기위한 소프트웨어 컴포넌트다.
서블릿을 사용하여 개발자가 비즈니스 로직에 집중하게 만든다.
@Controller
같은 어노테이션을 붙여서 클래스로 서블릿의 역할을 할 수 있다.
이러한 흐름을 컨테이너( 톰캣 )가 제어 -> IoC ( 제어의 역전 )
클라이언트의 요청 -> 필터
-> 디스패처 서블릿 ( 위임 ) -> 핸들러매핑 -> 핸들러어댑터
-> 컨트롤러( 페이지컨트롤러 ) -> 서비스 호출( 로직을 처리 ) -> 리포지토리 ( DB 접근 ) -> 데이터를 모델에 저장
-> 컨트롤러 -> 뷰 리턴( jsp )
-> 디스패처 서블릿에서 뷰리졸버 -> 컴포넌트( jsp )
-> 클라이언트에게 응답
디스패처 서블릿은 FrontController
패턴( 단일진입점 )과 RequestDispatcher
( 포워딩 )를 합친것
핸들러 매핑 - 요청URI 를( get/post/put/delete
)을 컨트롤러( 핸들러 )에 매핑해준다
커맨드핸들러(인터페이스) 를 구현한 핸들러에서 로직을 통해 뷰를 리턴
핸들러( 컨트롤러 ) -> 서비스클래스 - 비즈니스 로직 처리를 위임 ( 서비스의 메소드 호출 )
DB 연결이 필요하다면 컨트롤러 -> 서비스 클래스를 거쳐 DB 에 접근하는 리포지토리 - DAO
를 통해 결과를 받아온다 ( JPA
같은 라이브러리가 가 DAO
를 구현해준다 )
서비스 클래스( 로직 처리 )는 MVC 패턴의 모델로 볼 수 있다
로직은 서블릿 안에서 인스턴스 생성하여 처리 ( 다형성을 이용한 구현체 )
컨트롤러가 jsp를 리턴하면 디스패처서블릿이 뷰리졸버를 호출 -> 처리 결과를 보여줄 뷰를 결정
컨트롤러가 오브젝트를 리턴( @ResponseBody
)하면 메세지컨버터 호출
디스패처 서블릿이 요청에 맞는 컨트롤러를 찾고 컨트롤러가 없을 경우에만 정적인 자원을 탐색하게 하는 방식이 유용
web.xml
실행
->ContextLoaderListener
실행
->root-ApplicationContext
실행
->@Service
,@Repository
등을 스캔 -> DB 관련 객체를 생성
->디스패처 서블릿
에 의해서 ->servlet-ApplicationContext
실행
->뷰리졸버
,핸들러매핑
등의 객체 생성
-> 웹과 관련된@Controller
,@RestController
등을 스캔함
//root-ApplicationContext
에 의해서 로드된 객체들은 이후에 생성된 객체들에 접근할 수 없다
생성된 시점이 다르기 때문에 접근못함 !
ContextLoaderListener
가 root-ApplicationContext
실행시켜서
모든객체가 접근할수 있는 객체 생성( DB 관련 객체, 스레드마다 공통적으로 접근 )
위임을 받기 위해서는 먼저 메모리에 떠있어야 한다
디스패처 서블릿이 의존할 컨트롤러는 서버 실행시 디스패처 서블릿이 생성될때 같이 IoC 에 떠야함
디스패처 서블릿의 역할은 컴포넌트 스캔 -> ApplicationContext( IoC )
에 등록
ApplicationContext
는 두종류 root-ApplicationContext
// servlet-ApplicationContext
servlet-ApplicationContext
는 ViewResolver
, Interceptor
, MultipartResolver
등의 객체 생성
( 웹과 관련된 어노테이션 스캔 )
@Bean
은 eager loading / @Lazy 를 붙이면 lazy loading - 필요할때 메모리( IoC )에 로딩
스프링부트로 REST API 구현 + 프론트엔드 분리
스프링부트는 서블릿 엔진( 서블릿 컨테이너 = 톰캣 )을 사용하여 동적 웹 서버 구현을 쉽게 만들어준다
톰캣( 서블릿 기반 서버 )이 이해하는 클래스는 HttpServlet
을 상속한 클래스
톰캣을 이용하려면 javax.servlet.http.HttpServlet
을 상속받는 서브클래스를 작성해야함
아래 처럼 HttpServlet
을 상속한 디스페처 서블릿을 만들 수 있다
( 스프링이 request객체에서 자동 파싱 )
@WebServlet("/")
// 서블릿클래스를 주소와 매핑시킨다
// HttpServlet 상속 -> 웹에서 사용될 서블릿클래스 생성
public class MyServlet extends HttpServlet
@Override
protected void doGet.......
@Override
protected void doPost.......
스프링 부트는 DispatcherServlet
을 이미 구현하고 있음 ( HttpServlet
을 상속 )
스프링에서 구현한다면 아래의 web.xml 을 통해서 DispatcherServlet
을 구현하게 된다.
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
스프링 부트에서는 @SpringBootApplication
을 찾아서 애플리케이션이 실행될때 자동으로 디스패처 서블릿을 등록하게 된다.
Gradle
은 빌드자동화 툴 - 컴파일, 라이브러리 다운, 패키징, 테스팅 - JVM에서 실행되는 언어의 빌드 자동화
스프링의 spring-boot-starter-web
라이브러리가 저장시 자동으로 컴파일 -> 서블릿 생성
어노테이션과 인터페이스를 이용해 스프링이 비즈니스 로직을 이해하도록 구현하면 된다.
application.yml
스프링부트에서 사용하는 설정파일 ( .xml 파일을 대체함 )
스프링에서 정말 중요한 개념
⭐⭐⭐ 스프링 프레임워크의 핵심은 DI ( 의존성 주입 ) ⭐⭐⭐
DI
( 의존성주입 ) -> IoC컨테이너에 존재하는 인스턴스를 클래스가 의존함.
스프링은 IoC 컨테이너의 오브젝트가 필요로 할때 의존하는 오브젝트를 주입해준다 ( @Autuwired - DI
)
DI -> 디자인 패턴, IoC를 구현하는 방법중 하나
DI 는 유닛테스트에 좋다 ( Mock ( 테스트에 사용하는 임시 객체 ) 오브젝트 주입에 유용 )
IoC ( 제어의 역전 )
IoC
- 기존 객체들은 개발자가 관리했지만 스프링에서는 컨테이너가 객체의 생성과, 생명주기를 관리한다 ( 제어의 역전 )
객체 관리의 주체는 프레임워크가 된다.
IoC 컨테이너
( 자바 오브젝트 )에 인스턴스( 자바빈 )를 생성 -> 개발자는 마음대로 생성하지 못하고 IoC컨테이너의 인스턴스를 사용 -> 제어의 역전( IoC )
IoC 컨테이너에 생성된 인스턴스는 하나만 존재 -> 싱글톤
( <-> 프로토타입 - 매번 다른 객체 사용 )
세션객체도 브라우저마다 1개가 존재 -> IoC 컨테이너에 존재
IoC로 싱글톤을 사용하는 이유 - 프로젝트 규모가 커지면 오브젝트 생성시간이 길어진다
@Componet
- 클래스를 자바빈으로 등록하라고 알려주는 어노테이션 - IoC컨테이너에 인스턴스 저장
@SpringBootApplication
이 실행될때 자동적으로 컴포넌스 스캔이 실행되어 @Componet
이 붙은 클래스를 IoC에 등록한다.
@Controller
, @Repository
, @Service
같은 스테레오 타입 어노테이션들은 보다 구체적인 기능의 인스턴스를 생성
( 내부에 @Component
가 달려있다 ) - 마찬가지로 IoC 컨테이너에 생성
@Bean
어노테이션은 @Componet
와 다르게 메소드에 붙여서 메소드가 반환하는 객체를 IoC 에 등록하게 되는데 @Configuragion
과 함께 사용된다.
사용자의 요청( 가입신청, 조회, 글쓰기, 회원정보수정 등..)을 처리 하는 기능 ( 비즈니스 로직 )
DAO 클래스( 스프링의 @Repository
) 이용해서 DB연동 처리 ( MyBatis 이용하면 @Mapper
)
( 일반적으로 DB 와 DAO 는 1:1 매칭 )
@Service
어노테이션을 이용해서 IoC 컨테이너에 인스턴스 생성
서비스 클래스에서 비즈니스 로직을 트랜잭션으로 관리한다 - 하나라도 익셉션 발생시 롤백
DB 에 접근하기 위해 DB 커넥션 객체와 DAO 객체에 접근한뒤 PreparedStatement
로 쿼리를 작성해야 하는데 라이브러리( MyBatis )가 이를 대신해준다
처리의 최소 단위, 예를들면 송금할때 나의 계좌에서 돈이 빠져나가고, 상대의 계좌에 돈이 추가되고, 송금에 대한 history를 기록하는 3가지의 과정을 말함
트랜잭션은 ACID 원칙
을 지켜야하는데
A ( 원자성 ) -> 트랜잭션의 모든 처리가 완료되지 않으면 처음상태로 롤백 하게 되고, 모든 처리가 성공적으로 완료되면 커밋을 한다.
여러 트랜잭션을 처리할때는 서비스 레이어에서 처리한다
컨트롤러와 리포지토리 사이에서 트랜잭션 처리가 필요하면 서비스 레이어를 거쳐야 한다
트랜잭션 중에는 동기화가 이루어진다 -> 컨트롤러에서 트랜잭션을 걸면 안된다 ( 대기 시간 낭비 )
따라서 최대한 DB와 가까운 레이어에서 트랜잭션을 걸어야 한다
서비스 레이어의 존재 이유는 트랜잭션을 줄이기 위함이다
스프링에서는 @Transactional
어노테이션을 붙이면 런타임 익셉션 발생시 강제로 롤백을 해준다
( setAutoCommit(false)
+ commit()
// rollback()
)
필터 또한 서블릿
필터에서 로그인 상태를 확인 -> 로그인 상태에 따른 다른 페이지 제공 ( 동적 페이지, 서버로직 )
필터는 조건에 따라 흐름을 제어한다
필터는 데이터를 변화시킬 수 있다 ( 데이터의 흐름을 변화 시킨다 - 다른 리소스 제공 )
여러개의 필터가 사용될 수 있다 ( 예를들면 로그인 후 구독자 전용 페이지, 회원 등급에 따른 페이지 )
필터 분리 -> 서블릿( 클래스 ) 분리 -> 유지보수가 편하다