Intro. 스프링부트의 3가지 핵심 개념

개발 99·2025년 3월 21일

스프링이란 무엇인가?

애플리케이션 프레임워크로 애플리케이션의 바탕이 되는 틀과
공통 프로그래밍 모델, 기술 API를 제공한다.

1. 애플리케이션의 기본 틀 - 스프링 컨테이너

스프링 컨테이너(런타임 엔진)는 config를 참고로 해서 애플리케이션을 구성하는 object를 생성 및 관리한다.

스프링 컨테이너는 독립적으로도 가능하지만, 서비스나 서블릿으로 등록해서 사용한다.

Servlet이란?

클라이언트의 요청을 처리하고, 그 결과를 반환하는 역할을 수행하는 요소

  • 특징
  1. 클라이언트의 요청에 대해 동적으로 작동하는 웹 어플리케이션 컴포넌트

  2. Java Thread를 이용하여 동작한다.

  3. MVC 패턴에서 Controller로 이용된다.

  4. HTTP 프로토콜 서비스를 지원하는 java.servlet.http.HttpServlet 클래스를 상속받는다.

  5. html을 사용하여 요청에 응답한다.

  6. html 변경 시 Servlet을 재컴파일 해야한다.

일반적으로 웹서버는 정적인 페이지만 제공을 한다.
그렇기 때문에 동적인 페이지를 제공하기 위해서는 웹 서버는 다른 곳에 요청하여 동적인 페이지를 작성해야 한다.
( Servlet이 동적인 페이지를 생성할 수 있도록 해준다. )

[ 서블릿 동작 방식 ]

  1. 사용자 URL 입력하면 HTTP Request가 Servlet Container로 전송

  2. Servlet Container는 HttpServlet Request/Response 객체 생성

  3. web.xml 기반으로 분석해서 어느 서블릿에 대한 요청인지 찾는다.

  4. 해당 서블릿서 service 메서드를 호출한 후, 클라이언트의 GETP, POST에 따라 doGet(), doPost()를 호출한다.

  5. doGet(), doPost()로 동적 페이지를 생성한 후 HttpServletResponse 객체에 응답을 보낸다.

  6. 응답이 끝나면 HttpServeletRequest/Response 두 객체를 소멸시킨다.

2. 공통 프로그래밍 모델 -IoC/DI, 서비스 추상화, AOP

object가 생성되고 동작하는 방식에 대한 틀을 제공해줄 뿐만 아니라,
코드가 어떻게 작성돼야 하는지 기준도 제시한다.

스프링은 3가지 핵심 프로그래밍 모델을 지원한다.

1. IoC/DI

object 생명주기와 의존관계에 대한 프로그래밍 모델.
(객체 지향 설계 원칙과 디자인 패턴의 핵심을 담고 있음)

1.1 DI(Dependency Injection)

객체를 직접 생성하는 것이 아니라, 외부에서 생성한 후 주입 시키는 방식임.
인터페이스를 사이에 둬서 클래스 레벨에서 의존 관계가 고정되는 것이 아닌, 런타임 시 동적으로 주입.
(모듈 간의 결합도가 낮아지고 유연셩이 높아진다.)

두번째 방식으로 외부에서 생성된 B,C를 주입시켜 A객체를 형성한다.

스프링에서는 객체를 @Bean이라고 부르며, 프로젝트가 실행시
Bean으로 관리하는 객체들의 생성과 소멸에 관련된 작업을 자동적으로 수행해주는데 객체가 생성되는 곳을 Bean Container라고 부른다.

1.2 IoC(Inversion of Control)

제어의 역전을 의미하며,
메서드나 객체의 호출작업을 개발자가 결정하는 것이 아니라,
외부에서 결정되는 것을 의미한다.

객체의 의존성을 역전시켜 객체 간의 결합도를 줄이고 유연한 코드를 작성할 수 있으며,
가독성 및 코드 중복, 유지 보수가 유리하다.

기존

객체 생성 -> 의존성 객체 생성(클래스 내부에서 생성) -> 의존성 객체 메서드 호출

스프링

객체 생성 -> 의존성 객체 주입(제어권을 스프링에 위임하여 스프링이 만든 객체를 주입한다.) -> 의존성 객체 메서드 호출

빈과 빈의 생명주기

스프링의 IoC 컨테이너는 Bean객체들을 책이지고 의존성을 관리한다.
( 객체의 생성~소멸까지 컨테이너가 대신 관리해준다.)

스프링 빈은 객체 생성 -> 의존관계 주입의 과정을 거치는데,
의존관계 주입이 다 끝난 다음에야 필요한 데이터를 사용할 준비가 완료된다.

의존성 주입 과정

  1. Spring Ioc 컨테이너 생성
    (@Component, @Bean으로 빈을 등록가능)


또는 위와 같이 @Configuration을 통해 IoC 컨테이너 안에 Bean 등록을 한다.


의존 관계를 주입하기 전, 객체의 생성이 일어난다.

생성자 주입(객체 생성과 의존 주입 동시) vs Setter 주입(객체 생성 -> 의존 주입)

@Controller public class CocoController{    
	private final CocoService cocoService;
	public CocoController(CocoService cocoService) {        
      this.cocoService = cocoService;    
  }
}

생성자 주입은 객체 생성과 주입이 동시에 발생하므로 컴파일 오류를 잡을 수 있다.(NullPointerException 발생x)

setter 주입은 객체 생성 -> 의존 관계 주입이 나누어져 있어서
의존관계가 없어도 객체를 생성할 수 있다.
(Null 객체를 넣을 수 있다는 것을 의미한다.)

스프링 빈 이벤트 라이프 사이클(초기화/소멸 콜백)

스프링 IoC 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 메소드 호출 → 사용 → 소멸 전 콜백 메소드 호출 → 스프링 종료

그리고 생성자안에서 무거운 초기화 작업을 함께 하는 것보다는
객체를 생성하는 부분과 초기화 하는 부분을 명확하게 나누는 것이 좋다.
( 단순히 내부 값들만 변경하는 경우는 생성자로 한번에 처리하는 게 더 낫다. )

아래와 같은 방식으로 빈 생명주기를 콜백하자

  1. 인터페이스( InitializingBean, DisposableBean )
  2. 설정 정보에 초기화 메소드, 종료 메소드 지정
  3. @PostConstruct, @PreDestroy 어노테이션 지원 

2. 서비스 추상화

환경, 서버, 특정 기술에 종속되지 않고 이식성이 뛰어나며 유연한 애플리케이션을 만들 수 있다.

추상화를 통해서 공통의 로직은 상위 클래스에서 처리하고,
구체적인 로직은 하위 클래스에서 구현할 수 있다.
( 뛰어난 이식성 )

  • Repository 패턴
    JpaRepository를 이용해서 사용자 정의 Repository를 만듦으로써,
    CRUD를 할 수 있다.
public interface UserRepository extends JpaRepository<User, Long> {
    // 사용자 정의 쿼리 메소드를 추가할 수 있습니다.
    Optional<User> findByUsername(String username);
}

  • Service 계층
    비즈니스 로직을 담당하며, Repository와 Controller 계층을 연결한다.
    ( Controller는 내부 로직을 몰라도 된다. )
@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User findUserById(Long id) {
        return userRepository.findById(id).orElseThrow(() -> new UserNotFoundException("User not found"));
    }
}

  • Controller 계층
    서블릿으로 http요청을 처리하고, 응답한다.
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = userService.findUserById(id);
        return new ResponseEntity<>(user, HttpStatus.OK);
    }

}
  • 기타 추상화
  1. 로깅 추상화 (@SLF4J)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Service
public class LogService {

    private static final Logger logger = LoggerFactory.getLogger(LogService.class);

    public void logInfo(String message) {
        logger.info(message);
    }
}
  1. 데이터 소스 추상화
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=root
  1. 캐싱 추상화
    똑같은 것 계속해서 가져올 때 적절하다.
@Service
public class ProductService {

    @Cacheable("products")
    public Product findProductById(Long id) {
        // DB 조회 등의 로직
        return product;
    }
}
  1. Security 추상화
@PreAuthorize("hasRole('ADMIN')") // 권환체크
@GetMapping("/admin")
public String getAdminPage() {

    return "Admin Page";
}

등 ...

3. AOP

AOP는 관점 지향 프로그래밍으로
어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고
그 관점을 기준으로 각각 모듈화를 한다는 것을 의미한다.
( 모듈화란 어떤 공통된 로직, 기능을 하나의 단위로 묶는 것을 의미한다 )

결국 핵심적인 관점은 비즈니스 로직이 되고,
부가적인 관점은 핵심 로직을 실행하기 위한 DB 연결, 로깅, 파일 I/O 등이 있다.

위와 같이 Aspect로 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용을 하겠다는 것이 AOP 취지이다.

애플리케이션에서 산재해서 나타나는 부가적인 기능을
독립적으로 모듈화하는 프로그래밍 모델이다.
( 깔끔한 코드 유지 )


정적 웹 페이지 vs. 동적 웹 페이지

1. 정적 웹 페이지

  • 이미 저장된 파일(HTML, 파일, 이미지 등)을 클라이언트에게 전송하는 웹 페이지다.

  • 서버에 저장된 데이터가 변경되지 않는 한 고정된 웹 페이지를 보게 된다.
    (웹 콘텐츠 캐싱이 적절할 듯)

  • 따라서 모든 사용자는 같은 결과의 웹 페이지를 서버에 요청하고 응답 받게 된다.

장점

  • 다른 처리 없이 요청에 대한 파일만 전송하기 때문에 빠르다.

  • 호스틍 서버에 연결하는 비용이 적다.

단점

  • 저장된 정보만 보여주기 때문에 서비스가 한정적임.

  • 추가, 삭제, 수정 등 모두 코드를 직접 건드려야 한다.

2. 동적 웹 페이지

  • 서버에 저장된 파일이 아닌, 다른 변수들에 의해서 동적으로 만들어지는 웹 페이지

  • 요청에 관하여 사용자는 조건에 따라 다른 결과를 받게 된다.

  • 사용자는 상황, 시간, 요청 등에 따라 달라지는 웹 페이지를 보게 된다.

2.1 CSR(Client Side Rendering)


  • 데이터가 없는 HTML 문서나 static 파일만 처음에 받아와 로드하고,
    이후 데이터를 요청하여 받아오는 형식이다.

  • 자바스크립트를 사용하여 브라우저에 페이지를 직접 렌더링하는 방식이다.

  • 모든 로직, 데이터 가져오기 등 서버가 아닌 클라이언트가 처리한다.

2.2 SSR(Server Side Rendeing)

  • 서버에서 동적으로 데이터까지 전부 삽입하여 완성된 HTML을 넘겨준다.(CSR과 반대)

  • 서버 렌더링은 브라우저에서 응답을 받기 전에 처리되므로 클라이언트에서 데이터를 가져오거나 템플릿 작성에 대한 왕복이 발생하지 않는다.
    (웹 서버가 모든 요청을 처리)

2.3 SPA(Single Page Application)


  • 웹에서 필요한 모든 정적 리소스를 최초 1번만 다운로드를 한다.
    그 이후 새로우 페이지에 대한 요청이 있을 경우, 페이지 갱신에
    필요한 데이터만 전달 받고 그 정보를 기준으로 페이지를 갱신한다.

  • React, Bue, Angular

  • 로컬 데이터를 효과적으로 캐싱할 수 있다.


profile
구구구구구!

0개의 댓글