본문 바로가기
면접 대비

CS 관련 면접 질문 정리 (Spring)

by Mecodata 2023. 2. 5.

1. Spring이란?
- Java 플랫폼을 위한 오픈소스 애플리케이션 프레임워크 중 하나 (자바 기반의 가볍고 유지 보수가 간편한 장점)

 

2. Spring의 특징

- IOC(Inversion Of Control, 제어의 역전) - 개발자가 직접 제어하는 것이 아닌 다른 주체(Spring)에게 제어권을 위임  
- DI(Dependency Injection, 의존성 주입) - 객체를 개발자가 직접 생성하는 것이 아니라 외부(Spring IOC container)에서 생성해서 사용하려는 주체에 객체를 주입시켜주는 방식 (생성자, 필드, setter 주입이 있음)
=> 객체 간의 종속성 감소, 코드 단순화, 코드의 재사용성과 유지보수성을 높여줌

=> 한 클래스를 수정하였을 때, 다른 클래스도 수정할 필요가 없어짐

- AOP(Aspect Oriented Programming, 관점 지향 프로그래밍) = 공통 기능을 분리(Aspect)한 뒤 모듈화하여 지정 시점에 해당 로직을 재사용하는 프로그래밍 방식

- 영속성(Persistence)과 관련된 다양한 서비스를 지원함

영속성(Persistence) = 데이터를 생성한 프로그램의 실행이 종료되더라도 사라지지 않는 데이터의 특성

- 자체적으로 Apache Tomcat과 같은 웹 어플리케이션 서버가 내장되어 있어 자바 웹 어플리케이션을 구동할 수 있음

※ 스프링 빈(Bean) = Spring IOC Container가 관리하는 자바 객체 

 

3. Spring의 작동 과정

  • Client에서 Request 요청
  • 해당 Request를 DispatcherServlet이 받아 HandlerMapping을 사용해 해당 요청을 매핑한 Controller 탐색 후 적합한  Controller에 Request 전달
  • Controller에서 비즈니스 로직 처리 후 결과값(Model)과 결과값을 출력할 View 이름을 DispatcherServlet에 반환
  • DispatcherServletViewResolver로 View 이름을 전달
  • ViewResolver가 해당 이름을 가진 View 탐색 후 DispatcherServlet반환
  • DispatcherServlet이 View에 Model을 전달
  • View가 전달받은 Model를 토대로 렌더링
  • 렌더링 된 View를 DispatcherServlet이 Client로 전달

※ Vue.js나 React.js처럼 사이드 프레임워크로 프론트를 구성하는 경우에는 ViewResolver 사용 없이 DispatcherServletModel만 Client로 전달

Servlet = 웹 통신에서 Client의 요청을 동적으로 처리하고 요청에 대한 응답을 Client에 전송하는 자바 객체

 DispatcherServlet = HTTP로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 전달해주고 그 결과값을 받아서 View에 전달하여 적절한 응답을 생성할 수 있도록 흐름을 제어하는 프론트 컨트롤러

=> 기존에는 모든 요청에 대한 각각의 서블릿을 web.xml에 모두 등록해야 했지만 DispatcherServlet이 모든 요청을 받음으로써 작업을 편리하게 진행할 수 있게 되었음

※ HandlerMapping = DispatcherServlet으로부터 받은 요청을 처리할 컨트롤러를 매핑해주는 객체

ViewResolver = DispatcherServlet으로부터 받은 View 정보를 토대로 실제 작업을 처리할 View를 탐색해주는 역할 (JSP나 서블릿 사용 시)

 

4. MyBatis, JPA 차이

MyBatis 
- SQL Mapper => 객체와 작성한 SQL문의 필드를 매핑하여 데이터를 객체화
- DB에 종속적
- SQL을 직접 작성하여 수행 결과를 객체와 매핑 => 복잡한 쿼리문 작성 가능
- CRUD 메소드를 직접 다 구현해야 함
- 쿼리문을 XML로 분리

 

JPA(Java Persistence API)
- ORM(Object Relational Mapping) => 객체와 DB Table을 매핑하여 객체화
- DB에 종속적 X => DB 데이터 변경시 객체만 변경하면 됨 (객체 중심 프로그래밍 가능)
- 쿼리 메소드를 통해 기본적인 CRUD 메소드 제공
- 다중 조인과 같은 복잡한 쿼리는 어려움
- 코드가 간단함
 

5. Service 인터페이스와 ServiceImpl 클래스 구분 이유

- 인터페이스와 구현체를 분리함으로써 구현체를 독립적으로 두고, 구현체 클래스를 변경하거나 확장해도 이를 사용하는 클라이언트의 코드에 영향을 주지 않도록 하기 위해서

- 이를 통해 객체 간의 결합도를 낮추고 코드의 가독성 및 확장성을 지키며 유지보수가 간편해짐

 

6. MVC 패턴

- 애플리케이션의 구성 요소 Model, View, Controller로 구분한 디자인 패턴

- Model = 데이터 객체 자체 + 데이터 조작 및 처리

- View = 사용자에게 보여지는 화면 (UI)

- Controller = View와 Model 사이의 중개자 역할 (요청에 따라 모델 객체와 뷰를 변화시킴)

- 일종의 식당의 요리사(Model), 매니저(Controller), 웨이터(View)와 비슷 

- 장점 = 비교적 간단한 패턴이라 구조파악과 확장이 용이함, 비즈니스 로직과 UI로직을 분리하여 유지보수를 독립적으로 수행할 수 있음

- 단점 = View와 Model 사이의 의존성이 큼 => 애플리케이션이 커지면 컨트롤러의 코드량이 증가하여 복잡해지기 때문에 유지보수가 어려움

 

7. 빈(Bean) 등록 방법

- xml 파일로 따로 관리

- java로 config 파일을 따로 만들어 @Configuration, @ComponentScan

- @SpringBootApplication (스프링 부트에서만) => 편리하게 자동 등록

 

8. 의존성 주입 (DI, Dependency Injection)

- 객체 내부(개발자)에서가 직접 생성하는 것이 아니라 외부(Spring IOC container)에서 런타임 시점에 생성해서 사용하려는 주체에 주입시켜주는 방식

- 의존성(Dependency) = 코드에서 두 모듈간의 연결 (의존대상 B가 변하면, 그것이 A에 영향을 미치는 것)

생성자 주입

- 가장 권장되는 의존성 주입 방식

- private final + 클래스 생성자의존성 주입

장점

- 순환 참조 방지

- 테스트 용이

- final 선언 가능으로 불변성 보장

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{

     private final TokenProvider tokenProvider;
	 
     @AutoWired // 생성자가 1개이면 @AutoWired 생략 가능
     public SecurityConfig(TokenProvider tokenProvider) {
            this.tokenProvider = tokenProvider;
     }
}

 

필드 주입

- 필드에 @AutoWired 입력해줌으로써 의존성 주입

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{

  @AutoWired
  private TokenProvider tokenProvider;
}

 

setter 주입

- setXX 메소드를 정의하여 의존성 주입

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{

  private TokenProvider tokenProvider;
  
  @AutoWired
  public void setTokenProvider(TokenProvider tokenProvider) {
  	this.tokenProvider = tokenProvider;
  }
  
}

 

 

9. 스레드(Thread) 사용 방법

Thread 클래스 확장

- Thread 클래스(추상 클래스)를 확장한 상태에서 run() 메소드를 오버라이드하여 실행할 작업을 정의

- start()로 스레드 실행

- 단일 상속만 가능

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread is running");
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // 새 스레드 실행
    }
}

 

Runnable 인터페이스 구현

- Runnable 인터페이스를 구현한 상태에서 run() 메소드를 오버라이드하여 실행할 작업을 정의

- Runnable 객체를 Thread 객체에 전달하여 start()로 스레드 실행

- Thread 클래스와 분리되어 있으므로 여러 스레드에서 동일한 작업을 공유(다중 상속)할 수 있어 권장되는 방식

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable thread is running");
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start(); // 새 스레드 실행
    }
}

 

댓글