SPRING 면접 질문

장현진·2022년 11월 7일
0

스프링 Spring

  1. 스프링에서 AOP가 뭔가요?
  • AOP는 관점 지향 프로그래밍의 약자, 기존의 OOP에서 기능별로 클래스를 분리했음에도 불구하고, 공통적으로 반복되는 중복코드가 발생하는데 이를 해결할 수 있도록 실행시 비즈니스 로직의 앞과 뒤에서 원하는 지점에 해당 공통 관심사를 수행할 수 있게 한다.

Spring AOP는 프록시 패턴이을 사용해서 AOP 효과를 낸다.

@Transactional

@Transactional 애노테이션이 붙어있으면 OwnerRepository 타입의 프록시가 새로 만들어지고 Spring AOP에 의해 자동으로 생성되는 OwnerRepository의 프록시에는 @Transactional 애노테이션이 지시하는 코드가 삽입

JDBC에서 트랜잭션 처리를 하려면 SQL 실행문 앞뒤에 setAutoCommit()와 commit()/rollback() 코드가 항상 붙는데 @Transactional 애노테이션은 프록시에 자동으로 그 코드를 넣어서 반복, 중복되는 코드를 생략

그외에도 로깅같은 기능을 정의하여 사용 가능

스프링 DI

객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입 시켜주는 방식이다.
DI(의존성 주입)를 통해서 모듈 간의 결합도가 낮아지고 유연성이 높아진다.

  • IOC컨테이너에서 생성된 B, C객체를 조립(주입)시켜 setter 혹은 생성자를 통해 사용하는 방식

DI종류 - @Autowired 사용

  1. 필드 주입
 @Autowired
    private BService Bservice;
  1. setter 주입
   private BService bService;
 
    @Autowired
    public void setBService(BService bService) {
        this.bService = bService;
    }
  1. 생성자 주입
@Controller
public class BController {
 
	//3. 생성자주입
      private final BService bService;
 
      @Autowired
      public BController(BService bService) {
          this.bService = bService;
      }
}
무엇이 다른가? 
3가지 방식에는 빈을 주입하는 순서가 다르다.

Field Injection은

  1. 주입받으려는 빈의 생성자를 호출하여 빈을 찾거나 빈 팩토리에 등록

  2. 생성자 인자에 사용하는 빈을 찾거나 생성

  3. 필드에 주입

Setter based Injection은

  1. 주입받으려는 빈의 생성자를 호출하여 빈을 찾거나 빈 팩토리에 등록

  2. 생성자 인자에 사용하는 빈을 찾거나 생성

  3. 주입하려는 빈 객체의 수정자를 호출하여 주입

위에 2가지 방식은 런타임에서 의존성을 주입하기 때문에 의존성을 주입하지 않아도 객체가 생성될 수 있다.

Constructor based Injection은
1. 생성자의 인자에 사용되는 빈을 찾거나 빈 팩토리에서 생성

  1. 찾은 인자 빈으로 주입하려는 생성자를 호출



	무엇을 사용하는 것이 좋을까?

스프링에서는 현재 생성자 주입 방식을 권고하고 있다.

  1. 필드에 final 키워드 사용이 가능하다.
    Field Injection시 final 키워드를 사용할 수 없지만,
    Constructor based Injection 은 사용할 때 final 키워드를 사용할 수 있다.
    그러므로 불변(immutable)하게 사용할 수 있다.
  1. 순환 참조를 방지할 수 있다.
    어떤 클래스 A가 B를 참조하고, B가 A를 참조하는 경우를 순환 참조라고 말한다.
    Field Injection, Setter based Injection은 빈이 생성된 후 참조를 하기에 애플리케이션은 아무런 오류나 경고 없이 구동된다. 실제 코드가 호출되기 전까지는 문제를 알 수 없는 것이다.

반면, Constructor based Injection으로 통해 실행해보면 BeanCurrentlyInCreationException이 발생한다.

순환 참조뿐만 아니라 의존 관계에 내용을 외부로 노출시킴으로써 애플리케이션을 실행하는 시점에서 오류를 체크할 수 있다.

Ioc(Inversion of Control)

IOC 는 제어의 역전으로 인스턴스의 생성부터 소멸까지 개발자가 아닌 컨테이너가 대신 관리해주는 것을 말한다.

IOC 컨테이너는 DI를 통해 주입시킨다.

스프링이 모든 의존성 객체를 스프링이 실행될때 다 만들어주고 필요한곳에 주입시켜줌으로써 Bean들은 싱글턴 패턴의 특징을 가지며,
제어의 흐름을 사용자가 컨트롤 하는 것이 아니라 스프링에게 맡겨 작업을 처리

Bean

Spring IoC 컨테이너가 관리하는 자바 객체 - bean

스프링 필터 vs 인터셉터

실행되는 시점에서 차이가 있다. 필터는 dispatcherServlet으로 요청이 가기전에 실행되고 인터셉터는 Controller로 요청이 가기전에 실행된다.
따라서 컨트롤러에 들어가기 전 작업을 처리하기 위해 사용하는 공통점이 있지만, 호출되는 시점에서 차이가 존재한다.
둘다 빈등록은 된다

  • 필터(Filter)의 용도 및 예시
    • 공통된 보안 및 인증/인가 관련 작업
    • 모든 요청에 대한 로깅 또는 감사
    • 이미지/데이터 압축 및 문자열 인코딩
      Spring과 분리되어야 하는 기능
  • 인터셉터(Interceptor)의 용도 및 예시
    • 세부적인 보안 및 인증/인가 공통 작업
    • API 호출에 대한 로깅 또는 감사
    • Controller로 넘겨주는 정보(데이터)의 가공


Entity vs Domain vs DTO

  • DTO(Data Transfer Object)의 핵심 관심사는 이름 그대로 데이터의 전달,
    다른 계층(Layer) 또는 다른 컴포넌트들로 데이터를 넘겨주기 위한 자료구조

  • Domain 사용자가 이용하는 앱의 기능, 회사의 비즈니스 로직을 정의하고 있는 영역

Entity 클래스는 실제 데이터베이스의 테이블과 1:1로 매핑되는 클래스로 DB의 테이블내에 존재하는 컬럼만을 속성으로 가져야한다.


Setter를 무분별하게 사용하면 안되는 이유

엔티티를 작성할 때 Setter를 무분별하게 사용하면 객체(Entity)의 값을 변경할 수 있으므로 객체의 일관성을 보장할 수 없다.
단 setter 한 대상이 다른 곳에서 변경될일이 절대 없다면 사용해도 무방
(명칭의 문제)

인스턴스화 된다라는게 무슨의미인가여

클래스로부터 객체를 만드는 과정을 클래스의 인스턴스화(instantiate)라고 한다.
어떤 클래스로부터 만들어진 객체를 그 클래스의 인스턴스(instance)라고 한다.


String, StringBuffer, StringBuilder 차이점이 무엇인가요

  1. 첫번째 차이점은 String은 불변하다는 특징을 가지고 있어서 수정을 하지못하고 변경시 새로운 String 인스턴스가 생성되고 전에 있던 String은 GC에 의해 사라지게 된다.
    그래서 좋은 성능을 기대하기는 힘들다. (String 불변, StringBuffer, StringBuilder 가변)

  2. StringBuffer/StringBuilder 는 가변성 가지기 때문에 .append() .delete() 등의 API를 이용하여 동일 객체내에서 문자열을 변경하는 것이 가능합니다. 따라서 문자열의 추가,수정,삭제가 빈번하게 발생할 경우라면 String 클래스가 아닌 StringBuffer/StringBuilder를 사용

  1. StringBuffer는 동기화 키워드를 지원하여 멀티쓰레드 환경에서 안전

반대로 StringBuilder는 동기화를 지원하지 않기때문에 멀티쓰레드 환경에서 사용하는 것은 적합하지 않지만 동기화를 고려하지 않는 만큼 단일쓰레드에서의 성능은 StringBuffer 보다 뛰어남


프로세스 vs 스레드

  • 멀티프로세스

    • 여러 개의 자식 프로세스 중 하나에 문제가 발생하면 그 자식 프로세스만 죽고 다른 프로세스에는 영향이 확산되지 않는다.

    • 프로세스는 운영체제의 메모리 자원을 받는 독립적인 객체이며 스레드는 프로세스 내에서 스택 영역을 제외한 코드, 데이터, 힙 영역의 메모리를 공유하는 실행의 단위, 프로세스는 독립적인 객체이기 때문에 스레드처럼 메모리에 직접적으로 접근하는 것은 불가

    • 커널 영역에서 IPC라는 내부 프로세스간 통신을 제공하게 되고, 프로세스는 커널이 제공하는 IPC를 이용해서 데이터를 공유( ipc = 메시지 큐 , 세마포어 , 소켓 , 공유 메모리)

  • 멀티 쓰레드

    • 시스템 자원 소모 감소 (자원의 효율성 증대)
      • 프로세스를 생성하여 자원을 할당하는 시스템 콜이 줄어들어 자원을 효율적으로 관리할 수 있다.
    • 시스템 처리량 증가 (처리 비용 감소)
      • 스레드 간 데이터를 주고 받는 것이 간단해지고 시스템 자원 소모가 줄어들게 된다.
      • 스레드 사이의 작업량이 많아 context switching이 빠르다.
    • 간단한 통신 방법으로 인한 프로그램 응답 시간 단축
      • 스레드는 프로세스 내의 stack 영역을 제외한 모든 메모리를 공유하기 때문에 통신의 부담이 적다.
    • 다른 프로세스에서 스레드를 제어할 수 없다. (즉, 프로세스 밖에서 스레드 각각을 제어할 수 없다.)
    • 멀티 스레드의 경우 자원 공유의 문제가 발생한다. (동기화 문제)
    • 하나의 스레드에 문제가 발생하면 전체 프로세스가 영향을 받는다.

MVC에 대해서 설명해주세요

MVC 패턴은 Model, View, Controller 이 3가지로 나뉘어 역할을 분할하여 처리한다.
역할을 나누어 처리하기 때문에 서로의 결합도가 낮아져서 좋은 코드가 되며 유지보수도 하기 편해진다.

JPA

  • ORM(Object-Relational Mapping)
    우리가 일반 적으로 알고 있는 애플리케이션 Class와 RDB(Relational DataBase)의 테이블을 매핑(연결)한다는 뜻
  • JPA는 Java Persistence API의 약자로, 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
    JPA는 단순히 명세이기 때문에 구현이 없다.
  • Hibernate는 JPA라는 명세의 구현체 = > JPA를 사용하기 위해서 반드시 Hibernate를 사용할 필요가 없다
  • Spring Data JPA는 JPA를 쓰기 편하게 만들어놓은 모듈

JPA 영속성 컨텍스트

영속성 컨텍스트란 엔티티를 영구 저장하는 환경을 의미 한다.

애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할

  • 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태(등록전)
  • 영속(managed): 영속성 컨텍스트에 저장된 상태(등록후)
  • 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태(등록해제후)
    • 영속성 컨텍스트가 관리하던 영속 상태의 엔티티 더이상 관리하지 않으면 준영속 상태가 된다. 특정 엔티티를 준영속 상태로 만드려면 em.datach()를 호출하면 된다.
  • 삭제(removed): 삭제된 상태(디비와 컨텍스트 모두 삭제)

영속성 컨텍스트의 특징

  • 영속성 컨텍스트는 엔티티를 식별자 값으로 구분한다. 따라서 영속 상태는 식별자 값이 반드시 있어야 한다.

  • 영속성 컨텍스트와 데이터베이스 저장
    JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터 베이스에 반영하는데 이를 flush라 한다.

영속성 컨텍스트가 엔티티를 관리하면 다음과 같은 장점이 있다.

  1. 1차 캐시 => 디비 접속 최소화(이미 컨텍스트에 있는 경우 조회 X)

  2. 동일성 보장 => 같은 정보의 데이터의 인스턴스는 동일한 인스턴스

  3. 트랙잭션을 지원하는 쓰기 지연 => 트랜잭션이 끝날때 까지 내부 쿼리 저장소에 모아 뒀다가 한번에 전송

  4. 변경 감지 :트랙잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시가 호출된다.

    • 엔티티와 스냅샷을 비교하여 변경된 엔티티를 찾는다.
    • 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 저장한다.
    • 쓰기 지연 저장소의 SQL을 플러시한다.
    • 데이터베이스 트랜잭션을 커밋한다.
  5. 지연 로딩

    • 프록시를 통해 맵핑된 모든 데이터를 가져오지 않고(1:다 , 다:1) 가짜 객체를 전달한다. 이후 필요한 상황이 생기면 그때 지연로딩 한다.
      (실제로 필요한 시점에 데이터베이스에 쿼리가 나간다.)
    • 실무에서 많이 만나게 되는 문제
      • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 초기화 문제가 발생한다. 즉, 트랜잭션의 범위 밖에서 프록시 객체를 조회하려고 할 때!

N+1 문제

연관 관계에서 발생하는 이슈로 연관 관계가 설정된 엔티티를 조회할 경우에 조회된 데이터 갯수(n) 만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어오게 된다. 이를 N+1 문제라고 한다.

원인
그렇다면 왜 문제가 발생하는 것일까요? JPQL을 실행하면 JPA는 이것을 분석해서 SQL을 생성합니다. JPQL 입장에서는 즉시 로딩, 지연 로딩과 같은 글로벌 패치 전략을 무시하고 JPQL만 사용해서 SQL을 생성합니다.

JPQL 특징이 있습니다.. findById() 같은 경우에는 엔티티를 영속성 컨텍스트에서 먼저 찾고 영속성 컨텍스트에 없는 경우에 데이터베이스에 찾는 반면 JPQL은 항상 데이터베이스에 SQL을 실행해서 결과를 조회합니다. 그리고 아래와 같은 작업을 진행하게 됩니다.

  • JPQL을 호출하면 데이터베이스에 우선 적으로 조회한다.
  • 조회한 값을 영속성 컨텍스트에 저장한다.
  • 영속성 컨텍스트에 조회할 때 이미 존재하는 데이터가 있다면(같은 영속성 컨텍스트에서 이미 조회한 유저가 있는 경우) 데이터를 버린다.

JPQL의 동작 순서는 위와 같고 그렇다면 왜 N+1이 발생하는 것일까요? JPQL에서는 글로벌 패치 전략을 완전히 무시하고 SQL을 생성합니다. findAll()메서드를 호출하게 되면 아래와 같은 SQL이 실행됩니다.

select *from member // findAll()의 SQL
  

자바 코드 와 예시

members = memberRepository.findAll()

즉시 로딩인 경우

JPQL에서 동작한 쿼리를 통해서 members에 데이터가 바인딩 됩니다. 그 이후 JPA에서는 글로벌 패치 전략(즉시 로딩)을 받아들여 해당 member 대해서 추가적인 레이지 로딩으로 N+1을 발생시킵니다.

지연 로딩인 경우

동일하게 members에 데이터가 바인 딩되지만 JPA가 글로벌 패치 전략을 받아들이지만 지연 로딩이기 때문에 추가적인 SQL을 발생시키지 않습니다. 하지만 위에서 본 예제처럼 레이지로 딩으로 추가적인 작업을 진행하게되면 결국 N+1 문제가 발생하게 됩니다.

Join, Fetch Join 차이점

  • 일반 Join

    • Fetch Join과 달리 연관 Entity에 Join을 걸어도 실제 쿼리에서 SELECT 하는 Entity는
      오직 JPQL에서 조회하는 주체가 되는 Entity만 조회하여 영속화
      (조인 대상은 영속화 x , 억지로 하자면 조인 대상도 select 해야함)

    • 조회의 주체가 되는 Entity만 SELECT 해서 영속화하기 때문에 데이터는 필요하지 않지만 연관 Entity가 검색조건에는 필요한 경우에 주로 사용됨

  • Fetch Join

    • 조회의 주체가 되는 Entity 이외에 Fetch Join이 걸린 연관 Entity도 함께 SELECT 하여 모두 영속화
    • Fetch Join이 걸린 Entity 모두 영속화하기 때문에 FetchType이 Lazy인 Entity를 참조하더라도
      이미 영속성 컨텍스트에 들어있기 때문에 따로 쿼리가 실행되지 않은 채로 N+1문제가 해결됨

어떻게 보면 무조건 Fetch Join이 좋아 보이기도 합니다. 하지만 일반 Join이 쓰임새도 분명 있습니다.
JPA는 기본적으로 "DB ↔ 객체" 의 일관성을 잘 고려해서 사용해야 하기 때문에
로직에 꼭 필요한 Entity만을 영속성 컨텍스트에 담아놓고 사용해야 합니다.
그러니 무작정 Fetch Join을 사용해서 전부 영속성 컨텍스트에 올려서 쓰기보다는
일반 Join을 적절히 이용하여 필요한 Entity만 영속성 컨텍스트에 올려서 사용하는 것이 괜한 오작동을 미리 방지할 수 있는 방법

패치조인 실행결과

[
    Team(
        id=1,
        name=team1,
        members=[
            Member(
                id=1,
                name=team1member1,
                age=1
            ),
            Member(
                id=2,
                name=team2member2,
                age=2
            ),
            Member(
                id=3,
                name=team3member3,
                age=3
            )
        ]
    ),
    Team(
        id=2,
        name=team2,
        members=[
            Member(
                id=4,
                name=team2member4,
                age=4
            ),
			Member(
                id=5,
                name=team2member5,
                age=5
            )
        ]
    )
]

https://velog.io/@jinyoungchoi95/JPA-%EB%AA%A8%EB%93%A0-N1-%EB%B0%9C%EC%83%9D-%EC%BC%80%EC%9D%B4%EC%8A%A4%EA%B3%BC-%ED%95%B4%EA%B2%B0%EC%B1%85

QueryDsl을 사용하는 이유

QueryDsl을 사용하면 컴파일 타임에 오류를 잡을 수 있고, 동적 쿼리를 쉽게 작성할 수 있다.
원하는 필드만 뽑아서 DTO로 만드는 기능도 지원한다.

순환참조

순환참조가 발생되면 컴포넌트 간의 명확한 경계가 사라지고 연쇄적으로 변경에 의한 영향이 발생할 수 있다. 이로 인하여 개발과 유지보수 속도에 영향을 끼치고 예상치 못한 문제점을 만들어 낼 가능성도 높다. 이 후에 컴포넌트들을 분리해내도 어려워진다. 컴포넌트를 분리해내기 어렵다면 단기적으로 테스트하기가 어려워질 것이고, 장기적으로 협업하기 어려워지고 DDD나 MSA와 같은 개발 아키텍처를 구성할 수 없게 된다.

=> 해결
DTO 객체를 만들어서 반환하는 것 -> 추천!
나는 처음에 첫 번째 방법을 사용하다가 DTO 객체를 사용하는 방법으로 갈아탔는데, 이 이유는 Entity를 그대로 반환하게 되니까 필요 없는 정보까지 다 나오게 되는 경우가 있었다.

자바 컬렉션 List, set, map에 대한 설명
List : 순서가 있는 데이터의 집합으로 데이터의 중복을 허용한다.
Set : 순서를 유지하지 않는 데이터의 집합으로 데이터의 중복을 허용하지 않는다.
Map : 키, 값으로 이루어진 데이터의 집합으로, 순서는 유지되지 않으며 키의 중복을 허용하지 않으나 값의 중복은 허용한다.
스프링 자세하게 DI에 대한 설명, 생성자 injection이 좋은 이유
필드주입을 사용하게 되면 배터리 일체형 핸드폰과 같다고 볼 수 있다 반면에 setter와 생성자 주입은 분리형으로 볼 수 있어 조금 더 유연하다
테스트 코드에 대한 설명??
단위테스트를 사용하면 좋은점은 개발 초기에 문제를 발견할 수 있다.

ORM에 대한 설명
ORM이란 객체와 DB테이블이 매핑을 이루는 것을 말한다. 즉 객체가 테이블이 되도록 매핑 시켜주는 것을 말함
ORM을 이용하면 SQL Query가 아닌 직관적인 코드(메서드)로서 데이터를 조작할 수 있습니다.

spring security
스프링 기반의 어플리케이션의 보안(인증과 권한)을 담당하는 프레임워크이다.
세션-쿠키 방식으로 인증한다.

jwt
JWT 방식은 확장성에 큰 강점을 가진다. 만약 세션을 사용하는 경우, 서버를 확장할 때마다 각 서버에 세션 정보를 저장하게 된다. 이렇게 될 경우, 특정 서버에서 로그인 인증을 받을 때 다른 서버에서는 로그인을 했는지 알 수 없다는 단점이 있다.
OAuth2
OAuth (OpenID Authentication) 란, 타사의 사이트에 대한 접근 권한을 얻고 그 권한을 이용하여 개발할 수 있도록 도와주는 프레임워크다. 구글, 카카오, 네이버 등과 같은 사이트에서 로그인을 하면 직접 구현한 사이트에서도 로그인 인증을 받을 수 있도록 되는 구조다.
OAuth2 로그인을 사용한다면 UsernamePasswordAuthenticationFilter 대신 OAuth2LoginAuthenticationFilter 가 호출되게 해야한다.
oauth는 세션대신 토큰을 사용하여 인증 진행 (토큰을 또 jwt 토큰으로 바꿔서 사용 많이함)

0개의 댓글