Spring Boot 간단 예제로 시작하기 6 (Repository계층, Service계층)

이번에는 JPA로 아주 손쉽게 CRUD 기능을 할 수있는 JPA Repository 를 만들고 사용해보자

폴더위치

package com.example.demo.repository;

import com.example.demo.domains.Member;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberRepository extends JpaRepository<Member, Long> {
}

인터페이스로 MemberRepository를 만들고 JpaRepository를 상속받으면 기본적인 CRUD기능을 사용 할 수 있다.

JpaRepository<사용 테이블(엔티티), Pk 타입>을 적어주어야 한다

 

Repository 사용에 앞서 서버에서의 처리 과정을 설명하자면 크게 3개로 분리 할 수 있다

Controller

  • 클라이언트의 요청을 받음
  • 요청에 대한 처리는 서비스에게 전담
  • 클라이언트에게 응답

Service

  • 사용자의 요구사항 처리
  • DB 정보가 필요할 때는 Repository에게 전담

Repository

  • DB 관리(연결, 해제, 자원 관리)
  • DB CRUD 작업 처리

💡 Controller와 Service를 구분하는 이유


중복되는 코드가 생기기 때문이다. 비즈니스 로직 코드가 컨트롤러에 구현되어 있는 경우 다른 컨트롤러의 핸들러 메소드에서 똑같은 로직 코드를 구현해야 하므로 중복코드가 발생하고 재사용성이 줄어든다.
결론적으로, controller와 service를 구분하면 확장성과 재사용성이 좋아지고 중복코드를 제거할 수 있다는 장점이 있다.

 

먼저 IndexController를 조금 수정해야 한다.

package com.example.demo.controller;

import com.example.demo.domains.Member;
import com.example.demo.repository.MemberRepository;
import com.example.demo.service.MemberService;
import lombok.RequiredArgsConstructor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;


@Controller
@RequiredArgsConstructor
public class IndexController {

    private final MemberService memberService;

    @GetMapping("/main")
    String index() {
        return "main";
    }


    @RequestMapping(value = "/member/join", method = RequestMethod.POST)
    ResponseEntity<?> memberJoin(@RequestBody Member member){
        Long member_id = memberService.save(member);
        return new ResponseEntity<>(member_id, HttpStatus.CREATED);
    }
}

@RequiredArgsConstructor 이란?

final 키워드가 붙은 변수의 생성자를 자동으로 생성해주는 어노테이션이다.

MemberService를 자동으로 생성해줘서 @Autowired를 안해줘도 되고 이게 더 지향적인 방법이다.

그 이유는 아래 포스트를 참고하자

https://velog.io/@limsubin/Spring-Boot%EC%97%90%EC%84%9C-Autowired-%EB%8C%80%EC%8B%A0-RequiredArgsConstructor-%EB%A5%BC-%EC%8D%A8%EB%B3%B4%EC%9E%90

 

@RequestMapping 이란?

@RequestMapping DefaultAnnotationHandlerMapping에서 컨트롤러를 선택할 때 대표적으로 사용하는 애노테이션이다. url당 하나의 컨트롤러에 매핑되던 다른 핸들러 매핑과 달리 메서드 단위까지 세분화하여 적용할 수 있으며,
url 뿐 아니라 파라미터, 헤더 등 더욱 넓은 범위를 적용할 수 있다.

https://joont92.github.io/spring/@RequestMapping/

value 값에 url값을 지정하고, method 타입을 POST로 적어주자

그다음 ResponseEntity를 반환하는 이유는 나중에 js로 json데이터를 보낼때 HttpStatus를 반환해줘야 해서 넣어줬다.

 

@RequestBody 란?

json데이터를 받을 때 java객체로 매핑 하기 위해서 사용하는 어노테이션이다.

여기서 왜 꼭 json으로 데이터를 주고받아야 하는지 모르겠다면 REST API를 참고하자.

https://gmlwjd9405.github.io/2018/09/21/rest-and-restful.html

 

마지막으로 memberService에 save함수를 json으로 받은 member객체를 받아주면된다. service계층 코드를 살펴보자.

 

package com.example.demo.service;

import com.example.demo.domains.Member;
import com.example.demo.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;

    @Transactional
    public Long save(final Member member) {
        Member getMember = memberRepository.save(member);
        return getMember.getId();
    }
}

service 계층은 위에서 언급했듯이 사용자의 요구사항을 처리하고 db관리는 전적으로 repository에게 넘긴다.

그래서 memberRepository를 DI해줬다.

controller에서 넘겨받은 member객체를 받아서 repository를 이용해서 save만 해주면 되는 아주 간단한 예제이다.

 

다시 html로 넘어가보자.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

<body>

<input type="text" id="name" name="name" maxlength="4" placeholder="이름">
<input type="text" id="age" name="age" maxlength="3" placeholder="나이">
<button type="submit" id="save">제출</button>


<script  src="http://code.jquery.com/jquery-latest.min.js"></script>
<script th:src="@{/js/main.js}"></script>
</body>
</html>

제이쿼리를 이용해 ajax통신으로 json데이터를 controller로 넘겨줄 꺼기 때문에 jquery를 넣어주자.

js 파일보다 아래있으면 잘 안먹힐 수 있다.

 

다음은 js파일이다.

$('#save').click(function () {
    const jsonData = JSON.stringify({
        name: $('#name').val(),
        age: $('#age').val()
    });
    $.ajax({
        url: "/member/join",
        method: "POST",
        data: jsonData,
        contentType: 'application/json;charset=utf-8',
        dataType: "json",
        success: function(){
            alert('제출 성공! :)');
            location.href = '/main';
        },
        error: function(){
            alert('제출 실패! :(');
        }
    });
});

1. id가 save인 버튼을 클릭하면 function이 실행된다.

2. 아까 input에서 받은 데이터들을 json으로 변경 후 jsonData라는 변수에 담아준다.

3. ajax통신을 통해 controller에서 value="/member/join" 이라고 되있던거를 똑같이 적어준다.

4. method = "POST"

5. data : 아까 넣어준 jsonData를 넘겨준다

6. contentType : json으로 정보를 넘겨준다는 의미이다.

7. 여기서 success, error로 갈리는데 이 기준은 아까 controller에서 HttpStatus가 결정한다. 정보를 잘 받고 return을 잘해줬다면 success가 실행되고 에러가난다면 error를 실행 될 것이다.

 

이제 한번 실행해보자!

제출 성공이 되면 잘 된것이다!

h2 db를 확인해보면

성공적으로 잘 들어간 것을 볼수있다!!