지나공 : 지식을 나누는 공간

[직무인터뷰] Spring과 DB 이야기 - 1편 본문

Tech Interview

[직무인터뷰] Spring과 DB 이야기 - 1편

해리리_ 2021. 6. 5. 00:58

대표 질문은 형광펜 표시했습니다.

 

한 질문에 대해 파생된 꼬리질문이 많아서, 구분선을 그렸습니다.

 

객체지향설계의 원칙은?

  • SRP(Single Responsibility Principle) 단일책임 원칙
    한 클래스는 하나의 책임만 가진다. 변경이 있을 때 파급효과가 적으면, 다른 곳에 영향을 덜 미치면 단일책임원칙을 잘 따른것임.
  • OCP(Open/closed principle) 개방폐쇄원칙
    SW 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
    확장을 하려면 당연히 기존 코드를 변경하는 것 같지만!!! 다형성을 떠올리자.
    인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현하잖아?
    • 그런데 문제점이 있다. 구현 객체를 변경하려면 클라이언트 코드를 변경해야 한다는 것이다. 
    •   
    • 그래서 이걸 해결해야 한다. 객체를 생성하고 관계를 맺어주는 별도의 조립, 설정자가 필요하고 이걸 스프링 컨테이너가 한다.
  • LSP(Liskov substitution principle) 리스코프 치환 원칙
    다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것. 자동차 인터페이스의 엑셀은 앞으로 가는 기능인데 이를 뒤로 가게 구현하면 LSP 위반임.
  • ISP(Interface segregation principle) 인터페이스 분리 원칙
    특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다. 분리를 하면 인터페이스가 명확해지고 대체 가능성이 높아진다.
  • DIP(Dependency inversion principle) 의존관계 역전 원칙
    프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다.", "의존 관계를 맺을 때 변화하기 쉬운 것보다 변화하기 어려운 것에 의존하라". 역할에 의존해야지, 구현에 의존하면 안된다. 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있다. 구현체에 의존하게 되면 변경이 아주 어려워 진다. 스프링의 의존성 주입이 이 원칙을 따르는 방법 중 하나다. 
    자동차 역할을 알아야지, 아반떼를 자세히 알 필욘 없다.

DIP를 위반하고 있다. 

위 예시는 DIP를 위반한다. 추상체인 MemberRepository 외에 MemoryMemberRepository도 알고 있기 때문이다.

추상체와 구현체 둘 다한테 의존하고 있는 상태다. MemberRepository한테만 의존해야 한다.


스프링이 왜 나왔나? 객체지향설계 원칙과 연관 지어라.

- 객체 지향의 핵심은 다형성이다.

- 다형성만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경되어 버려서 쉽게 부품을 갈아 끼우듯이 개발할 수 없다. (MemberRepository의 구현체를 memory -> jdbc로 변경할 때 코드가 바뀌어 버리니까)

- 다형성 만으로는 OCP, DIP를 지킬 수 없다.

- 그래서 뭔가 필요하다. 그게 바로 스프링.

 

스프링이 나오기 전에는 하나의 기능을 구현하기 위해 클래스 간 상속, 인터페이스 구현 등 클래스 간의 의존도가 커지면서 작은 것을 변경하더라도 할 일이 많아지는, 배보다 배꼽이 더 큰 상황이 왔었다. 그래서 "간단한 자바 오브젝트로 돌아가자"(마틴 파울러 왈, POJO의 기원)라는 말을 시작으로 기존의 불편함을 해결하고자 두 가지 오픈 소스가 등장한다. 그러면서 스프링이 시작된다.

1. 로드 존슨의 J2EE design and development의 예제 코드 -> 지금의 Spring의 기본코드.

2. 개빈 킹의 하이버네이트 ORM 프레임워크

 

*J2EE란?

자바2 엔터프라이즈 에디션. 자바 기술로 기업 환경의 애플리케이션을 만드는 데 필요한 스펙들을 모은 집합. 가장 큰 특징은 "플랫폼 독립성"이다. JVM이라는 가상 머신을 통해 각각의 OS에 맞게 바이트 코드로 변환되므로, JVM이 탑재된 플랫폼이라면 어떤 플랫폼이든 동일한 자바 코드를 실행시킬 수 있다.

*J2EE의 구성요소 

Servlet :

클라이언트가 보내는 HTTP 요청을 처리하는 서버 측 자바 프로그램

JSP :

html이나 java 코드를 통해 사용자에게 정보를 출력하는 페이지

EJB :

Enterprise Java Beans : 분산 컴포넌트(독립적인 단위모듈) 기술임, 컴포넌트는 하나의 클래스로만으로도 작성되기도 하고 여러 클래스로 작성되기도 한다. 독립적인 모듈이다.

JDBC :

Java Database Connector라고 해서, 여러 종류의 데이터베이스에 접근하는 단일 인터페이스를 제공한다. 각각의 데이터베이스에 맞느 JDBC 드라이버가 있어야 한다.

RMI : 

Remote Method Invocation, 프록시를 써서 원격에 있는 Java 객체의 메소드를 실행시키기 위한 기술

 

이외에도 JNDI, JCA...등이 있다.

 

그래서 스프링 프레임워크가 뭔데?

자바 엔터프라이즈 개발을 위한 오픈소스 어플리케이션 프레임워크이다.

 

[용어정리]

* 프레임워크란?

개발할 때 설계 기본이 되는 뼈대나 구조나 환경으로 상호 협력하는 클래스와 인터페이스의 집합이다.

 

* 음 혹시 프레임워크와 라이브러리 차이도 아는가?

프레임워크와 라이브러리의 차이점은 흐름을 누가 가지고 있냐에 있다. 프레임워크는 전체적인 흐름을 자체적으로 가지고 있어 프로그래머는 그 안에서 필요한 코드를 작성한다. 반면 라이브러리는 프로그래머가 전체적인 흐름을 가지고 있어 라이브러리를 자신이 원하는 기능을 구현하고 싶을 때마다 가져다 사용할 수 있다.


그럼 스프링의 특징이 뭐가 있는지 설명해볼 수 있는가?

  • 경량 컨테이너로서 자바 객체를 직접 관리함. (스프링 컨테이너)
    각각의 객체 생성, 소멸과 같은 라이프사이클을 관리하면서 스프링으로부터 필요한 객체를 얻어올 수 있다.
  • IoC (제어의 역전)
    기존 프로그램은 클라이언트 구현 객체가 스스로 필요한 서버 구현 객체를 생성하고, 연결하고, 실행하면서 스스로 프로그램의 제어 흐름을 조종했다. 하지만 스프링에서는 구성 정보를 가져서 제어의 흐름을 외부에서 관리되도록 한다. 이걸 제어의 역전이라고 표현한다.
  • DI (의존성 주입)
    애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결되는 것을 의존 관계 주입이라고 한다. 이를 통해 클라이언트 코드를 변경하지 않고 클라이언트가 호출하는 대상의 타입 인스턴스를 변경할 수 있다.(개방폐쇄원칙을 지키려면 이 상황에서 구현체가 변경될 때 클라이언트 코드를 변경하지 않는 식으로 영향을 덜 미쳐야 하고, 그러려면 또 의존관계역전 원칙이 같이 지켜져야 한다. DI를 통해 모듈 간 결합도를 낮춰 이런 원칙들을 지킬 수 있다.
  • AOP (관점지향프로그래밍)
    트랜잭션이나 로깅, 보안과 같이 여러 모듈에서 공통적으로 사용하는 기능은 해당 기능을 분리해서 관리할 수 있다.

결론적으로 공통 부분의 소스 코딩이 용이하고 확장성도 높다. 객체 지향적 설계를 가능하게 하니까.

 

IoC와 DI가 헷갈릴 수 있는데, DI는 IoC 모델을 구현하는 방식 중 하나다. IoC는 제어의 역전이라는 좀 범용적인 의미이고, Spring에서 의존관계를 외부에서 주입하니까 이를 좀 더 명확하게 이야기하고자 DI라는 용어가 나왔다.


그럼 각각의 특징으로 말한 것들을 설명해봐라.

AOP가 뭔가?

관점(관심) 지향 프로그래밍으로, 핵심 관심사항과 공통 관심사항을 분리해서 로깅, 트랜잭션, 보안과 같은 인프라나 부가기능을 모듈화하는 것이다. 결국 공통된 기능을 재사용하는 기법이다.

 

장점으로는,

  • 애플리케이션 전체에 흩어진 공통 기능이 하나의 장소에서 관리된다.
  • 다르 서비스 모듈들이 본인의 목적에만 충실하고 다른 건 신경쓰지 않아도 된다.

*용어정리

- 타겟(Target) :

부가 기능을 부여할 대상. 즉 핵심 기능.

- 애스펙트(Aspect) :

객체지향 모듈을 오브젝트라 한다면, 부가기능 모듈은 애스펙트다. 핵심 기능에 부가되어서 의미를 갖는다.

- 어드바이스(Advice) :

실질적으로 부가 기능을 담은 구현체. 어드바이스는 타겟 오브젝트에 종속되지 않으므로 순수하게 부가기능에만 집중할 수 있다. 어드바이스는 애스펙트가 '무엇'을 '언제'할지 정의한다. 

- 포인트컷(PointCut) :

부가 기능이 적용될 대상(메소드)를 선정하는 방법을 말한다. 즉, 어드바이스를 적용할 조인 포인트를 선별하는 기능이 정의되어 있는 모듈이다.

- 조인포인트(JoinPoint) : 

어드바이스가 적용될 수 있는 위치를 말한다. 스프링에서는 메소드 조인포인트만 제공하므로 스프링에서 조인포인트라고 하면 메소드라고 생각하면 된다.

- 프록시(Proxy) : 

타겟을 감싸서 타겟의 요청을 대신 받아주는 Wrapping 오브젝트다. 클라이언트에서 타겟을 호출하면 타겟이 아닌 타겟을 감싼 프록세가 호출되고, 타겟 메소드가 실행되기 전의 선처리, 실행된 후의 후처리를 하도록 구성되어 있다.

- 인트로덕션(Introduction) :

타겟 클래스에서 코드를 변경하지 않고 신규 메소드나 멤버변수를 추가하는 기능

- 위빙(Weaving) :

지정된 객체에 애스팩트를 적용해서, A라는 객체가 실행되기 전 커넥션을 열고 실행이 끝나면 커넥션을 종료하는 기능이 추가된 프록시 객체가 생성되고, 이 프록세 객체가 앞으로 A객체가 호출되는 시점에 사용된다. 여기서 프록시 객체가 생성되는 과정을 위빙이라고 한다.

 

Spring AOP는 프록시를 기반으로 한 런타임 위빙 방식이다. (런타임에 위빙된다.) 종류는 두 가지가 있다.

JDK Dynamic Proxy, CGlib를 통한 프록시가 있는데 전자는 리플렉션, 후자는 상속을 기반으로 이뤄진다.

 

또한 CGlib는 상속을 이용하므로 상속이 불가능한 final/private 키워드가 적용된 메소드는 Aspect가 적용되지 않는다.

 

AOP 동작 원리는? 

구체적인 동작 순서는 @Trasactional 처리 과정을 통해 볼 것이니 일단 이해 안가도 보자!

먼저 도표를 통해 어디에 프록시가 생기는 지 살펴보자.

공통 로직인 시간측정로직을 Aspect로 만들어 분리했다.

Aspect로 만들기 전과 후를 비교해보자.

AOP 적용 전 의존관계
AOP 적용 후 의존관계

공통로직이 쓰이는 타겟 클래스들에 대한 프록시가 생긴다. 프록시는 대리인이다. 

 

* 프록시가 왜 필요한가?

프록시는 보조 업무(공통로직 처리)를 한다. 보조 업무가 필요하면 프록시만 추가하면 되고, 보조 업무의 코드를 변경해야 하면 프록시가 하는 일만 변경하면 된다. 실제 핵심 업무를 수정할 필요가 없어진다는 것이다. 만약 이런 보조 업무를 해 주는 프록시가 없다면 공통 로직이 쓰인 모든 메소드에 중복 코드가 생길 뿐더러, 수정해야할 때 이 모든 공통처리를 각각 전부 수정해야 한다.

 

* 프록시 처리 순서 (동작원리)

1. 프록시 호출

2. 보조 업무 처리

3. 프록시 처리 메소드가 실제 구현 함수를 호출해서 주요 업무를 처리

4. 제어권이 다시 프록시로 넘어오고 나머지 다른 보조 업무를 처리

5. 모든 처리가 끝나면 메소드를 호출한 곳으로 반환됨.

 

@Transactional은 대표적인 AOP의 예시인데, 이걸로 예를 들어 처리 과정을 보자

JDBC에서 트랜잭션 처리를 하려면 SQL 실행문 앞에는 Connection 가져오는 작업 & setAutoCommit() / 뒤에는 commit() or rollback()의 코드가 필요하다. 근데 이건 핵심사항이 아닌 공통 사항이고 이걸 필요한 모든 메소드에 적으면 코드가 중복될 것이다. 그래서 @Transactional은 이런 커밋, 롤백 코드를 자동으로 넣어서 개발자가 작성을 생략할 수 있게 한다.

DB에 쿼리를 수행할 때 필요한 작업들

 

게시글 추가 API가 있다고 가정하자. 해당 서비스의 메소드에는 repository.save(); 하는 코드가 있을 것이고, 이를 수행하는 서비스의 createPost (대충 이런 이름을 가진) 메소드에 @Transactional을 걸었다고 하자.

 

Target = Service 내부에 있는 createPost 메소드

Proxy = 타겟인 createPost에 대한 프록시. createPost를 상속 받아서 만들어진 createPost를 감싸는 프록시 객체

Aspect = 트랜잭션이 해야 할 업무(부가기능)의 모듈. JDBC와의 작업에 필요한 코드들을 가지고 있다.

 

@Transactional 동작 과정

1. Controller에서 Service의 createPost 호출 (게시글 추가 API 실행이라고 가정)

2. @Trasactional을 통해 프록시가 적용되어 있으므로 createPost의 실제 구현체가 아닌 이를 상속받은 프록시 객체가 호출된다.

3. 호출된 createPost의 프록시는 첫 보조 업무인 Connection 가져오기 & setAutoCommit(false) 작업을 수행한다.

(아 물론 직접 프록시 자체에서 open, commit 등의 처리를 하진 않고 트랜잭션 매니저한테 이런 일을 위임해서 처리한다. 쉬운 이해를 위해 이렇게 쓴 것임)

4. 이를 수행한 뒤 실제 createPost를 호출해서 주 업무인 게시글 등록 SQL을 처리한다.

5. 다시 제어권이 프록시로 돌아와서 남은 보조 업무를 처리한다. 메소드의 성공 여부에 따라 commit 또는 rollback를 할 것이다.

6. 처리 작업이 완료되면 createPost메소드를 호출했던 Controller로 반환된다.

 

*참고로 setAutoCommit은 기본값이 true일 때 자동으로 커밋하는 코드다. 트랜잭션을 처리할 때는 false로 해야 한다. true로 해서 자동으로 커밋이 되어 버리면 여러 개의 쿼리문장이 동시에 작업되어야 할때 All or Nothing이 적용되지 않기 때문이다. 하나라도 안되면 전부 rollback되어야 하는데 한 쿼리씩 자동으로 바로 commit되면 안되잖아..!

 

@Transactional이 나온 김에, 더 정리해보자.

 

* @Transactional의 우선순위

클래스 메소드 > 클래스 > 인터페이스 메소드 > 인터페이스

* 주의사항

호출된 곳과 해당 프록시가 같은 빈에 존재하는지 확인해야 한다. 프록시의 외부에서 접근해야 AOP가 적용되기 때문이다. 그래서 @Autowired를 통해 해당 빈을 주입받든가 아님 @Service를 분리해서 다른 서비스에서 다른 서비스의 메소드를 호출하는 방식으로 해야 AOP가 적용된다.

 

* 전파 유형(Propagation)

트랜잭션 내부에서 트랜잭션을 또 호출하면 스프링은 어떻게 처리할까? 

새로운 트랜잭션이 생성될 수도 있고, 이미 실행된 트랜잭션이 있다면 부모 트랜잭션에 합류할 수도 있다. 하튼 이런 상황에서 어떻게 처리할지를 설정하는 것이 트랜잭션의 전파 설정이다.

 

1. 기본 값은 REQUIRED로, 부모 트랜잭션이 존재하면 부모 트랜잭션에 합류하고, 부모 트랜잭션이 없으면 새로운 트랜잭션을 생성한다. 중간에 롤백이 발생하더라도 모두 하나의 트랜잭션이기 때문에 진행 사항이 모두 롤백된다.

2. REQUIRES_NEW는 무조건 새로운 트랜잭션을 생성한다. 각각의 트랜잭션이 롤백되어도 서로 영향을 주지 않는다.

3. MANDATORY는 부모 트랜잭션에 합류하고, 부모 트랜잭션이 없으면 예외를 발생시킨다.

4. NESTED는 부모 트랜잭션이 존재한다면 중첩 트랜잭션을 생성한다. 중첩된 트랜잭션 내부에서 롤백이 발생하면 그 중첩된 트랜잭션의 시작지점 까지만 롤백된다. 중첩 트랜잭션이 커밋 가능하다면 부모 트랜잭션이 커밋될 때 같이 커밋되고, 부모 트랜잭션이 없으면 새로운 트랜잭션을 생성한다.

5. NEVER는 트랜잭션을 생성하지 않는다. 부모 트랜잭션이 존재한다면 예외를 발생시킨다.

6. SUPPORTS는 부모 트랜잭션이 있다면 합류하고, 없다면 자기 트랜잭션도 생성하지 않는다.

7. NOT_SUPPORTED는 부모 트랜잭션이 있다면 보류하고, 진행 중인 부모 트랜잭션이 없으면 트랜잭션을 생성하지 않는다.

 

* Isolation level은.....DB 정리할 때 쓸 예정...ㅎㅎㅎ 더 들어가면 흐름 끊김...ㅠ


AOP가 나와서 말인데,,,, AOP, Filter, Interceptor의 차이가 뭔지 아는가?

일단 세 가지의 등장 배경은, 공통 부분을 빼서 따로 관리하기 위한 것이다. 

Interceptor와 Filter는 Servlet단위에서 실행되고 반면 AOP는 메소드 앞에서 Proxy 패턴의 형태로 실행된다.

 

Interceptor와 Filter는 주소를 보고 구분해서 거르지만 AOP는 주소, 파라미터, 어노테이션 등 다양한 방법으로 타겟을 지정할 수 있다.

요청이 들어오면 Filter를 거쳐 DispatcherServlet을 거쳐 Interceptor를 거쳐 Controller에 도달한다.

거기서 AOP가 호출되어 로직을 수행하고 실제 서비스의 동작을 처리하고 다시 AOP의 남은 보조 업무가 처리되어 Cotroller로 돌아가면, 응답이 나가게 되고, 다시 Interceptor를 거쳐 DispatcherServlet을 거쳐 Filter를 거쳐 응답이 이뤄진다.

이제 각각이 뭔 일을 하나 살펴보자. 이걸 이해하려면 먼저 스프링의 요청, 응답 처리 과정을 알아야 한다.

스프링의 request/response 처리 과정 및 DispatcherServlet에 대해 설명하라.

1. 클라이언트가 URL로 정보를 요청 (Request)

2. DispatcherServlet에게 요청이 도착함

3. DispatcherServlet은 HandlerMapping을 통해 해당 URL과 매핑된 Controller가 있는지 검색

4. Controller에게 처리 요청

5. 요청을 처리한 뒤 결과를 DispatcherServlet한테 전달(Response)

6. 클라이언트로 최종 결과 전송

 

*DispatcherServlet

톰캣과 같은 서블릿 컨테이너를 통해 들어오는 모든 요청을 제일 앞에서 받는 컨트롤러.

- 공통된 작업을 처리한 후에 적절한 세부 컨트롤러로 작업을 위임한다. 

- 각각의 세부 컨트롤러는 처리를 마친 뒤 반환할 view를 디스패처 서블릿한테 넘긴다.

 

*Filter

요청과 DisptacherServlet 사이, 응답과 DispatcherServlet 사이에 존재한다.

DispatcherServlet 영역에 들어가기 전이나 들어갔다 나온 뒤에 요청이나 응답에 대해 변경이나 조작을 한다.

인코딩 변환 처리 등이 예시이다.

 

*Interceptor

DispatcherServlet과 Controller 사이에 존재한다.

디스패처서블릿이 컨트롤러를 호출하기 전과 후에 끼어들어서 로그인이나 권한을 체크하는 등의 일을 한다.

 

*AOP

OOP를 보완하기 나온 개념(구체적으로는 개방폐쇄원칙일 것 같다. 중복이 많으면 모든 곳을 변경해야 하니까)으로, 중복을 줄여 유지보수를 편하게 하기 위해 관점적으로 바라보고 핵심기능과 부가기능을 분리한다. 트랜잭션, 에러 처리 등을 한다. 


스프링 컨테이너와 빈에 대해 설명해봐라

* 스프링컨테이너란?

객체를 관리하는 컨테이너로, 빈의 생성과 의존 관계, 생명 주기 등을 관리한다. 스프링 컨테이너는 @Configuration이 붙은 클래스를 설정 정보로 하고 이 안에 @Bean이라고 적힌 메소드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다. 이때 스프링 IoC 컨테이너에 등록되어 관리되는 객체를 스프링 빈이라고 한다.

 

더 정확히는 스프링 컨테이너를 부를 때 BeanFactory, ApplicationContext로 구분해서 얘기하는데 일반적으로 ApplicationContext를 스프링 컨테이너라고 부른다.

BeanFactory는 스프링 컨테이너의 최상위 인터페이스로 스프링 빈을 관리, 조회한다. ApplicationContext는 BeanFactory기능을 상속 받아서 제공하는데, 둘의 차이를 말하자면... 애플리케이션을 개발할 때 빈은 관리하고 조회하는 기능 외에도 더 많은 부가 기능을 제공한다. 메시지 소스를 통한 국제화 기능 등등...

 

스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하는 동시에 객체 인스턴스(빈)를 싱글톤으로 관리한다.


그럼 싱글톤 패턴이 뭔가? 걔의 문제점과 주의사항은 뭐고, 스프링 컨테이너는 이걸 어떻게 해결하는가?

 

싱글톤 패턴이란, 

클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴으로, 객체 인스턴스를 2개 이상 생성하지 못하도록 막는다. 특히, 요청이 들어올 때마다 객체를 생성하는 게 아니라, 이미 만들어진 객체를 공유해서 효율적으로 사용할 수 있다.

 

하지만 문제점은,

클라이언트가 구현체에 의존하면서 DIP를 위반하게 되고, 이로 인해 OCP 원칙을 위반하게 된다.

 

스프링 컨테이너는 싱글톤 컨테이너로, 싱글톤 패턴을 적용하지 않아도 빈을 싱글톤으로 관리한다. 이렇게 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라고 한다. 스프링 컨테이너의 이런 기능 덕분에 싱글톤 패턴의 단점을 해결하면서 객체를 싱글톤으로 유지할 수 있다.

 

싱글톤 레지스트리가 어떻게 진행이 되냐면,

스프링 컨테이너는 싱글톤 레지스트리이므로 스프링 빈이 싱글톤이 되게 보장하기 위해서 클래스의 바이트코드를 조작하는 라이브러리를 사용한다. 이게 CGLIB라는 라이브러리인데 Code Generator Library의 줄임말로, 런타임에 동적으로 자바 클래스의 프록시를 생성해 준다. 이걸 통해서 같은 빈이 여러 번 등록되지 않도록 싱글톤을 보장한다.

 

만약 동일한 클래스의 객체를 빈으로 등록하는 코드가 두 번 나온다고 하면, 맨 처음은 그냥 바로 빈으로 등록하고, 이미 컨테이너에 등록된 빈이면 그 클래스를 상속받은 임의의 다른 클래스를 만들어서 걔를 빈으로 등록한다. 그러면 나중에 이 빈을 조회할 때 맨 처음 등록됐던 빈의 자식타입인 CGLIB의 클래스로 만들어진 빈이 조회된다. 부모타입으로 조회하면 자식타입의 빈까지 다 조회된다. 복잡하니까 나중에 새 포스트 파겠다...ㅎ @Configuration을 적용해야만 CGLIB 기술이 적용된다. 이게 없이 @Bean만 쓰면 CGLIB 기술이 없어서 싱글톤이 보장되지 않는다.


스프링과 노드의 차이?

Spring MVC는, 

  • 클라이언트 요청 시 각 요청마다 스레드가 생성된다.
  • 각 스레드는 맡은 요청의 응답이 마무리 될 때까지 그 요청을 담당한다.
  • 들어온 요청을 스레드 풀에서 꺼내서 순차적으로 실행한다.
  • 장점
    • 스레드가 여러개라서 많은 요청을 동시에 처리할 수 있다.
  • 단점
    • 너무 많은 요청이 동시에 들어오면 처리할 때 수행 시간이 증가한다.
      • 특히 I/O 작업을 수행할 때 클라이언트가 요청의 응답을 하염없이 기다려야 해서 블로킹 현상이 발생할 수도 있다.

Node.js는,

  • 들어온 요청을 단일 스레드가 처리하는데, 순차적으로 진행하다가 어떤 순서에서 이벤트가 발생하는 요소가 존재하면(특정 라이브러리의 함수 호출이나 다른 function으로 데이터를 전달하는 등) 그 일을 대신해주는 이벤트 루프한테 전달해서 그 일은 이벤트 루프가 따로 수행하도록 한다. 자바처럼 순차적으로 코드를 해석하기는 하지만 이벤트 발생 부분에서는 그 일을 맡은 다른 녀석한테 전달하고 그 이벤트가 끝나든지 말든지 다음 코드를 해석해 버린다. (비동기 처리)

예를 들어, DB에 접속해서 정보를 가져오는 프로그램이 있다면 스프링과 노드는 각각 아래처럼 실행된다.

 

* 동기와 비동기

작업을 수행하는 주체가 두 개 이상이어야 한다.

* 블로킹과 논블로킹

작업의 대상이 두 개 이상이어야 한다.

* 동기

작업을 수행하는 두 개 이상의 주체가 서로 동시에 수행하거나, 동시에 끝나거나, 끝나는 동시에 시작할 때를 말한다. 시작과 종료를 동시에 하거나 하나의 작업이 끝나는 동시에 다른 주체가 작업을 시작한다.

동기

* 비동기

두 주체가 서로의 시작, 종료 시간과는 관계 없이 각자의 시간대로 수행시작과 수행 종료를 하는 것이다. 

비동기

* 블로킹

자신의 작업을 하다가 다른 누군가가 작업하는 것의 시작부터 끝까지 기다렸다가 다시 자신의 작업을 시작하는 것이다.

블로킹

* 논블로킹

다른 주체의 작업과 상관 없이 자신의 작업을 계속 하는 것이다.

논블로킹

* 동기 + 블로킹 조합

동기이면서 블로킹이라면 요청에 대한 응답이 끝날 때까지 기다린 뒤에 새로 시작하는 특성과 작업 중간에 다른 누군가의 작업이 시작하고 끝나는 것을 기다리는 특성이 있는 것이다. 예를 들자면 

 

출처

https://deveric.tistory.com/86

https://velog.io/@sa833591/Spring-Filter-Interceptor-AOP-%EC%B0%A8%EC%9D%B4-yvmv4k96

https://velog.io/@gwontaeyong/Spring-AOP%EC%97%90%EC%84%9C-Proxy%EB%9E%80

https://imiyoungman.tistory.com/9

https://goodncuteman.tistory.com/25

https://jojoldu.tistory.com/71

https://velog.io/@max9106/Spring-AOP%EB%9E%80-93k5zjsm95

728x90
Comments