프로젝트/오운완

Spring Boot + R2DBC(Mariadb) + Next 연동

jh280722 2024. 4. 11. 00:24

Spring R2DBC 연동

gradle 설정

먼저 R2DBC + mariadb를 사용하기 위해 spring data r2dbc 와 r2dbc-mariadb(db driver) 를 gradle에 추가한다.

    implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
    runtimeOnly("org.mariadb:r2dbc-mariadb")

Config 추가

repository를 사용할 것이므로 AppConfig에 @EnableR2dbcRepositories 어노테이션을 추가한다.
R2dbc audit은 JPA audit과 유사하다. @CreatedBy, @CreatedDate, @LastModifiedBy, @LastModifiedDate annotation을 활성화시켜준다.

https://docs.spring.io/spring-data/relational/reference/r2dbc/auditing.html

 

import org.springframework.context.annotation.Configuration;
import org.springframework.data.r2dbc.config.EnableR2dbcAuditing;
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;

@Configuration
@EnableR2dbcRepositories
@EnableR2dbcAuditing
class AppConfig {
}

Repository 추가

Account 모델에 대한 Repository를 구성한다. ReactiveCrudRepository<T, ID> 를 상속해서 인터페이스를 만들면 된다.

public interface AccountRepository extends ReactiveCrudRepository<Account, Long> {
}

모델(엔티티) 추가

@Data
@AllArgsConstructor
public class Account {
    @Id
    private Long id; // @id 어노테이션으로 PK를 지정한다.
    @NonNull private String nickname;
    private String kakaoId;
    private String googleId;

    public static Account fromNickname(String nickname) {
        return new Account(null, nickname, null, null);
    }
}

Account 모델을 만들어서 R2dbc와 연결해준다.
@Data 로 getter, setter 등을 추가하고
@AllArgsConstructor로 생성자를 추가한다.
위의 단순한 보일러플레이트 코드를 lombok으로 편하게 추가할 수 있다.

그리고 팩토리 메소드를 생성하여 nickname을 통해 새로운 계정을 생성할 수 있도록 하였다.(테스트 용)

DB 관련 어노테이션 관련 설명

  • @Table annotation을 통해 매핑할 데이터베이스 table을 설정
  • @Id annotation을 통해 Primary key를 설정한다.
  • @Column annotation을 통해 Table column을 설정한다. 위 예제에서 사용되지 않았듯, 테이블 필드 이름과 동일하다면 사용하지 않아도 된다.
  • @CreatedDate, @LastModifiedDate 는 위에서 설명했듯 Audit annotation으로 R2dbc에서 audit을 활성화하면 자동으로 값을 넣어준다.

지원하는 default field type에 대한 내용은 여기를 참조하자.

이 때, @Id annotation 설정 필드는 값을 할당하느냐 안하느냐로 SQL query가 달라진다.

  • @Id 필드가 null인 상태로 save 메서드를 실행하면, Insert statement가 실행된다.
  • @Id 필드가 값이 설정된 상태로 save 메서드를 실행하면, Update statement가 실행된다.

그리고 r2dbc, jpa 모두 orm 역할을 하지만 DB 마이그레이션까지 해주지는 않는다.
해당 기능을 해주는 도구로는 https://flywaydb.org/ 를 사용하면 된다.

Service, Controller, DTO 구성

아래와 같이 Reactive하게 Service, Controller, Dto를 구성하면 이제 모든 연동이 완료된다.
하나씩 차근차근 살펴보자.

DTO

@Data
public class AccountDto {
   Long id;
   String nickname;
   String kakaoId;
   String googleId;
}

단순한 BODY를 받아오기 위한 DTO이다.
@Data 어노테이션을 써서 보일러 플레이트 코드를 제거하였다.

Service

@Service
public class AccountService {
    private final AccountRepository accountRepository;

    public AccountService(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
    }

    public Mono<Account> createAccountByNickname(String nickname) {
        return accountRepository.save(Account.fromNickname(nickname));
    }
}

생성자 주입을 통해 앞에서 만든 AccountRepository를 빈으로 주입해준다.
그리고 nickname을 통해 계정을 생성하여 accountRepository.save를 호출한다.

ReactiveCrudRepository를 상속받고 있으므로 반환값이 Mono를 지정해야한다.

Controller

@RestController
@RequestMapping("/v1/accounts")
public class AccountController {
    private final AccountService accountService;

    public AccountController(AccountService accountService) {
        this.accountService = accountService;
    }

    @PostMapping()
    public Mono<Account> createAccountByNickname(@RequestBody AccountDto accountDto) {
        return accountService.createAccountByNickname(accountDto.getNickname());
    }
}

디비 설계

프로젝트의 기본 DB 구조를 구성해봤다. 추후 확장 가능성은 있지만 필수 기능을 먼저 구현하기 위한 디비를 구성하였다.

ER Diagram

Layered Architecture 구성

백엔드 프로젝트의 아키텍쳐로는 레이어드 아키텍쳐를 선택했다.

기본적으로 중/대규모 서비스에서 많이 사용하고 (카카오도...) 간략한 장단점은 아래와 같다.

장점

  • 유지보수성: 각 구성 요소가 명확한 역할을 가지므로 코드의 이해와 수정이 쉬움
  • 재사용성: 특히 서비스와 리포지토리 계층은 다른 컨텍스트에서도 재사용될 가능성이 높음
  • 확장성: 애플리케이션의 각 부분이 독립적으로 확장될 수 있어, 새로운 기능 추가나 변경이 용이
  • 테스트 용이성: 각 계층이 분리되어 있어, 단위 테스트나 통합 테스트를 수행하기 쉬움

단점

  • 초기 개발 속도: 초기 프로젝트 구조 설정에 시간이 소요되며, 작은 프로젝트의 경우 과도한 설계일 수 있음
  • 복잡성: 각 계층 간의 상호작용이 복잡해질 수 있어, 프로젝트의 크기가 커질수록 관리하기 어려움
  • 오버헤드: 각 계층을 거칠 때마다 추가적인 호출이 발생하여 성능에 영향을 줄 수 있음

아래는 DB 연동 작업 및 실제 호출까지 테스트를 마친 프로젝트의 구조이다.

Layered Architecture