SpringBoot에 PostgreSQL과 MongoDB 연결하기
연결이유
RDS를 사용한지 어엿.. 3주밖에 되지 않았으나 3만원 결제 통보를 받고 말았다.. 💸
RDS는 자동 백업 기능,, 데이터베이스 복원을 위해 스냅샷을 생성 ,, 등의 이슈로 생각보다 비싸다고 한다. 🙄 그래서 갓선배님의 조언으로 마이그레이션을 해보기로 했다 ! (사실 서비스 전이라 엄밀히 따지면 마이그레이션 아님)
암튼 application.yml 과 함께라면 처음 써보는 PostgreSQL DB 연동도 두렵지 않기 때문에 넉넉한 프리티어를 제공해주는 Supabase PostgreSQL을 도입하게 되었다.
Supabase PostgreSQL 연동
우선 이 글 을 통해 DB를 만들고, 아래 DB Settings 를 참고하여 Datagrip과 연동해주면 된다. ..끝 ! JDBC Template 은 위대해 ✨
postgreSQL 의존성을 추가해준다.
// DB - postgresql
implementation 'org.postgresql:postgresql:42.6.0'
application.yml 파일로 DB를 연동한다.
spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://db.tlps***.supabase.co:5432/postgres #?serverTimezone=Asia/Seoul&useSSL=false&characterEncoding=utf8&allowPublicKeyRetrieval=true
username: ${POSTGRES_DB_USERNAME}
password: ${POSTGRES_DB_PW}
jpa:
database-platform: postgres
show-sql: true
hibernate:
ddl-auto: create
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
generate-ddl: false
MongoDB 연동
추가로 NoSQL도 연동해보았다.
프로젝트 기능 중에 학생별로 푼 문제들을(50개~1000개 가량) 매주 싹 긁어오는 API가 필요하다. 예를 들어 이런 형태다.
{
'name': '20wjsdudtj',
'week': '1',
'solvedBaekjoon': '1000, 1001, 1002, 1004',
}
{
'name': '20wjsdudtj',
'week': '2',
'solvedBaekjoon': '1000, 1001, 1002, 1004, 19283, 24523',
}
한 학생이 푼 문제들을 리스트 형태로 가져오게 되는데, RDB 형태에서 굳이 문제 Table을 만들어 학생들과 문제들을 N:M 매핑할 필요성은 없다고 판단하였다. 문제가 3만 개 이상일뿐만 아니라 계속해서 갱신되는(문제들이 만들어지는) 형태이고, 문제를 이용한 기능은 존재하지 않기 때문이다.
🤔 문제들을 RDB table에 구분자(,) 형태로 나열해 저장할까 하다가 이 참에 NoSQL을 사용해 연동해보기로 하였다.
MongoDB ( NoSQL ) 란?
mongoDB는 데이터를 도큐먼트 형태로 저장하는 도큐먼트 데이터베이스이다. 아래와 같은 형태로, 도큐먼트는 JSON 형태의 데이터로 구성된다.
CRUD 또한 그대로 JSON을 넣는 형태이며, insert / find 명령어로 POST/GET 요청을 손쉽게 할 수 있다.
//Post 요청
db.users.insert({
name: '홍길동',
age: 20,
});
//Get 요청
db.users.find(); // 출력 - { "_id" : ObjectId("6239sdf39..."), "name":"홍길동", "age": 20}
NoSQL을 RDB와 같이 쓰면 좋지 않은 부분이 바로 JOIN 연산이다.
RDB는 Row/Col의 table 형태이고, NoSQL은 key-value 형태이므로 둘을 JOIN 하는 개념으로 사용할 수 없다.
따라서 나는 기존에 Entity(DB Table)로 할당되어있던 History의 특정 컬럼(List 형태로 들어가던 solved_backjoon_week) 만을 NoSQL로 분리하고, 이 때 JOIN은 아니지만 History id 값을 key값으로 사용하여 둘을 동기화 하기로 하였다.
mongoDB Repository 구현
우선 mongodb를 사용하기 위한 의존성을 추가해준다.
//DB - mongodb
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
implementation 'org.postgresql:postgresql:42.6.0'
SpringApplication 구성 클래스 경로에 @EnableMongoRepositories 어노테이션을 붙여 MongoDB 를 사용하도록 설정하자.
src/main/java/com/example/demo/DemoApplication.java
@SpringBootApplication
@EnableMongoRepositories
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
///
@Entity 어노테이션이 아닌 @Document 어노테이션을 이용해 MongoDB의 '문서 클래스'임을 나타내자.
src/main/java/com/example/demo/domain/BaekjoonHistory.java
@Document(collection="history")
@Getter @Setter @AllArgsConstructor
public class BaekjoonHistory {
@Id
private Long id;
private List<Integer> solvedBaekjoon = new ArrayList<>();
}
JPARepository가 아니라 MongoRepository를 사용한다. MongoRepository를 통해 JPARepository와 마찬가지로 기본 CRUD 생성, 커스텀 쿼리 생성 등을 동일하게 사용할 수 있다.
@Repository
public interface BaekjoonHistoryRepository extends MongoRepository<BaekjoonHistory, Long> {
}
아래와 같이 MongoDB 에서 데이터를 조회해보면 잘 저장된 것을 볼 수 있었다.
리뷰
구현 도중 RDB와 NoSQL을 함께 쓰는 구조가 과연 옳을까? 에 대한 고민이 본능적으로 계속 들었다. 결론적으로 말하면 확장 측면에서 봤을 때, "이 NoSQL이 RDB와 JOIN할 일이 있을까?" 라는 질문에 "절대 없을 것 같다"가 아니라면 NoSQL을 덤으로 쓰는 것은 그리 좋은 방식은 아닌 것 같다.
물론 현재 아키텍처에서는 1. NoSQL을 써보고 싶다.라는 욕구 충족과 2. DB 분산으로 인한 비용/접근 시간 감소 라는 장점이 있지만 '유지 보수' 관점에서 큰 서비스로 발전하고자 한다면 단점이 장점에 상회한다. 하지만 현재 상황에서 문제들을 조인할 일은 없을 것이므로 괜찮다 !😬
이후 서비스 이후 들었던 비용에 대해 이 글에 나중에 정리해봐야겠다.
'BackEnd' 카테고리의 다른 글
[DB] @Transactional 이해하고 사용하기 (0) | 2024.01.17 |
---|---|
[Java|Spring|JSP] Koala 출석부 제작 후기 ..그리고 보완 방향! (0) | 2024.01.16 |
[ELK|Python] REST API로 ES에 데이터 색인하기 (0) | 2024.01.04 |
[크롤링|Lambda] AWS Lambda를 이용해 백준, 티스토리 크롤링하기 (2) | 2024.01.02 |
[API] URL/URI 와 RESTFul API 설계 방법 (3) | 2023.12.20 |