springboot 서버 만들기

정원·2023년 4월 5일

React+Spring

목록 보기
6/8

메타코딩 springboot 서버 만들기

VSCode에서 Spring Boot 설치하기

라이브러리 설치

  • Extension Pack for Java
  • Spring Boot Extension Pack

spring 프로젝트 생성

Ctrl + Shift + P 를 눌러 커맨드 팔레트(Command palette)를 열어 ‘Spring initalizr: Create a Gradle Project’ 를 선택한다.

spring boot 버전

  • 2.7.10

언어 선택

  • java

Group,Artifact Id 등록

Group Id 등록: ex) io.honeymon.boot.springboot.vscode
Artifact Id 등록: spring-boot-of-vs-code

Packaging type 선택

  • JAR

java 버전 선택

  • 8

Dependnecies 선택

  • Spring Boot DevTools
  • Lombok
  • Spring Web
  • Spring Data JPA
  • H2 Database
  • MySQL Driver

✨ 생성완료~ 😍


application.yml 설정

# encoding 설정
server:
  servlet:
    encoding:
      charset: utf-8
      enabled: true

# h2 DB 설정
spring:
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:testdb

  # MySQL 설정
  # spring:
  #   datasource:
  #     driver-class-name: com.mysql.cj.jdbc.Driver
  #     url: jdbc:mysql://localhost:3306/matchup?useSSL=false&serverTimezone=Asia/Seoul&characterEncoding=UTF-8
  #     username: root
  #     password: root

  # jpa설정
  jpa:
    hibernate: # hibernate 사용 설정
      # 애플리케이션 실행 시점에 테이블을 다 지우고, 내가 가진 entity 정보를 보고 다시 테이블 자동 생성
      # if exists drop table 해주고 다시 만들어준다고 보면 된다.
      ddl-auto: create
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    show-sql: true

Book.java

package com.igo.matchup.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String title;

    private String author;
}

BookRepository.java

@Repository 적어야 스프링 IoC에 빈으로 등록이 되는데
JpaRepository를 extends하면 생략가능

package com.igo.matchup.domain;

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

public interface BookRepository extends JpaRepository<Book, Long>{
    
}

BookService.java

기능을 정의할 수 있고, 트랜잭션을 관리할 수 있음.

@RequiredArgsConstructor

Lombok으로 스프링에서 DI(의존성 주입)의 방법 중에 생성자 주입을 임의의 코드없이 자동으로 설정해주는 어노테이션이다.

@RequiredArgsConstructor는 초기화 되지않은 final 필드나, @NonNull 이 붙은 필드에 대해 생성자를 생성해 줍니다.

새로운 필드를 추가할 때 다시 생성자를 만들어서 관리해야하는 번거로움을 없애준다. (@Autowired를 사용하지 않고 의존성 주입)

@Transactional(readOnly = true)

가져오는건 @Transactional 없어도 되지만 붙이는 이유 :
JPA 변경감지라는 내부 기능이 있어서 계속 확인한다.
1. readOnly = true 를 하면 기능이 비활성화되어 연산을 줄여준다.
2. update시의 정합성을 유지해줌.
insert의 유령데이터현상(팬텀현상)은 못막음

더티체킹

함수 종료 -> 트랜잭션 종료 -> 영속화 되어있는 데이터를 DB로 갱신(flush) -> commit

package com.igo.matchup.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.igo.matchup.domain.Book;
import com.igo.matchup.domain.BookRepository;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor 
@Service
public class BookService {

    private final BookRepository bookRepository;

    @Transactional
    public Book 저장하기(Book book) {
        return bookRepository.save(book);
    }

    @Transactional(readOnly = true)
    public Book 한건가져오기(Long id) {
        return bookRepository.findById(id).orElseThrow(()->new IllegalArgumentException("id를 확인해주세요!"));
    }

    @Transactional(readOnly = true)
    public List<Book> 모두가져오기() {
        return bookRepository.findAll();
    }

    @Transactional
    public Book 수정하기(Book book, Long id) {
        // 더티체팅 update 치기
        Book bookEntity = bookRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("id를 확인해주세요!")); // 영속화(book오브젝트) -> 영속성 컨텍스트에 보관
        bookEntity.setTitle(book.getTitle());
        bookEntity.setAuthor(book.getAuthor());
        return bookEntity;

    @Transactional
    public String 삭제하기(Long id) {
        bookRepository.deleteById(id);
        return "ok";
    }    
}

BookController.java

ResponseEntity<?>

return값이 변동될 경우를 생각해서 리터럴에 <?>

package com.igo.matchup.web;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.igo.matchup.domain.Book;
import com.igo.matchup.service.BookService;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@RestController
public class BookController {

    private final BookService bookService;

    // @GetMapping("/")
    // public ResponseEntity<?> findAll() { 
    //     return new ResponseEntity<String>("ok", HttpStatus.OK);
    // }

    @PostMapping("/book")
    public ResponseEntity<?> save(@RequestBody Book book) { 
        return new ResponseEntity<>(bookService.저장하기(book), HttpStatus.CREATED); // 제네릭 리턴타입 생략가능
    }

    @GetMapping("/book")
    public ResponseEntity<?> findAll() {
        return new ResponseEntity<>(bookService.모두가져오기(), HttpStatus.OK);
    }

    @GetMapping("/book/{id}")
    public ResponseEntity<?> findById(@PathVariable Long id) {
        return new ResponseEntity<>(bookService.한건가져오기(id), HttpStatus.OK);
    }

    @PutMapping("/book/{id}")
    public ResponseEntity<?> update(@PathVariable Long id, @RequestBody Book book) {
        return new ResponseEntity<>(bookService.수정하기(book,id), HttpStatus.OK);
    }
    
    @DeleteMapping("/book/{id}")
    public ResponseEntity<?> deleteById(@PathVariable Long id) {
        return new ResponseEntity<>(bookService.삭제하기(id), HttpStatus.OK);
    }
}

0개의 댓글