본문 바로가기

SpringBoot

[스프링 부트 3 백엔드 개발자 되기] ch 3. 스프링 부트 3 구조 이해하기

1. 스프링 부트 3 구조 살펴보기 

 

- 스프링 부트는 각 계층이 양 옆의 계층과 통신하는 구조를 따른다. 

- 각 계층은 서로 소통할 수는 있지만 다른 계층에 직접 간섭하거나 영향을 미치지 않는다. 

 

 

- 스프링 부트에는 프레젠테이션, 비즈닛, 퍼시스턴스 계층이 있다. 이 계층이 통신하며 프로그램을 구성한다. 

 

1) 프레젠테이션 계층 

 

- HTTP 요청을 받고 이 요청을 비즈니스 계층으로 전송하는 역할을 한다. 컨트롤러가 바로 프레젠테이션 계층 역할을 한다.

 

2) 비즈니스 계층 

 

- 모든 비즈니스 로직을 처리한다. 비즈니스 로직이란 서비스를 만들기 위한 로직을 말한다. 서비스가 비즈니스 계층 역할을 한다. 

 

3) 퍼시스턴스 계층 

 

- 모든 데이터베이스 관련 로직을 처리한다. 이 과정에서 데이터베이스에 접근하는 DAO 객체를 사용할 수도 있다. 

DAO는 데이터베이스 계층과 상호작용하기 위한 객체이다. 리포지터리가 퍼시스턴스 계층의 역할을 한다. 

 

 

스프링 부트 프로젝트 디렉터리 구성하며 살펴보기 

 

 

main

 

- 실제 코드를 작성하는 공간

- 프로젝트 실행에 필요한 소스 코드나 리소스 파일은 모두 이 폴더 안에 들어 있다. 

 

test

 

- 프로젝트의 소스 코드를 테스트할 목적의 코드나 리소스 코드 

 

build.gradle

 

- 빌드를 설정하는 파일 

- 의존성이나 플러그인 설정 등과 같이 빌드에 필요한 설정을 할 때 사용 

 

settings.gradle

 

- 빌드할 프로젝트의 정보를 설정하는 파일

 

 

main 디렉터리 구성하기 

 

- main 디렉터리를 펼치면 java와 resources로 구성되어 있다. 

- HTML과 같은 뷰 관련 파일을 넣을 templates 디렉터리 생성

- static 디렉터리는 JS,CSS,이미지와 같은 정적 파일을 넣는 용도로 사용

 

- 스프링 부트 설정을 할 수 있는 application.yml 파일을 생성

- 이 파일은 스프링 부트 서버가 실행되면 자동으로 로딩되는 파일이다. 데이터베이스의 설정 정보, 로깅 설정 정보 등이 들어갈 수 있고, 직접 설정을 정의할 때 사용하기도 한다. 

 

 

2. 스프링 부트 3 프로젝트 발전시키기 

 

build.gradle에 의존성 추가

 

- build.gradle에 필요한 의존성을 추가한다. 

- 스프링 부트용 JPA인 스프링 데이터 JPA, 로컬 환경과 테스트 환경에서 사용할 인메모리 데이터베이스인 H2, 반복 메서드 작성 작업을 줄여주는 라이브러리인 롬복을 추가한다. 

 

build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web' // 웹 관련 기능 제공
    testImplementation 'org.springframework.boot:spring-boot-starter-test' //테스트 기능 제공
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa' //스프링 데이터 JPA

    runtimeOnly 'com.h2database:h2' //인메모리 데이터베이스

    compileOnly 'org.projectlombok:lombok' //롬복
    annotationProcessor 'org.projectlombok:lombok'

}

 

 

자바 그레이들 플러그인에서 의존성을 추가할 때 사용하는 키워드 

 

- 각 키워드마다 뒤에 나올 의존성을 어떻게 관리할지 정함

implementation 프로젝트 코드가 컴파일 시점과 런타임에 모두 해당 라이브러리를 필요로 할 때 사용
testImplementation 프로젝트의 테스트 코드를 컴파일하고 실행할 때만 의존성을 설정, 테스트 코드에서만 사용, 메인 애플리케이션 코드에는 사용하지 않음
runtimeOnly 런타임에만 필요한 의존성을 지정. 컴파일 시에는 필요하지 않지만, 애플리케이션을 실행할 때 필요한 라이브러리를 설정
compileOnly 컴파일 시에만 필요, 런타임에는 포함하지 않아야 하는 의존성 지정
annotationProcessor  컴파일 시에 애너테이션을 처리할 때 사용하는 도구의 의존성 지정 

 

 

프레젠테이션, 서비스, 퍼시스턴스 계층 만들기 

 

1) 프레젠테이션 계층에 속하는 컨트롤러 작성 

 

TestController.java

@RestController
public class TestController {

    @Autowired //TestService 빈 주입
    TestService testService;

    @GetMapping("/test")
    public List<Member> getAllMembers() {
        List<Member> members = testService.getAllMembers();
        return members;
    }
}

 

 

2) 비즈니스 코드 계층 코드 작성 

 

TestService.java

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

    public List<Member> getAllMembers() {
        return memberRepository.findAll(); //멤버 목록 얻기 
    }
}

 

- MemberRepository라는 빈을 주입받은 후에 findAll() 메서드를 호출해 멤버 테이블에 저장된 멤버 목록을 모두 가져온다. 

 

 

3) 퍼시스턴스 계층 코드 작성 

 

- DB에 접근할 때 사용할 객체인 Member DAO를 생성하고 실제 DB에 접근하는 코드 작성 

 

Member.java

@NoArgsConstructor(access= AccessLevel.PROTECTED)
@AllArgsConstructor
@Getter
@Entity
public class Member {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id", updatable=false)
    private Long id; //DB 테이블의 'id' 컬럼과 매칭
    
    @Column(name="name", nullable=false)
    private String name; //DB 테이블의 'name' 컬럼과 매칭 
}

 

- member라는 이름의 테이블에 접근하는 데 사용할 객체 

 

 

4) 실제로 member 테이블과 Member 클래스를 매핑하는 코드 작성 

 

- 매핑 작업에는 인터페이스 파일이 필요하다. 

- MemberRepository.java 인터페이스를 새로 생성해 필요한 코드를 작성 

 

MemberRepository.java

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

}

 

- 이 인터페이스는 DB에서 데이터를 가져오는 퍼시스턴스 계층 역할을 한다. 

- member라는 이름의 테이블에 접근해서 Member 클래스에 매핑하는 구현체 

 

작동 확인하기 

 

 

- 아직은 데이터베이스에 결과물을 볼 수 있는 데이터가 하나도 입력되지 않은 상태 

- 보통은 이런 실행 테스트를 하기 위해 애플리케이션을 실행할 때 마다 SQL문을 실행해 데이터베이스에 직접 데이터를 넣는다. 

- 현재는 인메모리 데이터베이스를 사용하고 있기 때문에 애플리케이션을 새로 실행할때 마다 데이터가 사라져서 불편하다. 이를 해결하기 위해 애플리케이션을 실행할 때 데이터를 자동으로 넣는 작업 

 

 

 

- resources 디렉터리에 더미 데이터를 넣을 data.sql 파일 생성 

 

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')

 

 

- application.yml 파일을 열어 아래 코드로 변경

- show_sql, format_sql 옵션은 애플리케이션 실행 과정에 데이터베이스에 쿼리할 일이 있으면 실행 구문을 보여주는 옵션

- defer-datasource-initialization 옵션은 애플리케이션을 실행할 때 테이블을 생성하고 data.sql 파일에 있는 쿼리를 실행하도록 하는 옵션 

 

application.yml

spring:
  jpa:
  # 전송 쿼리 확인
    show-sql: true
    properties:
      hibernate:
        format_sql: true
	# 테이블 생성 후에 data.sql 실행
    defer-datasource-initialization: true

 

- 수정했다면 SpringBootDeveloperApplication.java 파일 탭을 누른 다음 재실행 아이콘 클릭 

 

- 서버 실행 후 콘솔창에서 ctrl+F를 누르고 CREATE TABLE을 검색해 테이블이 잘 만들어졌는지 확인 

 

 

- 포스트맨으로 HTTP 요청을 시도

- 포스트맨을 켜고 HTTP 메서드는 [GET]으로, URL에 http://localhost:8080/test를 입력

- [Send] 버튼을 눌러 스프링 부트 서버에 HTTP 요청을 전송 

 

 

 

 

3. 스프링 부트 요청-응답 과정 한 방에 이해하기 

 

 

  1.  포스트맨에서 톰켓에 /test GET 요청을 한다. 그러면 이 요청은 스프링 부트 내로 이동한다. 이때 스프링 부트의 디스패처 서블릿이 URL을 분석하고, 이 요청을 처리할 수 있는 컨트롤러를 찾는다.  TestController가 /test라는 패스에 대한 GET 요청을 처리할 수 있는 getAllMembers() 메서드를 가지고 있으므로 디스패처 서블릿은 TestController에게 /test GET 요청을 전달한다. 

   2. /test GET 요청을 처리할 수 있는 getAllmembers()와 이 요청이 매치된다. 그리고 getAllMembers() 메서드에는 비즈          니스 계층과 퍼시스턴스 계층을 통하면서 필요한 데이터를 가져온다.

 

   3. 뷰 리졸버라는 템플릿 엔진을 사용해 HTML 문서를 만들거나 JSON, XML 등의 데이터를 생성한다. 

 

   4. 그 결과 members를 return하고 그 데이터를 포스트맨에서 볼 수 있게 된다.