[스프링부트] 스프링부트+JPA 패키지 구조

손성우·2022년 7월 24일

스프링부트

목록 보기
2/4
post-thumbnail

Spring Boot 패키지 구조

스프링 패키지 + JPA 구조에 대해 알아보기 전에 먼저 MVC패턴에 대해 알아보자.

MVC패턴

Model, View, Controller 의 약자. 프로젝트를 구성할 때 그 구성요소를 세가지 역할로 구분한 패턴.

사용자가 Controller를 조작하면 Controller는 Model을 통해 데이터를 가져오고 그 데이터를 바탕으로 View를 통해 화면을 보여줍니다.
사용자 인터페이스로부터 비즈니스 로직을 분리하여 애플리케이션의 시작적 요소나 그 이면에서 실행되는 비즈니스 로직을 서로 영향 없이 쉽게 고칠 수 있는 애플리케이션을 만들 수 있게 됩니다.

웹에서 MVC 패턴 사용 예
1. 사용자가 웹사이트에 접속
2. Controller는 사용자가 요청한 웹페이지를 위해 모델을 호출
3. Model은 DB같은 데이터 소스를 제어한 후 return
4. Controller는 Model이 return한 결과를 view에 반영
5. View는 사용자에게 보여짐

Model

데이터를 가진 객체.(변수, 상수, 초기화값 등등)

  • 사용자가 원하는 모든 데이터를 담아야 한다.
  • 뷰나 컨트롤러에 대해 어떠한 정보도 알면 안됨.
  • 데이터 변경에 대한 처리방법이 있어야 한다.
  • 재사용 가능해야 한다.

View

데이터 및 객체의 입출력 담당.(input 텍스트 같은 사용자 인터페이스. 즉, 화면)

  • 모델이 가지고 있는 정보를 가지면 안된다.(그저 모델의 정보를 전달받아 출력하는 역할)
  • 모델, 컨트롤러와 같이 다른 구성요소들을 몰라야 한다.

Controller

모델과 뷰를 있는 다리역할. 사람으로 치면 뇌를 담당

  • 모델과 뷰에 대해 알고 있어야 한다.
    -> 모델과 뷰가 컨트롤러에 의존하게 되는 단점이 존재

MVC패턴 모델2

  • 디자이너와 개발자의 분업이 가능하며 유지보수 및 확장이 쉽다
  • 어렵다

요약

역할을 확실하게 구분하여, 유지보수, 확장성를 용이하게 하는 패턴
컨트롤러 : 조작
모델 : 데이터 객체
뷰 : 화면

이러한 MVC패턴을 적용한 다양한 프레임워크중 대표적인것이 스프링이다.

스프링부트 패키지 구조

스프링부트는 목적에 따라 패키지를 생성해서 프로젝트를 관리하는데 기본적인 구성은
다음과 같다.

  • Controller
  • DTO
  • Service
  • Repository
  • Domain (Entity)

Controller

클라이언트의 요청과 응답을 처리하는 클래스로 맨 앞단.

@Controller
@RequestMapping("/hello")
public class hello {

   
    @GetMapping(value = "/view")
    public String view(Model model, @RequestParam(value = "userName", required = true) String userName){
        User user = userService.retrieveUserInfo(userName);
        model.addAttribute("user", user);
        return "/user/userView";
    }
}

클라이언트가 HTTP요청을 하면 그 요청을 담당하는 Controller를 찾고 그 Controller가 담당해서 응답한다.

Domain

MVC패턴의 Model에 해당하는 부분.
DB 테이블과 직접 매핑되는 클래스. JPA 사용 시 Annotation을 이용하여 테이블, 필드 등을 설정한다. 또한 도메인과 클라이언트를 직접 연동하지 않기 위해 DTO를 분리하는데 이유는 아래와 같다

  • 클라이언트 쪽과 연결된 부분은 잦은 변경사항이 있는데 도메인과 직접 연결되어 자주 변경되면 여러 클래스에 영향을 미치기 때문이다.(도메인 캡슐화)
  • 뷰의 요구사항 변화대로 도메인을 변경해야하는 강한 결합이 된다.(스프링은 패키지간 느슨한 결합을 지향해야함)
  • View단과 DB 단을 확실하게 분리하기 위해서

@Entity
@Getter
@NoArgsConstructor
public class Board{

    // id, 제목, 작성자명, 작성 날짜, 비밀번호, 내용

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) //  insert에 pk가 빠지고 데이터베이스의 auto_increment 동작이 수행
    private Long id;
    @Column(nullable = false)
    private String title;
    @Column(nullable = false)
    private String userName;
    @Column(nullable = false)
    private String password;
    @Column(nullable = false)
    private String content;

    @Builder
    public Board(String title, String userName, String password, String content) {
        this.title = title;
        this.userName = userName;
        this.password = password;
        this.content = content;
    }

}

DTO

Data Transfer Object. "데이터 전송 객체"
Domain은 DB테이블에 대한 정보를 모두 가지고 있는 클래스이고, DTO는 CRUD를 위한 특정 필드를 담은 데이터 전송을 위한 객체라고 보면 된다. Controller, Service, Repository사이에서 사용

예시

@Getter
@NoArgsConstructor
public class BoardDTO{

    private Long id;
    private String title;
    private String userName;
    private String password;
    private String content;
    private LocalDateTime createdAt;
    private LocalDateTime modifiedAt;

    @Builder
    public BoardDTO(Long id, String title, String userName, String password, String content, LocalDateTime createdAt, LocalDateTime modifiedAt) {
        this.id = id;
        this.title = title;
        this.userName = userName;
        this.password = password;
        this.content = content;
        this.createdAt = createdAt;
        this.modifiedAt = modifiedAt;
    }

    public Board build(){
        return Board.builder()
                .id(id)
                .title(title)
                .userName(userName)
                .password(password)
                .content(content)
                .build();
    }
}

    // 게시글 저장
    @Service
@Transactional
public class BoardService{
   public BoardDTO save(BoardDTO boardDTO) {
        boardRepo.save(boardDTO.build());
        return boardDTO;
    }

}
   

위 처럼 요청에 필요한 필드만 담아서 생성한다.

Repository

DB에 접근하는 소스코드를 모아둔 인터페이스.JpaRepository interface를 상속받아서 관리하고자 하는 클래스, ID 필드 타입을 JpaRepository<Entity Class, PK type>
같이 넣어주면 자동으로 DB와 CRUD 연결을 할 수 있는 메소드를 생성해준다.

ublic interface BoardRepository extends JpaRepository<Member, Long> {

}

Service

Repository와 DTO를 통한 CRUD나 예외처리 등 로직을 담당한다.

public Long delete(Long id, String password) {
        if(boardRepo.findByIdAndPassword(id, password).isPresent()){
            boardRepo.deleteById(id);
            return id;
        } else {
            return null;
        }
    }

위 코드는 게시글을 삭제할 때 비밀번호를 입력받아 일치하는 경우 삭제시키는 로직이다. 이처럼 비밀번호를 입력하는 상황을 짜야하는(로직)이 필요한 경우 Service에서 작성한다.

profile
백엔드 개발자를 꿈꾸며 공부한 내용을 기록하고 있습니다.

0개의 댓글