1. 들어가며
현재 사이드 프로젝트로 크리에이터 디지털 굿즈(포토카드) 플랫폼을 개발하고 있다. 오늘은 개발의 첫 단추이자 가장 중요한 단계 중 하나인 DB 모델링(ERD)과 Spring Boot 프로젝트 초기 설정을 진행했다. 단순히 테이블을 만들고 서버를 띄우는 것이 아니라, 확장성과 데이터 무결성을 최우선으로 고려하며 설계한 과정과 그 이유를 기록한다.
2. DB 모델링: 왜 이렇게 설계했는가?
전체 도메인을 회원(Auth), 상점(Store), 결제(Commerce), 게임(Synthesis), 전시(Exhibition), 운영(Operation) 6개 영역으로 모듈화하여 총 22개의 테이블을 설계했다. 이 과정에서 고민했던 핵심 기술 포인트는 다음과 같다.
① PostgreSQL JSONB의 활용 RDBMS를 사용하지만 일부 데이터는 유연성이 필요했다.
- 상황: 크리에이터마다 사용하는 SNS(유튜브, 인스타, 틱톡 등)가 제각각이다. 이를 별도 테이블로 1:N 관계를 맺기에는 오버헤드가 크다고 판단했다.
- 결정: PostgreSQL의 강력한 기능인 JSONB 타입을 사용하여 SNS 링크 정보를 저장하기로 했다. 추후 PG사의 비정형 결제 응답 데이터(Raw Data)를 저장할 때도 이 방식을 사용하여 스키마 변경 없이 유연하게 데이터를 적재할 예정이다.
② 결제 로그의 불변성 (Immutability) 돈과 관련된 데이터는 수정되어서는 안 된다.
- 고민: 주문(Orders) 테이블의 상태(status) 값만 변경하는 방식은 부분 취소나 수수료 변동 이력을 추적하기 어렵다.
- 해결: Payment_logs라는 별도의 원장 테이블을 설계했다. 이 테이블은 절대 UPDATE 하지 않고, 환불이나 변경 사항이 발생하면 음수 금액 등을 포함한 새로운 로그를 INSERT 하는 방식으로 설계하여 정산의 무결성을 보장했다.
③ 유저와 크리에이터의 데이터 분리 모든 유저가 크리에이터는 아니다. Users 테이블에 정산 계좌 같은 크리에이터 전용 컬럼을 넣으면 NULL 값이 불필요하게 많아진다. 따라서 Users와 Creators 테이블을 1:1 식별 관계로 분리하여 데이터 희소성(Sparsity) 문제를 해결했다.

3. Spring Boot 초기 설정: 생산성을 높이는 패턴 적용
본격적인 비즈니스 로직 구현에 앞서, 반복되는 코드를 줄이고 협업 효율을 높이기 위한 공통 클래스들을 구현했다.
① JPA Auditing을 이용한 생성/수정일 자동화 (BaseTimeEntity) DB 테이블마다 공통적으로 들어가는 created_at, updated_at 컬럼을 매번 수동으로 setter를 통해 넣는 것은 번거롭고 실수하기 쉽다. JPA Auditing을 적용하여 이를 자동화했다.
@Getter
@MappedSuperclass // 상속받는 자식 엔티티에게 매핑 정보만 제공
@EntityListeners(AuditingEntityListener.class) // JPA Auditing 활성화
public abstract class BaseTimeEntity {
@CreatedDate
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}
Tip: 이 기능이 동작하려면 메인 애플리케이션 클래스에 @EnableJpaAuditing 어노테이션을 반드시 추가해야 한다.
② 통일된 API 응답 포맷 (ApiResponse) 프론트엔드와의 원활한 통신을 위해 성공/실패 여부에 관계없이 항상 동일한 JSON 구조를 반환하도록 래퍼(Wrapper) 클래스를 만들었다.
@Getter
private final String status; // "SUCCESS" or "ERROR"
private final String message; // 응답 메시지
private final T data; // 실제 데이터 (없으면 null)
// 성공 시 호출
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>("SUCCESS", "요청이 성공적으로 처리되었습니다.", data);
}
// 성공 시 메시지 커스텀
public static <T> ApiResponse<T> success(String message, T data) {
return new ApiResponse<>("SUCCESS", message, data);
}
// 실패 시 호출
public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>("ERROR", message, null);
}
private ApiResponse(String status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
}
이제 컨트롤러에서는 return ApiResponse.success(dto); 형태로만 반환하면 되므로, API 일관성이 크게 향상될 것이다.
4. 마치며 & 다음 계획
단순한 CRUD 게시판이 아닌, 실제 비즈니스 요구사항(정산, 조합 등)을 수용할 수 있는 DB를 설계하는 과정에서 많은 것을 배울 수 있었다. 다음에는 설계한 도메인별 패키지 구조를 잡고, 실제 JPA Entity 클래스들을 구현하여 애플리케이션의 뼈대를 완성할 예정이다.
그리고 오늘 설계하고 구현했던 것이 무조건 맞지 않다는 것을 알고 있다. 문제가 있거나 다른 방식으로 더 좋은 방향으로 수정이 가능한 부분을 계속 찾아보고 적용해서 이 프로젝트를 실제 론칭하는 목표를 달성하도록 노력할 것이다.
'사이드프로젝트' 카테고리의 다른 글
| [사이드 프로젝트] 디지털 굿즈 플랫폼 개발기 #6 - Spring Security + JWT + Redis로 구축하는 강력한 인증 시스템 (1편: 설계와 설정) (0) | 2026.01.19 |
|---|---|
| [사이드 프로젝트] 디지털 굿즈 플랫폼 개발기 #5 - Controller 구현과 입력값 유효성 검증 (0) | 2026.01.15 |
| [사이드 프로젝트] 디지털 굿즈 플랫폼 개발기 #4 - Service 계층 구현과 Mockito 단위 테스트 (0) | 2026.01.15 |
| [사이드 프로젝트] 디지털 굿즈 플랫폼 개발기 #3 - Repository 계층 구현과 테스트 코드 작성 (0) | 2026.01.14 |
| [사이드 프로젝트] 디지털 굿즈 플랫폼 개발기 #2 - JPA 엔티티 설계와 기술적 디테일 (1) | 2026.01.14 |
