스프링부트3 구조 이해

도람·2025년 6월 21일
post-thumbnail

스프링부트 3 구조

  • 스프링 부트는 다음 그림과 같이 각 계층이 양 옆 계층과 통신하는 구조를 따른다.
    여기서 계층이란 각자의 역할과 책임이 있는 어떤 소프트웨어의 구성 요소를 의미한다. 각 계층은 서로 소통할 수는 있지만 다른 계층에 직접 간섭하거나 영향을 미치지는 않는다.

스프링부트 3 계층

  • 스프링부트 3에는 프레젠테이션 계층, 비지니스 계층, 퍼시스턴스 계층이 있다.
  • 프레젠테이션 계층 : HTTP 요청을 받고 이 요청을 비지니스 계층으로 전송하는 역할
    • 컨트롤러가 프레젠테이션 역할을 한다.
  • 비지니스 계층 : 모든 비니니스 로직을 처리한다. 비지니스 로직이란 서비스를 만들기 위한 로직이다.
    • 서비스가 비지니스 계층의 역할을 한다.
  • 퍼시스턴스 계층 : 모든 데이터베이스 관련 로직으 처리한다. 이 과정에서 데이터베이스에 접근하기 위한 DAO 객체를 사용할 수도 있다.
    • 리포지토리가 퍼시스턴스 계층의 역할을 한다.

Main 디렉토리 구성

main 디렉토리는 java와 resource로 구성되어 있다.

  • java : 우리가 작성하는 코드들로 구성

  • resource

    • templete : HTML과 같은 뷰 관련 파일로 구성
    • static : JS, CSS, 이미지와 같은 정적 파일로 구성
    • application.yml (application.properties) : 스프링 부트 설정을 하는 파일로, 서버가 실행되면 자동으로 로딩된다.

      application.yml과 application.properties 차이
      Spring Initializr를 통해 Spring Boot 프로젝트를 생성하면 application.properties가 생성이 된다. 하는 역할은 둘 다 같지만, 내부적인 구조가 다르다.
      properties의 경우엔 각 줄마다 key=value의 형태로 이루어져 있지만, yml의 경우엔 들여쓰기로 구분되는 계층 구조 및 key: value의 형태로 이루어져 있다.
      결론적으로 둘 중 어떤 것을 사용해도 문제가 없지만 properties와 yml을 함께 사용하면 properties 파일이 우선순위가 높아 yml 파일에서 설정한 내용이 덮어씌워질 수 있기 때문에 둘 중 하나만 사용하는 것을 권장한다.


spring 동작 과정 예시 - 코드

Member

package me.nahyung.springboot3_developer;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Getter
@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false)
    private Long id;

    @Column(name = "name", nullable = false )
    private String name;
}
  • JPA 엔티티 클래스다. 테이블 'member'과 매핑.

MemberRepository

package me.nahyung.springboot3_developer;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {

}
  • 데이터베이스의 member 테이블에 대해 CRUD 및 페이징·정렬 메서드를 구현 없이 제공한다.

TestService

package me.nahyung.springboot3_developer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class TestService {
    @Autowired
    MemberRepository memberRepository; // 빈 주입

    public List<Member> getAllMembers(){
        return memberRepository.findAll();
    }
}
  • 비즈니스 로직을 담당하는 서비스 계층. MemberRepository를 주입받아 데이터베이스에서 모든 Member 엔티티를 조회하는 로직을 수행한다.

TestController

package me.nahyung.springboot3_developer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class TestController {

    //@GetMapping("/test")
    //public String test(){
    //    return "Hello, world!";
    //}
    @Autowired
    TestService testService;    //TestService 빈 주입

    @GetMapping("/test")
    public List<Member> getAllMembers(){
        List<Member> members = testService.getAllMembers();
        return members;
    }
}
  • /test GET 요청을 수신하면 TestService의 getAllMembers()를 호출해 데이터베이스에서 조회한 Member 목록을 JSON으로 반환한다.

data.sql

INSERT INTO member (id, name) VALUES (1, 'name 1')
INSERT INTO member (id, name) VALUES (2, 'name 2')
INSERT INTO member (id, name) VALUES (3, 'name 3')
  • data.sql은 애플리케이션 구동 시 H2 인메모리 데이터베이스의 member 테이블에 샘플 데이터를 삽입하는 초기화 스크립트다.
  • 현재는 인메모리 데이터베이스를 사용하고 있어, 테스트를 하기위해 SQL를 실행해 직접 데이터를 넣는데 이를 해결하기 위해 추가한다.

application.yml

spring:
  jpa:
    #전송 쿼리 확인
    show-sql: true
    properties:
        hibernate:
          format_sql:true

    #테이블 생성 후 data.sql 실행
    defer-datasource-initialization: true
  • show-sql, format-sql 옵션: 애플리케이션 실행 과정에 데이터베이스에 쿼리를 할 일이 있으면 실행 구문을 모두 보여주는 옵션.
  • defer-datasource-initialization: 애플리케이션을 실행할 때 테이블을 생성하고 data.sql파일에 이는 쿼리를 실행하도록 하는 옵션.

spring 동작 과정 예시 - 실행

  • 코드를 실행하고 Console을 보면,
Hibernate: drop table if exists member cascade 
Hibernate: create table member (id bigint generated by default as identity, name varchar(255) not null, primary key (id))

를 확인할 수 있다.
테이블이 잘 만들어졌다는 것을 확인할 수 있고, 포스트맨으로 HTTP 요청을 보내 TEST 한다.

이처럼, [GET] 요청으로 URL에 controller에 작성했던 ....../test 를 입력하여 send를 눌러 스프링 부트 서버에 HTTP 요청을 보내면,
아까 data.sql 파일로 작성해 저장한 데이터를 클라이언트에서 확인할 수 있다.


springboot 요청-응답 과정

    1. 포스트맨 -> 톰캣 : /test GET요청을 보낸다.
      이 요청은 스프링부트 내부로 이동한다.
    1. 스프링 부트의 "디스패처 서블릿"이 이 요청을 url을 분석하고, 이 요청을 처리할
      "컨트롤러"를 찾는다.
    1. 디스패처 서블릿은 TestController에게 /test GET 요청을 전달한다.
      (TestController가 /test라는 패스에 대한 GET요청을 처리할 수 있는 getAllMembers()라는 메서드 가지고 있기 때문에)
    1. /test GET 요청을 처리할 수 있는 getAllMembers() 메서드와 이 요청이 매칭된다.
    1. getAllMembers() 메서드에서는 비지니스 계층과 퍼시스턴스 계층을 통하면서 필요한 데이터를 갖고온다.
    1. 뷰 리졸버는 템플릿 엔진을 사용해 HTML 문서를 만들거나, JSON, XML등의 데이터를 생성한다.
    1. 그 결과 members를 return하고, 그 데이터를 포스트맨에서 확인할 수 있다.

해당 글은 다음 도서의 내용을 정리하고 참고한 글임을 밝힙니다.
신선영, ⌜스프링 부트 3 벡엔드 개발자 되기 - 자바 편⌟, 골든래빗(주), 2023, 384쪽

profile
정도를 걷는 엔지니어

0개의 댓글