[기술면접] Spring Boot

1. Why SpringBoot?

스프링부트의 특징

핵심 장점

  1. 의존성 관리

  2. 임베디드 톰캣 (독립 실행 가능한 어플리케이션)
    • 톰캣 위에 war 를 얹지 않아도, 스프링부트는 jar를 읽을 수 있도록 기능을 제공한다.
    • 내장 톰캣도 제공하면서, jar 도 실행가능 하도록 하므로 독립실행이 가능하다.
  3. Auto Configuration (@EnableAutoConfiguration)

    Spring Boot Auto Configuration 은 추가된 jar 의존성을 기반으로 Spring application을 자동적으로 설정하는 것을 시도한다. 예를 들어 HSQLDB 가 클래스패스에 있다면 어떤 데이터베이스 연결 빈을 정의하지 않아도 자동적으로 in-memory 데이터베이스에 접근할 것이다.

    • @SpringBootApplication 에 @EnableAutoConfiguration 이 포함되어 있다.
    • AutoConfiguration 을 담당하는 결국은 @Configuration 기반이다
    • spring.factories 에 명시되어 있는 클래스들에 한해 자동설정을 적용한다.
    • 알아서 조건에 따라 설정 Bean 을 생성하고 적용한다.
  4. 어노테이션 기반 Component Scan (@Component)

    현재 패키지 이하에서, @Component 어노테이션이 붙은 클래스들을 찾아 Bean 으로 등록한다. (제외도 가능)

    • 예를 들어 @Controller 어노테이션도 내부를 까보면 @Component 가 들어있다.

2. Servlet 과 JSP

1. 서블릿 (Servlet)

자바를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 Spec
자바 서블릿은 웹 서버의 성능을 향상하기 위해 사용되는 자바 클래스의 일종이다.
자바로 구현된 CGI 라고 말할 수 있다.

1.1 서블릿의 특징

1.2 서블릿 동작 방식

993A7F335A04179D20

  1. 클라이언트가 HTTP 요청시 이를 Servlet Container (WAS) 로 전송

  2. 이를 전송받은 Servlet Container 는 HttpServletRequest, HttpServletResponse 의 객체들을 생성한다.

  3. web.xml 은 사용자가 요청한 URL을 분석하여 어느 서블릿에 대해 요청을 한 것인지 찾습니다. (한개의 서블릿 당 한개의 컨트롤러 사상)

  4. 해당 서블릿 스레드 (객체 아님) 에서 service 메소드를 호출한 후 요청의 POST, GET 여부에 따라 doGet() 또는 doPost()를 호출

  5. doGet() or doPost() 메소드는 동적 페이지를 생성한 후 HttpServletResponse 객체로 응답.

  6. 응답이 끝나면 HttpServletRequest, HttpServletResponse 소멸

2. 서블릿 컨테이너 (aka WAS)

서블릿 컨테이너는 서블릿을 관리하며 네크워크 통신, 서블릿의 생명주기 관리, 스레드 기반의 병렬처리를 대행한다. 가장 대표적인 것으로는 아파치 톰캣(Tomcat)이 있다.

3. WS vs WAS (Apache VS Tomcat)

웹서버 는 클라이언트의 요청을 기다리고 요청에 대한 데이터를 만들어서 응답하는 역할을 한다. 이때 데이터는 정적인 데이터(html, css, 이미지등)로 한정된다. 말그대로 미리 정해진 자원들을 응답해 주는 것이다.

WAS 클라이언트의 요청이 있을 때 내부 프로그램을 통해 결과를 만들어내고 이것을 다시 클라이언트에게 돌려주는 역할을 한다. 이는 WS의 기능 일부와 웹 컨테이너의 결합으로 다양한 기능을 컨테이너에 구현하여 다양한 역할을 수행한다.

예전에는 static file 은 웹서버를 통해 제공하고, 동적인 기능들만 WAS 를 쓰는것이 효율적이라는 말이 있었는데, 톰캣은 5.5부터 Httpd 의 native모듈을 사용해서 스태틱파일을 처리하는 기능을 제공한다.

이 경우 아파치와 톰캣이 같은 모듈을 사용하는 셈이니 성능에서 차이가 날 이유가 없다. 실제 테스트 한 결과를 봐도 톰캣에서 아파치 Native 모듈을 사용하는 것이 순수하게 아파치 Httpd만 사용하는 것과 비교해서 성능이 전혀 떨어지지 않는다.

4. JSP (Java Server Page)

이는 웹 서버 어플리케이션의 발전속에 응답의 표현을 위해 자연스럽게 등장했다.

기존 서블릿 같은 경우 HttpServletResponse 객체에게 응답 html을 주려면 아래와 같이 상당히 귀찮은 작업이 필요했다.

public class HelloServlet extends HttpServlet {

	public void doGet(HttpServletRequest req,
                        HttpServletResponse res)
			throws ServletException,IOException {

    

		res.setContentType("text/html;
                                    charset=UTF-8");

		PrintWriter out = res.getWriter();

		out.println("<HTML>");
		out.println("<BODY>");
		out.println("Hello World!!");
		out.println("</BODY>");
		out.println("</HTML>");
		out.close();

	}

}

그래서 등장한 도구가 JSP (루비의 ERB 같은 느낌이라고 보면 된다)

<% @page import="java.util.Calendar" %> 
<% @page contentType="text/html; charset=UTF-8"%> 
<% String str = String.format("%tF",Calendar.getInstance()); %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html> 
    <head>
        <meta http-equiv="Content-Type" 
        content="text/html; charset=UTF-8"> 
        <title>Insert title here</title> 
    </head> 
    <body> 
        오늘은 <%= str %><br/> 한시간만 참으면 점심.... 
    </body> 
</html>

아무튼 이렇게 기존 방식보다 html 작성이 훨씬 빠르게 되었다.
내부적으로는 JSP 파일은 Tomcat이 Servlet으로 바꾸어서 돌린다고 한다.

3. 컨테이너 그리고 IOC / DI

3.1 스프링 컨테이너 란?

컨테이너는 보통 인스턴스의 생명주기를 관리하며,
생성된 인스턴스들에게 추가적인 기능을 제공하도록 하는 것이다.
컨테이너는 개발자가 작성한 코드의 처리과정을 위임받은 존재이다.
예를들어 서블릿 컨테이너는 서블릿을 생성하고 스레드를 이용해 사용자의 요청을 처리한다.

이와 마찬가지로, 스프링에도 의존성 주입을 이용하여 어플리케이션 컴포넌트들을 관리하는 컨테이너가 있다. 이것이 바로 Spring Container 이다.

3.2 IOC / DI

그리고 컨테이너는 아래와 같은 특징을 가진다

  1. IOC (Inversion Of Control - 제어의 역전)

    개발자는 보통 객체지향 프로그래밍을 하면 New 연산자, 인터페이스 호출, 팩토리 호출방식으로 객체를 생성하고 소멸시킨다.
    IOC란 이렇게 원래 개발자의 의무인 인스턴스의 생성부터 소멸까지의 객체 생명주기 관리를 컨테이너가 해 주는것을 말한다.

  2. DI (Dependency Injection - 의존성 주입)

    IoC를 실제로 구현하는 방법으로서 의존성있는 컴포넌트들 간의 관계를 개발자가 직접 코드로 명시하지 않고 컨테이너인 Spring이 런타임에 찾아서(getBean) 의존성을 주입해 주는 것이다.

    • 인터페이스에 알맞는 Bean 을 주입하는 방법을 많이 사용한다.
    • 생성자 주입이 권장된다 (final 선언으로 immutable / 과한 책임의 가시성)

3.2 스프링 컨테이너의 종류

  1. BeanFactory

    DI의 기본사항을 제공하는 가장 단순한 컨테이너이다.
    BeanFactory 는 빈을 생성하고 분배하는 책임을 지는 클래스
    빈 자체가 필요하게 되기 전까지는 인스턴스화를 하지 않는다 (lazy loading)

  2. ApplicationContext

    빈팩토리와 유사한 기능을 제공하지만 좀 더 많은 기능을 제공한다.

    • 국제화가 지원되는 텍스트 메시지를 관리해 준다.

    • 이미지같은 파일 자원을 로드 할 수 있는 포괄적인 방법을 제공해준다.

    • 리스너로 등록된 빈에게 이벤트 발생을 알려준다.

    • Lazy Loading이 아니라 컨텍스트 초기화 시점에 모든 싱글톤 빈을 미리 로드한다.

4. AOP 와 Proxy

https://minwan1.github.io/2017/10/29/2017-10-29-Spring-AOP-Proxy/

모듈을 쉽게 사용할 수 있도록 해주는 것이라고 보는것이 편함
특정 로직이 수평으로 흐른다면, 수직으로 원하는 흐름을 꽂아주는 방식이다.
핵심로직을 구현한 코드를 컴파일하거나, 컴파일된 클래스를 로딩하거나, 또는 로딩한 클래스의 객체를 생성할 때, Proxy 객체를 통해 호출할 때 AOP가 적용됨

AOP 기본

1. AOP 용어

  1. Advice
    언제 공통 관심 기능을 비즈니스 로직에 적용할 지 정의
    예를들어, ‘메서드를 호출 하기전에 트랜잭션 시작’ 기능을 적용한다는 것을 정의하고 있는 것

  2. Joinpoint
    Advice 를 적용 가능한 지점을 의미.
    메소드 호출, 필드값 변경 등이 Joinpoint에 해당

  3. Pointcut
    Joinpoint의 부분집합으로서 실제로 Advice가 적용되는 Joinpoint를 나타냄
    스프링에서는 정규 표현식이나 AspectJ의 문법을 이용하여 Poincut을 재정의 할 수 있습니다.

  4. Weaving
    Advice를 비즈니스 로직에 적용하는 행위

  5. Aspect
    여러 객체에 공통으로 적용되는 기능
    트랜잭션, 보안 등이 Aspect의 좋은 예

2. Weaving 방식

  1. 컴파일시에 Weaving하기
  2. 클래스 로딩 시에 Weaving하기
  3. 런타임시에 Weaving하기

1,2 번 같은 경우는 AspectJ라이브러리를 추가하여 구현할때 사용됨.
런타임시 위빙같은 경우는 Spring-AOP 에서 사용하는 방식으로, 소스코드나 클래스 자체를 변경하는 것이 아니라 프록시 객체 를 사용하여 모듈을 Weaving 하고 비즈니스 로직을 실행한다.

Proxy 를 이용한 런타임 위빙

스프링AOP

3. Spring-AOP

스프링은 자체적으로 프록시 기반의 런타임 AOP를 지원하고 있다.
따라서 11-2 에서 살짝 설명한 것과 같이,
Spring-AOP는 메서드 호출 Joinpoint 만을 지원함.
만약에 필드값 변경 과 같은 Joinpoint 를 사용하고 싶다면, AspectJ와 같이 다양한 Joinpoint를 지원하는 AOP프레임워크를 사용해야 한다.

또한 스프링은 3가지 방식으로 AOP를 제공한다.

어떠한 방식을 사용하더라도 Proxy를 통한 메서드 호출만 AOP가 적용된다는것을 알아두자

4. 프록시를 생성하는 방식

어쨌든 실행해야 하는 비즈니스 로직이 있는 클래스에 대해 무조건 Bean 객체가 생성되는데, 스프링 컨테이너가 지정한 Bean 객체에 대한 프록시 객체를 생성하고, 원본 Bean 객체 대신에 Proxy 객체를 사용하게 된다. 이때 프록시 객체를 생성하는 방식은 인터페이스 여부에 따라 두가지 방식으로 나뉜다.

  1. JDK Dynamic Proxy
    • 이는 인터페이스 기반으로 프록시 객체를 생성하기 때문에, 인터페이스에 정의되어있지 않은 메서드에는 AOP가 적용되지 않는 점을 주의해야 한다.
  2. CGLIB
    • 인터페이스를 따로 구현하고 있지 않는 클래스는 CGLIB 방식으로 프록시를 생성한다.
      이는 대상 클래스를 상속 받아 프록시를 구현하게 된다. 따라서 클래스가 final 인 경우는 프록시를 생성할 수 없고 private method 같은 경우에는 AOP 의 혜택을 받을 수 없다.

Spring @Transactional 이 적용되지 않는 사례들

스프링의 @Transactional annotation 은 Proxy Mode 와 AspectJ 모드로 동작한다.
일반적으로 많이 사용되는 것은 Proxy Mode 인데, 아무래도 Bean 이 아니라 프록시 객체를 이용하는 방법이다 보니, 의도치 않게 @Transactional 어노테이션이 동작하지 않는 사례들이 있다.

  1. private method에 @Transactional 달았을때

    Service Bean 에서 메서드를 호출하는 것이 아니라, Proxy 객체가 메서드를 호출하게 되므로 당연히 @Transactional 어노테이션이 적용되지 않는다.
    이 부분은 SonarLint 같은 정적 코드분석기도 알려주는 부분이라 발견하기 쉽다.

  2. 메서드 안에서 내부 메서드 call

    self call 에 관한 부분이다.
    내부 클래스에서의 동일 메소드를 호출 할 때에는 Proxy 객체가 아닌 this 를 이용해 메소드를 호출하기 때문이다. 프록시로 특정 메서드를 불러냈어도 그 메서드 흐름 안의 internal method 는 Bean 이 호출한다.
    따라서 프록시 객체에 의해 동작하는 Spring-AOP는 내부 method를 호출하는지 까지는 관심이 없다. 처음 프록시로 불러낸 메서드까지가 Proxy 관할 구역임

    아래와 같은 코드는 무용지물이다.
    프록시는 자신이 호출한 메서드 그 하나만 인식하는 것이다.

    @Transactional 
    public void cancel(String orgFlwNo) {
        cancelTransaction(orgFlwNo);
    }
    
    public void cancelTransaction(String orgFlwNo) {
        TradeRepository.cancelTrade(orgFlwNo);
        userPointRepository.savePoint(CURRENT_USER_ID)
    }
    

    당연히 아래와 같은 코드도 무용지물이다.
    어차피 프록시가 호출하지 않을 메서드이기 때문 (태어날때부터 self call이 예견되어 있다.)

    public void cancel(String orgFlwNo) {
        cancelTransaction(orgFlwNo);
    }
    
    @Transactional 
    public void cancelTransaction(String orgFlwNo) {
        TradeRepository.cancelTrade(orgFlwNo);
        userPointRepository.savePoint(CURRENT_USER_ID)
    }
    

    헷깔릴 수도 있는데, 프록시는 메서드를 불러주는 대리인이다.
    대리인은 말그대로 다른 클래스에서 호출하는 것과 마찬가지다.

    위 코드에서 cancel 메서드는 외부에서 부르게 되는 시나리오의 메서드이고, 아래 cancelTransaction은 cancel 내부에서 호출하고 실행해야 의미가 있는 내부 메서드이다. (위의 예제는 임시로 12.1 의 이유로 public scope로 되어 있음)

    따라서 proxy 입장에서 내부에서 호출해야만 하는 메서드에 아무리 @Transactional 을 달아봐야 외부인인 proxy 는 내부 메서드 cancelTransaction 의 흐름을 가로챌 기회가 없는 것이다.

  3. Try Catch 구문 안에서 사용할때

    @Transactional 어노테이션은 비즈니스 로직 실행 뒤 Exception을 인식하고 처리한다.
    따라서 로직 안에서 try - catch 를 해버리면 @Transactional이 예외를 인식하기도 전에 메서드 내부에서 예외를 catch 하게 되어 예외시 ROLLBACK이 불가능하다.

5. Spring MVC 동작 원리

99D9B34B5C9C5B501C

  1. 웹 어플리케이션이 실행되면 Tomcat(WAS) 에 의해 배포서술자 web.xml 로딩

  2. WAS 는 web.xml 에 등록되어 있는 ContextLoaderListener 생성
    • 자동으로 IoC container (ApplicationContext) 초기화
    • Servlet 을 사용하는 시점에 ServletContext 에 ApplicationContext 등록
    • Servlet 이 종료되는 시점에 ApplicationContext 삭제
  3. 위에서 생성된 ContextLoaderListener는 root-context.xml 을 로딩
    • root-context.xml 은 Service , Repository 와 같은 bean 을 설정함
  4. Root Spring Container 구동
    • ContextLoaderListener 가 root-context.xml 의 설정 기반으로 Root Spring Container 구동
  5. 사용자 요청 받음
    • 최초의 클라이언트 요청이라면 DispatcherServlet 객체 생성
  6. 두번째 Spring Container 구동 (By DispatcherServlet)
    • 두번째 스프링 컨테이너에는 컨트롤러 Bean 들이 들어 있고, 기존 root container 클래스를 상속받는다.
    • DispatcherServlet 객체는 WEB-INF/config 폴더에 있는
      servlet-context.xml` 파일을 로딩하여 두번째 스프링 컨테이너를 구동
    • servlet-context.xml을 보면 어노테이션을 스캔하여 bean 객체로 등록함을 볼 수 있음 (@Controller)
    • DispatcherServlet 이 두번쨰 스프링 컨테이너 를 구동 한다는 사실 기억할 것
  7. DispatcherServlet 의 doService() 호출
    1. doService() 에는 사용자의 요청을 처리하기 위한, Handler 과 Adapter 을 찾아 호출하기 위해 doDispatch() 호출
    2. doDispatch() 에서는 HandlerMapping 을 통해서 요청에 맞는 Controller 의 HandlerMethod 를 찾고, HandlerMethod 를 호출할 수 있는 HandlerAdapter 찾음
    3. HandlerAdapter 가 HandlerMethod 호출
  8. Controller 요청 처리 후, Controller 는 ModelAndView , View 리턴

  9. 리턴받은 View 는 ViewResolver가 먼저 받아 해당 view가 존재하는지 검색

  10. DispatcherServlet은 ViewResolver 로 부터 JSP 등 최종 표시 결과를 받아서 최종 결과를 사용자에게 전송

6. JPA vs MyBatis

6-1) 영속성 (Persistence)

spring-jdbc-layer

6-2) SQL Mapper 과 ORM

Persistence Framework 는 두가지가 있다.

1. SQL Mapper

2. ORM (Object - Relation Mapping)

2-1) JPA 그리고 Hibernate

overall_design

3. ORM의 장점과 단점

장점 :

단점 :

7. JAR vs WAR

class < jar < war < ear

5-1) JAR (Java Application Archive)

5-2) WAR(Web Application aRchive)

7. 외장 톰캣 vs 내장 톰캣

Log Level (강한 로그 순서대로)

1) FATAL : 가장 크리티컬한 에러 2) ERROR : 에러 3) WARN : 에러는 아니지만 주의할 것 4) INFO : 일반 정보 5) DEBUG : 일반 정보를 좀 더 상세히 (옵션을 켜야 보임) 6) TRACE : 에러 시 경로 추적 (스택 트레이스 같은 것)

9. Maven vs Gradle

GRADLE

10. Spring TEST

스프링부트는 처음 빌드 설정파일에 spring-boot-starter-test 넣으면 다 설치됨.
아래와 같은 것들이 포함된다고 한다.

11. JpaRepository save vs saveAll

12. 스프링 배치

스프링배치 어플리케이션의 조건

https://jojoldu.tistory.com/324