문제 상황
서비스 오픈 초창기, 혼자 백엔드를 맡아 개발하던 중이었다. 실제 서비스 운영 경험이 처음이라 매일 긴장하며 작업하던 나날 중 운영 서버에 배포 직후 서버가 다운되었다는 알림을 받았다.
에러 로그
[main] ERROR org.springframework.boot.SpringApplication - Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed;
nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory;
nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing column [has_event_invitation_reward] in table [member]
에러 메시지 분석:
- member 테이블에 has_event_invitation_reward 컬럼이 없음
- Hibernate 스키마 검증(validation) 단계에서 실패
- EntityManagerFactory 생성 실패로 애플리케이션 기동 불가
원인 분석
발생 경위
- 초대 이벤트 기능 구현을 위해 Member 엔티티에 hasEventInvitationReward 필드 추가
- 로컬 환경에서 테스트 후 dev 브랜치에 머지
- main 브랜치로 머지 후 운영 배포
- 운영 DB에 컬럼 추가 작업을 하지 않은 채 배포하여 애플리케이션 기동 실패
근본 원인
엔티티 변경 시 운영 DB 스키마 동기화 누락
# 로컬 환경
spring:
jpa:
hibernate:
ddl-auto: update
# 운영 환경
spring:
jpa:
hibernate:
ddl-auto: validate
- 로컬 환경: update 모드로 인해 엔티티 변경 시 자동으로 컬럼이 추가되어 문제를 인지하지 못했다
- 운영 환경: validate 모드로 정상적인 설정으로 수동으로 DB 스키마를 변경했어야 했다
해결 방법
1. 데이터베이스 스키마 확인
DESC member;
has_event_invitation_reward 컬럼이 존재하지 않음을 확인
2. 컬럼 추가
ALTER TABLE member
ADD COLUMN has_event_invitation_reward VARCHAR(1) NULL;
3. 애플리케이션 재시작
- 컬럼 추가 확인
- 애플리케이션 정상 기동 확인
ddl-auto 옵션
| 옵션동작 | 방식사용 | 환경 |
| create | 기존 테이블 삭제 후 재생성 | 사용 금지 (데이터 손실) |
| create-drop | 애플리케이션 종료 시 테이블 삭제 | 테스트 환경 |
| update | 엔티티 변경사항만 스키마에 반영 | 로컬 개발 환경만 |
| validate | 스키마 검증만 수행, 변경 없음 | 운영 환경 |
| none | 아무 작업도 수행하지 않음 | 운영 환경 |
운영 환경에서 validate를 사용하는 이유
- 안전성: 의도하지 않은 스키마 변경 방지
- 명시성: 모든 스키마 변경을 명시적으로 관리
- 추적성: 변경 이력 관리 용이
- 롤백 가능성: 문제 발생 시 되돌릴 수 있음
추가 개선 방안
- Flyway 도입하기
후기
분명 스프링부트를 공부할 때 JPA의 ddl-auto 설정 5가지를 모두 테스트하며 각 옵션의 동작 방식을 확인했었다. create는 테이블을 삭제하고 재생성하고, update는 변경사항만 반영하고, validate는 검증만 한다는 것을 알고 있었다.
하지만 막상 실제 운영 서비스에서 에러가 발생했다. 더 심각한 건 새벽도 아닌 낮시간대 유저들이 한창 서비스를 이용하고 있는 시간대에 서버가 다운되었다는 것이었다. 운영 서버 장애는 처음이었다. 로그를 확인하고, 원인을 파악하고, 급하게 SQL을 실행하고 서버를 재시작하는 30분에 온 에너지를 다 썼다. 심장이 매우 빠르게 뛰고 기진맥진했다.
정말 이론과 실전은 다르다
로컬에서는 ddl-auto: update로 설정되어 있어 엔티티만 수정하면 테이블 구조가 자동으로 변경되어서 이게 너무 편했고 당연하게 여겼다. 하지만 운영 환경에서는 validate 모드가 설정되어 있는 걸 알고 있어도 바로 대응하지 못했다.
이후로 스타트업이라 스프린트 단위가 짧고 그만큼 기능 추가가 잦아 DB 스키마 변경이 빈번하다는 꺠닫고 PR에 배포 전 체크리스트를 만들어 항상 확인하면서 작업해서 이 날 이후로는 해당 에러로 서버가 다운되는 일은 없었다.
백엔드를 혼자 작업하다보니 검토해 줄 사수도 물어볼 동료도 없었다. PR을 올려도 내가 직접 머지했고, 배포도 내가 직접 했다. 평소에는 이런 자율성이 좋았다. 빠르게 결정하고, 빠르게 구현하고, 빠르게 배포할 수 있었으니까. 하지만 이번 일로 자율성에는 그만큼의 책임이 따른다는 것을 뼈저리게 느끼게 되었다.
'운영·배포' 카테고리의 다른 글
| [운영/배포] 베타 오픈 준비 체크리스트 - 인프라부터 UX까지 (3) | 2026.03.09 |
|---|---|
| [트러블 슈팅] CORS 에러: 외부 API는 클라이언트에서 막히고 서버에서는 되는 이유 (2) | 2025.11.26 |