It's going to be one day 🍀

안녕하세요! 매일 매일 공부하려고 노력하는 백엔드 개발자 지망생의 공부 흔적입니다.

Back-End/Spring

[Spring] 스프링 부트 실습&데베연결/MVC 개념

2jin2 2024. 3. 4. 17:12

 

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

Step1.

프레젠테이션 계층 - 컨트롤러 MemberController.java 생성

package com.estsoft.hello.controller;

import com.estsoft.hello.service.MemberService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller
public class MemberController {

    private final MemberService memberService;

    public MemberController(MemberService memberService) {  // 빈 주입
        this.memberService = memberService;
    }

    @GetMapping("/members")
    @ResponseBody
    public List<MemberDTO> getAllMembers() {
        return memberService.getAllMembers();
    }
}

코드 뜯어보기

- @Controller : 이 어노테이션은 이 클래스가 Spring MVC에서 컨트롤러 역할을 하는 클래스임을 명시한다.

- 멤버 변수 및 생성자 추가하기 : MemberService 타입의 멤버 변수 memberService를 선언하고, 해당 클래스의 객체는 생성자를 통해 주입받음(의존성 주입)

- @GetMapping("/members") : /members 경로로 들어온 HTTP GET 요청을 처리함을 나타냄.

- @ResponseBody : 해당 메서드가 직접 HTTP 응답의 본문을 구성하고 반환한다고 명시함.

- getAllMembers() : memberService를 사용하여 모든 회원 정보를 가져오고 리스트로 반환하는 메서드 (메서드의 반환 타입은 List<MemberDTO>) -> 반환된 리스트는 Spring MVC에 의해 HTTP 응답의 본문으로 처리되어 클라이언트에게 전송됨.


Step2.

비즈니스 계층 - 서비스 MemberService.java 생성

package com.estsoft.hello.service;

import com.estsoft.hello.controller.Member;
import com.estsoft.hello.controller.MemberDTO;
import com.estsoft.hello.repository.MemberRepository;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class MemberService {
    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {   // 빈 주입
        this.memberRepository = memberRepository;
    }

    public List<MemberDTO> getAllMembers() {
        List<Member> memberList = memberRepository.findAll();
        System.out.println("memberList = "+memberList);
        List<MemberDTO> resultList = memberList.stream()
                .map(member -> new MemberDTO(member.getId(), member.getName())).toList();
        return resultList;
    }

}

코드 뜯어보기

- @Service : 이 클래스가 Spring MVC에서 서비스 역할을 하는 클래스임을 명시한다.

- 멤버 변수 및 생성자 추가하기 : MemberRepository 타입의 멤버 변수 memberRepository 를 선언하고, 해당 클래스의 객체는 생성자를 통해 주입받음(의존성 주입)

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

- getAllMembers() : 회원정보 조회 메서드. memberRepository를 통해 모든 회원 정보를 조회함. 조회된 회원 정보는 Member 객체의 리스트인 memberList에 저장되고 해당 리스트를 활용하여 새로운 MemberDTO 객체의 리스트를 생성함. map과 stream을 사용하여 각 Member 객체를 MemberDTO 객체로 변환하고, 최종적으로 리스트로 반환함.

 

이렇게 MemberService 클래스는 회원 정보를 조회하고 이를 MemberDTO 객체로 변환하여 리스트로 반환하는 역할을 수행함. 

 

지금까지 작성한 코드를 그림으로 표현했을 때


Step3.

퍼시스턴스 계층 - DB에 접근할 때 사용할 객체인 Member DAO 생성 후 실제 DB에 접근하는 Member.java 생성

package com.estsoft.hello.controller;

import jakarta.persistence.*;

@Getter
@Entity
public class Member {
    // insert into member (name) values ("이름");   id : 1, name: 이름
    // insert into member (name) values ("이름2");  id : 2, name: 이름2
    @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과 컬럼 매칭

    @Override
    public String toString() {
        return "Member{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

 

코드 뜯어보기

- @Entity : 이 클래스의 객체가 데이터베이스의 특정 테이블과 매핑되어 관리된다는 뜻.

- @Id : 해당 필드가 데이터베이스의 기본 키 역할을 한다는 뜻.

- @GeneratedValue : 기본 키의 값을 어떻게 생성할지 정의함. 여기서는 'GenerationType.IDENTITY'를 사용하여 데이터베이스에서 자동으로 생성되는 식별자를 사용함.

- @Column : 엔터티 클래스의 필드와 데베 테이블의 컬럼 간의 매핑을 정의함.

- updatable 속성 : 해당 컬럼이 업데이트 가능한지의 여부

- nullable 속성 : 해당 필드가 데베에 저장될 때 'null' 값을 허용할지의 여부

 

이 클래스는 데베 테이블의 구조와 Java 객체 간의 매핑을 정의하고, JPA를 통해 데베와 객체 간의 상호작용을 가능하게 함. Member라는 오브젝트와 데이터베이스 테이블 구조를 매핑시켜주는 기술은 ORM

 

- JPA(Java Persistence API) 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스

-ORM(Object-Relational Mapping) : Java Object(객체)와 데이터베이스의 Table 데이터를 자동으로 매핑(연결)해주는 기술.

ORM에도 여러 종류가 있다. 자바에서는 JPA(Java Persistence API)를 ORM 기술 표준으로 사용하는데 JPA를 사용하면 객체와 관계형 데이터베이스의 테이블 데이터를 매핑할 수 있다.

 

* 참고로 @Entity(ex Memebr)에서 Setter 사용은 지양한다 -> 값이 변경될 수 있기 때문에

 

DTO Layer 분리 - MemberDTO.java 

package com.estsoft.hello.controller;


import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
public class MemberDTO {
    private Long id;
    private String name;
}

코드 뜯어보기

- @Getter, @Setter : Lombok 어노테이션. 해당 어노테이션을 사용하면 자동으로 클래스의 Getter Setter 메서드를 생성함.

- @AllArgsConstructor : Lombok 어노테이션. 해당 어노테이션을 사용하면 모든 멤버 변수를 사용하는 생성자를 자동으로 생성함.

이 클래스는 간단한 데이터를 담는 데 사용되며, 주로 서비스 계층과 컨트롤러간의 데이터 전송을 위해 활용됨.


- DAO(Data Access Object) : 데이터베이스의 data에 접근하기 위한 객체임. 데이터베이스와의 상호작용을 담당함. JPA 어노테이션을 사용하여 데베 테이블과 매핑되고, 데이터베이스에 접근하는 로직이나 비즈니스 로직을 포함할 수 있음. (Member.java)

- DTO(Data Transfer Object) : 계층 간 데이터 교환을 하기 위해 사용하는 객체임. 로직을 가지지않고 순수하게 데이터를 전달하기 위한 목적으로 getter, setter만 가진 클래스임. (MemberDTO.java)

- VO(Value Object) : 값 오브젝트로써 값을 위해 쓰임. 오직 읽기만 가능함. DTO와 유사하지만 DTO는 setter를 가지고 있어 값이 변할 수 있음.

 

Step4. 

인터페이스 파일 만들기 - MemberRepository.java

package com.estsoft.hello.repository;

import com.estsoft.hello.controller.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

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

}

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


작동 확인하기

Controller, Service, Repository, DAO 코드를 가지고 스프링 부트 애플리케이션을 실행해보기!

인메모리 데이터베이스(h2)는 새로 실행할 때마다 데이터가 사라짐.

 

Step1.

애플리케이션이 실행될 때 저장할 더미 데이터를 넣을 data.sql 파일 생성. resources 디렉터리에 생성해줌.

INSERT INTO member (id, name) VALUES (1, 'name1');
INSERT INTO member (id, name) VALUES (2, 'name2');
INSERT INTO member (id, name) VALUES (3, 'name3');

 

Step2.

application.properties 파일 수정하기 - 옵션 추가

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.defer-datasource-initialization=true

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true

코드 뜯어보기

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

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

 

Step3.

Postman에서 위에서 만들었던 GET /members 컨트롤러 호출

Postman으로 HTTP 요청을 날렸을 때 입력한 sql이 확인됨.


요청 - 응답 과정 이해하기

1. Postman에서 톰캣에 HTTP GET 요청(/members)을 함. 이 요청은 스프링 컨테이너로 이동함.

2. 스프링의 DispatcherServlet이 URL을 분석하고, 이 요청을 처리할 수 있는 컨트롤러를 찾음. MemberController가 /members 라는 패스에 대한 GET 요청을 처리할 수 있는 getAllMembers() 메소드를 가지고 있으므로, DispatcherServlet은 MemberController에게 GET 요청을 전달함.

3. /members GET 요청을 처리할 수 있는 getAllMembers() 메소드와 이 요청이 매치되면 getAllMembers() 메소드에서는 비즈니스 계층과 퍼시스턴스 계층을 통하면서 필요한 데이터를 가져옴.

4. 그러면 뷰 리졸버는 템플릿 엔진을 사용해 HTML 문서를 만들거나 JSON, XML 등의 데이터를 생성함.

5. 그 결과 members를 return 하고 그 데이터를 Postman에서 볼 수 있게 됨.

 

과정 정리

  1. 프레젠테이션 계층 : HTTP 요청을 받고 비즈니스 계층으로 전송한다.
  2. 비즈니스 계층 : 모든 비즈니스 로직을 처리한다. 퍼시스턴트 계층에서 제공하는 서비스를 사용할 수도 있고, 권한을 부여하거나 유효성 검사를 하기도 한다.
  3. 퍼시스턴스 계층 : 모든 스토리지 관련 로직을 처리한다. 이 과정에서 데이터베이스에 접근하기 위한 객체를 사용할 수도 있다.

MVC(Model, View, Controller)

프로그램 처리 역할을 나눠서 프로그램을 작성하는 방법. 일종의 디자인 패턴임.

 

MVC 패턴의 이점

프로그램의 처리 역할을 MVC로 분류함으로써 다음과 같은 이점을 얻을 수 있음.

- 개발자의 분업화 용이

- 역할 분담을 통해 효율적인 개발 가능

- 설계 변경에 용이

과거에 MVC 모델이 있기 전에는 코드들이 매우 복잡하게 존재했음.

 

모델(Model)

모델은 시스템에서 비즈니스 로직을 담당함. 비즈니스 로직을 처리한 후의 작업 처리 결과를 모델이라고도 함.

 

ex) 회원 가입에 대한 비즈니스 로직

1) 입력한 아이디의 중복 확인

-> 사용자가 입력한 아이디 값을 메모리에 갖고 있다가 회원 DB에서 같은 아이디가 있는지 확인하고, 중복 여부를 화면에 표시할 수 있게 전달하는 것 까지가 비즈니스 로직. 여기서 클라이언트의 요청 사항을 구체적으로 처리하는 영역을 서비스 계층 (Service Layer) 이라고 하고, 요청 사항의 처리를 위해 구현한 Java 코드를 비즈니스 로직이라고 함. 

 

2) 본인 인증

-> 보통은 외부 인증 모듈을 사용하는데, 이러한 외부 인증 모듈을 사용하기 위한 API 호출은 서비스 계층 (Service Layer) 영역에 개발할 수 있음.

 

뷰(View)

사용자의 입/출력 결과 등의 표현을 담당함. 웹 어플리케이션에서는 주로 화면을 담당함.

ex) JSON 같은 특정 형식의 데이터로 변환 및 출력

 

컨트롤러(Controller)

Controller는 서비스 처리를 담당하는 Model과 화면 표시를 담당하는 View를 제어(Control)하는 역할을 함. 사용자가 입력한 내용을 View에서 받고, 받은 데이터를 기준으로 Model에 내용을 전달함.

 

Spring MVC 알아보기

서블릿 기반으로 클라이언트의 요청을 처리해주는 모듈. 사용자의 요청에 따른 화면 전환이나, 사용자가 사용하는 브라우저와 서버 간의 입출력 데이터 전달을 단순화 하는 기능이 있음.