Java-Faker사용기
일단 더미 데이터를 생성한다.
https://velog.io/@wongi-kim/JavaFaker-트러블-슈팅
여기서 아래 보면 build.gradle에 의존성을 추가해주었다.
// java-faker
implementation ('com.github.javafaker:javafaker:1.0.2') { exclude module: 'snakeyaml' }
implementation group: 'org.yaml', name: 'snakeyaml', version: '2.2'
의존성을 추가해주었다면 일단 내 코드 기준으로 다음과 같이 작성한다.
public CreateStatusResponseDto dummyCreateStatus(Long boardId, int count, User user) {
Board board = checkBoard(boardId);
Faker faker = new Faker();
checkManager(user.getRole().getValue());
try(FileWriter writer = new FileWriter("data.csv")){
writer.append("title,sequence,board_id\n");
for(int i=0; i<count; i++){
String title = faker.leagueOfLegends().champion();
int sequence = i+1;
Long board_id = boardId;
writer.append(title).append(",")
.append(Integer.toString(sequence)).append(",")
.append(board_id.toString()).append("\n");
}
System.out.println("CSV 파일 생성 완료!");
} catch (IOException e) {
System.out.println("CSV 파일 생성 중 오류 발생:");
e.printStackTrace();
}
CreateStatusResponseDto responseDto = CreateStatusResponseDto.builder()
.title("더미 클리어")
.build();
return responseDto;
}
단순히 Faker 객체를 하나 생성하고 title 부분만 더미데이터로 작성하기로 했다.
해당 코드를 실행 시키면 어플리케이션 폴더에 data.csv가 생성된다.

그리고 프로젝트 요구사항에 local-infile을 사용해서 DB저장을 하는 부분이 있는데 해당 부분을 활용하려면 몇가지 설정을 해야한다.
SHOW VARIABLES LIKE 'secure_file_priv';
해당 라인을 실행해보면

이렇게 결과가 출력되는데 local-infile이 허용되는 권한을 가진 폴더? 라고 해야겠다.
/var/lib/mysql-files/ 경로에 존재하는 파일만 DB에 입력이 가능하다.
local_infile을 사용하기 위해서 일단 아래 코드를 통해 상태를 확인해야 한다.
show global variables like 'local_infile';

현재는 허용되지 않은 상태라 off로 나오는데 On으로 바꿔주어야 하므로
set global local_infile=true;를 실행하고
show global variables like 'local_infile';다시 확인해보면

바뀐걸 볼 수 있다.
조금 늦게 얘기하긴 했는데 이번 프로젝트때는 MySQL을 Docker 컨테이너에 올려서 사용했기 때문에 적어도 내 환경에서는 로컬 터미널에서 아무리 작성해도 Local_Infile이 작동하지 않았다...
그래서 다시 한 번 얘기를 해보자면
set global local_infile=true 설정을 docker에서 해줘야 한다.
docker에서 컨테이너를 열어 DB - Files로 이동해보자

여기 보이는 my.cnf를 Edit file로 열어 (windows는 my.ini 였던가)

이렇게 설정을 해주면 된다.
위에서
/var/lib/mysql-files/ 경로에 존재하는 파일만 DB에 입력이 가능하다.
라고했는데 이 폴더도 docker에서 설정해줘야 한다.
일단 코드를 실행한다면 어플리케이션 폴더에 파일이 생기는 것은 동일하기 때문에
docker cp /파일 경로/data.csv c9c747(docker-container id):/var/lib/mysql-files
명령어를 사용하여 파일을 이동해준다.
(docker에서 해도 되고 로컬 터미널에서 docker명령어가 사용된다면 로컬에서 해도 무방하다.)
일단 나는 docker에서 mysql을 실행시킬 텐데 하면서 local-infile의 설정을 붙여줘야 한다.
mysql --local-infile=1 -u root -p 혹은
mysql --local-infile=1 -h 127.0.0.1 -P <포트> -u 사용자 -p
이렇게 해서 mysql이 실행되었다면
show global variables like 'local_infile';
를 통해 상태가 On인지 off인지 다시 확인 해보고 off라면 아래 명령어를 통해 다시 ON 으로 변경해주자.
set global local_infile=true;
LOAD DATA LOCAL INFILE '/var/lib/mysql-files/data.csv'
-> INTO TABLE status
-> FIELDS TERMINATED BY ','
-> ENCLOSED BY '"'
-> LINES TERMINATED BY '\n'
-> IGNORE 1 ROWS;
대충이런 명령어를 사용하면 되므로 여기서는 자세히 다루지는 않겠음
테이블 처음 생성하고 그것대로 데이터 넣는것이라면 딱히 문제 없이 들어갈테니까
위에서 안다룬다 해놓고 바로 여기서는 제대로 다룰 텐데, 일단 나는 Spring Boot를 통해 기존에 먼저 테이블을 만들어 놓기 때문에 오류가 발생한다.
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "status_id")
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private int sequence;
@ManyToOne
@JoinColumn(name = "board_id", nullable = false)
private Board board;
@OneToMany(mappedBy = "status", cascade = CascadeType.ALL)
private List<Card> cards = new ArrayList<>();
뭐 대충이렇게 생성하고 만드는것 자체는 문제 없지만 적어도 삽입할 때는 문제가 발생했다.
| Warning | 1366 | Incorrect integer value: 'Volibear' for column 'status_id' at row 1 |
| Warning | 1265 | Data truncated for column 'created_at' at row 1 |
| Warning | 1265 | Data truncated for column 'modified_at' at row 1 |
| Warning | 1261 | Row 1 doesn't contain data for all columns |
| Warning | 1261 | Row 1 doesn't contain data for all columns |
| Warning | 1261 | Row 1 doesn't contain data for all columns
뭐 대충 integer value가 와야 하는데 String이라던가,
'created_at','modified_at' 은 데이터가 없다던가,
모든 컬럼에 데이터가 존재하지 않는 행이 있다던가 이런 오류인데
결국 테이블의 컬럼과 CSV로 넘기는 데이터가 매핑이 정확히 이루어지지 않아서 생기는 문제 같다고 생각해서 매핑하는 조건까지 해줬다
현재 CSV 구조를 보면

title, sequence, board_id 순으로 데이터가 저장되어있는데
테이블을 보면

status_id, created_at, modified_at, sequence, title, board_id 순으로 되어있으니 앞에서 부터 3개를 하면 마지막 3개의 Column은 비어있으니 오류가 생성된다.
그러므로 데이터를 삽입할 때
LOAD DATA LOCAL INFILE '/var/lib/mysql-files/data.csv'
INTO TABLE status
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(title, sequence, board_id);
마지막줄 처럼 매핑을 진행해준다.
이 과정이 필요한 이유는 Spring에서 우리가 entity를 생성할 때 전략으로 @GeneratedValue(strategy = GenerationType.SEQUENCE) 로 생성 전략을 작성했는데
인텔리제이같은 IDE같은 경우에는 DB에 데이터를 생성할 때 id가 정상적으로 증가하는 반면
MySQL에서는 GenerationType.SEQUENCE를 기본적으로 지원하지 않기 때문에 문제가 발생할 수 있다.
mysql에서 SHOW CREATE TABLE로 정보를 까보면 다르게 나온다.

(status는 내가 이미 수정했기 때문에 사진만 board사진을 가져오겠다)
여튼 사진을 보면 AUTO_INCREMENT가 안되어있는 걸 볼 수 있는데 이런 경우를 해결하기 위해 일단 기본키를 제거해야 한다.
기본 키를 제거하고 추가해야 한다
-- 기존 기본 키 제거
ALTER TABLE status DROP PRIMARY KEY;
-- `status_id`를 AUTO_INCREMENT로 설정하고 기본 키로 추가
ALTER TABLE status MODIFY status_id BIGINT AUTO_INCREMENT PRIMARY KEY;
만약 기본 키 제거가 안되는 경우가 발생한다면 다른곳에서 외래 키로 참조중일 가능성이 매우 높으므로
외래키로 참조중인 테이블에 가서 외래키를 먼저 지우고 기본키 삭제, 추가 마지막으로 다시 외래 키 조건을 설정해주면된다.
-- 외래 키 제약 조건 제거
ALTER TABLE card DROP FOREIGN KEY FK9923qase5x5j6vqmftoe3tso9;
-- 기존 기본 키 제거
ALTER TABLE status DROP PRIMARY KEY;
-- `status_id`를 AUTO_INCREMENT로 설정하고 기본 키로 추가
ALTER TABLE status MODIFY status_id BIGINT AUTO_INCREMENT PRIMARY KEY;
-- 외래 키 제약 조건 다시 추가
ALTER TABLE card ADD CONSTRAINT FK9923qase5x5j6vqmftoe3tso9 FOREIGN KEY (status_id) REFERENCES status(status_id);
여기서 외래 키는 다 다르기 때문에 SHOW를 활용하여 외래키를 확인하고 지우자.
조금 더 쉬운 방법이긴 한데
@GeneratedValue(strategy = GenerationType.IDENTITY)
로 선언할 경우 테이블을 조회하면 id 부분에 딱히 문제 없이 값이 증가하며 정상적으로 작동한다.
mysql에서 데이터를 삽입하기 전에
DELIMITER //
CREATE TRIGGER before_insert_status
BEFORE INSERT ON status
FOR EACH ROW
BEGIN
SET NEW.created_at = NOW();
SET NEW.modified_at = NOW();
END; //
DELIMITER ;
이 코드를 실행하고 삽입하게 되면 잘 작동한다.
결국 docker에 MySQL이 있기 때문에 모든 과정, 터미널 명령은 docker에서 진행된다
docker cp /파일 경로/data.csv c9c747(docker-container id):/var/lib/mysql-files 로 파일 복사mysql --local-infile=1 -u root -pshow global variables like 'local_infile'; 통해서 On or off 확인set global local_infile=true;LOAD DATA LOCAL INFILE '/var/lib/mysql-files/data.csv'
INTO TABLE status
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(title, sequence, board_id);
일단 나는 이 과정을 통해 해결하긴 했으므로 여기서 정리를 마치도록 하겠다.