Search

2. 생애 최초 Database 조작하기

목표

1. 디스크와 메모리의 차이를 이해하고, Database의 필요성을 이해한다.
2. MySQL Database를 SQL과 함께 조작할 수 있다
3. 스프링 서버를 이용해 Database에 접근하고 데이터를 저장, 조회, 업데이트, 삭제할 수 있다.
4. API의 예외 상황을 알아보고 예외를 처리할 수 있다.

Database와 MySQL

저번에 서버를 종료하고 다시 접속하면 유저 정보가 날라갔다.
유저 데이터를 저장하지 않아서 생기는 문제.
CPU는 연산작업
RAM 메모리. 임시기억장치
DISK 장기보관기억장치
1.
개발하고 있는 서버는 DISK에 잠들어 있다.
2.
서버를 실행 시키면 DISK에 있는 코드 정보가 RAM으로 복사
3.
API가 실행되면 연산이 수행. CPU와 RAM을 왔다갔다 한다.
4.
즉 POST API를 통해 생긴 유저 정보는 RAM에 쓰여 있다.
5.
서버가 종료되면 RAM에 있는 모든 정보가 사라진다.
6.
서버를 다시 시작하면 유저 정보가 없다.

서버에서는 어떻게 Disk에 저장할 수 있을까?

File 클래스 등을 이용해 직접 Disk에 접근할 수 도 있지만, Database를 사용한다.

Database란?

데이터를 구조화 시켜 저장하는 친구.

RDB(Relational Database_ - MySQL

데이터를 표처럼 구조화 시켜 저장하는 친구

SQL(Structured Query Language)

표처럼 구조화된 데이터를 조회하는 언어

인텔리J Ultimate에서 Database 연결

+버튼 눌러서 mySQL 연결해주면 되는데 나는 지금 원래 MariaDB가 있어서 그걸로 연결
둘다 설치하면 충돌오류 생긴다해서 기존껄로 가기로 함.
root에 나는 비밀번호가 기존에 있어서 그거 넣어주고 밑에 테스트 연결 성공한거 보고 ok
그럼 바로 창하나 뜨는데 거기다가 이렇게 적고 컨트롤 엔터하면 하단에 값이 출력

CLI로 접근하는 방법

터미널에서
## 마리아DB 접속 >> mysql -u root >> 컴 비밀번호 입력 ## mysql 문법사용가능 >> Use mysql
Bash
복사

MySQL에서 테이블 만들기

폴더 = 데이터베이스
엑셀 파일 = 테이블
엑셀 파일의 헤더 = 테이블의 필드 정의
엑셀 파일의 서식 = 테이블의 필드 타입

테이블 생성

create database library;
SQL
복사

Database 조회

show databases ;
SQL
복사

Database 삭제

drop database library;
SQL
복사

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

현재는 library로
use library;
SQL
복사

테이블 목록보기

show tables;
SQL
복사
지금은 테이블이 없어서 아무것도 안뜸.

테이블 만들기

create table [테이블 이름] ( [필드1 이름] [타입] [부가조건], [필드2 이름] [타입] [부가조건], ... primary key ([필드이름]) );
SQL
복사
테스트용으로 과일 테이블을 만들어보자.
create table fruit ( id bigint auto_increment, name varchar(20), price int, stocked_date date, primary key (id) );
SQL
복사
정렬 옵션 + 커맨드 + i
auto_increment는 id를 명시적으로 넣지 않아도 1부터 1개씩 올라간다
primary key는 유일한 키를 말한다.

MySQL 타입

tinyint : 1바이트 정수
int : 4바이트 정수
bigint : 8바이트 정수
double : 8바이트 정수
decimal(A, B) : 소수점을 B개 가지고 있는 전체 A자릿수 실수
Decimal(4,2) = 12.23
소수점 2개를 가지고 있는 전체 4자릿수 실수
char(A) : A 글자가 들어갈 수 있는 문자열
varchar(A) : 최대 A 글자가 들어갈 수 있는 문자열
date : 날짜, yyyy-MM-dd
time : 시간, HH:mm:ss
datetime : 날짜와 시간을 합친 타입, yyyy-MM-dd HH:mm:ss
id는 혹시나 21억건을 넘을 수도 있으니 가장 큰 bigint를 사용한다
이제 과일 테이블을 만들고 show tables 하면 과일 테이블이 보인다.
그리고 이제 과일 테이블을 삭제하자

테이블 삭제하기

drop table fruit;
SQL
복사
여기까지 데이터베이스를 정의하는 언어라고 해서
DDL(Data Definition Language)이라고 한다.

테이블의 데이터 조작하기

데이터 넣기

INSERT INTO [테이블 이름] (필드1이름, 필드2이름, ...) VALUES (1,2, ...)
SQL
복사
INSERT INTO fruit(name, price, stocked_date) VALUES ('사과', 1000, '2024-05-01')
SQL
복사
대소문자를 사용해도 된다.
괄호 안의 데이터 값의 순서가 중요하다.
id는 지정해주지 않아도 auto_increment가 자동으로 넣어준다

데이터 조회하기

SELECT * FROM [테이블 이름];
SQL
복사
fruit 테이블의 모든 데이터를 조회하는 명령어
SELECT * FROM fruit;
SQL
복사
* (별) 대신에 필드 이름을 넣을 수 있다. 여러개도 가능
SELECT name,price FROM fruit;
SQL
복사
필터를 걸 수 있다.
SELECT * FROM [테이블 이름] WHERE [조건];
SQL
복사
SELECT * FROM fruit WHERE name = '바나나';
SQL
복사
AND 또는 OR을 이용해 조건을 이어 붙일 수 있다!
SELECT * FROM fruit WHERE name = '사과' AND price <= 2000;
SQL
복사
SELECT * FROM fruit WHERE name = '바나나' OR price = 1000;
SQL
복사
조건에는 =, <= 외에도 !=, <, >, >=, between, in, not in 등이 있다.
가격이 1,000원 ~ 2,000인 과일 조회
SELECT * FROM fruit WHERE price BETWEEN 1000 AND 2000;
SQL
복사
이름이 사과 또는 수박인 과일 조회
SELECT * FROM fruit WHERE name IN ('사과' ,'수박');
SQL
복사
이름이 사과가 아닌 과일 조회
SELECT * FROM fruit WHERE name NOT IN ('사과');
SQL
복사

데이터 업데이트

UPDATE [테이블 이름] SET 필드1이름=, 필드2이름=, ... WHERE [조건];
SQL
복사
사과인것들을 1500원으로 변경
UPDATE fruit SET price = 1500 WHERE name = '사과'
SQL
복사
만약 [조건]을 붙이지 않으면, 모든 데이터가 업데이트된다!!!

데이터 삭제하기

DELETE FROM [테이블 이름] WHERE [조건];
SQL
복사
DELETE FROM fruit WHERE name = '사과';
SQL
복사
만약 [조건]을 붙이지 않으면, 모든 데이터가 삭제된다!
지금까지 한 SQL들을 데이터들을 조작한다고 해서
DML(Data Manipulation Language)이라고 한다.
create database library; show databases ; drop database library; use library; show tables; create table fruit ( id bigint auto_increment, name varchar(20), price int, stocked_date date, primary key (id) ); drop table fruit; INSERT INTO fruit(name, price, stocked_date) VALUES ('사과', 1000, '2024-05-01') SELECT * FROM fruit; SELECT name,price FROM fruit; SELECT * FROM fruit WHERE name = '사과' AND price <= 2000; SELECT * FROM fruit WHERE name = '바나나' OR price = 1000; SELECT * FROM fruit WHERE price BETWEEN 1000 AND 2000; SELECT * FROM fruit WHERE name IN ('사과' ,'수박'); SELECT * FROM fruit WHERE name NOT IN ('사과'); UPDATE fruit SET price = 1500 WHERE name = '사과'; DELETE FROM fruit WHERE name = '사과';
SQL
복사

Spring에서 Database 사용하기

application.yml 생성하고 설정해주기

resources 안에 생성해주면 된다.
우선 나는 마리아DB라서
build.gradle에 이부분 추가
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
XML
복사
spring: datasource: url: "jdbc:mariadb://localhost/library" username: "root" password: "비번넣어" driver-class-name: org.mariadb.jdbc.Driver
XML
복사

데이터베이스에 user 테이블을 만들다

create table user ( id bigint auto_increment, name varchar(20), age int, primary key (id) );
SQL
복사

POST API 변경

UserController 부터 바꿔주자
package com.group.libraryapp.controller.user; @RestController public class UserController { private final JdbcTemplate jdbcTemplate; //자바 데이터베이스 커넥터에 대한 클래스. JDBC 구현 public UserController(JdbcTemplate jdbcTemplate){ // JDBC 템플릿을 받아서 생성자를 만듦. this.jdbcTemplate = jdbcTemplate; } @PostMapping("/user") // POST /user public void saveUser(@RequestBody UserCreateRequest request) { String sql = "INSERT INTO user(name, age) VALUES (?,?)"; jdbcTemplate.update(sql, request.getName(),request.getAge()); } . . }
Java
복사
private final JdbcTemplate jdbcTemplate; public UserController(JdbcTemplate jdbcTemplate){ this.jdbcTemplate = jdbcTemplate; }
Java
복사
jdbcTemplate을 이용해 SQL을 날릴 수 있다!
생성자를 만들어 jdbcTemplate을 파라미터로 넣으면, 자동으로 들어온다!
String sql = "INSERT INTO user(name, age) VALUES (?,?)";
Java
복사
SQL을 만들어 문자열 변수로 저장합니다.
값이 들어가는 부분에 ?를 사용하면, 값을 유동적으로 넣을 수 있습니다.
jdbcTemplate.update(sql, request.getName(),request.getAge());
Java
복사
jdbcTemplate.update()는
INSERT UPDATE DELETE
쿼리를 사용할 수 있다
첫 파라미터로는 sql을 받고, ?를 대신할 값을 차례로 넣으면 된다.
마저 수정해주면
@GetMapping("/user") public List<UserResponse> getUser(){ String sql = "SELECT * FROM user"; return jdbcTemplate.query(sql, new RowMapper<UserResponse>() { @Override public UserResponse mapRow(ResultSet rs, int rowNum) throws SQLException { long id = rs.getLong("id"); String name = rs.getString("name"); int age = rs.getInt("age"); return new UserResponse(id, name, age); } }); }
Java
복사
UserResponse에도 생성자 추가
public UserResponse(long id, String name, Integer age) { this.id = id; this.name = name; this.age = age; }
Java
복사

람다를 이용해서 수정해주자

RowMapper 부분을 옵션 엔터눌러서 람다로 변경
@GetMapping("/user") public List<UserResponse> getUser(){ 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); }); }
Java
복사
서버를 시작해주고
테이블 조회해주면
성공~

유저 업데이트 API, 삭제 API 개발과 테스트

도서관 사용자 이름을 업데이트

UserController
@PutMapping("/user") public void updateUser(@RequestBody UserUpdateRequest userUpdateRequest){ String sql = "UPDATE user SET name = ? WHERE id = ?"; jdbcTemplate.update(sql, userUpdateRequest.getName(), userUpdateRequest.getId()); }
Java
복사
package com.group.libraryapp.dto.user.request; public class UserUpdateRequest { private long id; private String name; public long getId() { return id; } public String getName() { return name; } }
Java
복사

도서관 사용자를 삭제

UserController
@DeleteMapping("/user") public void deleteUser(@RequestParam String name){ String sql = "DELETE FROM user WHERE name = ?"; jdbcTemplate.update(sql, name); }
Java
복사
기존 목록에 가서
수정 해보자
미안 르르땅
수정됨~ 주르르 36살,,,,
나이가 잘못됐으니 삭제해보자 ㅎㅎ
오 이제 삭제가 됐다.
DB가서
select * from user;
SQL
복사
잘 삭제돼서 데이터가 없다.
근데 문제가 있다
없는 유저를 삭제해달라고 보내면 성공인 200 OK가 온다.
이 부분은 예외 처리해줘야한다

유저 업데이트 API, 삭제 API 예외 처리 하기

만약 API에서 예외를 던져주면 어떻게 될끼?

@GetMapping("/user/error-test") public void errorTest(){ throw new IllegalArgumentException(); }
Java
복사
500 "Internal Server Error"가 나왔다
서버 내부에 문제가 있어서 너가 요청한게 실패했다는 뜻.

API에서 데이터 존재 여부를 확인해 예외를 던지자!

@PutMapping("/user") public void updateUser(@RequestBody UserUpdateRequest userUpdateRequest){ //조회 String readSql = "SELECT * FROM user WHERE id = ?"; //해당 id로 조회된 데이터 있는지 확인 boolean isUserNotExist = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, userUpdateRequest.getId()).isEmpty(); if(isUserNotExist){ throw new IllegalArgumentException(); } //업데이트 String sql = "UPDATE user SET name = ? WHERE id = ?"; jdbcTemplate.update(sql, userUpdateRequest.getName(), userUpdateRequest.getId()); }
Java
복사
jdbcTemplate.query(readSql, (rs, rowNum) -> 0, userUpdateRequest.getId())
Java
복사
SELECT SQL의 결과가 있으면 0 으로 반환된다.
jdbcTemplate.query(readSql, (rs, rowNum) -> 0, userUpdateRequest.getId())
Java
복사
그리고 반환된 0 은 최종적으로 List로 반환
jdbcTemplate.query()의 결과인 List가 비어 있다면, 유저가 없다는 뜻이다

포스트맨으로 확인

그전에 유저데이터가 없으니까 하나 넣어주자
insert into user (name, age) values ('A', 100);
SQL
복사
2번인 아이디의 이름을 BB로 변경했다.
잘 변경했다
이제 없는 유저의 아이디로 업데이트를 쳐보자
이제 삭제 API도 수정해주자.
@DeleteMapping("/user") public void deleteUser(@RequestParam String name){ //조회 String readSql = "SELECT * FROM user WHERE name = ?"; //해당 name 로 조회된 데이터 있는지 확인 boolean isUserNotExist = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, name).isEmpty(); if(isUserNotExist){ throw new IllegalArgumentException(); } String sql = "DELETE FROM user WHERE name = ?"; jdbcTemplate.update(sql, name); }
Java
복사
BB 유저명을 지워보자
성공
이제 없는 유저명으로 요청날려보자

Section2 정리. 다음으로

1. 디스크와 메모리 차이를 이해하고, Database의 필요성을 이해한다.
2. SQL을 이용해 MySQL Database를 조작할 수 있다.
3. 스프링 서버를 이용해 Database에 접근하고 데이터를 저장, 조회, 업데이트, 삭제할 수 있다.
4. API의 예외 상황을 알아보고 예외를 처리할 수 있다.

근데 큰 문제가 있다

한 클래스인 Controller에서 너무 많은 역할을 하고 있다.
한 API에서 무려 10개의 Table을 사용해야 한다면?!!!
왜 한 함수에서 모든 기능을 구현하면 안되는지
이 문제를 스프링을 이용해 어떻게 해결할 수 있는지 다음 섹션으로 ~

*참고