테스트 데이터 삽입 ‐ (2) 회원 데이터 삽입 - ttasjwi/board-system GitHub Wiki
회원 테이블 생성
use board_db_test;
drop table users;
create table if not exists users(
user_id BIGINT NOT NULL,
email VARCHAR(255) NOT NULL,
password VARCHAR(68) NOT NULL,
username VARCHAR(15) NOT NULL,
nickname VARCHAR(15) NOT NULL,
role VARCHAR(10) NOT NULL,
registered_at DATETIME NOT NULL
);
csv 파일 생성
package com.ttasjwi.board.system.app.user.data
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import java.io.File
import java.time.format.DateTimeFormatter
class UserDataInitializer {
@Test
@DisplayName("회원 벌크 삽입 csv 파일 생성")
fun initialize() {
val csvFile = File("users.csv")
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
csvFile.bufferedWriter().use { writer ->
for (id in 1L..12_000_000L) {
val paddedId = id.toString().padStart(8, '0')
val email = "user${paddedId}@gmail.com"
val password = "{bcrypt}$2a$10\$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy"
val username = "user${paddedId}"
val nickname = "user${paddedId}"
val role = "USER"
val registeredAt = java.time.LocalDateTime.now().format(formatter)
writer.write("$id,$email,$password,$username,$nickname,$role,$registeredAt\n")
if (id % 100000 == 0L) {
println("Written $id lines...")
}
}
}
}
}
}
- 이 방식을 통해 csv 파일을 생성할 수 있다.
- 테스트 편의를 위해 snowflake 알고리즘에 의해 생성하지 않고 숫자 id로 생성한다.
- csv 파일을 통해 데이터를 생성하는 이유?
- 트랜잭션 오버헤드 최소화
- 애플리케이션에서 직접 DB에 하나씩 INSERT를 수행하면 건당 트랜잭션이 발생하거나,
- 배치 처리하더라도 네트워크 I/O, DB 커넥션 횟수, 커밋 횟수가 많아짐.
- 반면, CSV를 생성해서 LOAD DATA INFILE 등의 방식으로 한 번에 삽입하면:
- 한 트랜잭션 또는 소수의 트랜잭션으로 처리 가능
- 성능이 수십 배 이상 향상되는 경우도 있음
- 네트워크 I/O 비용 감소
- 애플리케이션 → DB로 INSERT를 반복하면 빈번한 네트워크 왕복이 발생한다.
- 로컬 파일을 서버에 복사해두고 MySQL 서버 내부에서 읽으면 I/O 비용이 훨씬 적음
- MySQL 의 대량 삽입 최적화 지원
- MySQL은 LOAD DATA INFILE, LOAD DATA LOCAL INFILE, BULK INSERT 등의 대량 삽입 메커니즘을 제공
- CSV 형식은 그중 가장 직관적이고 지원이 넓은 표준 포맷이므로, 대량 삽입 시 효율적
- 데이터 생성과 삽입 분리
- CSV로 생성해두면 데이터 생성 로직과 DB 삽입을 분리할 수 있어 유지보수성과 재사용성이 높아짐
- 예: 성능테스트에서 여러번 재삽입하거나, DB 초기화 등에 재활용 가능
- 예: 일단 로컬에서 데이터를 생성하여 테스트 해보고, 나중에 테스트 서버에 데이터 삽입시에도 CSV 파일 재사용 가능
데이터 삽입 (로컬)
docker cp data/users.csv mysql:/tmp/users.csv
LOAD DATA INFILE '/tmp/users.csv'
INTO TABLE users
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
(user_id, email, password, username, nickname, role, registered_at);
데이터삽입 (RDS)


- advanced -> allowLoadLocalInfile 허용
회원 조회
mysql> select count(*) from users;
+----------+
| count(*) |
+----------+
| 12000000 |
+----------+
1 row in set (3.47 sec)
mysql> select * from users order by user_id asc limit 30;
+---------+------------------------+----------------------------------------------------------------------+--------------+--------------+------+---------------------+
| user_id | email | password | username | nickname | role | registered_at |
+---------+------------------------+----------------------------------------------------------------------+--------------+--------------+------+---------------------+
| 1 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000001 | user00000001 | USER | 2025-06-09 14:39:08 |
| 2 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000002 | user00000002 | USER | 2025-06-09 14:39:08 |
| 3 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000003 | user00000003 | USER | 2025-06-09 14:39:08 |
| 4 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000004 | user00000004 | USER | 2025-06-09 14:39:08 |
| 5 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000005 | user00000005 | USER | 2025-06-09 14:39:08 |
| 6 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000006 | user00000006 | USER | 2025-06-09 14:39:08 |
| 7 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000007 | user00000007 | USER | 2025-06-09 14:39:08 |
| 8 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000008 | user00000008 | USER | 2025-06-09 14:39:08 |
| 9 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000009 | user00000009 | USER | 2025-06-09 14:39:08 |
| 10 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000010 | user00000010 | USER | 2025-06-09 14:39:08 |
| 11 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000011 | user00000011 | USER | 2025-06-09 14:39:08 |
| 12 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000012 | user00000012 | USER | 2025-06-09 14:39:08 |
| 13 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000013 | user00000013 | USER | 2025-06-09 14:39:08 |
| 14 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000014 | user00000014 | USER | 2025-06-09 14:39:08 |
| 15 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000015 | user00000015 | USER | 2025-06-09 14:39:08 |
| 16 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000016 | user00000016 | USER | 2025-06-09 14:39:08 |
| 17 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000017 | user00000017 | USER | 2025-06-09 14:39:08 |
| 18 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000018 | user00000018 | USER | 2025-06-09 14:39:08 |
| 19 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000019 | user00000019 | USER | 2025-06-09 14:39:08 |
| 20 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000020 | user00000020 | USER | 2025-06-09 14:39:08 |
| 21 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000021 | user00000021 | USER | 2025-06-09 14:39:08 |
| 22 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000022 | user00000022 | USER | 2025-06-09 14:39:08 |
| 23 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000023 | user00000023 | USER | 2025-06-09 14:39:08 |
| 24 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000024 | user00000024 | USER | 2025-06-09 14:39:08 |
| 25 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000025 | user00000025 | USER | 2025-06-09 14:39:08 |
| 26 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000026 | user00000026 | USER | 2025-06-09 14:39:08 |
| 27 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000027 | user00000027 | USER | 2025-06-09 14:39:08 |
| 28 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000028 | user00000028 | USER | 2025-06-09 14:39:08 |
| 29 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000029 | user00000029 | USER | 2025-06-09 14:39:08 |
| 30 | [email protected] | {bcrypt}$2a$10$glxdATzDUUpvB92yt7OkI.zCPjgn1R1v2uWnQ43gHo.st2t9B9hMy | user00000030 | user00000030 | USER | 2025-06-09 14:39:08 |
+---------+------------------------+----------------------------------------------------------------------+--------------+--------------+------+---------------------+
회원테이블에 제약조건 추가
ALTER TABLE users
ADD CONSTRAINT unique_email UNIQUE (email),
ADD CONSTRAINT unique_username UNIQUE (username),
ADD CONSTRAINT unique_nickname UNIQUE (nickname);
DATA GRIP 에 맞게 생성
- 테이블을 생성하고 테이블에 우클릭해서 csv 파일을 import 할 수 있다.