0. 개요
DB란? : 데이터의 저장, 읽기, 삭제, 변경을 하는 소프트웨어
종류 : MySQL, H2,,,
언어 : SQL
H2 는 RDBMS의 한 종류로 서버가 켜져 있는 동안에만 동작합니다.
JPA는 자바 명령어를 SQL로 번역합니다. 자바를 활용해 데이터의 생성, 조회, 변경, 삭제를 할 수 있습니다.
Repository는 JPA를 동작할 때 사용하는 매개체입니다.
스프링은 데이터를 주고 받는 방법을 강제합니다. -> DTO
Lombok으로 코드 절약을 합니다.
패키지 : Spring Web, H2 Database, MySQL Driver, Spring Data JPA, Lombok
1. RDBMS
RDBMS란? : Relational Data Base Management System : 컴퓨터의 정보를 저장, 관리하는 소프트웨어
종류 : MySQL, PostgreSQL, Oracle Database...
H2 : in-memory DB, 서버가 작동할 때만 동작
MySQL : 서비스 배포용 데이터베이스, AWS RDS 서비스로 적용 가능
H2 적용하기 -> ./src/main/resources/application.properties에 붙여넣기
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
서버 동작 후 localhost:8080/h2-console로 H2 DB 접속하기
JDBC URL : jdbc:h2:mem:testdb
User Name : sa
H2 테스트 해보기
// 테이블 생성하기
CREATE TABLE IF NOT EXISTS courses (
id bigint NOT NULL AUTO_INCREMENT,
title varchar(255) NOT NULL,
tutor varchar(255) NOT NULL,
PRIMARY KEY (id)
);
// 데이터 삽입하기
INSERT INTO courses (title, tutor) VALUES
('웹개발의 봄, Spring', '남병관'), ('웹개발 종합반', '이범규');
// 데이터 조회하기
SELECT * FROM COURSES
테이블을 생성할 때 해당 테이블이 존재하지 않은 경우에만 생성합니다. id는 Primary KEY이며 Long 타입입니다. 또한 Null값을 허용하지 않으며 새로운 데이터가 생기면 id를 증가시킵니다. title, tutor는 데이터의 속성입니다. varchar는 문자 타입을 의미하며 마찬가지로 Null값을 허용하지 않습니다.
데이터 삽입하기는 courses 테이블에 데이터를 삽입합니다. 괄호 쌍에 맞게 값을 넣어 삽입합니다.
데이터 조회는 courses 테이블로부터 모든 데이터를 가져옵니다.
위 문법은 SQL에대한 기본적인 테이블 생성, 삽입, 조회 명령어 입니다. Java 명령어를 SQL로 변환해주는 Spring Data JPA를 통해 간단하게 SQL 기능을 구현할 수 있습니다.
연습) name, age 열을 가진 tutors 테이블을 생성하고 나의 이름, 나이 데이터를 삽입한 뒤 tutors 테이블을 조회하라
// 테이블 생성
CREATE TABLE IF NOT EXISTS tutors (
id bigint NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
age varchar(255) NOT NULL,
PRIMARY KEY (id)
);
// 데이터 삽입
INSERT INTO tutors (name, age) VALUES
('이름', '나이');
// 데이터 조회
SELECT * FROM tutors
2. JPA란?
JPA : 자바를 이용해 SQL문을 구현합니다.
기존의 DB 조작 방식을 살펴보자 :
Spring JDBC Documentation : https://www.baeldung.com/spring-jdbc-jdbctemplate
https://velog.io/@koseungbin/Spring-JDBC
https://gmlwjd9405.github.io/2018/05/15/setting-for-db-programming.html
[Spring JDBC] Spring JDBC를 이용한 데이터 접근 방법 - Heee's Development Blog
Step by step goes a long way.
gmlwjd9405.github.io
Spring JDBC
Spring JDBC 란? Spring JDBC는 JDBC의 모든 저수준 처리를 스프링 프레임워크에 위임하므로써, Connection 연결 객체 생성 및 종료, Statement 준비/실행 및 종료, ResultSet 처리 및 종료, 예외 처리, 트랙잭션 등
velog.io
JPA로 구현하기 :
Spring JPA Reference Documentation : https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Spring Data JPA - Reference Documentation
Example 119. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del
docs.spring.io
스프링의 Domani은 MySQL의 Table
스프링의 Repository는 MySQL의 SQL
./main/java/com.sparta.w2/domain/Course.java :
@NoArgsConstructor // 기본생성자를 대신 생성해줍니다.
@Entity // 테이블임을 나타냅니다.
public class Course {
@Id // ID 값, Primary Key로 사용하겠다는 뜻입니다.
@GeneratedValue(strategy = GenerationType.AUTO) // 자동 증가 명령입니다.
private Long id;
@Column(nullable = false) // 컬럼 값이고 반드시 값이 존재해야 함을 나타냅니다.
private String title;
@Column(nullable = false)
private String tutor;
public String getTitle() {
return this.title;
}
public String getTutor() {
return this.tutor;
}
public Course(String title, String tutor) {
this.title = title;
this.tutor = tutor;
}
}
Course 테이블에 데이터 삽입하기
./main/java/com.sparta.w2/repository/CourseRepository.java : 인터페이스로 생성합니다.
public interface CourseRepository extends JpaRepository<Course, Long> {
}
interface란? JPA는 JPARepository를 상속받아 사용할 수 있습니다. 인터페이스는 클래스에서 멤버가 빠진 메소드 모음집입니다.
이제 JPA를 활용해 테이블 생성, 데이터 삽입, 데이터 조회를 수행할 수 있습니다.
진행하기에 앞서서 HIibernate가 DB에 전송하는 모든 쿼리(DDL, DML)을 로그에서 확인할 수 있는 옵션이 있습니다.
appliation.properties에 다음 옵션을 추가합니다.
spring.jpa.show-sql=true
또한 로그에 보여지는 Hiberante SQL에 포맷을 설정함으로써 보다 예쁘게 로그를 확인해 볼 수도 있습니다.
spring.jpa.properties.hibernate.format_sql=true
보다 많은 Spring Boot SQL 옵션을 확인하고 싶으면 아래 링크를 참고합니다.
https://lannstark.tistory.com/14
Spring Boot SQL 보기 옵션 총 정리
Spring Boot에서 query DSL을 사용하건 spring boot JPA를 사용하건, 쿼리를 튜닝할 때 SQL을 봐야할 때가 있다. 그럴 때 사용할 수 있는 몇 가지 옵션을 조사해 보았다. 환경 : Spring boot 2.* + hibernate 5.3.10 이
lannstark.tistory.com
이제 데이터를 삽입하고 조회하는 예제를 살펴보겠습니다.
./src/main/java/com.sparta.w2/W2Application.java :
@SpringBootApplication
public class W2Application {
public static void main(String[] args) {
SpringApplication.run(W2Application.class, args);
}
// Week02Application.java 의 main 함수 아래에 붙여주세요.
@Bean
public CommandLineRunner demo(CourseRepository repository) {
return (args) -> {
Course course1 = new Course("웹 개발의 봄, Spring", "남병관");
repository.save(course1);
List<Course> courses = repository.findAll();
for(int i = 0; i < courses.size(); i++){
Course c = courses.get(i);
System.out.println(c.getTitle() + " " + c.getTutor());
}
};
}
}
스프링 부트가 실행되는 W2Application 클래스에는 실행함수 메인과 CommandLineRunner가 있습니다. 커맨드 라이너는 어플리케이션이 실행될 때 데이터를 생성, 저장, 조회 하는 함수를 반환하고 있습니다.
3. JPA 활용 : 생성일자, 수정일자 적용하기
./main/java/com.sparta.w2/domain/Timestamped.java :
@MappedSuperclass // 상속했을 때, 컬럼으로 인식하게 합니다.
@EntityListeners(AuditingEntityListener.class) // 생성/수정 시간을 자동으로 반영하도록 설정
public abstract class Timestamped { // abstract 는 상속으로만 구현을 제한합니다.
@CreatedDate // 생성일자임을 나타냅니다.
private LocalDateTime createdAt;
@LastModifiedDate // 마지막 수정일자임을 나타냅니다.
private LocalDateTime modifiedAt;
}
./main/java/com.sparta.w2/domain/Course.java :
public class Course extends Timestamped {
...
./src/main/java/com.sparta.w2/W2Application.java :
@EnableJpaAuditing
@SpringBootApplication
public class W2Application {
...
많은 테이블에서 공통적으로 사용되는 '생성시간'과 '수정시간' 컬럼은 각각을 Entity에 생성하는 것보다 부모 클래스를 만들어 상속받아 사용하는 것이 효율적입니다.
@MappedSuperclass
해당 어노테이션은 JPA에서 '생성 시간', '수정 시간'과 같은 공통 매핑 정보가 필요할 때 사용합니다. 상속받는 자식 클래스에 부모 클래스의 매핑 정보만을 제공합니다. 해당 클래스를 직접 생성하는 일은 거의 없기 때문에 해당 클래스는 추상클래스로 만들집니다.
@EntityListners
해당 어노테이션은 JPA에서 Entity에 이벤트가 발생했을 때 특정 로직을 실행합니다. 엔티티 클래스 또는 매핑된 슈퍼 클래스에 적용되며 콜백할 클래스를 지정합니다.
AuditingEntityListener class
해당 클래스는 touchForCreate(), touchForUpdate() 메서드가 있습니다. 대상 객체에 업데이트, 생성 등의 이벤트가 발생하도록 설정한 경우에 생성날짜, 수정날짜를 저장할 수 있도록 활성화 합니다. @PrePersist, @PreUpdate로 구현되어 있습니다.
@CreatedDate
해당 어노테이션은 Entity가 생성될 때 자동으로 시간을 저장합니다.
@LastModifiedDate
조회한 Entity의 값이 변경될 때 시간이 자동으로 저장됩니다.
@EnableJpaAuditing
JPA에서 날짜 생성 및 수정을 위한 어노테이션 입니다. 어플리케이션의 main메소드에 있는 클래스에 적용됩니다. JPA Auditing(감시,감사) 기능을 활성화 합니다. 이 어노테이션을 적용함으로써 createdDate, modifiedDate 와 같은 DB에 저장, 수정될 때 누가, 언제 했는지를 자동으로 관리하게 됩니다.
Entity Listener는 엔티티의 변화를 감지하고 테이블을 조작합니다. 이 때, Application class에 @EnableJpaAuditing 어노테이션을 적용해야 null값이 들어가지 않습니다.
https://wildeveloperetrain.tistory.com/76
JPA @CreatedDate @LastModifiedDate 생성 시간, 수정 시간이 저장되는 원리
@CreatedDate, @LastModifiedDate 데이터를 저장할 때 '생성된 시간 정보'와 '수정된 시간 정보'는 여러모로 많이 사용되고 또 중요합니다. JPA를 사용하면서 @CreatedDate, @LastModifiedDate를 사용하여 생성된 시
wildeveloperetrain.tistory.com
4. JPA 심화 : CRUD 구현하기
CRUD란? : Create, Read, Update, Delete
Create, Read 구현 예시 :
@Bean
public CommandLineRunner demo(CourseRepository repository) {
return (args) -> {
// 데이터 저장하기
repository.save(new Course("프론트엔드의 꽃, 리액트", "임민영"));
// 데이터 전부 조회하기
List<Course> courseList = repository.findAll();
for (int i = 0; i < courseList.size(); i++) {
Course course = courseList.get(i);
System.out.println(course.getId());
System.out.println(course.getTitle());
System.out.println(course.getTutor());
}
// 데이터 하나 조회하기
Course course = repository.findById(2L).orElseThrow(
() -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
);
//
};
}
데이터 수정과 삭제를 구현하기에 앞서 Spring MVC를 이해해야 합니다.
Controller : 가장 바깥, API 요청/응답 처리
Service : 어플리케이션의 여러 동작을 처리
Repo : 가장 안쪽, DB처리 (Repository, Entity)
데이터의 수정과 삭제는 Service Layer에서 구현되어야 합니다.
./main/java/com.sparta.w2/service/CourseService.java :
@Service // 스프링에게 이 클래스는 서비스임을 명시
public class CourseService {
// final: 서비스에게 꼭 필요한 녀석임을 명시
private final CourseRepository courseRepository;
// 생성자를 통해, Service 클래스를 만들 때 꼭 Repository를 넣어주도록
// 스프링에게 알려줌
public CourseService(CourseRepository courseRepository) {
this.courseRepository = courseRepository;
}
@Transactional // SQL 쿼리가 일어나야 함을 스프링에게 알려줌
public Long update(Long id, Course course) {
Course course1 = courseRepository.findById(id).orElseThrow(
() -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
);
course1.update(course);
return course1.getId();
}
}
@Transactional
트랜잭션은 데이터베이스의 상태 변화 작업 단위로서 데이터베이스에 데이터의 추가, 갱신, 삭제 등의 작업에 오류가 발생한 경우 모든 작업을 원상태로 복구(롤백) 합니다. 즉, 모든 작업을 성공적으로 처리했을 경우에만 데이터베이스에 반영합니다. (커밋)
스프링에서 트랜잭션 처리는 @Transactional 어노테이션을 통해 이루어집니다. DB와 관견되어 트랜잭션이 필요한 서비스 클래스나 메소드에 해당 어노테이션을 붙여줍니다.
해당 어노테이션은 DB에 상태 변화를 가져오는 작업을 처리할 때 롤백이 발생한 경우(예외가 발생한 경우) 해당 작업 단위 연산을 전체 취소합니다.
참고로 Entity의 id값을 자동으로 증가시키는 AUTO_INCREMENT 옵션의 경우 @Transactional의 관리 범위를 초과합니다.
https://tecoble.techcourse.co.kr/post/2021-05-25-transactional/
Transactional 어노테이션
@Transactional…
tecoble.techcourse.co.kr
이를 통해 수정과 삭제 기능을 구현할 수 있습니다.
@Bean
public CommandLineRunner demo(CourseRepository courseRepository, CourseService courseService) {
return (args) -> {
courseRepository.save(new Course("프론트엔드의 꽃, 리액트", "임민영"));
System.out.println("데이터 인쇄");
List<Course> courseList = courseRepository.findAll();
for (int i = 0; i < courseList.size(); i++) {
Course course = courseList.get(i);
System.out.println(course.getId());
System.out.println(course.getTitle());
System.out.println(course.getTutor());
}
Course new_course = new Course("웹개발의 봄, Spring", "임민영");
courseService.update(1L, new_course);
courseList = courseRepository.findAll();
for (int i = 0; i < courseList.size(); i++) {
Course course = courseList.get(i);
System.out.println(course.getId());
System.out.println(course.getTitle());
System.out.println(course.getTutor());
}
courseRepository.deleteAll();
};
}
5. Lombok, DTO
Lombok 이란? : 롬복은 자바 프로젝트를 진행하는데 거의 필수적으로 필요한 코드들을 자동생성해줌으로써 코드를 절약해주는 라이브러리 입니다. 롬복과 같은 외부 라이브러리가 컴파일 시 문제없이 작동하도록 설정을 해주기위해 어노테이션 프로세싱을 활성화 합니다.
설정 -> 에디터 -> 플러그인 : 마켓플레이스에서 Lombok 검색 후 해당 플러그인 설치
설정 -> 빌드, 실행, 배포 -> 컴파일러 -> 어노테이션 프로세서 : 어노테이션 처리 활성화
@Getter // getter 메소드
@NoArgsConstructor // 기본생성자
@Entity
public class Course extends Timestamped {
...
@RequiredArgsConstructor // 생성자주입
@Service
public class CourseService {
private final CourseRepository courseRepository;
...
DTO란? : Data Transfer Object, 데이터 전달 객체 : 계층간 데이터를 주고 받을 때 교환을 위해 사용하는 객체입니다. 기존Domain 클래스를 DAO와 DTO로 분리합니다. 참고로 DTO는 Read, Write가 가능한 반면 VO는 Read-Only입니다.
https://melonicedlatte.com/2021/07/24/231500.html
DAO, DTO, VO 란? 간단한 개념 정리 - Easy is Perfect
melonicedlatte.com
https://hudi.blog/data-transfer-object/
DTO의 개념과 사용범위
DTO는 우테코 과정 중 정말 많이 들어봤고, 나름 사용도 많이 했지만 이상하게 바람직하게 사용하고 있다는 확신이 들지 않는 개념이다. DTO에 대한 내용은 항상 새롭게 알아가는데, 이러다간 DTO
hudi.blog
./main/java/com.sparta.w2/domain/CourseRequestDto.java :
@Setter
@Getter
@RequiredArgsConstructor
public class CourseRequestDto {
private final String title;
private final String tutor;
}
이제 Course.java에서 이루어지는 데이터에 접근 관련 메소드들은 Dto가 싣고 온 데이터를 기반으로 수정 등의 작업이 이루어져야 합니다.
6. GET API 구현
클라이언트는 서버에 요청(Request)하고 서버는 요구사항을 처리하고 응답(Response)합니다.
REST는 주소에 명사를 요청 방식에 동사를 사용함으로써 의도를 명확하게 드러내는 설계 방식 입니다.
예를들어, CRUD를 구현할 때, 생성(POST)/ 조회(GET)/ 수정(PUT)/ 삭제(DELETE) 요청을 합니다.
- HTTP GET Request /courses -> 강의 전체 목록 조회 요청
- HTTP GET Request /courses/1 -> ID가 1번인 강의 조회 요청
- HTTP POST Request /courses -> 강의 생성 요청
- HTTP PUT Request /courses/3 -> ID가 3번인 강의의 수정 요청
- HTTP DELETE Request /courses/2 -> ID가 2번인 강의의 삭제 요청
REST API 설계 시 몇가지 원칙이 있습니다.
- 주소에 명사는 복수형을 사용합니다.
- 주소에 동사를 가급적 사용하지 않습니다.
- 주소에 명사는 소문자로 작성합니다.
- 주소는 /로 끝나지 않습니다.
./src/main/java/com.sparta.week02/controller/CourseController.java :
@RequiredArgsConstructor
@RestController
public class CourseController {
private final CourseRepository courseRepository;
@GetMapping("/api/courses")
public List<Course> getCourses() {
return courseRepository.findAll();
}
}
@RestController
스프링 프레임워크 4.x 버전 이상부터 사용 가능하며 @Controller와 @ResponseBody가 결합한 어노테이션 입니다. 객체를 반환합니다.
@Controller
해당 어노테이션을 View를 반환합니다. 데이터를 반환하는 경우 @ResponseBody 어노테이션을 추가해야 합니다.
https://mangkyu.tistory.com/49
[Spring] @Controller와 @RestController 차이
Spring에서 컨트롤러를 지정해주기 위한 어노테이션은 @Controller와 @RestController가 있습니다. 전통적인 Spring MVC의 컨트롤러인 @Controller와 Restuful 웹서비스의 컨트롤러인 @RestController의 주요한 차이점
mangkyu.tistory.com
참고) API 개발 도구
- ARC
- POSTMAN
7. POST API 구현하기
스프링을 요청을 주고 받는 방식을 강제합니다. POST, PUT 등 데이터를 주고 받을 때 API에서 넘어오는 데이터를 잘 받기 위해 어노테이션을 명시해야 합니다.
./src/main/java/com.sparta.week02/controller/CourseController.java :
@PostMapping("/api/courses")
public Course createCourse(@RequestBody CourseRequestDto requestDto) {
Course course = new Course(requestDto);
return courseRepository.save(course);
}
위의 예시에서와 같이 클라이언트에서 받는 데이터는 @RequestBody 어노테이션을 통해 명시가 되고 있습니다.
또한 데이터를 전송하는 클라이언트 쪽에서도 Content-Type에대한 명시가 필요합니다. ARC, POSTMAN 등으로 API 테스트를 보낼 때 헤더에 "Content-Type : application/json"을 추가해야 합니다.
클라이언트에서 전송할 데이터는 BODY에 작성합니다. 이 때 반드시 json 데이터는 쌍 따옴표로 키-값 쌍을 적습니다.
{
"title" : "웹 개발의 봄, Spring",
"tutor" : "남병관"
}
8. PUT API 구현하기
./src/main/java/com.sparta.week02/controller/CourseController.java :
@RequiredArgsConstructor
@RestController
public class CourseController {
private final CourseService courseService;
@PutMapping("/api/courses/{id}")
public Long updateCourse(@PathVariable Long id, @RequestBody CourseRequestDto requestDto) {
return courseService.update(id, requestDto);
}
}
수정하기는 @PutMapping을 통해 구현합니다. 주소에는 수정할 id 값을 중괄호로 감싸 유동적인 값을 받게 됩니다. 파라미터에는 @PathVariable을 통해 중괄호로 감싼 id 값을 가져 오게 됩니다. 또한 Post와 마찬가지로 변경할 데이터는 @RequestBody 어노테이션을 통해 가져오게 됩니다.
API 요청 테스트를 할 때에는 POST 요청을 했을 때와 마찬가지로 헤더에 "Content-Type" : "application/json"을 명시하며 BODY에는 수정할 데이터를 Json 형식으로 작성합니다.
9. DELETE API 구현하기
./src/main/java/com.sparta.week02/controller/CourseController.java :
@DeleteMapping("/api/courses/{id}")
public Long deleteCourse(@PathVariable Long id, @RequestBody CourseRequestDto requestDto) {
courseRepository.deleteById(id);
return id;
}
10. 과제
POST : /api/persons : 친구 생성
GET : /api/persons : 친구 목록 조회
PUT : /api/persons/{id} : 친구 정보 변경
DELETE : /api/persons/{id} : 친구 정보 삭제
'웹 개발 > 스프링' 카테고리의 다른 글
[스프링 부트 심화] 스프링 동작 원리 (0) | 2023.04.08 |
---|---|
[스프링 부트 기초] 스프링 나만의 셀렉샵 (0) | 2023.04.07 |
[스프링 부트 기초] 스프링 메모장 만들기 (0) | 2023.04.05 |
[스프링 부트 기초] RESTful Controller (0) | 2023.03.29 |
[스프링 부트 기초] 프로젝트 셋팅하기 (0) | 2023.03.28 |