스프링은 자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크입니다. 자바 SE로 된 자바 객체 POJO를 자바 EE에 의존적이지 않게 연결해주는 역할을 합니다. 스프링의 특징으로는 크기와 부하 측면에서 경량 시킨 것과, IOC 기술로 애플리케이션의 느슨한 결합을 도모시킨 것이 있습니다.
좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 자바 언어 기반의 프레임워크입니다. 즉, 객체지향의 핵심인 다형성 만으로는 OCP과 DIP를 지킬 수 없지만 스프링의 IoC, DI를 통해 OCP, DIP를 가능하게 지원합니다.
스프링 부트는 스프링에서 사용하는 프로젝트를 간편하게 셋업할 수 있는 서브 프로젝트입니다. 독립 컨테이너에서 동작할 수 있기 때문에 임베디드 톰켓이 자동으로 실행됩니다.
서블릿 컨테이너에서 HTTP 프로토콜을 통해 들어오는 모든 요청을 제일 앞에서 처리해주는 프론트 컨트롤러를 말함
따라서 서버가 받기 전에, 공통처리 작업을 디스패처 서블릿이 처리해주고 적절한 세부 컨트롤러로 작업을 위임해줍니다.
디스패처 서블릿이 처리하는 url 패턴을 지정해줘야 하는데, 일반적으로는 .mvc와 같은 패턴으로 처리하라고 미리 지정해줍니다.
디스패처 서블릿으로 인해 web.xml이 가진 역할이 상당히 축소되었습니다. 기존에는 모든 서블릿을 url 매핑 활용을 위해 모두 web.xml에 등록해 주었지만, 디스패처 서블릿은 그 전에 모든 요청을 핸들링해주면서 작업을 편리하게 할 수 있도록 도와줍니다. 또한 이 서블릿을 통해 MVC를 사용할 수 있기 때문에 웹 개발 시 큰 장점을 가져다 줍니다.
MVC 패턴은 코드의 재사용에 유용하며, 사용자 인터페이스와 응용 프로그램 개발에 소요되는 시간을 줄여주는 효과적인 설계 방식을 말합니다.
구성요소로는 Model, View, Controller가 있는데요. 모델은 핵심적인 비즈니스 로직을 담당하여 데이터베이스를 관리하는 부분이고, 뷰는 사용자에게 보여주는 화면, 컨트롤러는 모델과 뷰 사이에서 정보 교환을 할 수 있도록 연결시켜주는 역할을 합니다.
모델1은 JSP페이지 안에서 로직 처리를 위해 자바 코드가 함께 사용됩니다. 요청이 오면, 직접 자바빈이나 클래스를 이용해 작업을 처리하고, 이를 클라이언트에 출력해줍니다. 구조가 단순한 장점이 있지만, JSP 내에서 html 코드와 자바 코드가 같이 사용되면서 복잡해지고 유지보수가 어려운 단점이 있습니다.
모델2는 이와는 다르게 모든 처리를 JSP에서만 담당하는 것이 아니라 서블릿을 만들어 역할 분담을 하는 패턴입니다. 요청 결과를 출력해주는 뷰만 JSP가 담당하고, 흐름을 제어해주고 비즈니스 로직에 해당하는 컨트롤러의 역할을 서블릿이 담당하게 됩니다. 이처럼 역할을 분담하면서 유지보수가 용이해지는 장점이 있지만 습득하기 힘들고 구조가 복잡해지는 단점도 있습니다.
우선, 디스패처 서블릿이 클라이언트로부터 요청을 받으면, 이를 요청할 핸들러 이름을 알기 위해 핸들러맵핑에게 물어봅니다.
핸들러맵핑은 요청 url을 보고 핸들러 이름을 디스패처 서블릿에게 알려줍니다. 이때 핸들러를 실행하기 전/후에 처리할 것들을 인터셉터로 만들어 줍니다.
디스패처 서블릿은 해당 핸들러에게 제어권을 넘겨주고, 이 핸들러는 응답에 필요한 서비스를 호출하고 렌더링해야 하는 뷰 이름을 판단하여 디스패처 서블릿에게 전송해줍니다.
디스패처 서블릿은 받은 뷰 이름을 뷰 리졸버에게 전달해 응답에 필요한 뷰를 만들라고 명령합니다.
이때 해당하는 뷰는 디스패처 서블릿에게 받은 모델과 컨트롤러를 활용해 원하는 응답을 생성해서 다시 보내줍니다.
디스패처 서블릿은 뷰로부터 받은 것을 클라이언트에게 응답해줍니다.
실행되는 시점에서 차이가 있다. 필터는 dispatcherServlet으로 요청이 가기전에 실행되고 인터셉터는 Controller로 요청이 가기전에 실행됩니다. 따라서 컨트롤러에 들어가기 전 작업을 처리하기 위해 사용하는 공통점이 있지만, 호출되는 시점에서 차이가 존재합니다.
IOC (제어의역행) : 메소드나 객체의 호출작업을 개발자가 결정하는 것이 아니라 외부에서 결정되는 것을 의미합니다. 개발자는 필요한 부분을 개발해서 '끼워넣기'의 형태로 개발하고 실행됩니다. 최종 호출이 개발자가 아닌 프레임워크 내부에서 결정되는대로 이뤄지게 되는 것을 의미합니다.
DI (의존성 주입) : IOC가 일어날 때 스프링이 내부에 있는 객체들간의 관계를 관리할 때 사용하는 기법입니다. 의존성 주입은 말 그대로 의존적인 객체를 직접 생성하거나 제어하는 것이 아니라, 제어의 역행으로 특정 객체에 필요한 객체를 외부에서 결정해서 연결시키는 것을 의미합니다.
필드 주입, setter 주입, 생성자 주입 3가지가 있습니다.
AOP (관점지향프로그래밍) : AOP는 핵심 비즈니스 로직에 있는 공통 관심사항을 분리하여 각각을 모듈화 하는 것을 의미하며
공통 모듈인 인증, 로깅, 트랜잭션 처리에 용이합니다.
핵심 비즈니스 로직에 부가기능을 하는 모듈이 중복되어 분포되어 있을 경우 사용할 수 있습니다.
AOP의 가장 큰 특징이자 장점은 중복 코드 제거, 재활용성의 극대화, 변화수용의 용이성이 좋다는 점입니다.
PSA : PSA란 Portable Service Abstraction의 줄임말로 환경과 세부 기술의 변화에 관계없이 일관된 방식으로 기술에 접근할 수 있게 해주는 것을 의미합니다. PSA가 적용된 대표적인 곳은 JDBC, JPA, Spring Transaction 등이 있습니다.
스프링 컨테이너가 생성한 객체들을 '빈'이라고 합니다.
빈을 등록하는 방법은 기본적으로 2 가지가 있습니다.
우선 가장 쉬운 방법으로 @Component 어노테이션을 사용하는 것입니다.
@Controller, @Service, @Repository는 모두 @Component를 포함하고 있습니다.
설정 클래스를 따로 만들어 @Configuration 어노테이션을 붙이고,
해당 클래스 안에서 빈으로 등록할 메소드를 만들어 @Bean 어노테이션을 붙여주면 자동으로 해당 타입의 빈 객체가 생성됩니다.
스프링 IoC 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 메소드 호출 → 사용 → 소멸 전 콜백 메소드 호출 → 스프링 종료
스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메소드를 통해 초기화 시점을 알려주며,
스프링 컨테이너가 종료되기 직전에도 소멸 콜백 메소드를 통해 소멸 시점을 알려준다.
초기화 콜백으로는 @PostConstruct,
소멸 전 콜백으로는 @PreDestroy를 사용할 수 있습니다.
객체의 생성과 초기화를 분리하자.
생성자는 파라미터를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가진다.
반면에 초기화는 이렇게 생성된 값들을 활용해서 외부 커넥션을 연결하는 등 무거운 동작을 수행한다.
따라서 생성자 안에서 무거운 초기화 작업을 함께 하는 것보다는 객체를 생성하는 부분과 초기화 하는 부분을 명확하게 나누는 것이 유지보수 관점에서 좋다.
물론, 초기화 작업이 내부 값들만 약간 변경하는 정도로 단순한 경우에는 생성자에서 한번에 처리하는게 나을 수 있다.
singleton (default)
애플리케이션에서 Bean 등록 시 singleton scope로 등록
Spring IoC 컨테이너 당 한 개의 인스턴스만 생성
요청이 들어올 때마다 매번 객체를 생성하지 않고, 이미 만들어진 객체를 공유하기 때문에 효율적인 사용이 가능합니다.
컨테이너가 Bean 가져다 주입할 때 항상 같은 객체 사용
메모리나 성능 최적화에 유리
싱글톤 객체의 프로퍼티는 thread-safe하다고 보장할 수 없다.
싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다. -> 무상태(stateless)로 설계해야 한다.
1. 특정 클라이언트에 의존적인 필드가 있으면 안된다.
2. 특정 클라이언트가 값을 변경할수 있는 필드가 있으면 안된다.
3. 가급적 읽기만 가능해야 한다.
4. 필드 대신에 자바에서 공유되지 않는, 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.
프로토타입 빈은 싱글톤(default bean) 빈과는 달리 컨테이너에게 빈을 요청할 때마다 매번 새로운 객체를 생성하여 반환해줍니다.
이렇게 빈의 scope를 간단하게 관리해줄 수 있는 것이 spring의 장점입니다.
빈의 scope 설정은 @Scope 어노테이션으로 설정하며, 프로토타입 scope로 설정하려면 @Scope(”prototype”)와 같이 문자열로 지정해줍니다.
@Transactional을 메소드 또는 클래스에 명시하면, AOP를 통해 Target 객체를 상속한 Proxy 객체가 생성되며, Proxy 객체의 메소드를 호출하면 Target 메소드 전 후로 트랜잭션 처리를 수행합니다.
@Transactional(readonly=true) : 영속성 컨텍스트에 관리를 받지않아 읽기 전용 쿼리의 성능 최적화
A 메소드는 프록시로 감싸진 메소드가 아니므로 트랜잭션이 적용되지 않은 일반 코드가 수행됩니다.
프록시는 클라이언트가 타겟 객체를 호출하는 과정에만 동작하며, 타겟 객체의 메소드가 자기 자신의 다른 메소드를 호출할 때는 프록시가 동작하지 않습니다.
트랜잭션 전파 수준에 따라 달라지는데, 만약 기본 옵션인 Required를 가져간다면 로컬 트랜잭션 3개가 모두 부모 트랜잭션인 A에 합류하여 수행됩니다.
그래서 부모 트랜잭션이나 로컬 트랜잭션 3개나 모두 같은 트랜잭션이므로 어느 하나의 로직에서 문제가 발생하면 전부 롤백이 됩니다.
컨테이너(Container)는 보통 인스턴스의 생명주기를 관리하며, 프로그래머가 작성한 코드를 스스로 참조한 뒤 알아서 객체의 생성과 소멸을 컨트롤해준다.
Spring 프레임워크는 다른 프레임워크들과 달리 컨테이너 기능을 제공하고 있다. 이와 같은 컨테이너 기능을 제공하는 것이 가능하도록 하는 것이 IoC 패턴이다.
기존에는 설정 정보 클래스에서 필요한 인스턴스들을 직접 생성하고, 직접 의존 관계를 주입해줬어야 했습니다. 스프링 컨테이너가 가진 기능을 사용하면, 예를 들어 컴포넌트 스캔 같은 어노테이션을 활용하면 객체 생성이나 의존 관계 주입 같은 일들을 자동으로 처리할 수 있습니다.
그리고 싱글톤으로 사용되어야 할 클래스 인스턴스들을 자동으로 싱글톤화 합니다.
필드 인젝션은 점차 테스트 코드의 중요성이 부각됨에 따라 필드의 객체를 수정할 수 없는 필드주입은 사용하지 않는것이 좋습니다. 또한 필드 주입은 반드시 DI 프레임워크가 존재해야 하므로 반드시 사용을 지양해야 합니다.
순환 참조 방지
순환 참조는 A -> B를 참조하면서, B -> A를 참조하는 경우 발생하는 문제
생성자 주입은 먼저 빈을 생성하지 않고 주입하려는 빈을 찾는다. 그래서 실행시 바로 순환참조 에러가 뜨면서 찾을 수 있다.
final 선언이 가능
생성자 주입 시, 의존성 주입이 클래스 인스턴스화 중에 시작되므로 final을 선언할 수 있다. 따라서 객체를 변경이 불가능하게 할 수 있다.
테스트 코드 작성 용이
스프링 컨테이너 도움 없이 테스트 코드를 더 편리하게 작성 가능
Target : Aspect를 적용하는 곳 (클래스, 메서드 .. )
Advice : 실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 공통기능을 담은 구현체
Joinpoint : Advice를 적용이 가능한 지점을 의미 (before, after 등등)
Pointcut : Joinpoint의 부분집합으로, 실제로 Advice가 적용되는 Joinpoint를 나타냄
Weaving : Advice를 핵심 로직코드에 적용하는 것
Aspect : 공통기능을 모듈화 한 것.
프록시는 타겟을 감싸서 타겟의 요청을 대신 받아주는 랩핑 오브젝트이다.
프록시의 단어 자체로는 '대리인'이라는 의미를 내포하고 있음. 스프링 AOP에서의 프록시란 말그대로 대리하여 업무를 처리. 함수 호출자는 주요 업무가 아닌 보조 업무를 프록시에게 맡기고, 프록시는 내부적으로 이러한 보조 업무를 처리.
DB에 데이터를 조회하거나 조작하는 기능들을 전담합니다.
Mybatis를 이용할 때는, mapper.xml에 쿼리문을 작성하고 이를 mapper 클래스에서 받아와 DAO에게 넘겨주는 식으로 구현합니다.
DTO란 Data Transfer Object의 약자로, 계층 간 데이터 전송을 위해 도메인 모델 대신 사용되는 객체이다. 이때, 계층이란 Presentation(View, Controller), Business(Service), Persistence(DAO, Repository) 등을 의미한다.
VO는 DTO와 동일한 개념이지만 read only 속성을 갖습니다. VO는 특정한 비즈니스 값을 담는 객체이고, DTO는 Layer간의 통신 용도로 오고가는 객체를 말합니다.
DTO 대신 도메인 모델을 계층간 전송에 사용하면, UI 계층에서 도메인 모델의 메소드를 호출하거나 상태를 변경시킬 수 있다.
도메인 모델을 계층간 전송에 사용하면, 모델과 뷰가 강하게 결합될 수 있다. 뷰의 요구사항 변화로 도메인의 코드를 변경해야할 일이 생기는 것은 좋지 않다.
소스코드에 @어노테이션의 형태로 표현하며 클래스, 필드, 메소드의 선언부에 적용할 수 있는 특정기능이 부여된 표현법을 말합니다.
자바 파일에 어노테이션을 적용해서 개발자가 설정 파일 작업을 할 때 발생시키는 오류를 최소화해주는 역할을 합니다.
어노테이션 사용으로 소스 코드에 메타데이터를 보관할 수 있고, 컴파일 타임의 체크뿐 아니라 어노테이션 API를 사용해 코드 가독성도 높여줍니다.
@Controller : dispatcher-servlet.xml에서 bean 태그로 정의하는 것과 같음.
@RequestMapping : 특정 메소드에서 요청되는 URL과 매칭시키는 어노테이션
@Autowired : 자동으로 의존성 주입하기 위한 어노테이션
@Service : 비즈니스 로직 처리하는 서비스 클래스에 등록
@Repository : DAO에 등록
데이터베이스 테이블과, 자바 객체 사이의 단순한 매핑을 간단한 설정을 통해 처리하는 것
기존의 JDBC에서는 구현하고 싶은 로직마다 필요한 SQL문이 모두 달랐고, 이에 필요한 Connection, PrepareStatement, ResultSet 등을 생성하고 Exception 처리도 모두 해야하는 번거러움이 존재했습니다.
Spring에서는 JDBC와 ORM 프레임워크를 직접 지원하기 때문에 따로 작성하지 않아도 모두 다 처리해주는 장점이 있습니다.
객체 지향 언어인 자바의 관계형 데이터베이스 프로그래밍을 좀 더 쉽게 할 수 있게 도와 주는 개발 프레임워크입니다.
객체, 데이터베이스, Mapper 자체를 독립적으로 작성하고, DTO에 해당하는 부분과 SQL 실행결과를 매핑해서 사용할 수 있도록 지원함
기존에는 DAO에 모두 SQL문이 자바 소스상에 위치했으나, MyBatis를 통해 SQL은 XML 설정 파일로 관리합니다.
설정파일로 분리하면, 수정할 때 설정파일만 건드리면 되므로 유지보수에 매우 좋습니다. 또한 매개변수나 리턴 타입으로 매핑되는 모든 DTO에 관련된 부분도 모두 설정파일에서 작업할 수 있는 장점이 있습니다.
동기식 - 요청과 결과가 동시에 이루어지는 것. 설계가 간단하지만 결과가 주어질 때까지 아무것도 못하고 대기해야 하므로 비동기식 보다 비효율적이다.
비동기식 - 요청과 결과가 동시에 이루어지지 않는 것. 하나의 요청을 처리하는 동안 다른 요청도 처리가능. 동기보다 복잡하고 결과가 주어지는데 시간이 걸리더라도 그동안 다른 작업을 할 수 있으므로 자원을 효율적으로 사용할 수 있음.
테스트 코드를 작성해야 하는 이유
@RequestBody 는 클라이언트가 전송하는 JSON 형태의 HTTP Body 내용을 MessageConverter를 통해 Java Object로 변환시켜주는 역할을 합니다.
값을 주입하지 않고 값을 변환 시키므로(Reflection을 사용해 할당), 변수들의 생성자, Getter,Setter가 없어도 정상적으로 할당된다.
@RequestParam 은 1개의 HTTP 요청 파라미터를 받기 위해 사용합니다. @RequestParam은 필수 여부가 true이기 때문에,
기본적으로 반드시 해당 파라미터가 전송되어야 합니다. 전송되지 않으면 400Error를 유발할 수 있으며,
반드시 필요한 변수가 아니라면 required의 값을 false로 설정해줘야 합니다.
@ModelAttribute 는 HTTP Body 내용과 HTTP 파라미터의 값들을 생성자,Getter,Setter를 통해 주입하기 위해 사용합니다.
값 변환이 아닌 값을 주입시키므로 변수들의 생성자나 Getter,Setter가 없으면 변수들이 저장되지 않는다.
Lombok은 메소드를 컴파일 하는 과정에 개입해서 추가적인 코드를 만들어냅니다. 이것을 어노테이션 프로세싱이라고 하는데,
어노테이션 프로세싱은 자바 컴파일러가 컴파일 단계에서 어노테이션을 분석하고 처리하는 기법을 말합니다.
(Lombok 라이브러리를 추가할 때 CompileOnly, AnnotationProcessor를 추가하는 이유도 된다.)
클라이언트의 요청을 처리하고, 그 결과를 반환하는 Servlet 클래스의 구현 규칙을 지킨 자바 웹 프로그래밍 기술입니다.
Spring MVC에서 Controller로 이용되며, 사용자의 요청을 받아 처리한 후에 결과를 반환합니다.
간단히 - 자바를 사용해 웹을 만들기 위해 필요한 기술
사용자(Client)가 URL을 입력하면 HTTP Request가 Servlet Container로 전송됩니다.
요청 받은 Servlet Container는 HttpServletRequest, HttpServletResponse 객체를 생성합니다.
web.xml을 기반으로 사용자가 요청한 URL이 어느 서블릿에 대한 요청인지 찾습니다.
해당 서블릿에서 service메소드를 호출한 후 GET, POST여부에 따라 doGet() 또는 doPost()를 호출합니다.
doGet() or doPost() 메소드는 동적 페이지를 생성한 후 HttpServletResponse객체에 응답을 보냅니다.
응답이 끝나면 HttpServletRequest, HttpServletResponse 두 객체를 소멸시킵니다.
스케일 업을 통해 하드웨어 스펙을 향상 / 스케일 아웃을 통해 서버를 여러대 추가해 시스템을 증가
Web Server는 클라이언트로부터 HTTP 요청을 받아 정적인 컨텐츠인 HTML, CSS, Image 파일 등을 제공하는 서버를 말합니다. Web Server의 대표적인 예로는 Apache Server, Nginx 등이 존재합니다.
Web Application Server는 DB 연산이나 다양한 로직 처리를 요구하는 동적인 컨텐츠를 제공하기 위해 만들어진 Application Server를 의미합니다. 이는 HTTP를 통해 컴퓨터나 장치에 애플리케이션을 수행해주는 미들웨어입니다. WAS는 Web Container 혹은 Servlet Container라고도 불리며 대표적인 예로는 Tomcat, Jetty 등이 존재합니다.
Web Server와 WAS를 분리하는 이유는 자원 이용의 효율성 및 장애 대응, 배포 및 유지보수의 편의성을 위해서 분리합니다.
Java Reflection이란 컴파일 타임에 클래스나 메서드의 이름을 알지 못해도 런타임에 클래스나 인터페이스, 메서드, 필드 등을 접근할 수 있게 해주는 기능을 의미합니다. Java Reflection을 사용하면 새로운 객체를 만들수도 있고, 메서드를 호출할 수 있으며, 필드 값을 얻거나 설정할 수 있습니다.
Java Reflection이 사용되는 대표적인 예로는 스프링의 Dependency Injection이 존재하며 Jackson 라이브러리의 ObjectMapper 에서도 사용되어집니다.
Servlet이란 특정한 타입의 네트워크 요청에 응답하기 위한 Java EE의 인터페이스 즉, 명세입니다. 보통 구현체로 HttpServlet을 사용하며 Spring MVC에서는 DispatcherServlet을 Front Controller로 사용합니다.
Servlet Container는 설정 파일(web.xml)을 읽고, servlet 클래스를 식별한 다음 ClassLoader를 사용하여 JVM에 로드하고 요청에 알맞는 Servlet을 실행합니다. Servlet Container는 요청이 들어올 때마다 서블릿을 생성하며 하나의 서블릿에는 하나의 쓰레드가 할당됩니다.