공통코드 초기화 스크립트 추가로 신규 환경 세팅 자동화
목차
공통코드 초기화 스크립트는 만들 때 별거 아닌 것 같지만, 없으면 신규 환경 세팅할 때마다 DBA한테 SQL 달라거나, 개발자 로컬에서 NPE 터지거나, "왜 드롭다운이 비어있어요?" 슬랙이 날아오는 상황이 반복된다. 이번에 그 반복을 끊은 작업이었음.
공통코드가 뭐고 왜 초기화 스크립트가 필요한가
공통코드(Common Code)는 시스템 전반에서 쓰이는 상태값, 타입, 카테고리 같은 값들을 DB로 관리하는 구조다. 예를 들어 "사용여부 = Y/N", "주문상태 = 대기/처리중/완료" 같은 것들. 이걸 코드에 하드코딩하면 값이 바뀔 때마다 배포가 필요하고, 관리자가 직접 수정할 수도 없다. 그래서 DB에 올려두고, 관리자 화면에서 코드 목록을 수정할 수 있게 한다.
구조는 단순하다.
공통코드 그룹 (GROUP_CODE)
├── 코드 항목 1 (CODE_VALUE, CODE_NAME)
├── 코드 항목 2
└── ...
그룹이 있고, 그 하위에 실제 항목들이 달린다. Spring MVC + MyBatis 구조에서는 SQL XML에서 그룹 코드 기준으로 쿼리를 뽑아 JSP 뷰 레이어에 전달하는 흐름이 일반적이다.
문제는 신규 환경을 구성할 때다. 애플리케이션 코드는 git으로 받으면 되는데, DB 기초 데이터는 별도로 넣어줘야 한다. 이게 자동화 안 되어 있으면 로컬 세팅 가이드 문서가 따로 돌아다니거나, 누군가가 개발 DB에서 수동으로 INSERT 쳐서 넣어준다. 그 과정에서 데이터가 빠지거나 틀리면 찾기도 쉽지 않다.
초기화 스크립트를 하나 두면 이 문제가 깔끔하게 해결된다. 로컬이든 개발 서버든 신규 서버든, 스크립트 한 번 실행으로 기본 공통코드 전체가 들어간다.
구현하면서 챙긴 것들
작업 규모가 작더라도 DB를 건드리는 스크립트는 몇 가지를 꼭 확인하게 된다.
트랜잭션 범위. 초기화 스크립트는 여러 그룹과 항목을 한꺼번에 넣기 때문에, 중간에 실패하면 어중간한 상태로 남는다. @Transactional 범위를 명확히 잡아서 전체 성공 or 전체 롤백 되게 해야 한다. MyBatis에서는 SqlSession 레벨 트랜잭션을 수동으로 잡는 경우도 있어서 어노테이션만 믿으면 안 될 때가 있다. 실제 커밋이 어디서 일어나는지 한 번은 trace해보는 게 낫다.
예외 처리 방향. RuntimeException을 그냥 전파할지, 잡아서 응답으로 변환할지 선택을 해야 한다. 초기화 스크립트는 보통 배치성으로 한 번 실행하는 거라, 예외가 터지면 바로 실패로 보이는 쪽이 낫다. 조용히 삼키면 절반만 들어간 줄 모르고 지나친다.
중복 처리. 초기화 스크립트를 여러 번 실행했을 때 어떻게 될지를 정해둬야 한다. 옵션은 크게 세 가지다.
| 방식 | 설명 | 적합한 상황 |
|---|---|---|
| INSERT IGNORE | 기존 데이터 있으면 그냥 넘김 | 이미 있는 값 건드리면 안 될 때 |
| UPSERT (ON DUPLICATE KEY UPDATE) | 있으면 값 업데이트 | 초기값이 변경될 수 있을 때 |
| 선행 삭제 후 삽입 | 기존 전부 지우고 새로 넣음 | 완전히 새로 시작해야 할 때 |
이번엔 신규 환경 세팅 목적이라 INSERT IGNORE 방식으로 처리했다. 기존 데이터를 실수로 덮어쓰는 사고를 막을 수 있다.
로그. 운영 환경에서 초기화 스크립트를 돌리면 뭔가 잘못됐을 때 추적이 안 되면 곤란하다. 그룹코드별로 몇 건 넣었는지, 스킵한 건 뭔지 INFO 로그로 남겨뒀다. 디버그 레벨로 숨겨두면 운영에서 못 본다.
캐시 전략까지 이어지는 이유
공통코드를 DB에서 관리하는 구조면, 화면에서 드롭다운 렌더링할 때마다 DB를 치면 부담이 생긴다. 그래서 앱 시작 시 공통코드 전체를 캐시해두고 메모리에서 읽는 방식을 주로 쓴다.
@Component
public class CommonCodeCache {
private Map<String, List<CommonCode>> codeMap = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
List<CommonCodeGroup> groups = commonCodeMapper.selectAllGroups();
for (CommonCodeGroup group : groups) {
List<CommonCode> codes = commonCodeMapper.selectByGroupCode(group.getGroupCode());
codeMap.put(group.getGroupCode(), codes);
}
}
public List<CommonCode> getByGroup(String groupCode) {
return codeMap.getOrDefault(groupCode, Collections.emptyList());
}
}
@PostConstruct로 앱 뜰 때 한 번 로드하고, 이후 조회는 메모리에서 처리한다. 관리자가 코드를 수정했을 때 캐시를 어떻게 갱신할지는 별도로 처리해야 하는데, 재시작으로 갱신하는 단순한 방식도 운영 수준에 따라 충분하다.
이 패턴이 제대로 동작하려면 앱 시작 시점에 공통코드 데이터가 DB에 있어야 한다는 전제가 깔린다. 초기화 스크립트가 없으면 새 환경에서 앱 올릴 때마다 codeMap이 비어있고, 화면 드롭다운 전부가 빈 채로 뜬다. 이번 작업이 그 연결고리였다.
작업 자체는 크지 않았다. 로컬에서 기동 확인하고, 개발 DB에 반영하고, 배포하는 흐름으로 마무리했다. 근데 이런 류의 작업이 쌓이면 나중에 "왜 이게 자동화 안 돼있었지?" 하는 질문이 나올 일이 없어진다. 신규 멤버가 들어왔을 때 세팅 삽질로 반나절 날리는 것도 줄어든다. 눈에 안 띄는 변경이지만 그래서 오히려 챙겨야 하는 작업이라고 생각한다.
댓글 0
첫 댓글 달아줘.