메타코딩 springboot 서버 만들기


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


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





✨ 생성완료~ 😍

# 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
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;
}
@Repository 적어야 스프링 IoC에 빈으로 등록이 되는데
JpaRepository를 extends하면 생략가능
package com.igo.matchup.domain;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book, Long>{
}
기능을 정의할 수 있고, 트랜잭션을 관리할 수 있음.
Lombok으로 스프링에서 DI(의존성 주입)의 방법 중에 생성자 주입을 임의의 코드없이 자동으로 설정해주는 어노테이션이다.
@RequiredArgsConstructor는 초기화 되지않은 final 필드나, @NonNull 이 붙은 필드에 대해 생성자를 생성해 줍니다.
새로운 필드를 추가할 때 다시 생성자를 만들어서 관리해야하는 번거로움을 없애준다. (@Autowired를 사용하지 않고 의존성 주입)
가져오는건 @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";
}
}
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);
}
}