[SpringBoot] Chap 2 - 도서 관리 서비스 DB 조작하기

01_MySQL 명령어


📍 DDL(Data Definition Language)

CREATE DATABASE

데이터베이스 생성하기

CREATE DATABASE [데이터베이스 이름];

 

 

SHOW

데이터베이스 목록 보기

테이블 목록 보기

SHOW DATABASES;
SHOW TABLES;

 

 

DROP

데이터베이스 삭제하기

테이블 삭제하기

DROP DATABASE [데이터베이스 이름];
DROP TABLE [테이블 이름];

 

 

USE

데이터베이스 안으로 들어가기

USE [데이터베이스 이름];

 

 

CREATE TABLE

테이블 생성하기

CREATE TABLE [테이블 이름] (
	[필드1 이름] [타입] [부가조건]
    [필드2 이름] [타입] [부가조건]
    [필드3 이름] [타입] [부가조건]
    ...
    PRIMARY KEY ([필드 이름])
);

AUTO_INCREMENT : 데이터를 명시적으로 집어넣지 않아도 1부터 1씩 증가하여 자동 기록된다.

 

  • 타입 종류
    • tinyint : 1바이트 정수
    • int : 4바이트 정수
    • bigint : 8바이트 정수
    • double : 8바이트 실수
    • decimal(A, B) : 소수점을 B개 가지고 있는 전체 A자릿수 실수
    • char(A) : A글자가 들어갈 수 있는 문자열
    • varchar(A) : 최대 A글자가 들어갈 수 있는 문자열
    • date : 날짜. yyyy-MM-dd 형식으로 들어간다.
    • time : 시간. HH:mm:ss 형식으로 들어간다.
    • datetime : 날짜와 시간을 합친 형식. yyyy-MM-dd HH:mm:ss 형식으로 들어간다.

 

 

 

📍 DML(Data Manipulation Language)

INSERT INTO ... VALUES

데이터 삽입하기(Create)

INSERT INTO [테이블 이름] (필드1 이름, 필드2 이름, ...) VALUES (값1, 값2, ...)

 

 

SELECT ... FROM

데이터 조회하기(Read)

SELECT [필드 이름] FROM [테이블 이름];

 

조건을 달고 싶다면 WHERE을 사용하면 된다.

SELECT [필드 이름] FROM [테이블 이름] WHERE [조건];

 

 

UPDATE ... SET

UPDATE [테이블 이름] SET 필드1이름 = 값1, 필드2이름 = 값2, ... WHERE [조건];

 

 

DELETE FROM ...

DELETE FROM [테이블 이름] WHERE [조건];

 

 

 

 

02_Spring에서 DB 사용


📍 application.yml 설정

스프링 서버가 MySQL에 접근하도록 설정하려면 우선 연결을 해야 한다.

src/main/resources 아래에 application.yml 파일을 생성한다.

 

src/main/resources/application.yml

spring:
  datasource:
    url: "jdbc:mysql://localhost/library"
    username: "root"
    password: "비밀번호"
    driver-class-name: com.mysql.cj.jdbc.Driver

🔍 코드 설명

  • url : 우리가 연결할 데이터베이스 주소
    • jdbc:mysql:// : jdbc를 이용해 mysql에 접근한다.
    • localhost : 접근하려는 mysql은 localhost에 있다.
    • /library : 접근하는 데이터베이스는 library이다.
  • username : MySQL에 접근하기 위한 계정명
  • password : MySQL에 접근하기 위한 비밀번호
  • driver-class-name : 데이터베이스에 접근할 때 사용할 프로그램

 

 

 

 

03_DB로 CRUD API 구현


📍 데이터베이스 생성

MySQL에 유저 테이블을 생성한다.

CREATE TABLE user (
    id bigint AUTO_INCREMENT,
    name varchar(25),
    age int,
    PRIMARY KEY (id)
);

 

 

 

📍 POST API 구현

controller/user/UserController

@RestController
public class UserController {
    private final JdbcTemplate jdbcTemplate;
    
    public UserController(JdbcTemplate jdbcTemplate) {
    	this.jdbcTemplate = jdbcTemplate;
    }
    
    @PostMapping("/user")
    public void saveUser(@RequestBody userCreateRequest request) {
    	String sql = "INSERT INTO user(name, age) VALUES(?, ?)";
        jdbcTemplate.update(sql, request.getName(), request.getAge());
    }
}

🔍 코드 설명

  • JdbcTemplate : MySQL에 SQL을 보낸다. final 변수를 만들고 생성자를 만들어두면, 스프링이 알아서 JdbcTemplate을 넣어준다.
  • "VALUES(?, ?)" : ? 위치에 SQL과 함께 값을 넣어줄 수 있다.
  • jdbcTemplate.update() : 첫 파라미터로 SQL을 받고, ?를 대신할 값을 차례대로 넣어준다. INSERT, UPDATE, DELETE 쿼리에 사용한다.

 

 

 

📍 GET API 구현

controller/user/UserController

@RestController
public class UserController {
    (중략)
    
    @GetMapping("/user")
    public List<UserResponse> getUsers() {
    	String sql = "SELECT * FROM user";
        return jdbcTemplate.query(sql, (rs, rowNum) -> {
            long id = rs.getLong("id");
            String name = rs.getString("name");
            int age = rs.getInt("age"),
            return new UserResponse(id, name, age);
        }
    }
}

🔍 코드 설명

  • jdbcTemplate.query() : SELECT 쿼리를 날릴 수 있다.

 

 

 

📍 PUT API 구현

HTTP Method : PUT
HTTP Path : /user
HTTP Body : {"id": Long, "name": String}
결과 반환 : x (HTTP 상태 200 OK이면 충분)

 

controller/user/UserController

@RestController
public class UserController {
    (중략)
    
    @PutMapping("/user")
    public void updateUser(@RequestBody UserUpdateRequest request) {
    	String sql = "UPDATE user SET name = ? WHERE id = ?";
        jdbcTemplate.update(sql, request.getName(), request.getId());
    }
}

 

dto/user/reqest/UserUpdateRequest

// PUT /user API의 HTTP Body를 위한 새로운 클래스
public class UserUpdateRequest {
    private long id;
    private String name;
    
    public long getId() {
        return id;
    }
    
    public String getName() {
        return name;
    }
}



 

📍 DELETE API 구현

HTTP Method : DELETE
HTTP Path : /user
HTTP Body : 문자열 name(삭제되어야 하는 사용자 이름)
결과 반환 : x (HTTP 상태 200 OK이면 충분)

 

controller/user/UserController

@RestController
public class UserController {
    (중략)
    
    @DeleteMapping("/user")
    public void deleteUser(@RequestParam String name) {
    	String sql = "DELETE FROM user WHERE name = ?";
        jdbcTemplate.update(sql, name);
    }
}

 

→ 문제점 : 수정 API와 삭제 API를 사용할 때 수정이나 삭제가 정상적으로 이루어지지 않더라도 200 OK를 반환한다.

 

 

 

 

03_UPDATE, DELETE API 예외 처리


📍 PUT API 예외 처리

controller/user/UserController

@RestController
public class UserController {
    (중략)
    
    @PutMapping("/user")
    public void updateUser(@RequestBody UserUpdateRequest request) {
        String readSql =  = "SELECT * FROM user WHERE id = ?";
        boolean isUserNotExist = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, request.getId()).isEmpty();
        if (isUserNotExist) {
            throw new IllegalArgumentException();
        }
        
        String updateSql = "UPDATE user SET name = ? WHERE id = ?";
        jdbcTemplate.update(updateSql, request.getName(), request.getId());
    }
}

🔍 코드 설명

  • readSql : id를 기준으로 유저가 존재하는지 확인
  • jdbcTemplate.query() :SELECT 쿼리를 DB에 날려 데이터가 있는지 확인
  • if (isUserNotExist) :만약 유저가 존재하지 않는다면 IllegalArgumentException 을 던짐

DB에 존재하지 않는 10번 유저의 이름을 변경하려고 했더니 500 Internal Server Error가 나온다.

 

 

 

📍 DELETE API 예외 처리

controller/user/UserController

@RestController
public class UserController {
    (중략)
    
    @DeleteMapping("/user")
    public void deleteUser(@RequestParam String name) {
        String readSql = "SELECT * FROM user WHERE name = ?";
        boolean isUserNotExist = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, name).isEmpty();
        if (isUserNotExist) {
            throw new IllegalArgumentException();
        }
        
        String udeleteSql = "DELETE FROM user WHERE name = ?";
        jdbcTemplate.update(deleteSql, name);
    }
}