bash로 mysql복사 스크립트 작성해본 후기

2024.05.12

개발용 데이터베이스에 있는 데이터를 각자의 로컬db로 복사하는 bash 스크립트를 작성한 후기입니다. 이 포스팅의 내용은 지극히 개인적인 아이디어 입니다.



서론

현재 재직중인 직장에서 프로젝트를 진행하고있다. 시니어도 없고 일이 바쁜 스타트업 특성상 개발환경이 영좋지 못하거나 심각하게 어질러진 경우는 매우흔하다. 나 또한 똑같은 문제를 겪고있었다. 내가 마주한 상황은 개발을 진행하는데 모든 팀원이 하나의 데이터베이스를 공용으로 쓰고면서 db의 스키마가 지속적으로 충돌하거나 덮어씌워지는 문제였다.

문제점

현재 우리회사의 개발환경은 이러하다.

  1. prisma ORM 사용
  2. 데이터베이스는 rds에 올라가있는 dev용 db 하나만 쓰고있는상태
  3. 각자 브랜치를 따서 개발을 진행하지만 테스트를 진행할때 각자 로컬db를 사용하는게 아니라 수정한 스키마를 rds에 바로 반영하는 상태

여기서 문제가 생기는 이유는 prisma orm 특성상 schema.prisma 파일에 db의 구조를 작성하고 명령어를 통해 db에 푸쉬하면 그대로 db에 반영이 된다.
하지만 앞서 말했듯이 새로운 기능을 추가하면서 팀원 각각의 스키마 파일이 다른상태에서 서로 rds에 푸쉬를 하다보니 서로의 스키마 내용대로 db가 계속 덮어씌워 진다는것이었다. 아래의 그림을 보자.

pic1 [그림 1]

rds에 원래 상태는 테이블 A,B,C 세개가 있는 상태에서 팀원1이 D 라는 테이블을 추가하고 rds에 푸쉬했다.

pic2 [그림 2]

pic3 [그림 3]

이후 팀원2가 E 라는 테이블을 추가하고 rds에 푸쉬를 했다 이때 팀원2의 스키마 파일에는 테이블 E 가 없는상태이므로 rds에 있던 테이블 D가 누락된다. 반대로 팀원1 이 스키마를 다시 푸쉬하면 팀원2의 테이블 E가 날아간다. 이러한 문제 때문에 개발을 하는데 큰 혼란과 불편함이 자주 발생했다.

원인 파악

가만 생각해보니 대체 우리가 왜 이런짓을 하고있는지 생각해보았다. 사실 개발 초기단계에서는 각자의 로컬db를 사용하여 개발을 하긴했었다. 다만 개발이 진행될수록 새로운 기능을 추가할때마다 기존 기능과 연동되는것들이 많아 제대로 연결이 되었는지 테스트를 진행해야 했는데 이 과정에서 연동된 데이터를 추가하거나 수정하는 작업이 정말 복잡하고 시간이 많이 걸렸다. (예를 들면 '요리' 라는걸 생성하려면 그 요리에 필요한 '재료' 가 전부 준비되어야 생성가능) 그런데 rds에 있는 db에는 기능 테스트를 진행하며 검증이 완료된 데이터들이 저장된 상태였고 그 데이터들을 그대로 사용하기 시작하면서 부터 로컬db에서 개발하는것이 아닌 rds db를 그대로 사용하게된거 같았다.
사실 개발 초기단계부터 마이그레이션도 없이 prisma db push 만 남용하며 개발을 진행하며 간간히 충돌이 있었지만 그때는 규모가 작아서 대충 수정하는식으로 눈감고 방치하고 있던게 점점 규모가 커질수록 감당하기 어려워져서 커다란 문제가 터지기 시작한거였다.

해결방안 찾기

다시한번 우리의 상황과 문제점들을 정리해 보았다.

  1. rds에 있는 dev db를 그대로 쓰고있는 이유는 테스트에 필요한 데이터가 들어있어서 그걸 그대로 쓰고있다.
  2. 로컬에 직접 db를 생성하고 작업할순 있지만 테스트 데이터를 직접 넣어주기가 너무 불편하다.

이렇게 정리가 되었다. 그리고 난 첫번째로 prisma ORM 자체의 마이그레이션 기능으로(개발팀 모두가 마이그레이션이 뭔지도 모르는상황) 현재 우리 상황을 해결할수있는 방법들을 찾아보았다.(이때는 마이그레이션을 안해서 생기는 문제인줄 알았음) 공식문서, github 토론 페이지, prisma 문서에 있는 ai, 기타 개발자 커뮤니티에 질문등 내가 찾을수있는 자료는 전부다 찾아보았고 아래와 같은 결론을 내렸다.

애초에 여러사람이 동시에 공용db를 수정하면서 작업한다는 상황 자체가 말이 안돼는 상황이며 db를 나누는게 아닌 이상 이건 근본적으로 해결할수있는 방법이 없다.

결국은 로컬db를 사용한 개발을 할수밖에 없다는것인데 그렇다면 차라리 자기 로컬에 필요한 데이터를 복사하는거라도 좀 쉽고 간단하게 만들자 로 방향을 틀었다. 가만 생각해보니 이전까지는 테스트에 필요한 데이터가 있다면 로컬db에다 직접 넣어주는 방식으로 테스트를 진행했었다. 근데 dev db에 있는 데이터가 필요한거라면 dev db를 덤프해서 로컬로 복사하는 쉽고 간단한 방법이 있는데 이때까지 그런 방법이 있다는 사실조차 까먹고 있었다. 역시 사람이 스트레스를 받으면 인지능력이 떨어지나보다. 그래도 매번 dump를 하고 자기 로컬에 복사하는 작업은 귀찮으니 이걸 package.json에 스크립트로 작성해보기로 했다. 갑자기 떠오른 아이디어였지만 나름 합리적이고 괜찮은 방법이라 생각했다.

im so smart

본론

rds에 있는 db를 덤프하고 로컬db에 복사한다음 덤프파일을 지우는 작업까지 묶어서 package.json에 스크립트를 작성해보았다.

//package.json
"scripts": {
  //...
  "dumpDatabase":"mysqldump -h {원격 db url} -u {유저} -p{비밀번호} --databases {복사할 db} --add-drop-database > dump.sql && mysql -u {로컬유저} -p{로컬비밀번호} < dump.sql && rm dump.sql"
},

이렇게 작성한 다음 dumpDatabase 명령어를 실행시켰을때 제대로 원격에 있는 데이터베이스가 로컬로 복사되는걸 확인했다. 하지만 두가지 문제가 있었다. 첫번째로 package.json 파일은 원격저장소에 푸쉬되는데 이 파일에다 db url이나 비밀번호같은 민감함 정보를 적으면 안된다. 두번째로 각자 로컬db에 복사를할때도 로컬db의 정보가 사람마다 전부 다르다는것이다. 그래서 각자의 .env 파일에 있는 정보를 불러와 스크립트를 실행해야했다. 그런데 cli 에서 파일을 읽어 환경 변수를 사용하려니 특정 패키지 설치가 필요하다고 해서 그냥 쉘스크립트 파일로 작성하기로 했다.

.env 파일

HOST_URL=호스트 url
HOST_PASSWORD=원격db 비밀번호
HOST_USER=원격db 유저

LOCAL_USER=로컬db 유저
LOCAL_PASSWORD=로컬db 비밀번호

전체 쉘 스크립트 코드

#!/bin/bash
# 변수 선언
host=$(grep 'HOST_URL=' .env | sed 's/^.*=//')
password=$(grep 'HOST_PASSWORD=' .env | sed 's/^.*=//')
user=$(grep 'HOST_USER=' .env | sed 's/^.*=//')

local_user=$(grep 'LOCAL_USER=' .env | sed 's/^.*=//')
local_password=$(grep 'LOCAL_PASSWORD=' .env | sed 's/^.*=//')

mysqldump -h $host -u $user -p$password --databases prisma --add-drop-database > dump.sql
mysql -u $local_user -p$local_password < dump.sql
rm dump.sql

프로젝트의 루트 디렉토리에 dump.sh 파일을 만들었다.

host=$(grep 'HOST_URL=' .env | sed 's/^.*=//')

위 명령어는 .env 파일에서 데이터를 읽어서 변수로 지정하는 명령어다. grep 부분은 스택 오버플로우 에서 찾은 방법인데 .env 파일에서 HOST_URL= 로 시작하는 문자열을 찾은다음 정규식을 통해 뒷 내용만 잘라서 가져오는 명령어다. HOST_URL=top_secret 문자열이 있으면 ^.*= 패턴으로 시작부터 = 까지를 빈문자열로 치환한다. 즉, 뒷쪽의 'top_secret' 만 추출할수있다.

쉘 스크립트 파일 실행 하기

package.json 파일에 스크립트를 수정해준다.

"scripts": {
  "dumpDatabase":"dump.sh"
}

위에서 작성했던 쉘 스크립트를 직접 실행하면 정상적으로 rds의 db를 로컬로 덤프 -> 로컬db에 복사 -> dump 파일 삭제 순서대로 실행된다. 일단 윈도우 기준 문제 없이 잘 동작하지만 맥은 아직 확인 못했다. db충돌 문제때문에 일단은 로컬db를 사용하되 dev db의 데이터가 필요한거라면 그걸 로컬로 복사해서 사용하자 라는 방식인데 추가적으로 dev db의 데이터를 복사하는 작업도 귀찮으니 명령어 한번으로 실행할수있게끔 스크립트를 작성해 보았다. 이 방법이 근본적인 해결방법은 아닐수도 있지만 나름대로 결과는 만족스러웠다.

Do you want something exciting?

© 2022. YSH All rights reserved.