이 가이드는 MySQL 데이터베이스(대부분의 다른 가이드와 많은 샘플 애플리케이션에서 사용하는 메모리 내 임베디드 데이터베이스와 반대)에 연결된 Spring 애플리케이션을 생성하는 과정을 안내합니다. 이는 데이터베이스에 액세스하기 위해 Spring Data JPA를 사용하지만 이는 가능한 많은 선택 중 하나일 뿐입니다(예를 들어 일반 Spring JDBC를 사용할 수 있음).
https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/
https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-22-04
https://www.ionos.com/digitalguide/websites/web-development/mysql-on-ubuntu-2204/
sudo apt update
sudo apt install mysql-server
systemctl status mysql
$ sudo mysql
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '비밀번호';
mysql > exit
보안 스크립트를 실행하면 익명 유저, 테스트 데이터베이스, 루트 권한 사용자의 원격 이용 등 여러 보안 관련 설정에 대한 문답이 이어진다. 모든 변경사항을 적용하려면 mysql을 닫고 다시 실행한다.
$ mysql -u root -p
ALTER USER 'root'@'localhost' IDENTIFIED WITH auth_socket;
IDENTIFIED WITH auth_socket
은 MySQL의 사용자 계정이 운영 체제의 인증 소켓(authentication socket)을 통해 인증되도록 하는 것을 의미합니다. 이 설정은 사용자가 MySQL에 암호 없이 로그인할 수 있게 만듭니다. 즉, 해당 운영 체제에 로그인한 사용자는 MySQL에 암호 입력 없이 자동으로 접속할 수 있게 됩니다. 즉, sudo mysql
로 접속할 수 있다.
$ sudo mysql
CREATE USER 'username'@'host' IDENTIFIED WITH authentication_plugin BY 'password';
사용자 인증 플러그인을 선택할 때 여러 가지 옵션이 있습니다. 앞서 언급한 auth_socket
플러그인은 유효한 사용자가 데이터베이스에 액세스하기 위해 비밀번호를 입력하지 않고도 강력한 보안을 제공하므로 편리할 수 있습니다. 그러나 이는 외부 프로그램이 MySQL과 상호 작용해야 할 때 상황을 복잡하게 만들 수 있는 원격 연결도 방지합니다.
대안으로, 사용자가 MySQL의 기본 플러그인인 caching_sha2_password
를 사용하여 인증하도록 구문의 WITH authentication_plugin
부분을 완전히 생략할 수 있습니다. MySQL 문서에서는 강력한 보안 기능으로 인해 비밀번호로 로그인하려는 사용자에게 이 플러그인을 권장합니다.
caching_sha2_password
로 인증하는 사용자를 생성하려면 다음 명령을 실행합니다. sammy
를 원하는 사용자 이름으로 변경하고 비밀번호를 선택한 강력한 비밀번호로 변경하세요.
CREATE USER 'sammy'@'localhost' IDENTIFIED BY 'password';
GRANT PRIVILEGE ON database.table TO 'username'@'host';
작업용 계정에 권한을 부여한다. 이를테면 아래와 같이 부여할 수 있다.
GRANT CREATE, ALTER, DROP, INSERT, UPDATE, INDEX, DELETE, SELECT, REFERENCES, RELOAD on *.* TO 'sammy'@'localhost' WITH GRANT OPTION;
MySQL 데이터베이스를 생성하고 Spring 애플리케이션을 빌드한 후 새로 생성된 데이터베이스에 연결합니다.
MySQL은 GPL 라이선스를 받았으므로 MySQL과 함께 배포하는 모든 프로그램 바이너리도 GPL을 사용해야 합니다. GNU 일반 공중 사용 허가서를 참조하세요.
터미널(Microsoft Windows의 명령 프롬프트)을 열고 새 사용자를 생성할 수 있는 사용자로 MySQL 클라이언트를 엽니다.
예를 들어 Linux 시스템에서는 다음 명령을 사용합니다.
$ sudo mysql --password
이는 루트로 MySQL에 연결하고 모든 호스트에서 사용자에 대한 액세스를 허용합니다. 이는 프로덕션 서버에 권장되는 방법이 아닙니다.
새 데이터베이스를 생성하려면 mysql 프롬프트에서 다음 명령을 실행합니다.
mysql> create database db_example; -
mysql> create user 'springuser'@'%' identified by 'ThePassword';
mysql> grant all on db_example.* to 'springuser'@'%';
application.properties
FileSpring Boot는 모든 것에 대한 기본값을 제공합니다. 예를 들어 기본 데이터베이스는 H2
입니다. 따라서 다른 데이터베이스를 사용하려면 application.properties
파일에서 연결 속성을 정의해야 합니다.
다음 목록에 표시된 대로 src/main/resources/application.properties
라는 리소스 파일을 만듭니다.
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/db_example
spring.datasource.username=springuser
spring.datasource.password=ThePassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.jpa.show-sql: true
여기서 spring.jpa.hibernate.ddl-auto
는 none
, update
, create
또는 create-drop
일 수 있습니다. 자세한 내용은 Hibernate 문서를 참조하세요.
none
: MySQL의 기본값입니다. 데이터베이스 구조는 변경되지 않습니다.
update
: Hibernate는 주어진 엔터티 구조에 따라 데이터베이스를 변경합니다.
create
: 매번 데이터베이스를 생성하지만 닫을 때 삭제하지 않습니다.
create-drop
: 데이터베이스를 생성하고 SessionFactory
가 닫힐 때 삭제합니다.
아직 데이터베이스 구조가 없기 때문에 create
또는 update
부터 시작해야 합니다. 첫 번째 실행 후 프로그램 요구 사항에 따라 update
또는 none
으로 전환할 수 있습니다. 데이터베이스 구조를 일부 변경하려면 update
를 사용하십시오.
H2
및 기타 내장 데이터베이스의 기본값은 create-drop
입니다. MySQL
과 같은 다른 데이터베이스의 경우 기본값은 none
입니다.
데이터베이스가 프로덕션 상태가 된 후에는 이를
none
으로 설정하고 Spring 애플리케이션에 연결된 MySQL 사용자의 모든 권한을 취소하고 MySQL 사용자에게SELECT
,UPDATE
,INSERT
및DELETE
만 제공하는 것이 좋은 보안 관행입니다. 이에 대한 자세한 내용은 이 가이드 끝부분에서 확인할 수 있습니다.
@Entity
모델 만들기다음 목록(src/main/java/guides/accessingdatamysql/User.java)에 표시된 대로 엔터티 모델을 생성해야 합니다.
package guides.accessingdatamysql;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity // Hibernate에게 이 클래스로부터 테이블을 만들라고 말함
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Hibernate는 자동으로 엔터티를 테이블로 변환합니다.
다음 목록(src/main/java/repository/accessingdatamysql/UserRepository.java)에 표시된 대로 사용자 레코드를 보관하는 저장소를 생성해야 합니다.
package guides.accessingdatamysql;
import org.springframework.data.repository.CrudRepository;
// This will be AUTO IMPLEMENTED by Spring into a Bean called userRepository
// CRUD refers Create, Read, Update, Delete
public interface UserRepository extends CrudRepository<User, Integer> { }
Spring은 동일한 이름을 가진 빈에서 이 저장소 인터페이스를 자동으로 구현합니다(대소문자가 변경되어 userRepository
라고 함).
다음 목록(src/main/java/guides/accessingdatamysql/MainController.java)에 표시된 대로 애플리케이션에 대한 HTTP 요청을 처리하는 컨트롤러를 생성해야 합니다.
package guides.accessingdatamysql;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller // This means that this class is a Controller
@RequestMapping(path="/demo") // This means URL's start with /demo (after Application path)
public class MainController {
@Autowired // This means to get the bean called userRepository
// Which is auto-generated by Spring, we will use it to handle the data
private UserRepository userRepository;
@PostMapping(path="/add")
public @ResponseBody String addNewUser (@RequestParam String name, @RequestParam String email) {
// @ResponseBody means the returned String is the response, not a view name
// @RequestParam means it is a parameter from the GET or POST request
User n = new User();
n.setName(name);
n.setEmail(email);
userRepository.save(n);
return "Saved";
}
@GetMapping(path="/all")
public @ResponseBody Iterable<User> getAllUsers() {
// This returns a JSON or XML with the users
return userRepository.findAll();
}
}
앞의 예에서는 두 엔드포인트에 대해
POST
및GET
을 명시적으로 지정합니다. 기본적으로@RequestMapping
은 모든 HTTP 작업을 매핑합니다.
Spring 초기화는 애플리케이션을 위한 간단한 클래스를 생성합니다. 다음 목록은 이 예제를 위해 초기화가 생성한 클래스(src/main/java/guides/accessingdatamysql/AccessingDataMysqlApplication.java)를 보여줍니다.
package guides.accessingdatamysql;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AccessingDataMysqlApplication {
public static void main(String[] args) {
SpringApplication.run(AccessingDataMysqlApplication.class, args);
}
}
이제 애플리케이션이 실행되고 있으므로 curl
이나 유사한 도구를 사용하여 테스트할 수 있습니다. 테스트할 수 있는 두 개의 HTTP 엔드포인트가 있습니다.
GET localhost:8080/demo/all
: 모든 데이터를 가져옵니다. POST localhost:8080/demo/add
: 데이터에 한 명의 사용자를 추가합니다.다음 curl 명령은 사용자를 추가합니다.
$ curl http://localhost:8080/demo/add -d name=First -d email=someemail@someemailprovider.com
응답은 다음과 같아야 합니다.
Saved
다음 명령은 모든 사용자를 표시합니다.
$ curl http://localhost:8080/demo/all
응답은 다음과 같아야 합니다.
[{"id":1,"name":"첫번째","email":"someemail@someemailprovider.com"}]
sudo mysql
show databases;
show tables;
select * from user;
프로덕션 환경에서는 SQL 주입 공격에 노출될 수 있습니다. 해커는 DROP TABLE
또는 기타 파괴적인 SQL 명령을 주입할 수 있습니다. 따라서 보안을 위해 애플리케이션을 사용자에게 공개하기 전에 데이터베이스를 일부 변경해야 합니다.
다음 명령은 Spring 애플리케이션과 관련된 사용자의 모든 권한을 취소합니다.
revoke all on db_example.* from 'springuser'@'%';
이제 Spring 애플리케이션은 데이터베이스에서 아무 것도 할 수 없습니다.
애플리케이션에는 일부 권한이 있어야 하므로 다음 명령을 사용하여 애플리케이션에 필요한 최소 권한을 부여하세요.
grant select, insert, delete, update on db_example.* to 'springuser'@'%';
모든 권한을 제거하고 일부 권한을 부여하면 Spring 애플리케이션에 구조(스키마)가 아닌 데이터베이스의 데이터만 변경하는 데 필요한 권한이 제공됩니다.
데이터베이스를 변경하려는 경우:
1. 권한을 다시 부여합니다.
2. spring.jpa.hibernate.ddl-auto
를 변경하여 update
하세요.
3. 응용 프로그램을 다시 실행하십시오.
그런 다음 여기에 표시된 두 명령을 반복하여 애플리케이션을 프로덕션 용도로 다시 안전하게 만듭니다. 더 나은 방법은 Flyway 또는 Liquibase와 같은 전용 마이그레이션 도구를 사용하는 것입니다.